You’ll analyze router logs from a firmware prone to OS Command Injection. Discover the attacker’s initial entry using default router credentials to successfully exploiting the vulnerable firmware and attempting to upload a reverse shell.
An internal host logged into a router’s web admin interface after two failed guesses with default credentials, then succeeded with an empty password. From the authenticated session the attacker abused a vulnerable settings handler on the router’s apply.cgi endpoint, injecting shell commands inside backticks into a USB-share parameter. They proved execution with a harmless identity command, then repeatedly tried to pull a reverse-shell script from an external web server with wget. A malformed injection returned an error from the router before a correct one landed, and the script’s body shows the callback address of the attacker’s listener.
tshark. All traffic is cleartext HTTP, so every request and response body is readable with Follow TCP StreamTimestamp note: Q4 wants a UTC time to the second. Wireshark’s default shows local time, so set View > Time Display Format > UTC Date and Time of Day, or use
-t udintshark.
List the HTTP requests in the capture and look at who talks to the router. Every login attempt and every exploit POST aimed at the router at 192.168.10.1 comes from a single internal client, so that client is the attacker’s starting point.
tshark -r trent.pcap -Y 'http.request.method == "POST"' -T fields -e frame.number -e ip.src -e ip.dst -e http.request.uri
Walk the login POST requests in order from Q1’s client. The first two (admin/admin and admin/password) get the login page back, which is a failure. The third attempt is answered with a redirect to /index.asp, the authenticated landing page. Count the failures before that redirect.
tshark -r trent.pcap -Y 'http.request.method == "POST" && ip.src == 192.168.10.2' -T fields -e frame.number -e frame.time -e http.request.uri -e urlencoded-form.value
Take the successful third POST from Q3 (frame 22826) and read the time of the response that carries the redirect to /index.asp. Display the time in UTC so the answer needs no conversion.
tshark -r trent.pcap -t ud -Y 'frame.number >= 22826 && http.response' -T fields -e frame.number -e frame.time -e http.response.code -e http.location | head
Gotcha: The request and its response are a fraction of a second apart. The accepted time is the second of the response that accepts the login.
Open the successful login POST from Q4 with -V and read the form fields. log_pass is empty: the router accepted the default admin user with a blank password.
tshark -r trent.pcap -Y 'frame.number == 22826' -V | grep -E 'log_(user|pass)'
Tip: An empty value is a valid answer here, so the length is zero rather than a missing field.
The router’s own pages carry its identity. Export the HTML bodies returned to the attacker’s client and search for the model variable that the web UI sets in its JavaScript.
tshark -r trent.pcap -Y 'http.response && ip.src == 192.168.10.1' --export-objects http,objs && grep -rh 'var model' objs
The model name is on most pages, but the firmware version only shows up in the status page the attacker loaded after logging in (adm_status.asp). Its fwVer span holds the version followed by the build date.
grep -rh 'fwVer' objs
Hint: Only the status page carries it. If a global search of other responses finds nothing, load the post-login pages.
After the login, the client sends several POST requests to apply.cgi. In them, one field of the USB-share (Samba) settings contains shell metacharacters: a command wrapped in backticks. That field is the injection point.
tshark -r trent.pcap -Y 'http.request.method == "POST" && http.request.uri contains "apply.cgi"' -T fields -e frame.number -e urlencoded-form.key -e urlencoded-form.value | grep '`'
This one comes from reference research, not the capture. The Q2 model, the Q6 firmware version and the Q7 parameter point to the OS command injection in the Trendnet TEW-827DRU firmware, tracked by SentinelOne’s vulnerability database as the CVE below. The answer was reasoned from that single source and the format mask rather than read from an artifact, and HTB accepted it.
Order the injected POST requests by frame number. The earliest one (frame 44975) sets the vulnerable parameter to admin plus a backticked command, a harmless identity check used to confirm execution.
tshark -r trent.pcap -Y 'frame.number == 44975' -T fields -e urlencoded-form.key -e urlencoded-form.value
Keep reading the injected values after Q9. A later POST (frame 47681) carries a backticked wget against an external address on port 8000, fetching a small shell script.
tshark -r trent.pcap -Y 'frame.number == 47681' -T fields -e urlencoded-form.key -e urlencoded-form.value
Compare the injected values of the wget attempts from Q10. One of them is missing its closing backtick, so the injection is malformed. Read the router’s response to that request (frame 51885): it is an HTTP 403 with a short body.
tshark -r trent.pcap -Y 'frame.number == 51885' -V | grep -iE 'HTTP/1|forbidden'
Gotcha: The failure is not a 500 or a shell error. The router’s own handler rejects the malformed request with a 403 page, so look at the body text, not just the status code.
The pcap contains the HTTP download of a1l4m.sh from the attacker’s web server on port 8000. Follow that TCP stream to read the script. It is a bash -i reverse shell redirected to /dev/tcp/<ip>/<port>, and the listener port differs from the 8000 used for hosting.
tshark -r trent.pcap -Y 'http.request.uri contains "a1l4m.sh"' -T fields -e tcp.stream
tshark -r trent.pcap -q -z follow,tcp,ascii,<stream>
Tip: Don’t answer with the web server port from Q10. That port served the script, while the shell calls back to a different one.
| Phase | Action |
|---|---|
| Initial Access | Internal host tries admin/admin and admin/password against the router login, both fail |
| Initial Access | Third attempt with the admin user and an empty password succeeds |
| Discovery | Attacker browses post-login pages, including the status page that exposes model and firmware |
| Execution | Backticked command injected into the USB-share admin name parameter on apply.cgi |
| Execution | First injected command is whoami, confirming code execution |
| Command and Control | Several wget injections try to pull a1l4m.sh from an external web server |
| Command and Control | A malformed injection returns a 403 from the router |
| Command and Control | Script retrieved; it opens a bash -i reverse shell to the attacker’s listener |
| Type | Value |
|---|---|
| IP (attacker, internal) | 192.168.10.2 |
| IP (router) | 192.168.10.1 |
| IP (staging and C2) | 35.159.25[.]253 |
| URL | hxxp[://]35[.]159[.]25[.]253[:]8000/a1l4m[.]sh |
| C2 | 35.159.25[.]253:41143 |
| Parameter | usbapps.config.smb_admin_name |
| Technique | ID | Description |
|---|---|---|
| Valid Accounts: Default Accounts | T1078.001 | Router login with the default admin user and an empty password |
| Exploit Public-Facing Application | T1190 | OS command injection through the router’s apply.cgi handler |
| Command and Scripting Interpreter: Unix Shell | T1059.004 | Backticked shell commands, then a bash -i reverse shell |
| Ingress Tool Transfer | T1105 | wget of a1l4m.sh from an external host |
Default credentials on a management interface are a complete bypass. The login accepted admin with no password after two guesses. Forcing a password change at first setup, and blocking the admin interface from user networks, removes the entry point for everything that followed.
Cleartext HTTP admin traffic hands the attacker’s every move to anyone capturing it. Here that works for the defender: the full exploit, the parameter and the payload are readable in the pcap. Keep captures or flow logs on router segments, because they were the only evidence of this intrusion.
Command injection leaves a very recognisable shape on the wire. Backticks, $( or ; inside form values sent to apply.cgi, especially in a field like a share admin name, should trip an IDS rule. The whoami probe came before any payload, so an alert on that first request would have given warning.
Routers should not be able to reach arbitrary internet hosts. The download of a1l4m.sh and the callback on a high port both left the network unchecked. Egress filtering for the router and alerts on outbound wget to non-vendor addresses would have broken the final stage, and patching to a fixed firmware for the CVE removes the injection itself.
The attacker went from a blank-password login to a callback shell on a consumer router with a single injected parameter, and the failed attempts along the way (a typo and a 403) are the only noise in the whole chain. Default credentials plus an unpatched firmware did all the work.