Post

Huntress 2023 CTF Write-Ups

Exploring my favorite challenges from the month-long Huntress 2023 CTF event: insights and walkthrough.


Introduction

In October 2023, Huntress ran a month-long CTF to celebrate Cybersecurity Awareness Month. The competition ran from October 2, 12:00 PM ET - October 31, 11:59 PM ET, with new challenges released every day.

I solved a total of 57 challenges throughout this CTF, here are the write-ups for my favorite challenges that I encountered during the event!


Challenges - Warmups

Comprezz

Challenge: Someone stole my S’s and replaced them with Z’s! Have you ever seen this kind of file before?
Attachments: comprezz

Let’s start by figuring out the file type of the attached file using the file command:

┌──(user㉿kali)-[~/Downloads]
└─
$ file comprezz
comprezz: compress'd data 16 bits

This shows that comprezz is a UNIX-compressed file – and probably should have the .z extension. Adding the correct extension will allow us to decompress this file and see what’s inside.

┌──(user㉿kali)-[~/Downloads]
└─
$ mv comprezz comprezz.z

┌──(
user㉿kali)-[~/Downloads]
└─
$ uncompress comprezz.z

┌──(
user㉿kali)-[~/Downloads]
└─
$ file comprezz
comprezz: ASCII text

┌──(
user㉿kali)-[~/Downloads]
└─
$ cat comprezz
flag{196a71490b7b55c42bf443274f9ff42b}

Baking

Challenge: Do you know how to make cookies? How about HTTP flavored?

This challenge gives you a URL to a web application for a virtual oven. There are several different buttons you can click to cook things in the oven, pressing one will start a timer countdown, for example, cooking Muffins takes 15 minutes.

A web application showing a stove with a 15-minute timer, there is text above it that says 'Your Muffins are in the oven!'

When you choose Muffins a cookie named in_oven is set in your browser with the following value:

eyJyZWNpcGUiOiAiTXVmZmlucyIsICJ0aW1lIjogIjEwLzE0LzIwMjMsIDE0OjMwOjAwIn0=

This is a Base64 encoded string that, when decoded, becomes:

{"recipe": "Muffins", "time": "10/14/2023, 14:30:00"}

The flag seems like it might be linked to the Magic Cookies object, which has a very long cook time of 7200 minutes to complete. Let’s try to get these cookies baked without actually waiting all that time.

We can accomplish this by putting Magic Cookies into the oven and manipulating the data stored in our browser’s cookie to set a date that is 7200 minutes in the past.

Cookie:  eyJyZWNpcGUiOiAiTWFnaWMgQ29va2llcyIsICJ0aW1lIjogIjEwLzE0LzIwMjMsIDE0OjM1OjAwIn0=
Decoded: {"recipe": "Magic Cookies", "time": "10/14/2023, 14:35:00"}

Forgery: {"recipe": "Magic Cookies", "time": "1/1/1970, 00:00:00"}
Encoded: eyJyZWNpcGUiOiAiTWFnaWMgQ29va2llcyIsICJ0aW1lIjogIjEvMS8xOTcwLCAwMDowMDowMCJ9

After changing our Browser’s cookies (Chrome: Developer Tools > Application > Cookies) and refreshing the page, we can see this message:

Your Magic Cookies are done! Be careful they are hot! Congratulations flag{c36fb6ebdbc2c44e6198bf4154d94ed4}

Dialtone

Challenge: Well would you listen to those notes, that must be some long phone number or something!
Attachments: dialtone.wav

This challenge was solved by my fellow teammate, Toaste.

Look forward to a write-up from them soon!


Challenges - Forensics

Dumpster Fire

Challenge: We found all this data in the dumpster! Can you find anything interesting in here, like any cool passwords or anything? Check it out quickly before the foxes get to it!
Attachments: dumpster_fire.tar.xz

This challenge provides us with a 16MB archive file containing directories typical of a Linux/Unix environment. The challenge introduces the task of finding a password, with hints suggesting a connection to Firefox.

Let’s look through the files for potential files stored in Firefox directories.

┌──(user㉿kali)-[~/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release]
└─
$ ls AlternateServices.txt extension-preferences.json places.sqlite-wal ClientAuthRememberList.txt extensions prefs.js SecurityPreloadState.txt extensions.json protections.sqlite SiteSecurityServiceState.txt favicons.sqlite saved-telemetry-pings addonStartup.json.lz4 favicons.sqlite-wal search.json.mozlz4 addons.json formhistory.sqlite security_state bookmarkbackups gmp-gmpopenh264 sessionCheckpoints.json broadcast-listeners.json handlers.json sessionstore-backups cert9.db key4.db shield-preference-experiments.json compatibility.ini lock storage containers.json logins.json storage.sqlite content-prefs.sqlite minidumps times.json cookies.sqlite permissions.sqlite webappsstore.sqlite crashes pkcs11.txt webappsstore.sqlite-wal datareporting places.sqlite xulstore.json

Searching for more information about Firefox’s password storage online, I discovered that the crucial password files are typically named logins.json, along with key3.db or key4.db for versions 58 and above.

I navigated into my own Firefox folder, located at /home/user/.mozilla/firefox/b33f.default-release. After making a backup of my own logins.json and key4.db files, I replaced them with the ones from the challenge’s folder:

┌──(user㉿kali)-[/]
└─
$ cd /home/user/.mozilla/firefox/b33f.default-esr/ ┌──(user㉿kali)-[~/.mozilla/firefox/b33f.default-esr]
└─
$ cp ~/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/logins.json logins.json ┌──(user㉿kali)-[~/.mozilla/firefox/b33f.default-esr]
└─
$ cp ~/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/key4.db key4.db

After restarting Firefox, and navigating to Menu > Passwords, we discovered login credentials containing the flag! flag{35446041dc161cf5c9c325a3d28af3e3}

A blank Firefox profile containing the challenge's flag within the password manager.

Bad Memory

Challenge: A user came to us and said they forgot their password. Can you recover it? The flag is the MD5 hash of the recovered password wrapped in the proper flag format.
Attachments: image.zip (>600MB)

In the ‘Bad Memory’ challenge, we are given the task of extracting a password from a Windows Memory Dump, totaling 4.8GB in size. We will use Volatility 3, a free memory forensics tool developed and maintained by Volatility Foundation, which is commonly used to analyze memory dumps.

Downloading Volatility 3 and running the help (-h) command, we can see a lot of plugins that would help in investigating further. The example below shows a few of these plugins.

┌──(user㉿kali)-[~Downloads]
└─
$ git clone https://github.com/volatilityfoundation/volatility3.git Cloning into 'volatility3'... done.

┌──(user㉿kali)-[~Downloads]
└─
$ pip3 install -r volatility3/requirements.txt

┌──(user㉿kali)-[~Downloads]
└─
$ python3 volatility3/vol.py -h
Volatility 3 Framework 2.7.0
An open-source memory forensics framework

Plugins:
For plugin specific options, run 'volatility <plugin> --help'
windows.cmdline.CmdLine
Lists process command line arguments.
windows.crashinfo.Crashinfo
Lists the information from a Windows crash dump.
windows.drivermodule.DriverModule
Determines if any loaded drivers were hidden by a rootkit
windows.driverscan.DriverScan
Scans for drivers present in a particular windows memory image.
windows.dumpfiles.DumpFiles
Dumps cached file contents from Windows memory samples.
windows.filescan.FileScan
Scans for file objects present in a particular windows memory image.
windows.memmap.Memmap
Prints the memory map
windows.pslist.PsList
Lists the processes present in a particular windows memory image.
windows.registry.hivelist.HiveList
Lists the registry hives present in a particular memory image.
windows.registry.printkey.PrintKey
Lists the registry keys under a hive or specific key value.

There are a lot of useful plugins installed here, however, online research shows that the plugin that we would need to use is windows.hashdump.Hashdump - a plugin that isn’t appearing on the list.
Navigating to volatility3/volatility3/framework/plugins/windows, there exists a hashdump.py plugin that we need to use.

Running this Python file, it appears that an error was caused due to the script using a deprecated library:

┌──(user㉿kali)-[/]
└─
$ python3 clone hashdump.py
Traceback (most recent call last):
File "/home/user/Downloads/volatility3/volatility3/framework/plugins/windows/hashdump.py", line 10, in <module>
from Crypto.Cipher import AES, ARC4, DES
ModuleNotFoundError: No module named 'Crypto'

Researching this issue online, it seems like many people suggested using the ‘pycryptodome’ library instead of ‘crypto’. This is an easy fix, as all we need to do is type pip install pycryptodome to get the plugin working correctly.

┌──(user㉿kali)-[~Downloads]
└─
$ pip install pycryptodome

┌──(user㉿kali)-[~Downloads]
└─
$ python3 volatility3/vol.py -f image.bin windows.hashdump.Hashdump
Volatility 3 Framework 2.7.0
Progress: 100.00 PDB scanning finished
User rid lmhash nthash

Administrator 500 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
Guest 501 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0

DefaultAccount 503 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
WDAGUtilityAccount 504 aad3b435b51404eeaad3b435b51404ee 4cff1380be22a7b2e12d22ac19e2cdc0
congo 1001 aad3b435b51404eeaad3b435b51404ee ab395607d3779239b83eed9906b4fb92

We can crack the NT hash of congo’s account using an online tool (crackstation.net), and view their plaintext password, goldfish#.

The challenge said, “The flag is the MD5 hash of the recovered password wrapped in the proper flag format.”

┌──(user㉿kali)-[~Downloads]
└─
$ echo -n "goldfish#" | md5sum | awk '{print "flag{" $1 "}"}'
flag{2eb53da441962150ae7d3840444dfdde}

Challenges - Malware

PHP Stager

Challenge: Ugh, we found PHP set up as an autorun to stage some other weird shady stuff. Can you unravel the payload?
Attachments: phonetic

The ‘PHP Stager’ challenge tasks us with working through obfuscated code. I’ve created a video guide for this one, check it out below or on YouTube!

Batchfuscation

Challenge: I was reading a report on past Trickbot malware, and I found this sample that looks a lot like their code! Can you make any sense of it?
Attachments: batchfuscation

‘Batchfuscation’ provides us with an obfuscated Windows Batch script that we will need to break down to figure out what it does.

┌──(user㉿kali)-[~Downloads]
└─
$ file batchfuscation
batchfuscation: DOS batch file, ASCII text, with very long lines (1241)

An image of the long strings of text saved within the Batch file.

Let’s take a look at the first few lines of this very long file:

@echo off
set bdevq=set
%bdevq% grfxdh= 
%bdevq%%grfxdh%mbbzmk==
%bdevq%%grfxdh%xeegh%mbbzmk%/
%bdevq%%grfxdh%jeuudks%mbbzmk%a
%bdevq%%grfxdh%rbiky%mbbzmk%c

Batch scripts use ‘%’ signs to indicate variables. Variables are defined using the set command set message=Hello World and then can be referenced elsewhere %message%.

Knowing how variables are set and referenced, we can take a look at the first lines again after making substitutions:

@echo off
set bdevq=set
set grfxdh= 
set mbbzmk==
set xeegh=/
set jeuudks=a
set rbiky=c

Now we can see that the beginning of this Batch script is creating variables for each ASCII character that are being used to obfuscate the rest of the code.

Instead of manually replacing each variable in this file, let’s have Batch do it for us! By changing the first line of the script to @echo on and running it, we should be able to see some readable output.

C:\Users\win10\Desktop>batchfuscation.bat > batch.txt

This method worked for deriving all of the set variables at the beginning of the file:

An image of the batch script running beside a notepad file containing the set variables.

This method didn’t work for deobfuscating the rest of the code. One of my teammates suggested searching for keywords within the Batch script.

Using the dictionary, flag is represented in the script as %vdqvoyxss%%djkxbuskp%%jeuudks%%mljmage% and can be found on 41 different lines.

We can copy these 41 lines into a new Batch script to decipher. By including the set variables at the top, and the “flag” lines at the bottom with an echo command, we can run the script to decipher each of these lines:

An image of a batch script running beside a notepad file containing the set variables and echo commands.

It looks like we found the flag! We are left with a bunch of lines like this:

::setflag_character5={
::setflag_character26=3
::setflag_character3=a

After cutting off the ::setflag_character text and sorting the lines numerically, we are left with our flag: flag{acad67e3d0b5bf31ac6639360db9d19a}

Speakfriend

Challenge: It seems like this website was compromised. We found this file that seems to be related… can you make any sense of these and uncover a flag?
Attachments: main.7z

‘Speakfriend’ provides us with a URL to a backdoored website, and a related malware file, main, stored within main.7z.

┌──(user㉿kali)-[~Downloads]
└─
$ file main
main: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f020f8b12bc1a0b0f3122413b698344bfbfd1d9d, for GNU/Linux 3.2.0, not stripped

A webpage for a cyber security company that was recently breached.

Trying to run this ELF binary does not produce any output. Let’s try figuring out how this program works through Static Analysis.

Opening the program in Ghidra, we are able to determine a few things:

  • The starting point for the file is a function called main, which takes in two parameters.

The main function has been decompiled in Ghidra, the line has the code: main(int param_1, long param_2)

  • There are a lot of curl API calls, meaning the script is reaching out to a website.
  • Depending on what param_1 is, a new variable might get set with a value of “443”, which would coincide with an HTTPS connection.

Code decompiled in Ghidra, the word 'curl' has been highlighted multiple times, as well as the variable numbered '443'.

  • This script has a hidden reversed string hardcoded into it! It can be easy to miss since Ghidra only shows you its contents if you hover over it.

A code snippet taken from Ghidra that shows a hidden user agent, starting with the word 'Mozilla'

This hidden string appears to be a user agent, a string included in HTTP headers to identify the device requesting online content. If this script is reaching out to the website using curl and a custom user agent, we should be able to do this too.

┌──(user㉿kali)-[~Downloads]
└─
$ curl --insecure --user-agent "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e" https://chal.ctf.games:30282
<!doctype html>
<html lang=en>
<title>Redirecting ... </title>
<h1>Redirecting ... </h1>
<p>You should be redirected automatically to the target URL: <a href="/93bed45b-7b70-4097-9279-98a4aef0353e/c2">/93bed45b-7b70-4097-9279-98a4aef0353e/c2</a>. If not, click the link

By visiting the compromised website using the custom user agent, we are redirected to a suspicious subdomain that appears to be a command-and-control (c2) server!

When we access this suspicious webpage, we are greeted with our flag: flag{3f2567475c6def39501bab2865aeba60}

Snake Eater II

Challenge: Snake Eater II - Revenge of the Snake Eater

The Threat Actor must have gotten word that you had no trouble dissecting Snake Eater. They said this one is a bit more… involved.
Attachments: snake_eaterII.7z

HuskyHacks was responsible for making the three challenges that I found to be the most difficult during this CTF - Crab Rave, BlackCat II, and Snake Eater II. The first two stumped me, so I suggest HuskyHack’s walkthrough videos if you’d like to know more about them.

As for the ‘Snake Eater II’ challenge, we are provided the malicious snake_eaterII.exe stored within a .7z archive.

Static analysis of the file shows that it was packed with pyinstaller (Python 3.11).

┌──(user㉿kali)-[~Downloads]
└─
$ diec snake_eaterII.exe
PE64
Packer: PyInstaller(-)[-]
Compiler: Microsoft Visual C/C++(2022+)[-]
Linker: Microsoft Linker(14.35**)[Console64,console]

We can also use dynamic analysis to learn more about what snake_eaterII.exe is doing. Since this is a .exe, we can run the file on a Windows VM and use Sysinternals - Process Monitor to monitor if any changes are made to our file system in real-time.

In the first Snake Eater challenge, a text file containing the flag in its filename was being written to a random folder in our Windows %AppData% folder:

A screenshot of Process Monitor showing the first Snake Eater challenge, here snake_eater.exe has created a file with the flag in its filename.

This challenge is very similar, however, the flag is now stored within the text file, rather than in the filename - and it is overwritten and deleted instantly after it is created.

A screenshot of Process Monitor with the current Snake Eater II challenge, the same operations occurred but the file is named flag.txt.

I attempted a few things:

  • Creating folder Auditing Security settings to log whenever a new file is modified or written within the %AppData% folder. This did not work because the actual contents of the file are not logged.

Windows NTFS settings showing how to set auditing permissions to log changes to a folder.

  • Going through the assembly code line-by-line in the debugger in x64dbg to be able to view the contents of the file before it is deleted. This did not work because a new process was created by the .exe. Going through this sub-process line-by-line eventually leads to one single command that when executed creates and destroys the file at immediately.

A before-and-after picture showing how snake_eaterII.exe creates all of its processes instantaneously.

  • I eventually discovered a tool called SysAnalyzer that was capable of viewing the contents of a .txt file (after a few attempts). Using the ‘Directory Watcher’, the tool was able to show any changes made to files within a directory.

A SysAnalyzer window open showing flag.txt's contents.

My solution may have had some luck involved in it, but we were still able to get the flag! flag{be47387ab77251ecf80db1b6725dd7ac}


Challenges - Miscellaneous

M Three Sixty Five

Challenge: This is the challenge portal with a separate deployable container environment for 4 separate “M Three Sixty Five” challenges.

This challenge allowed participants to connect to an actual Office 365 tenant running the AADInternals PowerShell module. AADInternals is widely used for administering Azure Active Directory (AD) and Office 365. It also contains features that can be leveraged by threat actors for malicious purposes like creating backdoor users, stealing passwords, and stealing encryption keys.

Most of this challenge just comes down to reading the AADInternals documentation. Helping me discover what a powerful tool administration through PowerShell can be, and a great example of how a tool might be leveraged by both the red team and blue team.

  • Challenge 1 - Can you find any juicy details, like perhaps the street address this organization is associated with?
    • Get-AADIntTenantDetails | select street
  • Challenge 2 - This tenant looks to have some odd Conditional Access Policies. Can you find a weird one?
    • Get-AADIntConditionalAccessPolicies | select displayName
  • Challenge 3 - We observed some sensitive information being shared over a Microsoft Teams message! Can you track it down?
    • Get-AADIntTeamsMessages | select Content
  • Challenge 4 - One of the users in this environment seems to have unintentionally left some information in their account details. Can you track down The President?
    • Get-AADIntUsers | grep -i president
    • Get-AADIntUsers | where Title -eq 'President'

Indirect Payload

Challenge: We saw this odd technique in a previous malware sample, where it would uncover its next payload by… well, you’ll see.

The ‘Indirect Payload’ challenge provides you with a URL, which when opened in Firefox warned me “This page isn’t redirecting properly - Firefox has detected that the server is redirecting the request for this address in a way that will never complete.”

Firefox is a little incorrect here since the request will complete eventually, there are just over two hundred Status 302 (REDIRECT) pages first:

BurpSuite window showing the sheer amount of redirect pages encountered when visiting the URL.

Curious to see if there was any information stored within the HTML of these pages, first I made a list of all of the URLs, then I curled through the entire list.

┌──(user㉿kali)-[~Downloads]
└─
$ wget -i --max-redirect 200 chal.ctf:port >> urls.txt

┌──(user㉿kali)-[~Downloads]
└─
$ curl ... $(cat urls.txt)
character 0 of the payload is f
character 1 of the payload is l
character 2 of the payload is a
character 3 of the payload is g
character 4 of the payload is {
character 5 of the payload is 4
character 6 of the payload is 4
character ...

The payload (flag) was separated, and had different embedded components across many different pages in the redirects! flag{448c05ab3e3a7d68e3509eb85e87206f}


Conclusion

A huge shoutout to Huntress Labs, as well as Chris Cochran, Alex Kelly, Matt Kiely, and John Hammond for making Huntress CTF 2023 possible. It was an incredible learning opportunity that I look forward to next year! 🥳

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