一、前言
本文主要是讲述用静态分析平台——破壳平台进行批量漏洞快速挖掘和扫描的思路分享。
破壳平台在近日提交了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
函数,造成了命令注入漏洞。
tplink-8630 CVE-2024-57357
这个漏洞其实从调用流程上来说比较复杂,首先他会使用aes解密用户传入的数据为json,然后从json中拿取一个字符串,然后根据这个字符串去一个虚标中调用对应的函数,然后再经过一个虚表调用后再经过一个假鉴权最终达到命令注入,但是核心漏洞点就是字符串拼接后执行了system
在sub_424ab0
函数中会将v20
这个变量直接传递给system
函数作为参数。
查询规则
针对于大部分命令注入漏洞都会经过字符串拼接函数这个漏洞模式,,使用正则进行字符串匹配或者单纯直接寻找sprintf
和system
函数作为危险函数点总体来说误报还是比较多的,而且不支持跨函数的情况。
因此个人采用的是使用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
,这里是snprintf
和sprint
f这两个函数,index
属性是指第几个参数,这里是写了多个,分别是第3,4,5,6,7个参数。同样的按照这种思路设置了一些常见的sink
点函数。最后调用VQL.taintPropagation
进行污点查询。另外加了个过滤条件,就是查询的source点的反汇编代码需要包含%s
,可以减少一些查询结果中只包含%d
这种不可能发生字符串拼接的误报。
误报情况
sprintf
的数据来自于配置文件等不可控数据源,而且容易漏掉命令执行函数或者字符串拼接函数是自实现函数的情况。
2.2 缓冲区溢出漏洞模式
漏洞模式一
strncpy
,memcpy
等函数第三个参数来自于strlen
,strncpy
和memcpy
的第三个参数代表着进行内存拷贝的长度,如果该长度可控那么很大可能存在漏洞。
Linksys E8450 CVE-2024-57536
存在漏洞的是该设备的cgi
文件,这个程序会解析用户传入的数据为json
,然后从中拿取json
字段。取出的page
字段在使用strncpy
时第三个参数为改字段的strlen
函数返回值,造成了缓冲区溢出。
查询规则
这里查询时同样采用了污点分析的查询思路,主要是strlen函数的返回值作为source点。strncpy
,memcpy
函数的第三个参数作为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)
误报情况
大多数误报都较好排除,就是规则写起来可能比较麻烦。这种漏洞模式的误报一般有两种情况。
路径过程中包含了对大小的判断和限制:
这里的第三步对大小进行了限制。
目标缓冲区是根据size malloc出来的。
漏洞模式二
用户解析数据的结果直接传递给strcpy
,sprintf
等危险函数,这里的思路是如果单纯把所有调用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 并点击使用。
然后这里选择新项目并上传我们要分析的二进制文件或是多个二进制文件的压缩包。
等待程序进入未激活状态就可以点击破壳分析。
进入查询界面后可以直接将上面我的规则自己进行复制并点击 提交执行。
也可以选择我共享的的规则组快速查询,在规则组处等待查询结束后即可查看查询结果。
五、总结
当前关于物联网设备的漏洞挖掘方法日益成熟,但是多数物联网厂商所做的安全措施缺却仍未与时俱进。这套自动化流程可以用于快速确定物联网设备的一些攻击面并进行批量快速的漏洞缺陷检测,从而大范围发现当前设备中存在的漏洞风险。