// HackTheBox  ·  writeup

Holmes 2025 3: The Enduring Echo

HackTheBox Easy EvtxECmdgrepPythonregipystrings
DFIR

Scenario

In this Sherlock players will investigate KAPE output to see the Windows management instrumentation artifacts and persistence left by a Threat actor using the machine to pivot to an internal workstation.

Executive Summary

An attacker authenticated over the network with the account Werni and drove the host through Impacket’s WMI remote shell, so every command ran as a child of the WMI provider host and wrote its output to a file on the ADMIN$ share. After some reconnaissance they mapped a hostname to their own server in the hosts file, then created a scheduled task that runs a PowerShell script from the user’s profile every two minutes as SYSTEM. The script creates a randomly named local administrator with a timestamp-based password, enables RDP and posts the new credentials to the mapped hostname. The attacker then switched off the firewall and added a netsh port proxy that exposes an SSH service on an internal workstation through the compromised host. Process-creation auditing with command lines, enabled earlier by the administrator, is what made all of it readable.

Artifacts

Timestamp note: EvtxECmd prints UTC. The host itself runs on Pacific Daylight Time (UTC-7), and that offset matters for Q9 because the script builds its password from local time.

Execution

Q14 — Before the attack, the administrator configured Windows to capture command line details in the event logs. What command did they run to achieve this?

Start here, because it explains why the rest of the case is readable at all. 4688 events only carry a command line when the ProcessCreationIncludeCmdLine_Enabled policy value is set. The Administrator’s PowerShell history file holds the reg add that set it, run before the intrusion window.

grep -ri "ProcessCreationIncludeCmdLine" evidence/C/Users/Administrator/AppData/Roaming/Microsoft/Windows/PowerShell/PSReadLine/ConsoleHost_history.txt
Click to reveal answer reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit" /v ProcessCreationIncludeCmdLine_Enabled /t REG_DWORD /d 1 /f
Q1 — What was the first (non cd) command executed by the attacker on the host?

Parse every log with EvtxECmd, then pull Security 4688 process creations out of the CSV. The unfiltered list is mostly service noise, so filter on the parent process (see Q2). The earliest attacker child is a cmd.exe /Q /c wrapper whose real payload is systeminfo, redirected to a file on \\127.0.0.1\ADMIN$. Earlier lines in the session are cd calls, which the question excludes.

EvtxECmd -d evidence/C/Windows/System32/winevt/logs --csv work/evtx
grep "WmiPrvSE.exe" work/p4688.txt | head

Gotcha: The first full dump of 4688 events is far too noisy to read. Filter on the parent process rather than scrolling.

Click flag to reveal systeminfo
Q2 — Which parent process (full path) spawned the attacker’s commands?

Every attacker cmd.exe in the 4688 data has the same parent: the WMI provider host. Legitimate WMI use rarely produces interactive-looking cmd.exe children, so a long run of them is the signal.

grep "cmd.exe /Q /c" work/p4688.txt | cut -d'|' -f1 | sort | uniq -c
Click to reveal answer C:\Windows\System32\wbem\WmiPrvSE.exe
Q3 — Which remote-execution tool was most likely used for the attack?

The command shape gives it away: cmd.exe /Q /c <command> 1> \\127.0.0.1\ADMIN$\__<epoch> 2>&1. That quiet-mode wrapper, the output redirected to a timestamp-named file on ADMIN$, and the WMI parent together match Impacket’s WMI shell. This is reasoned from the command pattern, not from a tool artifact on disk.

grep -o '1> \\\\127.0.0.1\\ADMIN\$\\__[0-9.]*' work/p4688.txt | sort -u
Click flag to reveal wmiexec.py

Lateral Movement

Q4 — What was the attacker’s IP address?

The 4688 events do not carry a source address, so pivot to 4624 logons around the first command. The Werni account shows type 3 network logons using NTLM from one address, matching the remote WMI activity.

grep "4624" work/evtx/*.csv | grep -i "Werni" | grep -i "NTLM"
Click to reveal answer 10.129.242.110

Persistence

Q5 — The attacker established multiple persistence mechanisms. What is set as the name of the earliest one created?

Search the WMI-spawned commands for schtasks. The earliest persistence is a scheduled task created at 23:03:50 UTC, set to run every two minutes as SYSTEM, and named to look like a routine update job.

grep "schtasks" work/p4688.txt
Click flag to reveal SysHelper Update
Q6 — Identify the script executed by the persistence mechanism.

From Q5, the /tr argument of the same schtasks /create line is a hidden, execution-policy-bypassing PowerShell launch of a script in the Werni profile.

grep "schtasks" work/p4688.txt | grep -o '\-File [^ ]*'
Click to reveal answer C:\Users\Werni\AppData\Local\JM.ps1

Note: The task line spells the path Appdata; the file on disk is under AppData\Local. Windows paths are case-insensitive.

Q7 — What local account did the attacker create?

JM.ps1 picks a name at random from a list of five update-service lookalikes, so the script alone cannot tell you which one ran. Security 4720 (user account created) settles it, at 23:05:09 UTC, about a minute after the task was registered.

cat evidence/C/Users/Werni/AppData/Local/JM.ps1
grep ",4720," work/evtx/*.csv
Click flag to reveal svc_netupd
Q9 — What password did the attacker's script generate for the newly created user?

The script builds the password as Watson_ plus (Get-Date).ToString("yyyyMMddHHmmss"), so the answer is the creation time in the host’s local clock. No log held the literal password, so this one is reasoned rather than read from an artifact. The 4720 event for the account is at 23:05:09 UTC. The SYSTEM hive reports the time zone as Pacific Daylight Time, UTC-7, which makes the local timestamp 16:05:09 on 2025-08-24. The seconds value is the only inferred part, and HTB accepted it.

python3 -I -c "from regipy.registry import RegistryHive; print(RegistryHive('SYSTEM').get_key('ControlSet001\\\\Control\\\\TimeZoneInformation').get_values(as_json=True))"

Gotcha: Using the UTC time from the event log produces a password that is seven hours off. The script formats local time.

Click flag to reveal Watson_20250824160509

Exfiltration

Q8 — What domain name did the attacker use for credential exfiltration?

The last line of JM.ps1 sends the new user|password pair, base64 encoded, to a hostname with Invoke-WebRequest. That name does not resolve publicly, and the attacker made it work by appending it to the hosts file through WMI at 23:00:15 UTC, pointing it at their own address from Q4.

grep -ri "NapoleonsBlackPearl" evidence/ work/p4688.txt
Click to reveal answer NapoleonsBlackPearl.htb

Command and Control

Q10 — What was the IP address of the internal system the attacker pivoted to?

After the firewall was disabled with netsh advfirewall, the attacker ran netsh interface portproxy add v4tov4 at 23:10:05 UTC. The connectaddress is the internal target.

grep "portproxy" work/p4688.txt
Click flag to reveal 192.168.1.101
Q11 — Which TCP port on the victim was forwarded to enable the pivot?

Same command as Q10. listenport is the port opened on the victim, bound to 0.0.0.0, and connectport=22 shows it relays to SSH on the internal host.

grep "portproxy" work/p4688.txt | grep -o "listenport=[0-9]*"
Click to reveal answer 9999
Q12 — What is the full registry path that stores persistent IPv4→IPv4 TCP listener-to-target mappings?

Reference knowledge: the iphlpsvc port proxy rules persist in the SYSTEM hive under the PortProxy service key, with one subkey per protocol pairing. It is the place to confirm a port proxy offline and to find rules that survive a reboot.

Click flag to reveal HKLM\SYSTEM\CurrentControlSet\Services\PortProxy\v4tov4\tcp
Q13 — What is the MITRE ATT&CK ID associated with the previous technique used by the attacker to pivot to the internal system?

A port proxy on a compromised host that relays traffic to another internal host is Proxy: Internal Proxy, a sub-technique of T1090.

Click to reveal answer T1090.001

Attack Summary

PhaseAction
Lateral MovementNetwork logons as Werni from the attacker host, then Impacket WMI shell
Executionsysteminfo and file reads through cmd.exe /Q /c under WmiPrvSE.exe
Command and ControlHosts file entry maps the exfil hostname to the attacker address
PersistenceSYSTEM scheduled task SysHelper Update runs JM.ps1 every two minutes
PersistenceJM.ps1 creates a random local admin and enables RDP
ExfiltrationCredentials posted to the mapped hostname over HTTP
Defense EvasionWindows Firewall disabled for all profiles
Command and Controlnetsh port proxy forwards a local port to SSH on an internal workstation

Forensic Timeline

Timestamp (UTC)Event
2025-08-24 22:51:09First attacker command, systeminfo, via WMI
2025-08-24 22:55:10type known_hosts run through the WMI shell
2025-08-24 23:00:15Hosts file entry added for the exfil hostname
2025-08-24 23:03:50Scheduled task SysHelper Update created
2025-08-24 23:05:09Local account svc_netupd created (4720)
2025-08-24 23:05:46Firewall disabled, retried at 23:06:08
2025-08-24 23:10:05Port proxy added to the internal host

IOCs

TypeValue
IP (attacker)10.129.242[.]110
IP (pivot target)192.168.1[.]101
Domain (exfil)NapoleonsBlackPearl[.]htb
Scheduled taskSysHelper Update
FileC:\Users\Werni\AppData\Local\JM.ps1
Accountsvc_netupd
PasswordWatson_20250824160509
Port proxy0.0.0.0:9999 to 192.168.1.101:22
RegistryHKLM\SYSTEM\CurrentControlSet\Services\PortProxy\v4tov4\tcp

MITRE ATT&CK

TechniqueIDDescription
Windows Management InstrumentationT1047Impacket WMI shell, commands spawned by WmiPrvSE.exe
Remote Services: SMB/Windows Admin SharesT1021.002Command output written to ADMIN$
Scheduled Task/Job: Scheduled TaskT1053.005SysHelper Update runs JM.ps1 as SYSTEM every two minutes
Create Account: Local AccountT1136.001svc_netupd added to Administrators and Remote Desktop Users
Exfiltration Over Alternative ProtocolT1048New credentials posted over HTTP to the mapped hostname
Impair Defenses: Disable or Modify System FirewallT1562.004netsh advfirewall set allprofiles state off
Proxy: Internal ProxyT1090.001netsh interface portproxy relay to an internal SSH host

Defender Takeaways

Command-line auditing is what made this case solvable. The administrator had set ProcessCreationIncludeCmdLine_Enabled before the intrusion, so every 4688 carried the full command. Without it the WMI activity would have been a list of bare cmd.exe launches. Enable it on every host, and add PowerShell script block logging.

WmiPrvSE.exe spawning cmd.exe /Q /c with output to ADMIN$ is a ready-made detection. The pattern is stable across Impacket versions. Alert on cmd.exe children of WmiPrvSE.exe, and treat the \\127.0.0.1\ADMIN$\__<epoch> redirect as a high-confidence match.

A SYSTEM scheduled task launching a script from a user profile is out of place. powershell -ExecutionPolicy Bypass -WindowStyle Hidden -File against AppData\Local on a two-minute interval should fire on 4698 task-creation events. Scripts that run as SYSTEM should live in locations that standard users cannot write to.

Watch for portproxy rules and firewall changes. A netsh interface portproxy add followed by a firewall disable is a pivot setup. Monitor the PortProxy registry key and netsh advfirewall commands, and check the key during triage on any host that sits between network segments.

Script-generated passwords built from the clock are guessable. Watson_ plus a timestamp is recoverable from the account creation time alone, so even without the exfil traffic the credential could be rebuilt. Rotate any account like this, and alert on new local administrators created outside change windows.

Conclusion

The attacker used one valid account and a WMI shell to plant a SYSTEM task, a backdoor administrator and a port proxy toward an internal SSH host, all with built-in Windows tooling. The whole chain showed up in process-creation events because command-line auditing was on, which is the control that separated a readable incident from an invisible one.