The (Danish) National Police Cyber Crime Center (NC3) has created a four part advent CTF challenge this year. In a CTF you compete against the clock ⏱ to be the first to solve a series of challenges/riddles that involve different aspects of programming & forensics.
This is a writeup explaining how I solved the third challenge in this years advent CTF, This time I only managed to get the 13th best time, but it was great fun and I learned a lot of new stuff!
We were presented with the following text and two links, the first link was to a binary file “three.dd”, the second link was to a manual explaining the architecture of the filesystem.
Den 7. august 2017 vandt professor Kiwistone prisen “Fejl00”, der hvert år gives til udvikleren af årets mest håbløst forfejlede stykke software. Professoren vandt prisen for filsystem “FragFS”. Filsystemet udmærker sig negativt ved et væld af manglende funktioner og voldsomt fragmenteret data. Dette forhindrede dog ikke gerningsmanden B. Tarp i at bruge det til at gemme koden til en krypteret harddisk. Find koden(flaget), der er gemt i filsystemet, ved at bruge manualen udfærdiget af Kiwistone.
Download filsystemet - md5sum: 757282428a5b4cb348248afafaae3d88 Download manualen - md5sum: 37c728752566c5793eb421abc9be4995
(It’s basically the story of “professor Kiwistone” who won the award for the year’s most hopelessly flawed piece of software. He wrote the filesystem “FragFS”, a file system that lacked even the most basic stuff and was incredibly fragmented. But this didn’t prevent the perpetrator “B. Tarp” form hiding the code for an encrypted drive in a FragFS filesystem. Our job is to find the code (the flag) hidden in the filesystem by using the manual Kiwistone wrote.)
The manual is reproduced here in full:
Et frygteligt fragmenteret filsystem
FragFS er et filsystem udviklet alene med det formål at give andre hovedpine. Filsystemet understøtter kun >små filer og diskstørrelser. Alt andet kan og vil gå galt.
FragFS benytter en sektor størrelse på 4096 bytes og har følgende overordnede struktur:
De første 512 bytes angiver hvilken version af filsystemet der anvendes.
FilID og Filsti/Filnavn tabel
Starter ved byteoffset 512 ved start fra offset 0.
Hver entry indeholder en MD5 checksum(FilID) skabt ud fra filsti+filnavn.
FilID og Offset tabel
Følger direkte efter den foregående tabel.
Starter ved sectoroffset 20/byteoffset 81920 fra start offset 0.
Slutter når filsystemet får lyst til at slutte.
Bunkevis. Brug spray eller DEET.
Af Professor Kenneth Kiwistone
From this we can extract the following variables that are going to be useful:
sectorSize = 4096 and
dataStart = sectorSize * 20 (I actually misunderstood the whole sector/data part the first time round, but we’ll get to that later)
This was the first time that I’ve been working with filesystems, so I needed to read up a bit before I could get anywhere with this challenge. I found a pretty good primer on the subject that got me started down the right path.
The first thing that I needed was to figure out how the different tables in the filesystem were seperated. For this I used the command
xxd three.dd | less this gave me the file contents in three columns
|00002cb0:||2032 3039 3000 ff36 3833 3036 3637 3132||2090..683066712|
|00002cc0:||3037 3764 3438 3139 3535 6632 6461 3539||077d481955f2da59|
We know that the two filetables are in the sectors 0 through 19, everything from sector 20 and onward is the filedata.
But we need to figure out how the tables are structured and where the first table
[FileID and Filepath/Filename] stops and the second table
[FileID and Offsets] starts. We start by inspecting the data from sector
0 (but we ignore the first 512 bytes, as they’re just the filesystem version info).
We know that the table starts with an MD5 hash of the Filepath + Filename, MD5 hashes are always 32 characters in length. Looking at the table above we can see that immediately after the first 32 chars, there is a
0x00 value, to the right of that value we can see the Filepath + Filename, let’s call it the “column” seperator. Next we’re hitting a
0x00 again but this time it’s followed by 2
0xFF values. Combined they give us
0x00 0xFF 0xFF this is a new set of seperators that mark the “rows” in the table. If we try to visualize it we get something that looks like this:
|MD5||Filename + Filepath|
Great we’ve got the first table down! One more to go! So we keep scrolling through the the xxd output until we hit something that looks fresh
Line 6832 is where it’s at! The output suddenly changes and “Huzzah!” now we’ve found the second table! and we’re in luck, there’s a new seperator!
0x00 0xFF 0xFF 0x00 0xFF 0x00 so now we know where both tables start and stop!
The first starts at offset
512 and ends when we encounter a
0x00 0xFF 0xFF 0x00 0xFF 0x00. The second table then start immediately after that and ends at sector
20 or offset
81920. Now let’s take a closer look and see if we can figure out how the second table
[FileID and Offsets] should be read:
This table is supposed to consist of the FileID (the MD5 from the first table) and a list of offsets specifying where the different parts of the file are stored. Here we find a new seperator
0x00 0xFF this is the “row” seperator for the second table. In the second table
0x00 is once again used as the “column” seperator. But the offsets are also seperated by a hex value
0x20 or simply a “ “ space char. If we visualize the second table we get:
|31f84df100b0c302c05f39c6e42deeb2||1002 1737 1560 1405 1272 1098 981 1597 1698 1943 1071 1386 590 1199 351 1176 1963 2104 1473 1557 1180 1659 1247 332 888 2093 1160 1771 1673 379 660 1697 804 1291 1023 2069 655 2131 437 1317 1076 1852 222 2166 1788 1871 847 329 1559 2193 328 2061 666 1024 1439 2075 489 766 1114 1381 2126 1613 1844 1621 986 1107 806 1564 884 1912 2182 980 1980 1638 1812 393 1712 1555 2059 706 733 875 1964 1685 1875 1786 1210 520 1989 1543 1683 1276 1064 299 1352 1566 1263 1243 1073 2090|
Now we’ve got everything we need to “carve” the files out the “three.dd” file. So I wrote a python script that would generate the files on my own harddrive, by reading the two tables and joining them on the FileID, then assembling the files from the offsets. The script ran and everything was peachy …
That is until I tried to open one of the files and it was total garbage 🗑. The pdf file on “stack smashing”? it was filled with random python code intermingled with binary data. Not good… Fortunately for me I knew that
autosolver was also doing the challenge so I hit him up on
<messenger app>, and it turns out that I (as I mentioned earlier) had misunderstood the part about the sector offsets. I thought that sector
0 would be placed at offset
81920, when infact sector
0 was at offset
0 😱. A GREAT BIG THANK YOU TO
Fixing that mistake, and running the script again gave me all the files… with the proper content this time quickly trawling through the files I found the flag in an image file.
⏱ Total time: 2 hours 23 minutes and 47 seconds
Almost forgot the script (⚠️ ugly code!):
TL;DR: It was a super fun challenge, I made a stupid mistake but I learned a lot. It was also my first time using Pythons bytearrays… they’re pretty pretty smart! And a big thank you to