Iron CTF 2024 - Writeups
This is a writeup for some forensics and web challenges from Iron CTF 2024. This was one of the more difficult CTFs I’ve played, and I only managed to solve a few challenges, but my team managed to solve a few more challenges.
JWT Hunt [Web]
We are presented with a simple web application that has a register and login page. After registering and logging in, we inspect the cookies to find a cookie with the session token and a cookie with the second part of “the key”. It is 16 characters long, which prompts towards forging a JWT token with the obtained key in order to get admin privileges and obtain the flag.
We plug the token into jwt.io and we notice that the username is saved in the token. We can change the username to “admin” and sign the token with the key we need to find. We then replace the session cookie with the forged token and refresh the page to get the flag.
Digging deeper, we find the robots.txt and sitemap.xml files, which contain the first and third part of the key. But where is the 4th part?
The robots.txt file hints towards a /secretkeypart4 directory, but accessing it gives us a 400 Bad Request error. Using Burp Suite, we change the request from GET to HEAD and are able to obtain the 4th part of the key.
In jwt.io, we change the username to “admin” and sign the token with the key we found. We replace the session cookie with the forged token and refresh the page to get the flag.
Flag: ironCTF{W0w_U_R34lly_Kn0w_4_L07_Ab0ut_JWT_3xp10r4710n!}
Random Pixels [Forensics]
We are given the flag PNG image file and a file called enc.py
that seems to shuffle the rows and columns of the image given a seed derived from the time of execution.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import random, time, numpy
from PIL import Image
from secret import FLAG
def randomize(img, seed):
random.seed(seed)
new_y = list(range(img.shape[0]))
new_x = list(range(img.shape[1]))
random.shuffle(new_y)
random.shuffle(new_x)
new = numpy.empty_like(img)
for i, y in enumerate(new_y):
for j, x in enumerate(new_x):
new[i][j] = img[y][x]
return numpy.array(new)
if __name__ == "__main__":
with Image.open(FLAG) as f:
img = numpy.array(f)
out = randomize(img, int(time.time()))
image = Image.fromarray(out)
image.save("encrypted.png")
Obtaining the time seed is easy. Running exiftool on the image gives us the time the image was created, which we can use as the seed. We can then reverse the shuffling process to obtain the original image.
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import random
import numpy
from PIL import Image
import time
def unrandomize(img, seed):
random.seed(seed)
new_y = list(range(img.shape[0]))
new_x = list(range(img.shape[1]))
random.shuffle(new_y)
random.shuffle(new_x)
# Create the inverse mappings
inverse_y = [0] * len(new_y)
inverse_x = [0] * len(new_x)
for i, y in enumerate(new_y):
inverse_y[y] = i
for j, x in enumerate(new_x):
inverse_x[x] = j
original = numpy.empty_like(img)
for i, y in enumerate(new_y):
for j, x in enumerate(new_x):
original[y][x] = img[i][j]
return numpy.array(original)
def main():
# Load the encrypted image
with Image.open("encrypted.png") as f:
img = numpy.array(f)
# Given file modification timestamp
given_timestamp = 1727902960
out = unrandomize(img, given_timestamp)
image = Image.fromarray(out)
image.save(f"decrypted_{given_timestamp}.png")
if __name__ == "__main__":
main()
Running the script gives us a QR code, which when scanned gives us the flag.
Flag: ironCTF{p53ud0_r4nd0m_f0r_4_r3450n}
Conclusion
Iron CTF really pushed my limits, and I’m glad I was able to solve a few challenges. I hope to participate in more CTFs in the future and improve my skills. If you have any questions or suggestions, feel free to reach out to me. Until then, happy hacking!