软件安全机制 ¶
约 1332 个字 22 行代码 预计阅读时间 5 分钟
Abstract
软件安全 lab4 实验报告(2023.06.10 ~ 2023.07.02)
仅供学习参考,请勿抄袭
实验内容 ¶
- Task 1: No eXecute bit
- 使用 NX bit enable/disable 分别编译 sbof2
- 使用 checksec 对两个程序进行分析
- 使用 lab-01 答案的本地攻击脚本对两个程序进行攻击
- 截图、说明二者的差异,解读报错信息
- Task 2: Canary
- 使用 canary enable/disable 分别编译 sbof1
- 使用 checksec 对两个程序进行分析
- 使用 lab-01 答案的本地攻击脚本对两个程序进行攻击
- 截图、说明二者的差异,解读报错信息
- 对 sbof1-harden 进行逆向,对其中任意一个函数首尾部分进行反汇编分析
- 画出开启 canary 与不开启的栈结构
- 在报告中回答 canary 低位为 0 的原因
- Task 3: PIE
- 使用 PIE enable/disable 分别编译 sbof1
- 使用 checksec 对两个程序进行分析
- 使用 lab-01 答案的本地攻击脚本对两个程序进行攻击
- 截图、说明二者的差异,解读报错信息
- 编写一个打印 main 地址的程序,比较开启 PIE 与不开启的 main 地址
- 注意地址随机化的粒度
- Task 4: SECCOMP
- 安装 seccomp 相关库和工具,编译 ban.c 和 no-ban.c
- 使用 checksec 对两个程序进行分析
- 使用 seccomp-tools 对两个程序进行分析
- Task Bonus: CFI
- 使用 CFI enable/disable 分别编译 password
- 使用 checksec 对两个程序进行分析
- 分别对两个程序进行攻击,说明差异,对报错信息进行解读
- 对两个程序的 main 函数进行反汇编,对比分析哪些控制流边被加固,哪些没有
No eXecute bit¶
关闭 NX 编译 sbof2,checksec,攻击:
开启 NX 编译 sbof2,checksec,攻击:
NX disabled 情况下攻击正常,enabled 时程序提前抛出 SIGSEGV,即段错误,反映了栈上数据段没有执行权限,被禁止所以抛出段错误。
Canary¶
关闭 Canary 编译 sbof1,checksec,攻击:
开启 Canary 编译 sbof1,checksec,攻击:
Canary disabled 情况下攻击正常,enabled 时程序打印出 *** stack smashing detected ***: terminated
,检测到了栈溢出破坏了 canary,抛出了 SIGABRT 终止程序,攻击失败。
使用 objdump 看一下 func 函数首尾部分的反汇编:
000000000040123f <func>:
40123f: f3 0f 1e fa endbr64
401243: 55 push rbp
401244: 48 89 e5 mov rbp,rsp
401247: 48 83 ec 60 sub rsp,0x60
40124b: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
401252: 00 00
401254: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
...
4012c3: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
4012c7: 64 48 2b 04 25 28 00 sub rax,QWORD PTR fs:0x28
4012ce: 00 00
4012d0: 74 05 je 4012d7 <func+0x98>
4012d2: e8 e9 fd ff ff call 4010c0 <__stack_chk_fail@plt>
4012d7: c9 leave
4012d8: c3 ret
函数开头先将 fs:0x28 的值保存到栈上 [rbp-0x8],即取出进程对应的 canary 放在栈上。函数结尾取出栈上 [rbp-0x8] 的值,与 fs:0x28 的值比较,如果相等则跳转 leave ret,否则调用 __stack_chk_fail 打印 detected 信息并抛出 ABORT 信号。
所以开不开启 canary 栈上的结构如下:
Canary 的低位一定为 0,这是为了保证在将局部变量当作字符串输出的时候,即使末尾没有 \0 也一定会在输出 canary 内容前截断,避免无意泄露 canary 的值。
PIE¶
PIE disabled 的情况和前面 Canary disabled 的一样,这里就不再放截图了。
PIE enabled 的编译、checksec、攻击:
可以发现程序跑出了 SIGSEGV 段错误,说明我们修改 return address 的地址不再是后门的地址,而是不可执行的段,所以程序抛出了段错误,可以看出 main 函数的地址被随机化了。
再写一个程序打印 main 地址:
在两种情况下进行编译运行:
可以发现没开启 PIE 的情况下输出的 main 函数地址都是 0x401136,而开启 PIE 后的地址每次都不同。同时也可以发现,开启 PIE 后地址都是 0x5????????149,说明低 12 位是不变的。
SECCOMP¶
按照实验指导安装好库和工具,编译 no-ban.c、checksec、seccomp-tools dump:
编译 ban.c、checksec、seccomp-tools dump:
可以发现二者 checksec 没有区别,都是满保护(gcc 编译默认开启
CFI¶
对两个编译好的程序 checksec:
发现保护都一样。然后进行攻击(八个 A 占满 pass,然后写入地址覆盖 func
发现没开启 CFI 的程序可以正常覆盖 func 到 success,而开启了 CFI 的程序会抛出 illegal hardware instruction 终止程序继续运行。
在 IDA 中打开反汇编,观察两个程序的 main 函数(左开启右关闭
可以发现在 scanf 之前的部分除了栈结构有略微差别之外都完全相同。在 scanf 后要调用 a.func,这时没开启 CFI 的程序直接调用跳转到了栈上存的地址。而开启了 CFI 的程序先将目的地址与 auth 函数地址进行比较,如果相同则 call,否则执行一条 ud2 指令,抛出 illegal hardware instruction 终止程序。
所以调用 a.func 的控制流边被加固了,因为编译器知道这里可能会被修改,而且只应该跳转到 auth 函数的地址,所以加了一层检查。而调用 printf scanf 等函数的控制流边都没有被加固,因为这里不会被修改。
创建日期: 2023年8月6日 22:04:08