Consumer-grade routers are notorious for poor security and bad default configurations. Attacking their poor security is a fun pastime for me. So I build a testing lab to demonstrate and practice WiFi penetration techniques. For this demonstration, I have purchased a Linksys WRT1200AC and set it up the same way an average user might. I changed the login details, Wifi password (PSK), and WiFi Name(SSID). Then enabled the guest WiFi with just the defaults. A few WiFi devices connect to the router to generate traffic. The goal is to keep the router set up to be as close to what an average user would do.

As the attacker, I am going to pretend I know nothing about the router or its configuration. Since I know nothing about the router I am going to start with some basic reconnaissance on the Wireless AP. I want to first know what is the make and model of the Access Point I am going to attack. In a real-world engagement, the first set to planning an attack is understanding what we are attacking.


WiFi Reconnaissance

Lets start by putting the wireless card into monitor mode and capturing some data.

# Disable Network Manager for the Wifi interface.
nmcli dev set wlan0 managed no
# Restart Network Manager to apply changes
systemctl restart NetworkManager
# Unblock the Wifi interface.
rfkill unblock 0
# Set Wifi interface to monitor mode
airmon-ng start wlan0
# start passively collecting data
airodump-ng wlan0 -w DataOut

After getting the WiFi monitor going, fire up Wireshark. Select wlan0 as the interface to monitor. After a minute you should have enough data. Use the below string as the Wireshark filter. The SSID “Hawks” is the main target WiFi network and the guest SSID is “Hawks-guest”.

(wps.device_name != "" || wps.model_name != "" || wps.model_number != "") && wlan.ssid == "Hawks"

Inside some of the captured data we can find the “Tag: Vendor Specific: Microsoft: WPS” field. Many routers freely broadcast data about their make and model, but not all. Below you can see I am able to confirm the router’s make and model that I am attacking.

Let’s move on to connecting to the guest WiFi and see what we can learn about the router’s web UI. I am going to use the OWASP ZAP tool to capture all the web traffic so I can analyze it. After connecting to the guest network, opening a ZAP proxied browser, and trying to browse to “google.com”, I am automatically redirected to a WiFi captive portal.

Linksys Guest WiFi captive portal

After about an hour of poking around in the web UI, I have found a few things. My connection to the guest network allows me to navigate to the router’s main login page to manage the router. However, it seems to error out when testing the login. The permissions on the management page maybe set correctly on it after all? What really caught my eye was a request to a file at “/ui/1.0.99.187766/dynamic/js/setup.js.localized”. Looking through the file I found this very conspicuous-looking array of variables.

No passwords hereā€¦.

I bought this router but did not research known vulnerabilities for it. My personal goal was to see what I could find on my own. So when I found this I had to see if I had discovered something new. Unfortunately, I was not the first to come across this. CVE-2019-7579 describes this vulnerability. This list of words shown in the above image is actually part of the password. The guest’s network has a password with one of the words found in the “setup.js.localized” file combined with a two-digit number. For example, the guest password may be “almond45”. The list contains 30 words, and with the two-digit number at the end that leaves a keyspace of only 3000! That’s crazy easy to break!

Let’s build a dictionary of all the possible combinations. I copied the array of variables into notepad and cleaned it up until there was only one word per line; without commas or quote marks. Then pass that word list to Hashcat to add the two-digit numbers at the end.

# Build the wordlist
hashcat --stdout -a 6 guestnet-words.lst ?d?d >> guestnet-words.dict
# Check the output
head -n4 ./guestnet-words.dict 
almond00
almond01
almond02
almond03
# Check count
cat ./guestnet-words.dict | wc -l 
3000

Now that we have the dictionary file of all possible passwords we can use ZAP’s fuzzing feature to test each password. To uses ZAP’s POST request fuzzing, we need to first have a normal POST request saved. You can do this by just testing the login with a random password, while using the ZAP proxie browser. Then find that POST request in ZAPs history section. Below is the guest password login POST when done normally.

Now lets fuzz the “password” variable in the POST request. Start by right-clicking on the POST request in the history field, then go to “Attack” –> “Fuzz…”.

Now we are going to highlight the variable we want to fuzz and click “Add…”.

A new “Payloads” window will appear. Click “Add…” again to open the “Add Payload” window.

A new “Add Payload” window will appear. Set the “Type” to “File”, then click “Select”. Navigate to the location of the dictionary(“guestnet-words.dict”) we created earlier and open it. Then click “Add…” to close the “Add Payloads” menu. Click “Add…” again to close the “Payloads” menu.

The Fuzzer should now look like the below image.

Before we start the fuzzer we need to slow down the fuzzer’s speed so we don’t overload the router. If this was a real attack, you should set the speed even slower to avoid detection. In the “Options” menu I set the following.

With the options all set, click “Start Fuzzer”. Now the fuzzer will launch testing every password in our dictionary.

It only took a few minutes to find the right password to the guest’s WiFi. You find which POST request was successful in the Fuzzer’s history by looking at the “Size Resp. Body”. In this case, all the POST requests that resulted in “result”: “ErrorInvalidPassword” are 36 bytes, whereas the successful password response of “result”: “OK” is only 18 bytes. The guest WiFi password is “kiwi09”.


Wrapping Up

This WiFi lab demonstrates how easy it is to break consumer-level routers. The level of access I was able to gain is pretty insignificant. If this was a real network attack, the only thing gained is free WiFi, not really a threat to the internal network. The takeaway here is that within an hour of starting the lab I was able to gain some access. A determined attacker could leverage the guest network access to gain additional access. For example, once connected to the guest WiFi you would be able to check the WAN IP address and attack the router from the Internet side. Gaining deeper network access is just a matter of how much time an attacker has.

So how do we fix this issue? The first thing I recommend with consumer-grade routers is to buy one that can have its firmware flashed with Open Source router software. I bought this router with the intent to flashed with OpenWRT. OpenWRT is one of the most secure and powerful consumer-grade routers OS that you can run. It turns basic hardware into full-featured near enterprise-grade routers. Linksys has embraced Open Source router firmware in recent years. It is now faster and easier than ever to upgrade their router’s firmware to OpenWRT. It is now as simple as uploading an OpenWRT package file and clicking update, basically anyone can do it now. As a bonus, Linksys brought back the old school WRT54G look, which is hands-down the coolest part!

The last security improvement we can add is to set a password on the guest WiFi and forgo a captive portal. Captive portals and router firmware have vulnerabilities discovered frequently. Discovered vulnerabilities rarely have patches, and when they do consumers rarely apply them. You are better off setting a password for the WiFi, different from your main WiFi password, and telling your guests it.