// HackTheBox  ·  writeup

Velociraptor

HackTheBox Medium tsharkVelociraptorPythonxxd
DFIR

Scenario

Analyze the security incident using network traffic and Velociraptor logs as a DFIR analyst.

Executive Summary

An external host attacked a CI server running in a Docker container and bypassed its authentication with a crafted REST request. It used that access to mint administrator accounts through the API, then logged in as the new user and delivered a JSP web shell packaged as a plugin. A second rogue account was created later and a second shell followed the same route. Through the shell the attacker ran OS commands and dropped a text file on the host as proof of access. The Velociraptor collection identifies the host and the container runtime; the packet capture holds the whole attack in cleartext HTTP.

Artifacts

Tip: http.file_data comes back empty for chunked or reassembled bodies. Use tshark -z follow,tcp,ascii,<stream> to read request and response bodies.

Discovery

Q1 — What is the Client ID associated with the EDR logs?

Start with the collection metadata. client_info.json carries the Velociraptor client_id for the endpoint the artifacts were pulled from. It follows the C. plus 16 hex characters shape from the format mask.

grep -o '"client_id": *"[^"]*"' client_info.json
Click flag to reveal C.e70aef07943d3e45
Q2 — What is the Flow ID of the current logs?

The flow is the collection job itself, so its identifier lives in collection_context.json as session_id. Flow IDs start with F., which matches the mask.

grep -o '"session_id": *"[^"]*"' collection_context.json
Click to reveal answer F.CP17L840N1CL0.H
Q3 — The web server was running on a container. What is the parent process ID of the container (PPID)?

Search the process tree for docker components. In Pstree.csv the Docker daemon is the parent of the docker-proxy processes that publish the container’s ports, so it is the process that sits above the container workload on the host. The containerd-shim and the container’s run-server.sh entry point also show up in the tree, which makes the question ambiguous.

grep -i docker Pstree.csv

Gotcha: The shim and the container’s startup script look like natural “parent” candidates but are not what the question wants. The daemon PID that parents docker-proxy is the accepted one.

Click flag to reveal 5123
Q4 — What is the docker version that is running the web server?

The collection includes a Docker version artifact. Read the server version from Docker.Version.csv.

head Docker.Version.csv
Click to reveal answer 26.1.2

Initial Access

Q5 — What is the IP address of the attacker?

Rank conversations in the capture, then look at the HTTP requests from the external talker. One address sends a stream of odd /hax?jsp=... requests to the web server port, which is not application traffic.

tshark -r capture.pcap -q -z conv,tcp
tshark -r capture.pcap -Y http.request -T fields -e ip.src -e http.request.method -e http.request.uri | sort | uniq -c | sort -rn
Click flag to reveal 95.164.9.144
Q6 — To determine if there are any exploits targeting the server, identify the build version of the web server service. What is the build version?

The server answers /app/rest/server with its version metadata, and the attacker’s own requests fetch it. Pull the response body from that frame and read the buildNumber. The product release (TeamCity 2023.11.3) maps to this build.

tshark -r capture.pcap -Y 'http.request.uri contains "/app/rest/server"' -T fields -e frame.number
tshark -r capture.pcap -q -z follow,tcp,ascii,<stream> | grep -i build
Click to reveal answer 147512
Q7 — The attacker took advantage of a known exploit to that version of the service. What is the CVE number for the exploit that he used?

The attacker requests REST endpoints with a ;.jsp suffix, such as /hax?jsp=/app/rest/users;.jsp. Appending .jsp makes the server treat the request as one of the paths that skip authentication, so the REST API runs unauthenticated. That is the TeamCity authentication bypass that affects builds before the 2023.11.4 fix. This answer comes from matching the request pattern and the Q6 build to the public advisory, not from a field in the capture.

tshark -r capture.pcap -Y 'http.request.uri contains ";.jsp"' -T fields -e frame.number -e http.request.method -e http.request.uri
Click flag to reveal CVE-2024-27198

Persistence

Q8 — The attacker created multiple usernames and passwords on the service. What is the first username and password created?Answer Format: username:password

The bypass lets the attacker POST to the users endpoint. Take the first POST to /hax?jsp=/app/rest/users;.jsp and read the request body, which carries the new account’s username and password in JSON. Follow the TCP stream to get the body, since the raw HTTP field is empty.

tshark -r capture.pcap -Y 'http.request.method == "POST" && http.request.uri contains "/app/rest/users"' -T fields -e frame.number -e tcp.stream
tshark -r capture.pcap -q -z follow,tcp,ascii,<stream>

Gotcha: Several accounts are created. Sort by frame number and take the earliest POST to the users endpoint.

Click to reveal answer ahup1rui:pXgdGJacOZ

Execution

Q9 — The attacker used the newly created user to upload a web shell. What endpoint was used to upload the web shell?Answer Format: page.ext

Filter the attacker’s POST requests that are not part of the REST API. The upload goes to the admin plugin upload page, a multipart POST carrying a ZIP.

tshark -r capture.pcap -Y 'ip.src == 95.164.9.144 && http.request.method == "POST"' -T fields -e http.request.uri | sort | uniq -c
Click flag to reveal pluginUpload.html
Q10 — The attacker uploaded a web shell using the newly created user. What is the full URL of the uploaded web shell?

Plugins land under /plugins/<name>/ once loaded. The attacker’s later POSTs carry a cmd= parameter against a JSP in that tree, which is the shell. Take host and port from the request’s Host header to build the full URL.

tshark -r capture.pcap -Y 'http.request.uri contains "/plugins/"' -T fields -e http.host -e http.request.method -e http.request.uri
Click to reveal answer http://18.159.50.167:8111/plugins/5z6p8kCA/5z6p8kCA.jsp
Q11 — The attacker created another user named 41m67llo and uploaded another web shell. What is the name of the ZIP file that was uploaded?

Find the request that creates the 41m67llo account and follow the upload that comes after it. That upload is a second pluginUpload.html POST whose multipart body names the ZIP in its filename= field. Following the stream (118) shows the upload and the post-exploitation that follows.

tshark -r capture.pcap -q -z follow,tcp,ascii,118 | grep -a 'filename='
Click flag to reveal V5HwJgS3.zip
Q12 — The attacker created a file on the system containing some text. What is the text inside that file?

Search the capture for shell commands sent through the cmd= parameter. One of them is an echo redirected into a file on the host, and the quoted string is the file content.

tshark -r capture.pcap -Y 'frame contains "cmd="' -T fields -e frame.number -e http.request.uri -e urlencoded-form.value
Click to reveal answer YOU ARE HACKED BUDD!

Attack Summary

PhaseAction
Initial AccessAttacker sends REST requests with a ;.jsp suffix to bypass TeamCity authentication
PersistenceCreates the first administrator account through /app/rest/users
ExecutionLogs in and uploads a JSP web shell as a plugin through /admin/pluginUpload.html
ExecutionRuns commands through the cmd= parameter of the shell
PersistenceCreates a second account, 41m67llo, and uploads a second plugin ZIP
ImpactWrites a taunting text file to the host with echo

IOCs

TypeValue
IP (attacker)95[.]164[.]9[.]144
IP (victim server)18[.]159[.]50[.]167
URL (web shell)hxxp[://]18[.]159[.]50[.]167:8111/plugins/5z6p8kCA/5z6p8kCA[.]jsp
Account (first)ahup1rui
Account (second)41m67llo
File (plugin upload)V5HwJgS3.zip
CVECVE-2024-27198

MITRE ATT&CK

TechniqueIDDescription
Exploit Public-Facing ApplicationT1190Authentication bypass on the TeamCity server
Create AccountT1136Administrator accounts created through the REST API
Server Software Component: Web ShellT1505.003JSP shells delivered as plugin uploads
Command and Scripting InterpreterT1059OS commands run through the shell’s cmd= parameter

Defender Takeaways

Patch internet-facing CI servers on the advisory, not the next cycle. The attacker’s very first requests used a known bypass against an unpatched build. Version disclosure on /app/rest/server also told them what they were facing, so restrict that endpoint and track vendor advisories for build tooling.

Alert on the ;.jsp request pattern. Paths with ;.jsp appended to REST routes have no legitimate use. A WAF or reverse-proxy rule on that pattern would have blocked the exploit and flagged the scan.

Admin account creation and plugin uploads are rare, high-signal events. A burst of new users via the API followed by a POST to pluginUpload.html should page someone. Audit the user list and installed plugins after any exposure.

Capture the container host as well as the container. The Velociraptor collection resolved the Docker daemon, version and port proxies, which is what ties the network evidence back to a specific process on the host.

Conclusion

The attacker went from one unauthenticated REST request to administrator accounts and a working web shell on a containerised CI server, then left a text file to say so. A single patch and a block on one URL pattern would have stopped the whole chain.