Avatar

Smyler.net

Hacking, software development, networking and everything in between

Keep the steam activated

CTF writeup: HackTheBox UniCTF 2021 qualifiers

2021/12/18

HackTheBox's yearly University CTF is one of the most interesting capture the flag in my opinion. It always has great original challenges and themes. 2021 was the first edition I played, along with HackademINT. We ended-up finishing 11th in the qualifiers and 5th at the finals. There is usually 3 challenges of increasing difficulty in the forensics category each year, and Keep the steam activated was the hardest one of the 2021 qualifiers. I did not solve it in time, and got the flag around 20 minutes after the CTF ended, so we did not get the points. I got revenge in the finals, where I solved all forensics challenges in a few hours (and found an honest mistake in one of the flags).

The challenge involved analyzing a network capture to find out how an Active Directory domain had been compromised.

Initial Recon

We are provided with a single pcap file that can be opened in Wireshark. Let's have a quick glance at what is going on.

Wireshark screenshot

There are two IPs in play, 192.168.1.9, and 192.168.1.10, communicating back and forth through different protocols.

The exchange can be summarized as follows:

  1. 192.168.1.9 tries to establish SMB2 connections to 192.168.1.10 with multiple usernames, and fails multiple times, as if it was enumerating username and password combination until one worked. It manages to open 2 sessions as corp.local\asmith, which are encrypted SMB3 traffic that cannot be decrypted at this point.
  2. As that 2nd SMB3 session continues, some DCERPC packets are exchanged, just business as usual in an AD domain.
  3. A rev1.ps1file is downloaded from 192.168.1.9 by 192.168.1.10 using an HTTP GET request. The file extension hints at a PowerShell script. Red flag here.
  4. An n.exe file is downloaded the same way. Red flag again.
  5. Lots of raw TCP packets follow, which seem to contain valid text data.
  6. 192.168.1.9 then makes a few HTTP POST requests to a wsman endpoint on 192.168.1.10 and establishes an encrypted spnego session as \Administrator on top of HTTP. Since 192.168.1.9 has previously been refused to open a connection as Administrator, this is a red flag, and we might want to look at it more deeply latter. The user agent of the client points us towards a WinRM protocol client, so this is apparently a remote administration endpoint.
  7. 192.168.1.10 downloads a drop.ps1 file from 192.168.1.9 with an HTTP get request. Another PowerShell script, so a red flag again.
  8. 192.168.1.10 does some HTTP requests to 192.168.1.9 for files with innocent looking names, but with unusually long GET parameters.

Wireshark screenshot Wireshark screenshot Wireshark screenshot Wireshark screenshot Wireshark screenshot

Payloads analysis

That quick look lets us with the impression that 192.168.1.9 attacked 192.168.1.10, starting with a brute-force attack. Since the HTTP requests at step 3, 4 and 7 download executable files, they are a good start to try figuring out how the attacked was conducted. We can extract those files using Wireshark's "Save value" option in the packet dissector. rev.ps1 is quite short, and therefore unlikely to contain anything useful. It might just have been used to retrieve the executable file downloaded shortly after. So let's have a look at n.exe. The easiest way to get something to start with would be to drop it into VirusTotal. The community tab makes it quite clear that this is in fact Netcat.

VirusTotal Netcat That gives an indication as to why there are raw TCP that wireshark doesn't know how to decode in the pcap. It's probably data sent over TCP using necat.

Let's look at the last powershell script:

v o (New-Object IO.MemoryStream);
sv d (New-Object IO.Compression.DeflateStream([IO.MemoryStream]
[Convert]::FromBase64String('huge base64 string here'),
[IO.Compression.CompressionMode]::Decompress));
sv b (New-Object Byte[](1024));
sv r (gv d).Value.Read((gv b).Value,0,1024);
while((gv r).Value -gt 0){
    (gv o).Value.Write((gv b).Value,0,(gv r).Value);
    sv r (gv d).Value.Read((gv b).Value,0,1024);
}
[Reflection.Assembly]::Load((gv o)
    .Value.ToArray()).EntryPoint
    .Invoke(0,@(,[string[]]@())) | Out-Null

A quick read indicates that the chunk of text in the middle is compressed data encoded as base64. Let's decode it in CyberChef:

CyberChef screenshot

The data starts with the header of a Windows PE binary, so let's save the file as drop.exe. Virus total is then once again useful and tells us it is an exploit from a framework we can find on GitHub (Covenant). A quick look at the code reveals that the data exchanged is encrypted, and we did not see any evident flaw in the encryption. It is also clear that the remaining http requests are from that malware, but this is most likely a dead end because of the encryption.

VirusTotal screenshot Covenat VirusTotal comment Covenant Github screenshot

Attacker process

We now have a rough idea of what happened:

  1. The attacker tried to open an SMB session with various users and passwords until one combinaison worked, using the unprivileged asmith account.
  2. They used the sessions to download netcat and used it to exfiltrate some data
  3. They used that data to log in as Administrator using WinRM
  4. They used that WinRM session to download a Remote Access Tool, so they can control the machine with their preferred C2 Framework.
  5. They keep exploiting the system with Covenant.

With that in mind, the next logical step is to replicate what the attacker did to gain further knowledge. The attacker gets complete access to the system at step 3, so the interesting data probably lies at step 3 or 4. Step 1 is therefore unlikely to be interesting here.

Data exfiltration

Since the data at steps 3 and 4 are encrypted, let's look at the data filtered at step 2. There may be multiple TCP connections in the huge chunk of TCP packets, and Wireshark's capability to filter packets by TCP stream is useful to ensure none is overlooked. The following filter only shows the TCP connection for HTTP connection that transferred the first powershell script and Netcat: tcp.stream eq 20. Starting there and Incrementing the TCP stream identifier, it is possible to identify raw HTTP connections by their pink-gray color, and to display them using Wireshark's follow TCP stream's function.

3 interesting clear text connections can be identified this way:

  1. N° 21 contains what looks like commands from a shell.
  2. N° 23 looks like a 46MB PEM certificate file. That size is way too big, this is a red flag.
  3. N° 24 is another such certificate.

Analysing the TCP stream n°21 shows output from the reverse shell the attacker got with the first powershell script.

TCP reverse shell screenshot

We get the following additional information:

cat ntds.dit.pem | head -n -1 | tail -n -1 | base64 -d > ntds.dit
cat SYSTEM.pem | head -n -1 | tail -n -1 | base64 -d > SYSTEM

Data leakage exploitation

Looking for a way to extract data from the two files we got out of the reverse shell, I found this step-by-step guide. The third step featured a tool that did exactly what I needed. It required powershell, but since I had a Windows VM started already, I decided to go through with it.

Windows Powershell Screenshot

We got plenty of information on the administrator account:

Windows Powershell Screenshot

WinRM decryption

Now that we have some credentials, let's see if it is possible to decrypt the administrator WinRM session. After lots of internet research, I ended up finding this amazing winrm-decrypt gist that does exactly what's needed here, and does not require the password but only the hash. Unfortunately, the script did not work out of the box:

Terminal screendshot

After some ugly python debugging, the main issue turned out to be that the way Wireshark (and by extension, Pyshark) organized the output for mime multipart messages was not the same as the one the script expected. Fixing that in the most rushed way possible, and suppressing a few exceptions, the script ended up working fine.

Terminal screenshot

Looking at the output, it seems that the interesting part of messages is base64 encoded:

winrm.txt base64

Writing down the most horrible python script in history solved that, and grep came to the rescue to print out the flag:

Flag output

Thanks to HackTheBox for this alluring challenge!

Related resources:

More articles