// HackTheBox  ·  writeup

Fragility

HackTheBox Easy 7-ZiptsharkjournalctlPython
DFIR

Scenario

This Sherlock will challenge the player to identify the exploitation of a vulnerable Splunk application, investigate server logs & packet captures and decrypt the attacker’s exfiltrated data.

Executive Summary

An attacker logged into a Splunk 9.0.5 web interface with a valid account, then uploaded a crafted XSL stylesheet to the search preview endpoint. Splunk rendered the stylesheet and ran the script inside it, which created a local backdoor user, put it in the sudo group and planted an SSH key. Seconds later the attacker connected over SSH as that user, and a few minutes on the host pushed an encoded file out to the attacker over a plain TCP port. The exfiltrated blob was layered in an EBCDIC re-encoding, base64 and AES, and the key material was derived from values the attacker’s own script exposed. A timezone change made on the host without a reboot left the journal and syslog disagreeing, so the two have to be normalised before any timeline is trusted.

Artifacts

Timestamp note: the journal is read in UTC, the syslog is not. Use journalctl --utc for every answer that asks for UTC.

Initial Access

Q8 — What are the username and password that the attacker uses to access Splunk?

The exploit needs an authenticated Splunk session, so the capture starts with a login. List the HTTP requests, find the POST to /en-US/account/login, and read the form body. The credentials are sent in clear over HTTP, so they sit in the request body as username and password fields.

tshark -r capture.pcapng -Y 'http.request.method == "POST" && http.request.uri contains "login"' -T fields -e frame.number -e http.file_data

Gotcha: the login form carries extra fields (CSRF token, cookie flag). Only the username and password values count, in that order.

Click to reveal answer johnnyC:h3Re15j0hnNy
Q1 — What CVE did the attacker use to exploit the vulnerability?

List every HTTP request in the capture. After the login there is an upload to /splunkd/__upload/indexing/preview carrying a file named search.xsl, and the server is Splunk 9.0.5. An XSL upload to the preview endpoint that gets rendered into code execution is the Splunk advisory for the XSLT injection flaw in versions before 9.0.7. That mapping to the CVE is reference knowledge, matched against the request path and version seen here.

tshark -r capture.pcapng -Y http.request -T fields -e frame.number -e http.request.method -e http.request.uri
Click flag to reveal CVE-2023-46214

Persistence

Q2 — What MITRE technique does the attacker use to maintain persistence?

Export the search.xsl body from the upload request. The script inside runs adduser nginx, sets its password with chpasswd, adds the user to the sudo group and writes an authorized_keys entry. Creating a local account is Create Account, and the question asks for the parent technique ID.

tshark -r capture.pcapng -Y 'http.request.uri contains "preview"' -T fields -e http.file_data
Click flag to reveal T1136
Q6 — What is the password for the account that the attacker used to backdoor?

The payload from Q2 does not hold the password in plain text. It is a base64 string that is decoded, reversed with rev, and piped into chpasswd for the nginx user. The shadow file in the collection is empty, so there is no hash to crack; reverse the same steps on the string from the payload.

echo '<base64 string from the payload>' | base64 -d | rev

Tip: a grep nginx over shadow returns nothing here. Go back to the packet capture instead of hunting for a hash.

Click to reveal answer f8287ec2-3f9a-4a39-9076-36546ebb6a93
Q3 — John has adjusted the timezone but hasn't rebooted the computer yet, which has led to some things either being updated or not updated with the new timezone. Identifying the timezone can assist you further in your investigation. What was the default timezone and the timezone after John's adjustment on this machine?

Compare the same boot in both logs. The journal, read with --utc, puts the boot at 06:18, while syslog stamps it 23:18, so the host was running at UTC-7 when it booted. Later in syslog there is a Changed time zone entry that sets Asia/Ho_Chi_Minh (+07). Because the host was not rebooted, lines written after the change use the new zone while earlier state still reflects the old one.

journalctl --directory var/log/journal --utc | grep -iE 'time zone|timezone'
Click flag to reveal utc-07/utc+07
Q4 — When did the attacker SSH in? (UTC)

With the timezone sorted in Q3, read the journal in UTC and filter sshd. The first Accepted publickey for nginx is the attacker using the key the XSL payload planted. It lands eight seconds after the nginx account was created.

journalctl --directory var/log/journal --utc | grep -E 'sshd.*Accepted'

Gotcha: syslog times are local to the host and differ from the journal by the offset from Q3. Answer from the UTC journal view.

Click to reveal answer 04-14 15:00:21
Q5 — How much time has passed from when the user was first created to when the attacker stopped using SSH?

The start is the useradd entry for nginx at 15:00:13 UTC. The end is the sshd disconnect for the attacker session at 15:03:08 UTC. The gap between those two is the answer.

journalctl --directory var/log/journal --utc | grep -E 'useradd|sshd.*(Accepted|Disconnected|session closed)'
Click flag to reveal 00:02:55

Exfiltration

Q7 — There is a secret in the exfiltrated file, what is its content?

List the TCP conversations and look for a stream that is not Splunk or SSH. The victim connects out to the attacker on destination port 8080, so the data is in the client-to-server direction. Dump that stream to a file. The contents are EBCDIC (cp037) text that decodes to base64, which decodes to an AES-256-CBC blob. The key is the SHA-256 of an epoch timestamp (1713106974) and the IV is the MD5 of the host’s uname -r string, both recoverable from the attacker’s script. Decrypting gives data.zip, which holds Important.pdf, and the secret is printed in the image inside it.

tshark -r capture.pcapng -Y 'tcp.dstport == 8080' -T fields -e data > exf.raw
raw = bytes.fromhex(open("exf.raw").read().replace("\n", "").replace(":", ""))
b64 = raw.decode("cp037")

Gotcha: the port 8080 traffic is victim-initiated, so filtering on tcp.srcport returns nothing useful. The epoch is also a derived value, so brute-force a window around the SSH session times if the script does not show it directly.

Click flag to reveal Th3_uNs33n_P4$$w0rd_is_th3_m05t_s3cur3

Attack Summary

PhaseAction
Initial AccessAttacker logs into the Splunk web interface with a valid account over cleartext HTTP
ExecutionUploads a crafted search.xsl to the preview endpoint, triggering CVE-2023-46214
PersistencePayload creates the nginx user, sets its password, adds it to sudo and plants an SSH key
Initial AccessAttacker connects over SSH as nginx using the planted key
ExfiltrationHost sends an EBCDIC, base64 and AES-wrapped archive to the attacker on port 8080
Initial AccessSSH session ends

Forensic Timeline

Timestamp (UTC)Event
04-14 06:18Host boots in UTC-7 (journal in UTC; syslog shows 23:18 local)
04-14 15:00:13nginx user created
04-14 15:00:21Attacker SSH login as nginx
04-14 15:02:54Epoch (1713106974) used to derive the exfiltration key
04-14 15:03:08Attacker SSH session ends

IOCs

TypeValue
CVECVE-2023-46214
Upload path/splunkd/__upload/indexing/preview
Payload filesearch.xsl
Backdoor accountnginx
Exfiltration portTCP 8080 (victim to attacker)
Splunk credentials usedjohnnyC

MITRE ATT&CK

TechniqueIDDescription
Exploit Public-Facing ApplicationT1190Malicious XSL upload to Splunk 9.0.5
Create Account: Local AccountT1136.001adduser nginx from the XSL payload
Account Manipulation: SSH Authorized KeysT1098.004Key written for the backdoor user
Obfuscated Files or InformationT1027Password stored as reversed base64 and exfil wrapped in layered encoding
Remote Services: SSHT1021.004Attacker session as nginx
Exfiltration Over C2 ChannelT1041Encoded archive pushed to the attacker on port 8080

Defender Takeaways

Patch Splunk and keep the web interface off untrusted networks. The whole chain started from an authenticated upload to a known-vulnerable preview endpoint. A version below the fixed release and a login reachable over cleartext HTTP were enough.

Credentials crossing the wire in cleartext are credentials the attacker keeps. The Splunk login was readable in the capture. TLS on the management and web ports removes that, and alerting on logins from unexpected sources catches reuse.

Watch for account creation by service processes. adduser and chpasswd spawned by the Splunk service account is not normal. Auditing useradd, sudo-group changes and writes to authorized_keys would have flagged this within seconds.

Mixed timezones break timelines. A zone change without a reboot left the journal and syslog disagreeing about when the host booted. Collect in UTC and record the zone history of the host before correlating sources.

Unusual outbound connections from a server are worth blocking. A Splunk host pushing data to an arbitrary external port 8080 should be stopped by egress filtering, and the encoding layers do nothing to hide that the connection exists.

Conclusion

The attacker went from a valid Splunk login to a sudo-capable backdoor account and an outbound data transfer in a few minutes, using one unpatched endpoint and a payload that did all the work itself. The sharpest lesson is that the evidence only lined up once the timezone mismatch was resolved.