#

# I IO_buf_base 劫持技术,

# 文章写的会比较凌乱,是在一边解题同时记录

# 环境以及保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
giantbranch@ubuntu:~/Desktop/pwnabletw/hijack_io_buf_base$ strings libc.so.6 | grep ubuntu 
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11) stable release version 2.23, by Roland McGrath et al.
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
giantbranch@ubuntu:~/Desktop/pwnabletw/hijack_io_buf_base$ checksec --file=tw2017parrot
[*] '/home/giantbranch/Desktop/pwnabletw/hijack_io_buf_base/tw2017parrot'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
giantbranch@ubuntu:~/Desktop/pwnabletw/hijack_io_buf_base$ file tw2017parrot
tw2017parrot: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=00fbccd873daf9400480cbb4dbd48845f1bb97b8, not stripped
giantbranch@ubuntu:~/Desktop/pwnabletw/hijack_io_buf_base$

ubuntu2.23 下的题目,只有 canary 保护没有开启,没有思路

# 主要代码

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
size_t size; // [rsp+8h] [rbp-18h] BYREF
void *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]

v5 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
sleep(3u);
while ( 1 )
{
puts("Size:");
_isoc99_scanf("%lu", &size);
getchar();
if ( !size )
break;
buf = malloc(size);
puts("Buffer:");
read(0, buf, size);
*((_BYTE *)buf + size - 1) = 0; //这个很关键
write(1, buf, size);
free(buf);
}
exit(0);
}

# 分析

# 初步印象

代码很简单,malloc,free 没有多余的操作,但是 malloc 不会清空申请的堆块中的内存,同时我们可以输入空。但是只有一个堆块,紧邻 topchunk, 无法利用 unsortedbin 泄露 main_arena, 铃响其他办法

malloc 的一个机制

当我们在应用层调用 malloc 申请堆的时候,在 glibc 中实际上调用的是_lib_malloc 函数,但是_lib_malloc 函数只是用来简单的封装_int_malloc 函数的,_int_malloc 函数才是申请堆的核心函数。
_int_malloc 会根据应用层用户申请的内存块大小,从而分配相应的 chunk 给用户使用。

函数的分配堆内存的主要执行流程
①请求大小在 fastbin 的范围内:在 fastbins 中找是否有对应的 chunk 可以使用。
②请求大小在 smallbin 的范围内:在 smallbin 中找是否有对应的 chunk 可以使用。
③请求大小在 largebin 的范围内:先调用 malloc_consolidate 对 fastbins 进行整理加入 unsortedbins。然后在 unsortedbin 中查看是否有满足要求的 chunk 可以使用。
④在 largebin 中寻找可用的 chunk 来使用。
⑤寻找较大的 bin 链中是否有可用的 chunk 来使用。
⑥切割 topchunk 来使用。
⑦topchunk 也不够了,再次调用 malloc_consolidate 整理 fastbins。
⑧topchunk 不够用,再次 malloc_consolidate 之后还没有可以用的,最终调用 sysmalloc(系统调用)申请内存。

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
pwndbg> p *(struct _IO_FILE_plus *)stdin
$1 = {
file = {
_flags = -72540021,
_IO_read_ptr = 0x7f0883e32964 <_IO_2_1_stdin_+132> "",
_IO_read_end = 0x7f0883e32964 <_IO_2_1_stdin_+132> "",
_IO_read_base = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n",
_IO_write_base = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n",
_IO_write_ptr = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n",
_IO_write_end = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n",
_IO_buf_base = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n",
_IO_buf_end = 0x7f0883e32964 <_IO_2_1_stdin_+132> "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x0,
_fileno = 0,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "\n",
_lock = 0x7f0883e34790 <_IO_stdfile_0_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x7f0883e329c0 <_IO_wide_data_0>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7f0883e316e0 <_IO_file_jumps>
}

这是正常的 stdin 的 IO_FILE_plus 结构体,_IO_buf_base 是输入缓冲区开始的地方(其实应该是 read_base 是开始的地方,但是这里肯可能是说 read_base = buf_base),记得程序代码那个末尾写 0 吗。在这里将 _IO_buf_base 最低位改为 0. 这个地址其实是_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
pwndbg> p *(struct _IO_FILE_plus *)stdin
$3 = {
file = {
_flags = -72539989,
_IO_read_ptr = 0x7f0883e32901 <_IO_2_1_stdin_+33> "",
_IO_read_end = 0x7f0883e32928 <_IO_2_1_stdin_+72> "",
_IO_read_base = 0x7f0883e32900 <_IO_2_1_stdin_+32> "",
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x7f0883e347a8 <__free_hook> "",
_IO_buf_end = 0x7f0883e347b8 <next_to_use> "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x0,
_fileno = 0,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "\n",
_lock = 0x7f0883e34790 <_IO_stdfile_0_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x7f0883e329c0 <_IO_wide_data_0>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7f0883e316e0 <_IO_file_jumps>
}
Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

dreamcat WeChat Pay

WeChat Pay

dreamcat Alipay

Alipay

dreamcat PayPal

PayPal