Contents

💣 P0wn3d 3

A detailed write-up of the Pwn challenge 'P0wn3d 3' from WolvCTF - 2025

/images/WolvCTF-2025/Pwn/P0wn3d3/challenge_presentation.png
Challenge Presentation

📊 Challenge Overview

Category Details Additional Info
🏆 Event https://wolvctf.io/challenges#p0wn3d_3%20-%20Pwn-31 Event Link
🔰 Category Pwn 💣
💎 Points 50 Out of 500 total
⭐ Difficulty 🟢 Easy Personal Rating: 1/10
👤 Author Didkd Profile
🎮 Solves (At the time of flag submission) 1 solve rate
📅 Date 23-03-2025 https://wolvctf.io/challenges#p0wn3d_3%20-%20Pwn-31
🦾 Solved By mH4ck3r0n3 Team: QnQSec

📝 Challenge Information

Time for a little bit of control flow redirection nc p0wn3d3.kctf-453514-codelab.kctf.cloud 1337

🎯 Challenge Files & Infrastructure

Provided Files

Files:

🔍 Initial Analysis

First Steps

This is the third challenge of the P0wn3d series. To follow the entire series, since I won’t explain everything again, here are the links: (P0wn3d, P0wn3d 2). Since this is a ret2win challenge, it is a good practice to check the security flags using checksec:

/images/WolvCTF-2025/Pwn/P0wn3d3/checksec.png
CheckSec

We have Partial RELRO, meaning only the Global Offset Table (GOT) is partially protected. There’s no stack canary, so we don’t have to worry about potential Buffer Overflow issues. Next, we have NX enabled, which means the stack is non-executable. Finally, No PIE means the binary has fixed memory addresses, and Stripped No, meaning the binary has not been stripped of debugging symbols. Let’s move on to the analysis of the attached files, as we were also given a C source code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void get_flag(void)
{
   char *args[] = {"/bin/cat", "flag.txt", NULL};
   execve(args[0], args, NULL);
}  

int main(void)
{
   char buf[32];
   ignore(); /* ignore this function */  
   printf("Now this is an original challenge. I don't think I've ever seen something like this before\n");
   sleep(2);
   gets(buf);
   puts("Drumroll please!");
   sleep(2);
   return 0;
}

As we can see, the get_flag() function is present to print the flag, but if we pay attention, it is not called! However, as with the other challenges, we have a Buffer Overflow. The buf variable has a 32-byte buffer, but the gets(buf) function is unsafe because it doesn’t check the length of the input. This allows us to write beyond the 32 bytes allocated for buf, overwriting the saved return address in the stack. So once we exceed the buffer size, we will cause a buffer overflow, which overwrites the stack registers. When a function is loaded, its return address is passed, which “remembers” where to return to after the function execution, as it is located in a different part of memory. The problem is that with a buffer overflow, this address can be overwritten…

🔬 Vulnerability Analysis

Potential Vulnerabilities

  • Buffer Overflow

🎯 Solution Path

Exploitation Steps

Initial setup

The RBP (Return Base Pointer) register is 8 bytes and is located before the saved return address, so we need to craft a payload that fills the buffer of the buf variable (32 bytes) and then also fills the RBP. What happens if we set the saved return address to that of the get_flag() function? Exactly… the program, instead of terminating, would jump to the get_flag() function and execute it. This way, we can obtain the flag!

Exploitation

So the first thing I do is find the memory address of the get_flag() function, which we’ll need to overwrite the saved return address. I can do this directly in gdb with p &get_flag:

/images/WolvCTF-2025/Pwn/P0wn3d3/get_flag_address.png
get_flag Address

Or with objdump by running objdump -D <filename> | grep "get_flag". As we can see, the address is 0x4011a5. Now that we have the address and know that it will remain the same on the server due to No PIE, we can construct the final payload consisting of 40 junk bytes (32 for the buffer + 8 for the RBP) + the get_flag address. I did this directly in the exploit, and when I ran it, I found the flag.

🛠️ Exploitation Process

Approach

The exploit connects to the server and sends A*40 to fill the buffer of the buf variable, then appends the address of the get_flag() function to overwrite the return address and execute the get_flag() function, thus retrieving the flag.

🚩 Flag Capture

Flag

wctf{gr4dua73d_fr0m_l1ttl3_p0wn3r!}

Proof of Execution

/images/WolvCTF-2025/Pwn/P0wn3d3/automated_flag.png
Automated Flag
Screenshot of successful exploitation

🔧 Tools Used

Tool Purpose
Python Exploit
PwnGDB Dynamic Analysis

💡 Key Learnings

Skills Improved

  • Binary Exploitation
  • Reverse Engineering
  • Web Exploitation
  • Cryptography
  • Forensics
  • OSINT
  • Miscellaneous

📚 References & Resources

Similar Challenges

Learning Resources


📊 Final Statistics

Metric Value Notes
Time to Solve 00:10 From start to flag
Global Ranking (At the time of flag submission) 8/445 Challenge ranking
Points Earned 50 Team contribution

Created: 23-03-2025 • Last Modified: 23-03-2025 Author: mH4ck3r0n3 • Team: QnQSec