一、前言

本文主要是讲述用静态分析平台——破壳平台进行批量漏洞快速挖掘和扫描的思路分享。

破壳平台在近日提交了TP-Link,TENDA,D-Link,Linksys,Edimax,Netgear等系列设备六十余个漏洞点,并已经获得30个CVE的批复。

CVE-2024-57357,CVE-2025-22900,CVE-2025-22903,CVE-2025-22904,CVE-2025-22905,CVE-2025-22906,CVE-2025-22907,CVE-2025-22911,CVE-2025-22912,CVE-2025-22913,CVE-2025-22916,CVE-2025-25456,CVE-2025-25458,CVE-2025-25453,CVE-2025-25455,CVE-2025-25454,CVE-2025-25457,CVE-2024-57536,CVE-2024-57537,CVE-2024-57538,CVE-2024-57539,CVE-2024-57540,CVE-2024-57541,CVE-2024-57542,CVE-2024-57543,CVE-2024-57544,CVE-2024-57545,CVE-2024-42520,CVE-2025-25579

后面我们会从三个方面来讲一下查询的策略:

  • 边界二进制寻找
  • 漏洞模式分析及规则编写
  • 平台使用

二、边界二进制寻找

所谓边界二进制就是用户可以访问到的二进制,一般我们进行漏挖也是主要针对这些程序进行针对性,无需对固件中所有的程序都进行分析。

而对于静态分析来说,上传的文件越多那么分析速度和查询速度就会相应的越慢。因此需要有一个初步的文件过滤的步骤来帮助我们初步筛选一些边界二进制。根据平时的漏洞挖掘经验对于常见的路由器,摄像头,nvr等设备,其http服务及相关cgi是漏洞出现的重灾区。 上传平台后平台会自动对压缩包,固件进行解压。同时平台可以设置正则表达式进行过滤:

\b(?:\w*httpd\w*|\w*.cgi|\w*onvif\w*|\w*boa\w*|\w*webs\w*|\w*goahead\w*)\b

个人平时在漏洞挖掘时也习惯使用下面的方式来大致确定一些具有网络服务的边界二进制:

cd usr
grep -r "bind" . | grep Binary

主要是因为值得分析的二进制一般会位于usr目录下,根目录下的bin和sbin一般是linux系统自己的系统服务。且边界二进制必然会监听端口,因此存在bind字符串的很大概率是边界二进制。

三、漏洞模式分析及规则编写

在物联网漏洞挖掘中,命令注入和缓冲区溢出是最为常见的高危漏洞类型。在我初期的自动化工作中也先以这两种漏洞模式进行查询。下面介绍这两种漏洞类型中我所使用的常见漏洞模式。

2.1 命令注入漏洞模式

大部分命令注入漏洞都会经过字符串拼接函数。

edimax CVE-2025-22905

下图是平台漏洞个查询结果,可以看到这个漏洞就是从websGetVar中获取到了对应字段,然后经过了字符串拼接后传递给了system函数,造成了命令注入漏洞。

这个漏洞其实从调用流程上来说比较复杂,首先他会使用aes解密用户传入的数据为json,然后从json中拿取一个字符串,然后根据这个字符串去一个虚标中调用对应的函数,然后再经过一个虚表调用后再经过一个假鉴权最终达到命令注入,但是核心漏洞点就是字符串拼接后执行了system

sub_424ab0函数中会将v20这个变量直接传递给system函数作为参数。

查询规则

针对于大部分命令注入漏洞都会经过字符串拼接函数这个漏洞模式,,使用正则进行字符串匹配或者单纯直接寻找sprintfsystem函数作为危险函数点总体来说误报还是比较多的,而且不支持跨函数的情况。

因此个人采用的是使用snprintf,sprintf函数的参数作为source点,常见的命令执行函数作为sink点,然后使用平台进行污点分析,相对来说误报较少,能查到的很大概率是会存在漏洞的。

MATCH (n:identifier)<-[:ast*2]-(code:code_line) 
WHERE n.callee in ["snprintf","sprintf"] and n.index in [2,3,4,5,6] and code.name contains "%s" 
WITH collect(id(n)) as sourceSet 
MATCH (m:identifier{index:0}) 
WHERE m.callee in ['system', '_system', '_popen', 'popen', 'wpopen', '_wpopen', 'spawn', '_wexecl', '_wspawnv', 'eval', '_wsystem', 'spawnve', '_wspawnlp', '_spawnl', 'execle', '_wspawnve', '_texeclp', '_wexeclp', '_spawnlpe', '_execvp', '_exec', 'COMM_SystemEx', '_wspawnl', '_wspawnvp', 'execlp', 'system_en', '_wspawnvpe', '_wexecv', 'WinExec', '_wspawnle', '_texecvp', 'CreateProcessW', 'twsystem_nowait', '_texecle', '_execv', '__system', '_spawn', 'spawnvp', '_tspawnl', 'doSystemCmd', 'callSystemCmd', '_tspawnlpe', 'CreateProcess', '_spawnve', '_tspawnv', 'spawnlp', 'spawnlpe', 'g_spawn_command_line_async', '_wexecle', 'spawnl', '_spawnvp', '_tspawnlp', '_tspawnle', '_execl', '_wexec', '_wexeclpe', '_tspawnve', 'spawnv', '_tspawn', 'twsystem', '_spawnle', '_execle', 'execvp', '___system', '_wspawn', '_texecl', '_tspawnvp', '_eval', '_texecv', '_spawnlp', '_spawnvpe', 'spawnle', '_execlp', 'execl', '_execlpe', 'CreateProcessA', '_spawnv', '_tspawnvpe', '_texec', '_wexecvp', 'bstar_system', 'prctl_runCommandInShellBlocking', 'execv', 'spawnvpe', '_wspawnlpe', '_texeclpe', 'execlpe', 'jhl_system', 'ATP_UTIL_ExecCmdNoHang', 'j_ATP_UTIL_ExecCmdNoHang', 'bs_SetCmd', 'ExeShell','doSystemCmd','lxmldbc_system'] 
WITH sourceSet,collect(id(m)) as sinkSet 
CALL VQL.taintPropagation(sourceSet, sinkSet) YIELD taintPropagationPath 
RETURN taintPropagationPath ORDER BY size(taintPropagationPath)

简单解释一下上面的查询命令,identifier 即我们要查询的节点类型,这里指变量类型。调用这个变量的函数就是callee,这里是snprintfsprintf这两个函数,index属性是指第几个参数,这里是写了多个,分别是第3,4,5,6,7个参数。同样的按照这种思路设置了一些常见的sink点函数。最后调用VQL.taintPropagation进行污点查询。另外加了个过滤条件,就是查询的source点的反汇编代码需要包含%s,可以减少一些查询结果中只包含%d这种不可能发生字符串拼接的误报。

误报情况

sprintf的数据来自于配置文件等不可控数据源,而且容易漏掉命令执行函数或者字符串拼接函数是自实现函数的情况。

2.2 缓冲区溢出漏洞模式

漏洞模式一

strncpymemcpy等函数第三个参数来自于strlenstrncpymemcpy的第三个参数代表着进行内存拷贝的长度,如果该长度可控那么很大可能存在漏洞。

Linksys E8450 CVE-2024-57536

存在漏洞的是该设备的cgi文件,这个程序会解析用户传入的数据为json,然后从中拿取json字段。取出的page字段在使用strncpy时第三个参数为改字段的strlen函数返回值,造成了缓冲区溢出。

查询规则

这里查询时同样采用了污点分析的查询思路,主要是strlen函数的返回值作为source点。strncpymemcpy函数的第三个参数作为sink点进行污点查询。

MATCH (n:identifier) 
WHERE (n.callee="snprintf" and n.index = -1) or (n.callee="recv" and n.index = 1) or (n.callee='recvfrom' and n.index=1) or (n.callee='read' and n.index=1) or (n.callee='getenv' and n.index=-1) or (n.callee='recvmsg' and n.index=1) or (n.callee='GetUrlValue' and n.index=-1) or (n.callee='strlen' and n.index=-1) with collect(id(n)) as sourceSet 
MATCH (m:identifier{index:2}) 
WHERE m.callee in ['strncpy', '_strncpy', 'memcpy', '_memcpy', 'strncat', '_strncat'] 
WITH sourceSet,collect(id(m)) as sinkSet 
CALL VQL.taintPropagation(sourceSet, sinkSet,2) YIELD taintPropagationPath 
RETURN taintPropagationPath ORDER BY size(taintPropagationPath)
误报情况

大多数误报都较好排除,就是规则写起来可能比较麻烦。这种漏洞模式的误报一般有两种情况。

  1. 路径过程中包含了对大小的判断和限制:

    这里的第三步对大小进行了限制。

  2. 目标缓冲区是根据size malloc出来的。

漏洞模式二

用户解析数据的结果直接传递给strcpysprintf 等危险函数,这里的思路是如果单纯把所有调用strcpy函数调用的结果返回那么结果又多又没有审计的意义,这里个人认为是经过用户解析函数的strcpy存在漏洞风险较大。

Tenda AC10 CVE-2025-25458

下图是破壳平台漏洞查询结果,可以看到用户输入的数据从websGetVar的返回值直接传递到了sprintf函数的参数,格式化字符串的内容是%s没有长度限制因此造成了缓冲区溢出漏洞。

这种类型的漏洞也在小设备中广泛存在,但是如何确定用户输入函数是一个比较困难的事情。websGetVar是一种常见的数据获取函数,但是在相当多的设备中,这种类型的函数名是其他名字或者是经过了去符号操作从而没有函数名。

这里我采用了下面两个规则来查询,本质上还是通过数据流进行查询。原理是先找到strcpy函数和sprintf函数的参数,然后向上最多跟踪4步数据流,在这4步中如果碰到的变量是函数的返回值,并且这个函数包含了一个参数是字符串那么就返回这个的路径。

比如根据上面平台查询出的结果,我们找到acStack_30c这个变量,向上寻找一步数据流是sprintf函数的第三个参数uVar1,再向上寻找一步则是websGetVar的返回值uVar1。此时他属于websGetVar的返回值,且websGetVar包含了一个字符串参数,因此就返回这个漏洞查询结果。

具体两个规则如下:

MATCH p=(source_code:code_line)-[:ast*2]->(srcCall:identifier{index:-1})-[:dfg*1..3]->(strcpyCall)
WHERE strcpyCall.callee IN ['strcpy', '_strcpy', 'wcscpy', '_wcscpy'] and source_code.name contains "\""
RETURN [n IN nodes(p) | id(n)] AS taintPropagationPath 
MATCH p=(source_code:code_line)-[:ast*2]->(srcCall:identifier{index:-1})-[:dfg*1..3]->(strcpyCall)<-[:ast*2]-(code:code_line)
WHERE strcpyCall.callee IN ['sprintf','_sprintf'] and source_code.name contains "\"" and code.name contains "%s"
RETURN [n IN nodes(p) | id(n)] AS taintPropagationPath
误报情况

如下面一些查询结果,这种方式可以查询出许多自己未曾硬编码过的中间数据解析点。

但是目前还存在着一些符合这些规则的误报,比如下图的strtok函数,还有一些读取配置文件函数以及类似nvram_get等这种可能性较小的函数都会被这种规则检测到。

四、平台使用

个人用户使用破壳暂时无法使用全自动化的漏洞挖掘流程,但是也可以初步帮助我们进行半自动化的漏洞挖掘 登陆破壳平台 https://poc.qianxin.com 并点击使用。

然后这里选择新项目并上传我们要分析的二进制文件或是多个二进制文件的压缩包。

等待程序进入未激活状态就可以点击破壳分析。

进入查询界面后可以直接将上面我的规则自己进行复制并点击 提交执行

也可以选择我共享的的规则组快速查询,在规则组处等待查询结束后即可查看查询结果。

五、总结

当前关于物联网设备的漏洞挖掘方法日益成熟,但是多数物联网厂商所做的安全措施缺却仍未与时俱进。这套自动化流程可以用于快速确定物联网设备的一些攻击面并进行批量快速的漏洞缺陷检测,从而大范围发现当前设备中存在的漏洞风险。