Isak Stöt

OutPost24 2025 CTF Writeup

My solutions for the 2025 Christmas CTF hosted by OutPost24.

Forensics

False Flag

the provided png image for the challenge

This challenge provided an png file called false_flag.png. I ran the command 'grep -a "O24{" false_flag.png' to see if the flag format was in the file as plain text. This revealed the flag:
O24{f!l3$_4R3_noT_A1w@y$_wh@t_7heY_sE3m_T0_b3}

FlagInThePackets

This challenge provided a pcap file containing network traffic. I opened it in WireShark and followed the TCP stream, which revealed this:
WireShark Output showing a TCP stream
In-between the junk, the stream contained lines like "You picked up the X character at position Y:Z". Taking a closer look at the characters I found all the characters of the flag format "O24{}" scattered. This suggested the characters needed to be rearranged based on their positions.

"O" had position 1:1, "2" was at 7:1, "4" at 55:3, "{" at 2:5. This suggested first sorting by the second number (Y coordinate), then by the first. So I wrote a small python script to extract the characters and their positions, sort them based on the 2D coordinates, and then join them to get the flag "O24{ORDER_IS_IMPORTANT}":


with open('./tcp_stream.txt', 'r') as file:
    stream = file.read()
# Split into lines
lines = stream.splitlines()
flag_chars = []
positions = []
for line in lines:
    # Find the "You picked up the X character at position Y:Z" lines
    if "You picked up the" in line:
        # Extract character and position
        parts = line.split()
        char = parts[4]
        flag_chars.append(char)
        position = parts[-1]
        positions.append(position)
chars_data = []
position1 = []
position2 = []
# Split positions into two lists
for pos in positions:
    position1.append(int(pos.split(':')[0]))
    position2.append(int(pos.split(':')[1]))
# Combine characters with their positions
for c, p1, p2 in zip(flag_chars, position1, position2):
    chars_data.append({'char': c, 'x': p1, 'y': p2})
# Sort characters based on a 2d grid (y first, then x)
sorted_data = sorted(chars_data, key=lambda k: (k['y'], k['x']))
# Join all the sorted characters to get the flag
flag = "".join([item['char'] for item in sorted_data])
print(f"Flag: {flag}")
                

Merry Mischief

For this challenge we got a file called "Mysterious.jar". JAR files are essentially just ZIP files, so I started by extracting the file. Right away I found a file called "end.png" which contained: This appeared to be the end of the flag, but I still needed to find the rest.

I opened the original file in a text editor and saw some interesting file headers. I then opened Cyberchef to preserve the binary data, which might get lost in a normal text editor, and extracted a JPG file:

Which contained:

And a PDF file:

Which contained an image:

Combining all of them gave the flag: "O24{Oops:)BitYourNose^w^}"

WEB

Bank

I unfortunately don't have any screenshots from the original website, but it had a feature to submit tickets for support or asking questions.


I tried creating a ticket myself and noticed that there was an ID in the URL, suggesting an Indirect Object Reference (IDOR). This could mean that anyone can access the ticket as long as they know the ID, so I tried changing the ID in the URL to the one before mine and it showed a different ticket. However, I did not have the flag yet. I tried going back a couple more ID's and tried reading the first 5 ID's as well, but still nothing.


So I started up BurpSuite to enable me to capture the HTTP requests and modify it before it is sent. In this case we want to send the same request but just change the ID to try everything within a certain range. This can be done using the "Intruder" feature in Burp Suite.


Recreated example of what was done:

Burp Suite Intruder

This allowed me to easily go through the tickets and eventually find one that contained the flag:

O24{N3v3r_Un3r35t1m4t3_Th3_p0w3r_Of_ID0R}