This is just a very short message to say that I have enhanced the "dechap" tool mentioned in my previous post. In addition to the original PPPoE support it can now extract and attack CHAP authentications sniffed from RADIUS and L2TPv2 protocols.
The syntax remains exactly the same and it should "just work". The code is available to download at https://www.github.com/theclam/dechap. Please post a comment if you have any feedback or suggestions.
Showing posts with label bruteforce. Show all posts
Showing posts with label bruteforce. Show all posts
Tuesday, 22 January 2013
Monday, 21 January 2013
Recovering CHAP Passwords from Sniffed PPPoE Sessions
In a previous blog post I outlined the theory behind setting up a PPPoE session including PPPoE discovery, LCP, NCPs and, more relevant to this post, the basics of CHAP authentication. At the time I was writing the post I wondered how easy it would be to work back from the CHAP messages on the wire to the original credentials, so I decided to find out.
As a reminder (or a very quick introduction), the CHAP process works something like this:
I wanted the tool to be flexible with regards to encap. Since I work primarily on carrier networks, I get really frustrated by tools that do a job perfectly but only accept untagged, unencapsulated frames. Once you have a packet capture in your hand, realising that it can't be used because it has two VLAN tags and a pair of MPLS labels is a nuisance.
The approach that seemed most sensible was to build a recursive decap function which would take in a (partial) frame plus a "hint" as to what type of header to expect. The function would then check for and record any matching criteria present (i.e. MACs for Ethernet, VLAN ID for 802.1Q - more on this in the next section) before either returning or calling itself on the remainder of the packet with a "hint" derived from the current header.
The initial call to the function passes the entire frame with an "Ethernet" hint. In the Ethernet header, the source and destination MAC addresses are read and stored. The EtherType field contains 0x8100, indicating an 802.1Q VLAN header is next. The function calls itself against the contents of the frame from byte 15 and on with a hint of "VLAN".
Now the function reads and stores the VLAN ID. Since this is the first VLAN we have seen it is stored as the C-VLAN for now. The EtherType is, again, 0x8100 so the function calls itself against bytes 5 and onward using a hint of "VLAN".
Again, the function reads and stores the VLAN ID. Since this is not the first VLAN tag found, the previously known VLAN ID is moved into the S-VLAN field and the value from the frame is stored in the C-VLAN field. This time the EtherType is 0x8864, indicating a PPPoE session header follows. The function calls itself against bytes 5 and onwards using a hint of "PPPoE".
The function now reads and stores the PPPoE session ID (SID). The only valid thing to follow a PPPoE session header is a PPP header, so the function calls itself on bytes 7 and onward, using a hint of "PPP".
The function now simply checks that the protocol ID in the PPP header is 0xC223 for CHAP. If so, it calls itself one last time against bytes 3 and onward using a hint of CHAP.
Finally we are down to the payload. The CHAP message type is checked and:
In order to be considered a challenge / response pair, I decided the following criteria must match:
Additionally, the thought occurred that even with the above details matching, there may be more than one challenge / response pair for the same PPPoE session so a response would have to be paired with the most recent challenge for which the criteria matched. In the program this is achieved by working backwards through the linked list, starting at the response, until a match is found. Data from matching challenge / response pairs are stored in another list for later consumption. If the search reaches the beginning without a matching challenge being found then the response cannot be used and is ignored.
For each challenge / response pair in the list, the next step is to cycle through a list of password guesses. Each candidate password is combined with the authentication ID and challenge data from the captured authentication and hashed. The resulting hash is compared to the one from the captured response and, for those that match, a correct guess is reported. If no password generated a matching hash then the word list does not contain the correct password and this is also reported back.
Provided the OpenSSL dev libraries are installed it should be possible to simply extract the source code, cd into the directory then run "make".
In the future I may add the capability to pull the auths from L2TP or RADIUS interactions but for now only PPPoE is supported. It also assumes that Ethernet control words are not present in MPLS encapsulated traffic.
The usage is pretty straightforward - there are only two parameters and both are mandatory. Specify your capture file (original pcap format) with the -c flag and your word list with the -w flag. Here's an example:
lab@lab:~/dechap$ ./dechap -w mywords.txt -c someauths.cap
Found password "tangerine" for user user1@testisp.com.
Unable to find a password for user user2@testisp.com.
Found password "password1" for user user3@testisp.com.
Found password "Africa" for user user4@testisp.com.
Found password "Frankenstein" for user user5@testisp.com.
lab@lab:~/dechap$
Considering that I've made no effort at all to make the code efficient, I've found the speed pretty good. On my '90s PC, a worst-case run (i.e. where no passwords are found) against 800 auths with 100k candidate passwords, a run still completes inside a minute. I don't think that's bad for parsing 15,000 packets and running 80 million concatenate - hash - compare sequences.
If you try this out, please leave a comment on this post with your experiences - good or bad.
Recap of CHAP Theory
As a reminder (or a very quick introduction), the CHAP process works something like this:
![]() |
| CHAP Authentication |
- The party requiring the opposite peer to authenticate (i.e. "server") sends a CHAP challenge message containing a challenge ID and some unpredictable "random" data.
- The party being authenticated (i.e. "client") concatenates the authentication ID, the password and the challenge data into a single unit, then generates an MD5 hash of that. The resulting hash, plus the client name (user ID or hostname) is passed to the server as a CHAP response.
- The server compares the incoming hash to the value it obtains by performing the same calculation locally and returns a CHAP success or CHAP failure message.
The Attack in Practice
While the process is intuitively simple, as usual there are a few corner cases to cover. Recovering CHAP authentications from a capture file full of other junk requires a certain amount of processing logic, then responses must be re-united with their corresponding challenges before they can be attacked.Gathering CHAP Packets
I wanted the tool to be flexible with regards to encap. Since I work primarily on carrier networks, I get really frustrated by tools that do a job perfectly but only accept untagged, unencapsulated frames. Once you have a packet capture in your hand, realising that it can't be used because it has two VLAN tags and a pair of MPLS labels is a nuisance.
The approach that seemed most sensible was to build a recursive decap function which would take in a (partial) frame plus a "hint" as to what type of header to expect. The function would then check for and record any matching criteria present (i.e. MACs for Ethernet, VLAN ID for 802.1Q - more on this in the next section) before either returning or calling itself on the remainder of the packet with a "hint" derived from the current header.
Worked Example
Let's process the following frame as an example. Data in black are used by the algorithm while data in grey are not.The initial call to the function passes the entire frame with an "Ethernet" hint. In the Ethernet header, the source and destination MAC addresses are read and stored. The EtherType field contains 0x8100, indicating an 802.1Q VLAN header is next. The function calls itself against the contents of the frame from byte 15 and on with a hint of "VLAN".
Now the function reads and stores the VLAN ID. Since this is the first VLAN we have seen it is stored as the C-VLAN for now. The EtherType is, again, 0x8100 so the function calls itself against bytes 5 and onward using a hint of "VLAN".
Again, the function reads and stores the VLAN ID. Since this is not the first VLAN tag found, the previously known VLAN ID is moved into the S-VLAN field and the value from the frame is stored in the C-VLAN field. This time the EtherType is 0x8864, indicating a PPPoE session header follows. The function calls itself against bytes 5 and onwards using a hint of "PPPoE".
The function now reads and stores the PPPoE session ID (SID). The only valid thing to follow a PPPoE session header is a PPP header, so the function calls itself on bytes 7 and onward, using a hint of "PPP".
The function now simply checks that the protocol ID in the PPP header is 0xC223 for CHAP. If so, it calls itself one last time against bytes 3 and onward using a hint of CHAP.
Finally we are down to the payload. The CHAP message type is checked and:
- For challenges, the authentication ID, challenge length and challenge data are stored.
- For responses, the authentication ID, response and client name are stored.
Pairing Up
A CHAP response must be paired up with its respective CHAP challenge, otherwise the maths don't work. In real life there may be several authentications in progress at one time across multiple PPPoE sessions, possibly over multiple different VLANs. Often the CHAP authentication ID is only unique within a PPPoE session. Similarly, the PPPoE session ID only needs to be unique within a broadcast domain so these are often re-used across VLANs. Care must be taken to ensure that the challenge and response really do belong together.In order to be considered a challenge / response pair, I decided the following criteria must match:
- Server and Client MACs
- S & C VLAN IDs (if present)
- PPPoE SID
- CHAP authentication ID
Additionally, the thought occurred that even with the above details matching, there may be more than one challenge / response pair for the same PPPoE session so a response would have to be paired with the most recent challenge for which the criteria matched. In the program this is achieved by working backwards through the linked list, starting at the response, until a match is found. Data from matching challenge / response pairs are stored in another list for later consumption. If the search reaches the beginning without a matching challenge being found then the response cannot be used and is ignored.
Brute Force Password Guessing
For each challenge / response pair in the list, the next step is to cycle through a list of password guesses. Each candidate password is combined with the authentication ID and challenge data from the captured authentication and hashed. The resulting hash is compared to the one from the captured response and, for those that match, a correct guess is reported. If no password generated a matching hash then the word list does not contain the correct password and this is also reported back.
Downloading the Tool
The C source code may be downloaded from: https://github.com/theclam/dechapProvided the OpenSSL dev libraries are installed it should be possible to simply extract the source code, cd into the directory then run "make".
In the future I may add the capability to pull the auths from L2TP or RADIUS interactions but for now only PPPoE is supported. It also assumes that Ethernet control words are not present in MPLS encapsulated traffic.
Using the Tool
The usage is pretty straightforward - there are only two parameters and both are mandatory. Specify your capture file (original pcap format) with the -c flag and your word list with the -w flag. Here's an example:
lab@lab:~/dechap$ ./dechap -w mywords.txt -c someauths.cap
Found password "tangerine" for user user1@testisp.com.
Unable to find a password for user user2@testisp.com.
Found password "password1" for user user3@testisp.com.
Found password "Africa" for user user4@testisp.com.
Found password "Frankenstein" for user user5@testisp.com.
lab@lab:~/dechap$
Considering that I've made no effort at all to make the code efficient, I've found the speed pretty good. On my '90s PC, a worst-case run (i.e. where no passwords are found) against 800 auths with 100k candidate passwords, a run still completes inside a minute. I don't think that's bad for parsing 15,000 packets and running 80 million concatenate - hash - compare sequences.
If you try this out, please leave a comment on this post with your experiences - good or bad.
Subscribe to:
Posts (Atom)

![[Ethernet][VLAN][VLAN][PPPoES][PPP][CHAP]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzfeDezRzU1M15L9m5rSWEMQbsWN2qoJ4u1_NZIL-i3RsQ4qYFZJvnkgKcZ9VuPn8wjfE8dYnkEmlrY77DkLL313SJsEZQlVQSwxvtDA6xxx7QmCqBhtG-94ZCWSDeVXz3bo_zyh0cnkd2/s1600/PPPoE+Worked+Example+1.gif)
![[VLAN][VLAN][PPPoES][PPP][CHAP]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-h01a8WlvcLuqpupQ33mQJ77DWnnofLUk3Hh66IpHv2DqTqgOEwWXI8t8aH8iunVjpj93UoMT4QBtRJdqkiEqBAHRHUKrxJtBKw4WilfbfWEgIiF-d7x4X3xW1uJWKRp-8ZegtPoi-Q8e/s1600/PPPoE+Worked+Example+2.gif)
![[VLAN][PPPoES][PPP][CHAP]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6SSNtBWBTZ8voJHLSiTgPNqmbhvMzpwbuWEVNXnDKA0TqFAb5oom58vokYvSK1VMGoqz3sqzrLRc4HDVufSYjvyFBeIOYMMBZ0AUIgpGXMbZNI-1kUs2C_u6qkD1FLC9Dt4k42IvOQ7Ra/s1600/PPPoE+Worked+Example+3.gif)
![[PPPoES][PPP][CHAP]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSFZ_enW7Ega8IOKiocuUATjDlnpxiVEXHt7GzR7rWz6LdWRSid3ROClHx9rCehHKM4KBB66r4DEeZzXL9fMm2lKnDEgQq_V31Rf0Fqp1DhL26iEomyfTiQ62T6IMGUJld4GSMiZrmLLzq/s1600/PPPoE+Worked+Example+4.gif)

![[CHAP]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdYsG-5BT15ZDWIbwXlXkmlgQ7mrS_o-0yxEUJXSFsql6enQu3H1IJvB6EGCaUEpJatcEUSodzLgym4OszC2P4TjNcQ7DUxlPHGoZuSAUrH2oUxh13K3-UQrVWqnUCC5S85-kneC1MNKwa/s1600/PPPoE+Worked+Example+6.gif)