William – WPA/WPA2 4-way handshake extraction script

Download v.0.1

If you’ve ever tested Aircrack against a packet capture containing a handshake from a network whose WPA/WPA2 passphrase is known, you may have sometimes frowned at the “Passphrase not in dictionary” message. One possibility for this is noted on the (excellent) Aircrack website – http://www.aircrack-ng.org/doku.php?id=aircrack-ng#wpa_wpa2_handshake_analysis_fails. Essentially Aircrack hasn’t parsed the handshake from the capture correctly because there is too much “noise”. If Aircrack picks packets from different 4-way handshake exchanges then the passphrase will not be found, even if it’s in the dictionary. A tool called “wpaclean” (which is included in Backtrack) tidies up four-way handshake captures but, in my experiments, it didn’t always work so I wrote an alternative clean-up script, called William, that gives you more control. The usage is:

william.sh <output_file> <input_file> [<input_file_2>...] [<mth_packet_2> [<nth_packet_1>]] [-f | -a] [-A <dict_file>] [-d]

m mth second packet of 4-way handshake (default 1)
n nth first packet of 4-way handshake that precedes the chosen second packet (default 1)
-f seek first message from start of capture working forwards
-a pair up ALL message 1s that precede the chosen message 2 (multiple output files)
-A run aircrack-ng against output files using the supplied dictionary file
-d don't skip duplicate packets
--help extra help

William is a bash script. If I’d known how it would turn out, I would have chosen another programming route, but never mind, it still works as a proof of concept. Let me explain how it works, which in turn will explain what the switches do. Here’s a messy capture file from attempts to crack the WPA2 key of a client using Airbase as a rogue access point. (In my experiments, using Airbase against clients as opposed to sniffing legitimate 4-way handshakes tended to make noisier capture files.) As a client-side attack, only the first 2 of the 4 messages in the 4-way handshake were captured (but that’s enough for Aircrack to work on):


The first EAPOL frame is selected, which Wireshark informs us is the first of the 4 messages in the 4-way handshake. Note the “WPA Key Nonce” value. Now let’s move to the second EAPOL frame:


The “WPA Key Nonce” is exactly the same. So the packet was either transmitted twice or recorded twice, which can happen when you’re sniffing on an interface that’s also engaged in active testing. Let’s move to the third EAPOL frame:


Now the “WPA Key Nonce” is different, so this constitutes a second attempt at sending a first handshake message. Now let’s look at the first instance of a second message in the 4-way handshake:


It turns out that there have been 14 unique first packets sent up to this point so the question is, which of them does this second message correspond to? Park that problem for now and note the “WPA Key MIC” value above instead. Let’s move to the next second message:


It’s apparently the same – no surprise, given what we saw earlier. But it turns out the third instance of the second handshake message is the same too.  And so is the fourth, the fifth, the sixth…When all these first and second handshake messages are mixed up, it’s no wonder that Aircrack can sometimes produce false negatives. When I ran wpaclean it picked the 14th (i.e. the last) first message and the 1st second message. In other words it picks the first instance of a second message and works backwards to pick the first handshake message. Perfectly reasonable – but in this particular case Aircrack failed to find the passphrase from this pairing. Like Aircrack, wpaclean’s logic is fixed, so I wrote William to give you more options. The default is that it works just like wpaclean, as there’s nothing wrong with its logic. If you use the -f switch, however, William picks the first handshake message by working forwards from the start of the capture.

The n and m switches give you the freedom to try out your own pairings. If you run William with a single number 2 (i.e. william.sh <out_file> <in_file> 2) it picks the 2nd instance of a second handshake message and works backwards to find the first handshake message. The second message is always counted forwards from the start of the capture file. If you run William with the numbers 2 4, it picks the 4th first handshake message working backwards from the 2nd instance of a second handshake message. Adding -f to this would count the 4th first message from the start of the capture file. The –help switch gives more examples.

If you think about it, the second message of the handshake must correspond to one of the first messages that preceded it (assuming the capture is complete). So William informs you of how many unique first messages there were as a guide. If you select the -a option, William will generate capture files that pair the chosen second message with all the preceding first messages (working backwards from the second message), each file containing a different pairing. This brute-force method should succeed, assuming the passphrase is in your dictionary, but it will be more time-consuming.


If you use the -A switch followed by the path to a dictionary file, William will run all output files through Aircrack automatically. It’s especially worth running this with -a because William will run Aircrack as the message pairs are written to file, so you may find the passphrase without having to wait for all the combinations to be generated. A successful screenshot follows:



As noted above, it’s possible that your capture file contains duplicate messages. By default, duplicates are ignored when counting the packet positions. If you use the -d option, duplicates are not ignored, i.e. the nth first packet and mth second packet are counted based entirely on their order of appearance in the file. As before, the second message is always counted forwards from the start of the capture file and the direction in which the first message is counted is set by the presence or absence of -f. The -d option won’t have much practical effect, it’s really just for experimentation.


When Aircrack parses a capture file, it first looks for the SSID of the network(s) whose traffic has been sniffed, and if it doesn’t find any it gives up. Why? Because the SSID is used with the passphrase to generate the Pairwaise Master Key. So even if you have the correct first and second handshake packets, without the correct SSID Aircrack will fail. William looks for packets that disclose the SSID based on the BSSID of the chosen second EAPOL message. If for some reason you’re unlucky enough to miss this, the script will ask you for a SSID and create a fake probe response packet to satisfy Aircrack.


Multiple input files can be specified. If you’ve used Airodump with –write wpa, for example, it’s best to run William with the input wpa*.cap.


The output file is the first parameter. If the -a switch has been used, the output filenames include a number to distinguish them. The number is an ordinal reflecting the position of the first message in the capture file so, as William works backwards from the second message, the number starts high and counts down to 1.


On a pentest job, outside the comfort of a known test environment, if Aircrack tells you the password isn’t in the dictionary, you have to believe it. Hopefully this posting will give you a few more ideas and you may want to run a few iterations of William just in case. I would suggest the following order, using -A <dictionary_file> each time:

  1. Try REVERSE mode to mimic wpaclean (default, don’t specify -f or -a)
  2. Try FORWARD mode with -f
  3. Run ALL mode with -a


Download v.0.1


This script is an extension of wpaclean and owes everything to the fantastic Wireshark command-line toolset, specifically tshark, mergecap and text2pcap.

23 thoughts on “William – WPA/WPA2 4-way handshake extraction script

    1. Jerome Post author

      No, there’s no way to know the length of the pre-shared key by examining the 4-way handshake. Prior to the handshake, the passphrase is converted to a fixed-length blob called the Pairwise Master Key (PMK) using the PBKDF2 algorithm, which for both TKIP and CCMP spits out 256 bits no matter what size the passphrase is.

  1. mARK



    william.sh [...] [ []] [-f | -a] [-A ] [-d]


    ./william.sh will.cap -a -A wordlist.lst


    1. Jerome Post author

      You don’t need two input files, the usage begins:

      william.sh <output_file> <input_file> [<input_file_2>...]

      The first parameter is an output file. This can be called anything – temp.cap, foo.cap, bar.cap – the .cap extension just helps to classify it as a capture file. From the input file or files, which typically come from Airodump, the first and second messages of the 4-way handshake are extracted and put in the output file. This file can then be sent to a cracking tool (and opened in Wireshark to see what’s happened). With -a set, where William extracts multiple pairs of first and second handshake messages, multiple output files are produced so a number is added into the output filename. With -A set, the output file(s) are fed into Aircrack.

      So your example command will fail because William expects at least two filenames – the first is for output and all the others are treated as input files.

  2. Tester

    it does not work for me. I tested different files.

    sudo bash ./william.sh wpa-output.cap XXX.cap
    Mode=REVERSE m=1 (second message) n=1 (first message) ignoring duplicates
    Using packet 85971 as second EAPOL packet
    CCMP network identified
    Client station is XX:XX:XX:XX:XX:XX
    Determined SSID from beacon frame (packet number 1)
    The SSID is XYZ
    There are 1 first messages before the chosen second message
    Using packet 85966 as first EAPOL packet
    Writing packets to wpa-output.capmergecap: Can’t open SSID.tmp.cap: No such file or directory

    Deleting temp files
    rm: cannot remove `eapol.1.tmp.cap’: No such file or directory
    rm: cannot remove `eapol.2.tmp.cap’: No such file or directory
    rm: cannot remove `SSID.tmp.cap’: No such file or directory
    Now run “aircrack-ng wpa-output.cap -w ”


    1. Jerome Post author

      Hi “Tester”
      Obviously the problem is with “SSID.tmp.cap”, which is a temporary file it creates. Judging from the output the script had no problem finding the SSID so I don’t know why the file is missing. Since you’re running with sudo you should be able to write to the directory. The line that writes the file is tshark -r in.tmp.cap -R "frame.number == ${SSID[0]}" -F libpcap -w SSID.tmp.cap 2>/dev/null so try removing the redirection of STDERR (i.e. 2>/dev/null) to see if that gives you some clues.
      Might also be worth clearing out the tmp files between attempts. If you note which tmp files have been created it will give you an idea of how far the script has got too.
      You’ve obfuscated some of the output – double-check they make sense with the network you’re running the tool against.
      Since you’re running the tool with the defaults, the script does nothing different to wpaclean so maybe try that too.
      Good luck.

  3. Tester

    Hello again! In my last post I said that the script does not work for me. I posted the error-result.
    Can u help me? Why was that post deleted?

    1. Jerome Post author

      Just to say it wasn’t deleted, it was awaiting approval – and I’ve been away recently. Hope my reply helps.

  4. Dave

    Hi and thank you very much for this tool :)

    I have wasted many hours / days and a lot of electricity testing what I later found out to be bad captures.

    I am a n00b with all this but I wondered if there is a method to make sure packet 1 is related to packet 2 ? I understand the nonce of packet 1 and 3 should be the same. This is also true of 2 and 4, but is there any relation between packet 1 and packet 2 or 4 ? I understand all that is needed for a crackable handshake is packet 1 with either packet 2 or 4, with ESSID obviously.

    So to simplify my question, is there any check available to make certain packet 1 is related to 2 or 4 ?

    I assume using william in default mode has no advantages over using wpaclean, is that right ?

    Have you found any enhancements which could be incorporated into william since it’s release ? Personally I am looking forward to a version 0.2 :)

    Thanks again for your work on william and especially for sharing it.

    1. Jerome Post author

      Hi Dave

      Correct – the script works like wpaclean without adding switches to alter the logic.

      In terms of finding valid pairs, this analysis shows that the Replay Counter could be used to match a packet 2 with the corresponding packet 1. In general, you should only see noisy handshakes when the client doesn’t know the password (in which case you won’t find it either!) or you’re using a rogue access point (AP) to trick a client into connecting, as in both cases there will be several retries. In the latter case, if the rogue AP is increasing the Replay Counter when generating new first EAPOL messages (the Counter won’t change if it’s just retransmitting them) then it should be much easier to pair the corresponding second message from the client as they will have the same Replay Counter value. But, as you can see from my example above, that wasn’t happening. Take a look at the third EAPOL frame, which contains a different nonce to the previous frames: the Replay Counter was still 0. Maybe in other circumstances the Replay Counter would be the magic indicator, so it would be something to look out for perhaps.


  5. Dave

    Hi Jerome and thank you for your reply.

    You will have to forgive anything I say which doesn’t make sense, as I am new to this and I seem to have jumped straight into something very complex :)

    The Replay Counter seems interesting :)

    I do not use rouge AP or test pairs when the client does not know the password. However I believe a bad capture can be made or, out of sequence Replay Counter’s can be produced, when the AP has more than one client connected to it.

    If the pen tester simply performs a deauth all, then all clients try to reconnect at once. I believe this is the cause of many bad captures.

    From what little I know, it seems like a genuine good capture “should” have all Replay Counter’s matching. If this is so then I believe it would be a good additional test for your script. Personally I suggest it should be default to check that all Replay Counter match, because in a good capture that would always be the case. I would make it optional for the user to ignore Replay Counter’s so they can try to work with damaged captures. Although to be honest, I doubt I would trust a capture without matching counters from now on which is why I suggest it is default to check for them.

    I just wish there was something more definitive to ensure any capture is good before spending time and electricity attempting to break them.

    A free program called pyrit seems to be able to notice some captures are bad when wpaclean or aircrack outputs them as good. Do you know what pryit tests for which others do not? At the moment I am passing my captures through pryit and then on to william. It would be nice if william was an “all in one” capture cleaning tool.

    Thanks again.


    1. Jerome Post author

      If multiple clients are trying to connect then you can filter the capture to just one client using the MAC address in Wireshark and then export the selected frames. In addition, it’s always best to use a directed deauthentication (aireplay’s -c switch) so you’re only targeting one client – that should reduce the amount of reconnection traffic. To be honest it’s a long time since I’ve looked at Wi-Fi (which you can tell from the subjects of future posts!) and I’d need to look in to the Replay Counter to see when it would (and wouldn’t) be useful. Nowadays Wi-Fi isn’t something I test regularly so it’s somewhat down the bottom of my list but obviously the script is open source so I encourage you (or another reader) to take it further if I don’t get there first!

      1. Dan Biles


        thanks for william, but it’s telling me a valid (works everywhere else) .cap file from Airodum-ng is not a valid capture file and doesn’t (yes it does….that’s all it contains!) contain any EAPOL packets. I’m running this in Kali 2.0 linux in a VirtualBox on a Macbook pro.
        thanks Dan Biles dbiles@comcast.net

        1. Jerome Post author

          Hi. Assuming you’re running with the defaults, try running this in the same directory as your capture file:
          tshark -r YOUR_FILE.cap -R “eapol.keydes.key_info == 0x010a || eapol.keydes.key_info == 0×0109″ -T fields -e eapol.keydes.mic -e wlan.bssid -e eapol.keydes.key_info -e wlan.sa
          What output do you get?

  6. Dave

    Thank you for the advice about deauthing one client at a time, that is my preferred method but I notice sometimes this seems to cause other clients to reconnect.

    I think you have made a new feature request !!! :) Make sure william checks for a continuous capture sequence from the same client before sorting the packets.

    I understand you have moved on to other things in life, it is a shame as william is such a useful tool and could be even better. I appreciate you say william is open source but things like this are so specialised very few people know about them and fewer can actually do it.

    I do not want to nag you or distract you from your other interests, so I will make this my last post. However if your interest has been reignited and you become interested in william again I will be more than happy to help test and make suggestions.

    Thank you very much for your replies and for william :)


  7. Pentesting

    Sorry if i’m asking a question a little bit far of the subject of this post.
    I would like to know if there is a solution to know the public ip address of a target Router AP (not connected to it) ?
    If I capture a 4-way handshake, can I decrypt it to discover the public ip address ?
    Thank you

      1. Pentesting

        Oh damn !
        Do you any suggestion to sniff public address ip of a target router AP (encrypted with WPA) ?
        There is a solution : “wesside-ng” but only if the targer AP is encrypted with WEP.
        Thank you in advance

        1. Jerome Post author

          To be honest I’ve not been keeping much of an eye on this space for some time but I don’t believe so, no. I mean, even if you decrypted the packets somehow, you wouldn’t see what the public IP address of the router was. You’d have to inject packets to talk to services that return such information, or log in to the router itself. Which as far as I know means knowing the passphrase!

  8. blake

    god, thank you so much. I don’t know why, but validating that packets contain EAPOL data has been soooo painful.


Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>