Post

P3rf3ctr00t CTF Challenge Creation

This blog post explains the thought process in creating the P3rf3ctr00t CTF as well as how different challenges were meant to be solved.

I had the privilege to create EASY CTF challenges for the P3rf3ctr00t CTF held on the weekend of 15th - 17th November 2024. This was fun and successful being the first timing hosting a local CTF that attracted players from all over the world. I created 8 easy challenges summarized below:

ChallengeSolves/142
SheetsNLayers - Flag 138
See Ya33
Dive25
SheetsNLayers - Flag 221
SheetsNLayers - Flag 321
SheetsNLayers - Flag 420
tGIF16
YAZ (Yet Another Zine)5

In this Post, I get to share with you my thought process in creating some of the challenges and perhaps how they were meant to be solved. Feel free to leave a comment incase you used a different technique to solve any of the challenges. I’m sure some challs had some un-intended solves 😅

Misc

DIVE

Challenge Overview:

Docker is really fun. DIVE deeper and you’ll probably get the flag 😎

Challenge Creation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Base Image
FROM alpine:latest

# Install basic tools
RUN apk update && apk add curl vim git
RUN echo "This is just a dummy layer" > /dummy_layer.txt

# Copy the flag file into the container
COPY flag.txt /root/flag.txt

# Final layer to make it less obvious
RUN apk del vim curl git && rm -rf /var/cache/apk/*

# Remove the flag file
RUN rm /root/flag.txt
  • Base Image: Alpine Linux (alpine:latest)
  • Flag Placement: The flag is initially placed in a file named flag.txt located in the /root directory. It is then removed in a subsequent layer to simulate the need for forensic analysis.

Challenge Artifact: The Docker image is saved as a tarball named dive.tar.

1
2
3
4
5
6
7
8
# Build image
sudo docker build -t dive .

#Confirm image has been build:
sudo docker images

# Export docker image to a tarball
sudo docker save -o dive.tar dive:latest

Solution

In order to interact with the file:

Import the docker image by running:

1
➜  docker load -i dive.tar

Next we can analyze the image using a tool like Dive

1
sudo dive dive:latest

I’m sure most people went straight into uncompressing the file and retrieving the flag, but perhaps we can try and understand a little bit about docker and how one can analyze/do forensics on an image.

But before we continue, lets first talk about docker layers. Docker images are built in layers, with each layer representing a single instruction in the Dockerfile (e.g., RUN, COPY, ADD). These layers are:

  • Immutable: Once created, a layer cannot be changed. If a Dockerfile is modified, only the changed layers and the ones that depend on them are rebuilt.
  • Shared: Layers can be reused across multiple images, reducing storage requirements and speeding up builds.
  • Union Filesystem: Layers are stacked, and the topmost layer represents the current state of the image.

Feel free to watch the video linked below for more information about docker images/layers.

When analyzing Docker layers, especially from a compressed tarball of the image, each layer corresponds to a filesystem snapshot and metadata detailing how it was created.

Lets see how we can analyze the docker image.

Run Dive as follows:

1
sudo dive dive:latest

You will then get an terminal interactive. In the top most layer, you will get a list of layers and respective commands executed in each layer. To navigate/inspect different layers, use arrow keys

In this case, you will notice a flag is being copied and then removed in the /root/flag.txt.

Take note of the Digest value (SHA-256 hash) highlighted.

image

To further interact with the layer, press Tab followed by Ctrl + U to confirm the flag was actually copied to /root/flag.txt

image

If you press Tab again and navigate to the last layer, you will see the flag has deleted (In red)

image

However, dive cannot directly extract files from the layers. For manual extraction of files, you need to decompress and inspect the specific layers individually.

Simply decompress as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sudo tar -xvf dive.tar

blobs/
blobs/sha256/
blobs/sha256/19dab3bdc1fcfe59f45ba4071f6b39bdac309e2a587c118ba4e56f3b1a69e44d
blobs/sha256/26b69c4d39add31e9fcbb2617c849d8b27d0a9aeb871a15b045130c0f7dd573a
blobs/sha256/32595609fc55156dd4899f32e2e028da7bea813bb8b47647960069b4ea3b2ef7
blobs/sha256/3a3688710208498d9f2acfd70943158de61368020992f8f9f240ab34bc5dfdef
blobs/sha256/3d850ad76eff62bd6752b61c8b9013704b466b07f8cb65ccd3b016baf065e2d9
blobs/sha256/63dc39fd7e9b99b37201364b25a176b4c419ad819aa4da6fdfd76bdce3eb386a
blobs/sha256/6c091594b15387b41416296ad385a363c788af1cf0d55d234750bb3848c8e758
blobs/sha256/7186a421d902123801bd7ccb204f9e9631175ccaf11af0de7d01f4e6b5d2ed8e
blobs/sha256/75654b8eeebd3beae97271a102f57cdeb794cc91e442648544963a7e951e9558
blobs/sha256/7cbb6c3a98354fe830deee81003184b1299dd36f428f9ebf67ca786724929962
blobs/sha256/8f2795eff7243c570c41a0286e8ede760453f6624245669cf3f392480a2d23bf
blobs/sha256/e7ee579f9e5e6fc497a781027dfe843141e644be474043ebca05859c271583b2
blobs/sha256/e846a361a9a5ab27aff3e73ed6388d661645d561fd2d9af1f10bd2ae1e2a98a0
blobs/sha256/e9eec7ad63db20407abcb5f63fa90e2ee06d8a94787b001f23614f729ce82957
index.json
manifest.json
oci-layout
repositories

If you care to understand each of the extracted files, I’ll just do a high level overview of each.

  • repositories - Maps repository names to their respective digests or tags.

image

image

  • manifest.json - lists all the layers and configuration details of the Docker image in the order they are applied. It acts as a roadmap, linking layer blobs and metadata required to reconstruct the image.

image

  • index.json - specifies the schema version and the manifest type (e.g., application/vnd.oci.image.manifest.v1+json) for the image. It serves as a catalog linking image metadata to its respective layers and configurations for OCI-compliant tools.

image

Finally, we have the blobs/sha256 directory , which contains all the image layers and metadata stored as content-addressable objects, identified by their SHA-256 hashes. Each file in this directory represents either a filesystem layer (added or modified files) or configuration data for the image. These blobs are referenced in manifest.json to reconstruct the image’s filesystem and settings.

image

It is also worth noting that this layers are usually a .tar.gz archives and need to be decompressed further.

While using Dive, I’d mentioned you need to note down the Digest value (SHA-256 hash) of the layer containing the flag.txt, In this case: 8f2795eff7243c570c41a0286e8ede760453f6624245669cf3f392480a2d23bf

Extract the specific layer containing the flag, you get:

image

r00t{41W4Y5_4N41YZ3_D0CK3r_14Y3r5}

Complimentary Resources

  • https://www.docker.com/resources/container-images-interactive-deep-dive-dockercon-2023/
  • https://docs.docker.com/get-started/docker-concepts/building-images/

tGIF

Challenge Overview:

Docker is really fun. DIVE deeper and you’ll probably get the flag 😎

Solution

This was another relatively easy and common challenge that involved fixing a corrupt GIF file (as the name suggests tGIF) and resizing its size in order to reveal the flag. Lets start looking into it.

First, the file doesnt have a file extension. Running file on it, you will notice its a bunch of data. Perhaps we can check it as well with a hex editor.

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  file tGIF
tGIF: data
➜  xxd tGIF | head
00000000: 4704 4638 3961 9001 c800 f700 0000 0000  G.F89a..........
00000010: 251e 253a 3134 3d43 6246 3e48 4835 324e  %.%:14=CbF>HH52N
00000020: 4d65 4f3a 3456 4f61 5745 465a 3f38 5e46  MeO:4VOaWEFZ?8^F
00000030: 3a5e 515c 5f47 425f 6272 5f65 8660 4c4c  :^Q\_GB_br_e.`LL
00000040: 605b 6264 6671 674d 4568 6773 696d 856a  `[bdfqgMEhgsim.j
00000050: 483b 6a70 7a6b 5951 6c4f 3d6c 504c 6f50  H;jpzkYQlO=lPLoP
00000060: 456f 5150 6f5f 626f 6f85 7052 5671 737a  EoQPo_boo.pRVqsz
00000070: 7277 8173 5a51 7367 7273 7990 766f 787a  rw.sZQsgrsy.voxz
00000080: 5c53 7a60 5f7b 7782 7c7e 907c 8089 7c82  \Sz`_{w.|~.|..|.
00000090: a183 5546 835f 5783 615f 838f ac84 685a  ..UF._W.a_....hZ

Here you will notice it is a GIF file. (G.F89a). This looks odd as the oiginal GIF header would look like GIF89a. This means that the magic bytes for I have been modified.

From xxd’s output above, we have: 47 04 46 38 39 61. If you head over to Wikipeda -File Signatures, we get the GIFs correct magic bytes as 47 49 46 38 39 61 . Notice 49 was swapped with 04. That’s what you need to fix.

image

Notice 49 was swapped with 04. That’s what you need to fix. Using your favourite hex editor, change that part and save the file as tGIF.gif as shown:

image

Opening the file, it renders as follows:

image

This is where the second part of this challenge continues. It involves fixing the GIF size in order to view the specific part containing a flag. Part of the challenge description, there was a second hint mentioning “dimensions are just off

Using exiftool, you can check the Image Size property as shown.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
➜  exiftool tGIF.gif
ExifTool Version Number         : 13.00
File Name                       : tGIF.gif
Directory                       : .
File Size                       : 3.0 MB
File Modification Date/Time     : 2024:11:23 18:23:59+03:00
File Access Date/Time           : 2024:11:23 18:37:05+03:00
File Inode Change Date/Time     : 2024:11:23 18:37:05+03:00
File Permissions                : -rw-------
File Type                       : GIF
File Type Extension             : gif
MIME Type                       : image/gif
GIF Version                     : 89a
Image Width                     : 400
Image Height                    : 200
Has Color Map                   : Yes
Color Resolution Depth          : 8
Bits Per Pixel                  : 8
Background Color                : 0
Animation Iterations            : Infinite
Comment                         : GIF edited with https://ezgif.com/add-text
Frame Count                     : 21
Duration                        : 2.73 s
Image Size                      : 400x200
Megapixels                      : 0.080

Here, we see the size as 400x200. Perhaps not the standard size for a GIF? Doing a quick search on google, you will learn the standards size is 640 width ×480 height. There are different ways one could go about fixing this:

image

Or

This site auto resizes and renders the complete GIF for you

image

Others Include:

render

r00t{d38252762d3d4fd229faae637fd13f4e}

See Ya

Challenge Overview:

You a fan of Zines? Well here’s my fav from the 90’s 🙂 , The “Hacker’s Manifesto”. Not every one will SEE it, but oh well, good luck.

Of course if you’re SEEing this, here’s the original one: https://phrack.org/issues/7/3.html

Solution

The challenge presents the Hacker’s Manifesto in Braille, with the keyword SEE hinting at translation.

image

Players were basically expected to decode the Braille using tools like CyberChef’s From Braille recipe. After decoding, the text can be compared to the original manifesto on Phrack.org. A difference in one paragraph reveals the hidden flag.

image

r00t{edae962125814dae54fa6e4b628fd6eb}

YAZ (Yet Another Zine)

Challenge Overview:

Yet Another Zine, damn. 🤦‍♂️ Anyway, I TWEETed about this zine no one seemed to relate. Can you maybe make sense out of it? 😅

For those who unlocked/redeemed the hint for 20 points 😭😭😭, It read:

flag: r00t{} , Flag starts with a 9 and ends with a 2 😎

Solution

The word TWEET has been stressed intentionally. Looking at the file given, it might looking visually similar to the original, although its not. A hidden message has been added to the text. If you do some research on google, you will learn of a steganography technique used to hide messages in tweets. There are many sites to decode such text. For example:

  • https://holloway.nz/steg/
  • https://wulfsige.com/crypto/

image

r00t{9333d0e7c6e1e085b9115d9d1971cce2}

SheetsNlayers-1

Challenge Overview:

What is flag 1?

Solution

You are given a zip achieve containingSheetsNlayers.vhdx

Simply put, “This is Windows hard disk image file. It acts like a real, physical hard drive, but is stored in a single file that’s located on a physical disk like a hard drive. VHDX files can contain an entire operating system for purposes such as testing software or running older or newer software not compatible with the host operating system, or simply to hold files like any other storage container.” ~Source: Lifewire

On windows, there are multiple ways to access the image:

Method 1

  • Double click on the file to mount it

image

image

Flag 1 can be found there:

image

However, it looks like base64 but strange. The trick was reversing the string and then base64 decoding. CyberChef Recipe

image

r00t{327a6c4304ad5938eaf0efb6cc3e53dc}

Method 2

Alternatively, you could have opened the vhdx file with FTK Imager as follows:

image

image

image

image

Method 3

  • On start, search for create and format hard disk partitions or just hit win+r and search for diskmgmt.msc
  • Click on Action > Attach VHD
  • Choose Disk Location and click Ok as shown:

image

image

SheetsNlayers-2

Challenge Overview:

What is flag 2?

Solution

On the same disk file, there was a note.txt file with further instructions:

image

Unzipping moreflags.zip requires you to have a password:

image

Get cracking with john & zip2john as shown:

1
2
3
4
5
6
7
8
9
10
11
➜  zip2john moreflags.zip > forjohn
ver 2.0 moreflags.zip/moreflags.xlsx PKZIP Encr: cmplen=10899, decmplen=15420, crc=F053B033 ts=7C3A cs=f053 type=8
➜  john -w=~/Desktop/rockyou.txt forjohn
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
ne.11.88         (moreflags.zip/moreflags.xlsx)
1g 0:00:00:03 DONE (2024-11-17 20:04) 0.3257g/s 1683Kp/s 1683Kc/s 1683KC/s neagupavel..nazir4u
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

After getting the password (ne.11.88) you extract an .xlsx file.

After that I later realized there were different ways to approach it…probably an oversite on my end but oh well. 🤦‍♂️

Opening the file on Excel, you get what looks like a blank workbook:

image

Unintended method 1 - Strings

Running strings on the file, you will notice that there are 4 sheets in the doc… Probably hidden?

1
2
3
4
5
6
7
8
9
10
➜  strings moreflags.xlsx

--- redacted ----

xl/worksheets/_rels/sheet1.xml.relsPK
xl/worksheets/_rels/sheet2.xml.relsPK
xl/worksheets/_rels/sheet3.xml.relsPK
xl/worksheets/_rels/sheet4.xml.relsPK

--- redacted ----

Unintended method 2 - Online tools

If you are familiar with IRIS-H Digital Forensics- an online site to analyze malicious documents, you could easily get hints to flag 2-4.

Eg:

image

image

Intended

Check for hidden sheets by right clicking on the visible sheet and click on unhide:

image

You will then get a list of hidden sheets.

image

Click on each to unhide. For flag 2, it’s preety easy to locate it in cell A1.

image

However its not as straight foward, but rather a weirdly looking string E\K1hHXp_E3+?<-2_nlQAi"$R2_m0G2.U3.AN;YW2e?DQ0ms … So looking it up on Cyberchef, you will get the flag Base85 encoded.

image

r00t{df38bae72ccf3f172345dcee96a7ea21}

SheetsNlayers-3

Challenge Overview

What is flag 3?

Solution

Unhiding Sheet 3 , it kinda looks blank, but the trick was more of searching/finding the exact cell containing the flag…

image

In this case the flag was in cell FLA3.

1
TVRFMElEUTRJRFE0SURFeE5pQXhNak1nTlRZZ05UVWdPVGtnT1RnZ09Ua2dPVGNnTlRRZ05Ea2dOVGNnTlRFZ05UVWdNVEF4SURFd01TQXhNREVnTlRRZ05UQWdORGdnTlRZZ05Ea2dOVEFnTVRBeUlEVTNJREV3TVNBMU5pQXhNREVnTlRNZ09Ua2dOVE1nTlRFZ01UQXdJRFE0SURRNElERXlOUT09

Again, using Cyberchef to analyze the string, you will notice it is double base64 encoded. From the decoded string, you get another hex string which you also need you need to add to the recipe.

image

image

r00t{87cbca61937eee620812f9e8e5c53d00}

SheetsNlayers-4

Challenge Overview

What is flag 3?

Solution

Unhiding sheet flag4, you will realize a section of cells written No comment. I thought this was gonna be preety straight foward but oh well 😅

image

So the trick here was basically to inspect the sheet for comments. Simply click on Info on the file tab:

image

Check for issues:

image

image

Make sure Comments is checked then click on Inspect

image

You will notice that there is actually a comment.

image

To view the comment, navigate to the Review tab and click on Show All Comments

image

You will then see a flag in cell B4. C__ELgf4342ehbf666ea_ga7h6g6d4db5__N

image

Another wierd string 😑. Doing a quick lookup on CyberChef or dcode.fr, you get the flag.

image

r00t{87cbca61937eee620812f9e8e5c53d00}

That comes to the end of my writeup, I hope you enjoyed it or learnt something new (for the beginners). My team members from Pwnus did a blog post on the rest of the challenges they managed to solve. Feel free to check it out. ✌ See you on my next post.

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.