Excuse the ads! We need some help to keep our site up.
House of Orange
- House of Orange is a technique that exploits the process of finding an error and printing an error message.
- If _int_malloc () does not find a free space in bins, the unsorted bin checks for available chunks.
- Then check the size of the chunks stored in the Unsorted bin (bins) to see if the memory is corrupted.
- If the chunk is corrupted, call malloc_printerr() to print an error message.
- The format of the output from malloc_printerr () depends on the first argument (action).
- If the value of action is equal to 5, request the output of the string stored in the str variable.
- And if the first bit is set in the value, "*** Error in`% s':% s: 0x% s *** \ n "will output an error message.
- The first string is the path of the program, the second string is the error message, and the third-string is the address of the memory.
__libc_message() prints out the contents of the error message received from malloc_printerr().
- Then print backtrace, memory map and call abort() to terminate this application.
- abort () unlocks SIGABRT and calls fflush() to flush all streams.
- fflush() is a macro function that calls _IO_flush_all_lockp().
- _IO_flush_all_lockp () stores "_IO_list_all" in "fp".
- And if "fp" is not "null", it executes the body code of the while loop.
- The function checks whether the value of "fp->_ mode" is less than or equal to 0 and whether the value of "fp->_ IO_write_ptr" is greater than the value of "fp->_ IO_write_base".
- Then check if the return value of _IO_OVERFLOW () is equal to EOF.
- If all of these conditions are true, we store "EOF" in the "result" variable.
- If the value of last_stamp is equal to the value of _IO_list_all_stamp, store the value of fp→_chain in fp and repeat the code before it.
The important function in the House of Orange is _IO_OVERFLOW.
This function is a macro function, the first argument is fp and the second argument is a character or EOF.
The function calls JUMP1 () and passes FP and CH received from __overflow and _IO_OVERFLOW() as argument values.
JUMP1 () expresses FP received from __IO_OVERFLOW () as THIS.
Then pass the value to __IO_JUMPS_FUNC () to return a pointer to the vtable that THIS has.
- JUMP1 () calls the function stored in that pointer, and the name of the function to be called is called via FUNC.
- Since _IO_OVERFLOW () passed __overflow as the value of FUNC, it calls the __overflow function on the vtable owned by THIS.
House of Orange changes the value of _IO_list_all to call the desired function.
- What you need to know in the House of Orange is that you need to understand the structure of the _IO_list_all variable.
- _IO_list_all uses the _IO_FILE_plus structure, which contains a file variable with the _IO_FILE structure and a * vtable variable with the _IO_jump_t structure.
The _IO_FILE structure contains pointers and information related to file input and output.
- The _IO_FILE structure is a vtable, and the function corresponding to the second argument can be called using a macro function called JUMP_FIELD ().
The House of Orange must be able to overwrite the values of the top chunk's prev_size, size, and bk, and can be implemented if data can be written to the chunk.
- Requests memory allocation, overwriting the value of the top chunk's "size" with a very small value.
- The value should be appended with the PREV_INUSE flag.
- Requests to allocate more memory than Top chunk->size.
- This expands the memory area of Arena and changes the address of the top chunk.
- Since the old Top chunk is larger than the size of the fastbin, it is placed in the Unsorted bin, and the address of Arena's top is stored in fd, bk.
- To change the value of _IO_list_all, store the value of "_IO_list_all" minus 16 in bk of the chunk placed in the Unsorted bin.
- To change the value of fp-> chain, save the first argument value (up to 8 bytes) of the function to be called in "size" and 0x61 in "bk".
- To bypass the "fp-> _ mode <= 0 && fp-> _ IO_write_ptr> fp → _IO_write_base" condition, create a fake _IO_list_all (_IO_FILE, _IO_jump_t) based on the address of the chunk placed in the Unsorted bin.
- "Address of chunk placed in Unsorted bin 0xC0" becomes fp-> _ mode and stores 0 in that variable.
- "Address of chunk placed in Unsorted bin + 0x20" becomes fp→_IO_write_base and stores 2 in that variable.
- "Address of chunk placed in Unsorted bin + 0x28" becomes fp-> _ IO_write_ptr and stores 3 in that variable.
- "Address of chunks placed in unsorted bin + 0xd8" becomes Fake _IO_jump_t and stores the "address of chunks placed in unsorted bin + 0x60" in that variable.
- Store the address of the function to call in Fake _IO_jump_t + 0x18.
- Requests memory allocation that is smaller than the size of the chunks placed in the Unsorted bin.
- The allocator places the chunks in bins.
- This changes the value of _IO_list_all to the address of Arena→top.
- The value of bins  will be "the address of Arena → top", but the value of bins -> bk will be 0, so malloc_printerr () will be called.
- Call the functions in the following order.
- malloc_printerr() → __libc_message() → abort() → _IO_flush_all_lockp()
- _IO_flush_all_lockp () stores the value stored in fp-> chain in fp because the value of _IO_list_all is the address of Arena → top.
- fp-> chain is located 0x40byte after fp and the location is bins .
- This causes the address of fake_IO_list_all to be stored in fp and bypasses the conditional statement to call the function stored in "Fake _IO_jump_t + 0x18".
- For example, allocate 1 memory and overwrite the top chunk's size with 0xc01.
- Requesting a memory allocation larger than this value increases the size of Arena and places the top chunks in the Unsorted bin.
- Save 0x7ffff7dd2510 (_IO_list_all (0x7ffff7dd2520)-16) to bk of chunk (0x602400) placed in the Unsorted bin.
- Save the first argument "/bin/sh" to prev_size and change the value of size to 0x61.
- Store 2 in 0x602420 (fp → _IO_write_base), and store 3 in 0x602428 (fp-> _ IO_write_ptr).
- And store 0 in 0x6024c0 (fp-> _ mode).
- Store 0x602460 in 0x6024d8(Fake _IO_jump_t) and the address of the function to call(0x4006e5) in 0x602470.
- If you request a memory allocation of size 10, the value of _IO_list_all is changed to 0x7ffff7dd1b78 and 0x602400 is stored in bins  and bins .
- _IO_flush_all_lockp () exits its if () because fp has a value of 0x7ffff7dd1b78 and fp-> _ mode has a nonzero value, and stores the fp-> chain (0x602400) value in fp.
- _IO_flush_all_lockp () calls _IO_OVERFLOW () because it satisfies the "fp-> _ mode <= 0 && fp-> _ IO_write_ptr> fp → _IO_write_base" condition.
- _IO_OVERFLOW () uses a fake vtable (0x602460) to call 0x4006e5 stored at 0x602478.
The code is the code described in the previous example.
Request allocation of memory of size 0x400-16.
Overwrite the top chunk's size value with 0xc01 and request an allocation of memory of size 0x1000.
The first argument of the function to be called is stored in prev_size of the Unsorted chunk, 0x61 in "size", and address of io_list_all-0x10 in "bk".
Then create a fake _IO_list_all (_IO_FILE, _IO_jump_t) behind the Unsorted chunk and request a memory allocation of size 0x10.
- Check the change in the value of main_arena → top → size at 0x400693 and 0x40069f, and check the change in main_arena at 0x4006ab after calling malloc ().
- How to find the address of _IO_list_all using the address of main_arena → top is checked at 0x4006dc, 0x4006e2.
- Set breakpoints at 0x400781 for analysis of __libc_message(), _ IO_flush_all_lockp().
After allocating memory, save the top chunk's address (0x602400) to the variable top (0x7fffffffe428) and overwrite the value of top → size (0x602408) with 0xc01.
- When you request a memory allocation of size 0x1000, the address of the expanded memory (0x602400 → 0x624010) is stored in main_arena.top.
- Because the top chunk has changed, the value of main_arena.top.size also changes, and the previous top chunk is registered in the Unsorted bin.
- And since the size of the previous Top chunk was smaller than the requested size, we expanded the Arena's area.
- The values of main_arena.system_mem and max_system_mem have been extended from 0x21000 to 0x63000.
- The program adds 0x9a8 from the main_arena.top address to the address of * _IO_list_all, and stores the value in io_list_all.
- Save the address of _IO_list_all minus 0x10 (0x7ffff7dd2520-0x10 = 0x7ffff7dd2510) to main_arena.bins  .bk (0x602418).
- Store the string "/bin/sh" in main_arena.bins  .prev_size.
- A fake _IO_list_all (_IO_FILE, _IO_jump_t) was created at 0x602400.
- The value of fp-> _ mode is 0x0, the value of _IO_write_base is 0x2, and the value of _IO_write_ptr is 0x3.
- These values can pass the "fp-> _ mode <= 0 && fp-> _ IO_write_ptr> fp → _IO_write_base" condition.
- 0x602460 is stored in the fake vtable (0x6024d8), and the address of the winner () function is stored 0x18 away from the value.
- Requesting a memory allocation of size 10 determines that memory corruption has occurred and calls malloc_printerr().
- Set the address of __libc_message (), _IO_flush_all_lockp () to Breakpoint for analysis.
The program is stopped at the __libc_message () function.
Before calling the function, the code called _int_malloc() called malloc_printerr() and malloc_printerr() called __libc_message().
- The value of _IO_list_all is now the address of main_arena.top by _int_malloc ().
- And the address of the previous Top chunk (0x602400) and the address minus 16 from _IO_list_all was placed in the Unsorted bin.
- Previous top chunks were placed in main_arena.bins  and main_arena.bins , which correspond to small bins.
- When you run the code, __libc_message() prints the message received from _int_malloc, and additionally prints backtrace and memory map information.
- To check the behavior of _IO_flush_all_lockp (), set breakpoints as follows.
- Check the value stored in run_fp at 0x7ffff7a89106 and the value of fp->_mode at 0x7ffff7a89165.
- Check the values of fp→_IO_write_base, fp→_IO_write_ptr at 0x7ffff7a89280, and check the values of fp→_chain at 0x7ffff7a8920a.
- Check the called function at 0x7ffff7a89184.
_IO_flush_all_lockp () stores 0x7ffff7dd1b78 in run_fp, which is the address of main_arena.top.
- _IO_flush_all_lockp () gets the value from the address that fp has plus 0xc0.
- The address points to fp-> _ mode, and the value stored in that variable is 0x7ffff7dd1c28.
The following code compares the values stored in [rbx + 0x20] and [rbx + 0x28].
[rbx (0x7ffff7dd1b78) + 0x20] points to fp → _IO_write_base and [rbx (0x7ffff7dd1b78) + 0x28] points to fp → _IO_write_ptr.
(fp-> _ mode <= 0 && fp-> _ IO_write_ptr> fp → _IO_write_base) This conditional statement fails because the value of "fp-> _ mode" is greater than 0 and does not satisfy the condition.
- The following code stores the value of fp→_chain in fp, and the value of fp→_chain is 0x602400.
- The address is fake _IO_list_all (_IO_FILE, _IO_jump_t).
- _IO_flush_all_lockp () saves the value of fp(0x602400) in run_fp and checks the value of fp→_mode using that value.
- The value fp → _mode has is 0x0.
- The value of fp → _IO_write_base is 0x2, and the value of fp-> _ IO_write_ptr is 0x3.
- fp->_ mode (0x0) <= 0 && fp->_ IO_write_ptr (3)> fp→_IO_write_base (2) This conditional statement will call _IO_OVERFLOW () because it satisfies all conditions.
- _IO_flush_all_lockp () gets the address of the vtable from 0x602400 plus 0xd8, and the value stored in __overflow is the address of the winner function.
- The function passes 0x602400 as the first argument, and the address contains the string "/bin/sh".
- Then call the address of the winner function stored in __overflow using the call command.
- Because of this, winner() passes "/bin/sh" to system() to get the shell.
- The following error message was displayed, but the shell was obtained.