#
# IO_buf_base 劫持技术,
# 文章写的会比较凌乱,是在一边解题同时记录
# 环境以及保护
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 保护没有开启,没有思路
# 主要代码
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(系统调用)申请内存。
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 的覆盖
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>
}