This Sherlock comprises a series of forensics challenges that involve analyzing memory dumps, disk images, logs, network traffic and malware on Windows and Linux operating systems.
An attacker at 192.168.1.5 scanned a web server, found DNS open, and pulled the whole zone with an unrestricted transfer. That exposed a monitoring subdomain, which they brute-forced through its login form and then abused to run a named-pipe netcat shell. Root came from a public polkit exploit run from that shell. For persistence the attacker replaced the system crontab, then poisoned the Debian package offered on the company’s download subdomain with a payload that writes into .bashrc. From the web server’s mail service they phished two executives with a macro document. The CEO’s workstation executed a hidden PowerShell stager against the attacker’s HTTP listener, which delivered a Cobalt Strike beacon kept alive by a scheduled task.
tshark. The scan, DNS, web attack, reverse shell and package downloads are all herestrings and grep. A Volatility profile (Ubuntu_5.3.0-70-generic_profile.zip) ships alongside, but strings were enough for the mail questionsMFTECmd, LECmd, EvtxECmd, plain file browsing for System32/TasksFormat note: Several answers are checked against a masked format (asterisks where letters go). Use the mask to confirm length and case before submitting; Q19 is expected in upper case.
Start with conversation and protocol stats to see who talks to whom. The scan traffic is answered by one host: SYN-ACKs come back from 192.168.1.3 to the scanner, and that host also serves HTTP, DNS and SMTP.
tshark -r traffic.pcapng -q -z conv,ip
The other end of the scan is the same address that later requests the zone transfer and sends the web login attempts, so one source covers recon, DNS and the web attack.
tshark -r traffic.pcapng -Y 'tcp.flags.syn==1 && tcp.flags.ack==0' -T fields -e ip.src | sort | uniq -c | sort -rn | head
An open port is one that answered the scan with a SYN-ACK. List the distinct source ports of SYN-ACKs from the server and count them. The raw count includes port 5555, which is the attacker’s later reverse-shell listener rather than a service found by the scan, so it is excluded.
tshark -r traffic.pcapng -Y 'ip.src==192.168.1.3 && tcp.flags.syn==1 && tcp.flags.ack==1' -T fields -e tcp.srcport | sort -nu
Gotcha: Counting every distinct SYN-ACK port gives 15. That sweeps in the shell port opened after the scan, so 14 is the scan result.
The wording is ambiguous, so take it as the five lowest open ports from the Q3 list, sorted numerically. This was reasoned from the wording rather than read from a single artifact, and HTB accepted it.
tshark -r traffic.pcapng -Y 'ip.src==192.168.1.3 && tcp.flags.syn==1 && tcp.flags.ack==1' -T fields -e tcp.srcport | sort -nu | head -5
Port 53 was open (Q4), and the capture contains a DNS query of type 252 (AXFR) for cs-corp.cd from the attacker. The server answered with the full zone, so zone transfers were not restricted to secondary servers. The mask XXX XXXX XXXXXXXX fits the technique name.
tshark -r traffic.pcapng -Y 'dns.qry.type==252'
Dump the AXFR response and count the host records in the answer section.
tshark -r traffic.pcapng -Y 'dns.qry.type==252 && dns.flags.response==1' -V
List the HTTP POSTs from the attacker. A long run of login POSTs targets one virtual host, a monitoring panel, and one of them stands apart from the failures.
tshark -r traffic.pcapng -Y 'http.request.method==POST' -T fields -e frame.number -e http.host -e http.request.uri
Frame 29388 is the login POST that breaks the pattern of the brute force. Read its form body for the credentials.
tshark -r traffic.pcapng -Y 'frame.number==29388' -T fields -e urlencoded-form.key -e urlencoded-form.value
After the login, filter the POSTs to sysmon.cs-corp.cd that are not login attempts. Frame 32414 posts to index.php with a command-injection payload that builds a FIFO and pipes it through bash and a netcat listener on 5555, which is the shell the attacker used afterwards.
tshark -r traffic.pcapng -Y 'http.request.method==POST' -V | grep -i mkfifo
Follow the TCP stream of the shell on port 5555 (stream 2785). The attacker-side commands download and run the PwnKit exploit, a polkit pkexec local privilege escalation.
tshark -r traffic.pcapng -q -z follow,tcp,ascii,2785
The same shell stream shows /etc/crontab being replaced, which is Scheduled Task/Job: Cron. The mask *****.**3 matches.
Modifying packages on the company’s own download site so customers install them is Supply Chain Compromise: Compromise Software Supply Chain. This was reasoned from the behaviour and the mask rather than read from an artifact, and HTB accepted it.
The package moves over HTTP on port 8000, which tshark does not decode as HTTP by default, so the object export misses it. Follow the raw stream (2861) and carve the .deb, then unpack it with ar, decompress the control and data archives, and read the maintainer script. The payload is base64 and zlib encoded; decoding it statically reveals the line appended to .bashrc.
tshark -r traffic.pcapng -q -z follow,tcp,raw,2861
ar x cs.deb && zstd -d data.tar.zst && tar -xf data.tar
Tip: Run
tshark -d tcp.port==8000,http --export-objects http,outto get the HTTP objects on the non-standard port. Several downloads sit in streams 2813 to 2868.
The web server’s mail runs in a Citadel groupware process. The memory image’s process listing and strings show citserver serving the mail.
strings -a Memory_WebServer.mem | grep -i citserver | head
Carve mail headers out of memory by grepping for Subject: near the Citadel message store.
strings -a Memory_WebServer.mem | grep -i '^Subject:'
The Citadel MIME part for that message names the attachment, a macro-enabled Word document.
strings -a Memory_WebServer.mem | grep -i '\.docm'
Pull the To: and From: lines around the phishing message and count the ceo- accounts. Two executive accounts appear. This was read from the mail headers, but which of them are the recipients was reasoned from the From lines of the two messages, and HTB accepted it.
strings -a Memory_WebServer.mem | grep -iE '^(To|From):' | grep -i ceo- | sort | uniq -c
Unzip DiskImage_CEO-US.zip and its nested archive. The KAPE ConsoleLog at the top of the collection records the machine it ran on.
head work/disk/*/*ConsoleLog*.txt
Parse the $MFT and search for policy, then confirm with the Office recent-files shortcut, which stores the target path of the opened document.
MFTECmd -f '$MFT' --csv work/mft --csv-f mft.csv
LECmd -f policy.LNK
Gotcha: The mask shows a Windows path; the accepted string is upper case (
C:\USERS\CEO-US\DOWNLOADS\POLICY.DOCM), matching how the LNK target was recorded.
Searching the collection for downloadstring finds the PowerShell logs. Parse the event logs and look at the script-block event (ID 600 in the PowerShell log): a hidden, no-profile IEX download cradle against the attacker’s port 806.
grep -rIl downloadstring work/disk
EvtxECmd -d winevt/logs --csv work/evtx --csvf ev.csv
The cradle fetches /a on a non-standard port 806 over plain HTTP, the staging pattern of a Cobalt Strike HTTP listener. This was reasoned from that pattern, not read from the beacon, and HTB accepted it.
Cobalt Strike names its payloads <os>-beacon_<listener>; an HTTP stager for a Windows host maps to the windows-beacon_http-reverse_http payload. This is naming convention reasoning (HTB accepted it), not a decoded beacon config.
Look for Task Scheduler event 106 (task registered) in the parsed event logs, then confirm in System32/Tasks. The WindowsUpdateCheck task runs C:\Users\Public\WindowsUpdate.exe, an executable in a world-writable folder dressed up as a Windows update.
grep ',106,' work/evtx/ev.csv | grep -i TaskScheduler
find work/disk -ipath '*System32/Tasks*' -type f
| Phase | Action |
|---|---|
| Reconnaissance | Port scan of the web server finds 14 open ports |
| Reconnaissance | DNS zone transfer for cs-corp.cd exposes 9 hosts |
| Credential Access | Brute force of the sysmon panel login succeeds |
| Initial Access | Command injection starts a FIFO and netcat shell on 5555 |
| Privilege Escalation | PwnKit (CVE-2021-4034) gives root |
| Persistence | /etc/crontab replaced |
| Resource Development | cs-linux.deb on the download site trojanised with a .bashrc payload |
| Initial Access | Phishing mail from the web server’s Citadel with policy.docm to two CEO accounts |
| Execution | CEO-US opens the document; hidden PowerShell cradle fetches a Cobalt Strike stager from port 806 |
| Persistence | WindowsUpdateCheck scheduled task runs C:\Users\Public\WindowsUpdate.exe |
| Type | Value |
|---|---|
| IP (attacker) | 192.168.1[.]5 |
| IP (web server) | 192.168.1[.]3 |
| Domain | sysmon[.]cs-corp[.]cd |
| Credentials | admin:Pass@000_ |
| URL | hxxp[://]192[.]168[.]1[.]5:806/a |
| File | cs-linux.deb |
| File | policy.docm (C:\Users\ceo-us\Downloads\policy.docm) |
| File | C:\Users\Public\WindowsUpdate.exe |
| Scheduled task | WindowsUpdateCheck |
| Email subject | Review Revised Privacy Policy |
| Technique | ID | Description |
|---|---|---|
| Active Scanning: Scanning IP Blocks | T1595.001 | Port scan of the web server |
| Gather Victim Network Information: DNS | T1590.002 | Unrestricted AXFR of cs-corp.cd |
| Brute Force | T1110 | Login brute force against the sysmon panel |
| Exploit Public-Facing Application | T1190 | Command injection through index.php |
| Command and Scripting Interpreter: Unix Shell | T1059.004 | FIFO and netcat shell |
| Exploitation for Privilege Escalation | T1068 | PwnKit |
| Scheduled Task/Job: Cron | T1053.003 | Replaced /etc/crontab |
| Supply Chain Compromise: Compromise Software Supply Chain | T1195.002 | Trojanised cs-linux.deb |
| Phishing: Spearphishing Attachment | T1566.001 | policy.docm sent to the CEOs |
| Command and Scripting Interpreter: PowerShell | T1059.001 | Hidden IEX download cradle |
| Application Layer Protocol: Web Protocols | T1071.001 | HTTP beacon staging on port 806 |
| Scheduled Task/Job: Scheduled Task | T1053.005 | WindowsUpdateCheck |
Restrict DNS zone transfers. An unrestricted AXFR gave the attacker the full host list in one query, including the internal monitoring panel. Allow transfers only to named secondaries, and alert on type 252 queries from anywhere else.
Keep admin panels off the same exposure as the web server and enforce lockout. The monitoring login fell to password guessing, and a single command-injection POST turned that access into a shell. Rate limiting, MFA and removing shell-capable features would each have broken the chain.
Patch polkit and watch for pkexec abuse. PwnKit has been public since 2022 and was the only step needed to reach root. A package update, plus detection of pkexec run with an empty argument list, closes it.
Sign and verify what the download site serves. The poisoned .deb carried an obfuscated payload that wrote to .bashrc. Publishing hashes and signatures out of band, and diffing package contents on release, would catch a swapped file.
Treat macro documents from internal senders as hostile. The phishing mail came from the compromised web server, so sender reputation did not help. Block Office macros from the internet, and alert on hidden powershell.exe IEX cradles and scheduled tasks that point into C:\Users\Public.
One exposed zone transfer led the attacker from a port scan to root, a trojanised download package, and a Cobalt Strike beacon on the CEO’s workstation. Each step reused something the previous one exposed, so a single control, such as restricting AXFR or the admin panel, would have cut the chain early.