Last weekend was CSAW CTF, the fifth time I’ve played with b01lers. This year for the first time ever, b01lers has qualified for finals! I’m really proud of the team and the new officers for making this happen. As far as I am aware, we have qualified as a team for only one other finals, DefCamp 2017, so this is a really impressive achievement.
The team was also one of only seven to achieve a perfect score of all challenges solved, shout out to bronson113 for the clutch solve of unsafe-linking right before the time ended.
Some Club History
b01lers was started at some point in the last decade, the details are fuzzy, but the club really started to take off thanks to GH0S1. GH0S1 made our annual bootcamp where we train new students into an event, with the promise of internships from our sponsors for high performing attendees. Naturally this brought in some talent interested in taking advantage of a great club, and who also wanted to get leet. Under GH0S1’s direction for the first couple years, then under our own steam, a new officer team came up and started to make some more enhancements to the club. We hosted our inaugural CTF, ran our bootcamp as an online class, took some high placements in well-known events, and started to collaborate with other teams in the midwest, building a community bigger than just our club. In no particular order, nsnc, PHSC138, maczilla, ZJam, and a0su, and myself were the senior members for several years.
After four years, most of the members of that officer team graduated in 2021, and we knew we were leaving the club in good hands. As with any handoff, there will be a rocky period, and the bus factor is never zero. With the benefit of hindsight, there are a lot of things that should have been written down. 2heen took the helm first, with mweepigeon, DJ, Duj, richzli, A1y, and M4y (and more) helping make the transition. This year, bronson113 took the reigns and is clearly piloting the club to new heights. We’ve already got a finals and another private CTF on the calendar for the year, and I can’t wait to see where they take it next.
Encrypted Disk Writeup
I’m one of the old people in the club now, so I don’t work as fast. I solved one problem along with r3x, Dx., and mobi. We get a download containing a memory image and a disk image. Obviously, we are going to need to check them out.
If we run xxd forensic.img
we see:
$ xxd forensic.img | head -n 100
00000000: 4c55 4b53 babe 0001 6165 7300 0000 0000 LUKS....aes.....
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000020: 0000 0000 0000 0000 6563 6200 0000 0000 ........ecb.....
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 7368 6131 0000 0000 ........sha1....
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: 0000 0000 0000 0000 0000 0800 0000 0010 ................
Ok, this is an encrypted LUKS disk image that was crypted using aes-ecb-sha1 mode. We will need to recover a master key from the memory image to decrypt the disk image. So I downloaded findaes which is very old but absolutely still works.
$ ./findaes-1.2/findaes ./memory.raw
Searching ./memory.raw
Found AES-128 key schedule at offset 0xad9f450:
8d 3f 52 7d e5 14 87 2f 59 59 08 95 8d bc 0e d1
Then we will want to write that key to a binary file:
$ python3 -c \
'from binascii import unhexlify; from pathlib import Path; Path("./key.bin").write_bytes(unhexlify("8d3f527de514872f595908958dbc0ed1"))'
Next, we’ll mount the image as a loop device:
$ sudo losetup --offset 0 /dev/loop9 forensic.img
Once that’s done, we will want to decrypt the image:
$ sudo cryptsetup luksOpen --master-key-file key.bin /dev/loop9 decrypted.img
And we can then mount this image and browse it:
$ sudo mkdir -p /mnt/decrypted && sudo mount decrypted.bin /mnt/decrypted
Inside, we will find three files:
- end.png
- findme.txt.gpg
- readme.txt
The readme is useless of course, we need to do a couple things.
First, binwalk extract from the png:
$ binwalk --extract end.png
We’ll find an encrypted zip file end.zip.gpg. We’ll need to find the passphrase to this file in the memory image. We can use volatility2 to find it if we wanted to, and there is a custom profile for this online. However, we can also just:
$ strings ../2022csaw/memory.raw| grep -iha passphrase
...snip...
gpg --yes --batch --passphrase=1m_4n_4dul7_n0w -c findme.txt
gpg --yes --batch --passphrase=Troll_Tr0ll_TrOll -c end.zip
gpg --yes --batch --passphrase=1m_4n_4dul7_n0w -c findme.txt
...snip...
It’s a lot faster and you don’t need a profile :)
Anyway, we have the passwords now:
$ gpg --yes --batch --passphrase=Troll_Tr0ll_TrOll -d end.zip.gpg > end.zip
$ gpg --yes --batch --passphrase=1m_4n_4dul7_n0w -d findme.txt.gpg
gpg: AES.CFB encrypted data
gpg: encrypted with 1 passphrase
The flag is not here of course !!!
You must find it :-)
Troll one day troll always ........
Yeah, so password is definitely in the zip, but it has a password. No problem:
from subprocess import run
from zipfile import ZipFile
wordlist = run(["strings", "memory.raw"], capture_output=True).stdout
for word in wordlist.splitlines():
print("Trying", word)
try:
with ZipFile("end.zip") as zf:
zf.extractall(pwd=word)
except:
print("Failed!")
else:
print("Done!")
break
We get “done” after password Cyb3rs3curit3
, and we get a flag.gif
file. It’s a GIF
of a bunch of QR codes, where each code is a letter of the flag. Cool! We can grab the
separate images with ImageMagick:
$ mkdir img && convert flag.gif img/xx_%05d.png
And then we can use a phone scanner to scan them all. This could be done with code of course, but sometimes the “old fashioned way” is fastest :)
Voila, flag! 1_Lik3_F0r3nS1c_4nd_y0u?