1、漏洞概要
Apache Solr 是Apache Lucene项目的开源企业搜索平台。
前两天Apache Solr 发布了一个身份认证绕过漏洞 CVE-2024-45216。从官方的描述中可以知道,漏洞是Solr 实例使用了PKIAuthenticationPlugin来进行鉴权,就可以在任何 Solr API URL 路径末尾构造一个特殊的结尾跳过身份验证。看着还怪神奇的,下面就详细看看这个漏洞是怎么个事。
影响版本 :
Apache Solr < 8.11.4
Apache Solr < 9.7.0
2、漏洞环境
这里环境搭建其实网上很多不必要太多赘述。
但值得注意的是,这里需要启用PKIAuthenticationPlugin来进行鉴权。根据官方的说法这种配置常用于 SolrCloud 模式的集群中,所以这里方便起见就可以用solr和zookeeper简单搭建一个集群,集群搭建的教程很多这里不过多赘述。
按照官方默认方式启动后,solr的接口都是直接未授权访问的。
然后重点是鉴权部分
- 需要制作一个 security.json 配置文件,这里用官方给的demo就可以。
{
"authentication":{
"class":"solr.BasicAuthPlugin",
"credentials":{"solr":"IV0EHq1OnNrj6gvRCwvFwTrZ1+z1oBbnQdiVC3otuq0= Ndd7LKvVBAaZIF0QAVi1ekCfAJXr1GGfLtRUXhgrF8c="},
},
"authorization":{
"class":"solr.RuleBasedAuthorizationPlugin",
"permissions":[{"name":"security-edit",
"role":"admin"}],
"user-role":{"solr":"admin"}
}}
- 使用solr命令加载 security.json 配置文件
>bin/solr zk cp ./security.json zk:security.json -z localhost:2181
配置完以后,这个时候就需要认证了
3、漏洞复现
根据公开出来的poc,大概就是这样,需要在URL结尾加上:/admin/info/key
这个结尾,然后需要配置 SolrAuth
header。
GET /solr/admin/info/properties:/admin/info/key HTTP/1.1
Host: 127.0.0.1:8983
SolrAuth: 11
Connection: close
比如刚才我们无法访问的接口,现在都可以直接访问和操作。
当然由于未授权,除了可以泄漏一些敏感信息,后续还可以有更多利用,比如获取和修改core的配置,基本上各种可以通过接口完成的工作都可以通过这个来绕过。
看到这就有个很自然的疑问,这是如何绕过的?下面就简单来分析分析
4、分析
通过版本间的比对,可以看到官方修复 commit 主要为这里https://github.com/apache/solr/commit/bd61680bfd351f608867739db75c3d70c1900e38
修复重点在 PKIAuthenticationPlugin.java
和 HttpSolrCall.java
中,这解析请求和鉴权的重点逻辑也就在这两个的代码中。
要点-1: PKIAuthenticationPlugin
在 solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java 中,主要的修复方式是删除了如下代码:
String requestURI = request.getRequestURI();
if (requestURI.endsWith(PublicKeyHandler.PATH)) {
assert false : "Should already be handled by SolrDispatchFilter.authenticateRequest";
numPassThrough.inc();
filterChain.doFilter(request, response);
return true;
}
这段代码从请求中获取 requestURI
,检查其是否以 PublicKeyHandler.PATH
结尾。如果满足条件,调用 filterChain.doFilter(request, response)
继续拦截并处理请求,并返回 true
通过认证。
这里 PublicKeyHandler.PATH
可以看到就是我们加的后缀 /admin/info/key
。
但似乎缺少了':' ?这个冒号是起的什么作用?
后续会看到
要点-2: HttpSolrCall
在 solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java 中,对以下代码进行了删除
int idx = path.indexOf(':');
if (idx > 0) {
// save the portion after the ':' for a 'handler' path parameter
path = path.substring(0, idx);
}
对以下进行了修改
// 原始代码
idx = path.indexOf('/', 1);
// 修改后
int idx = path.indexOf('/', 1);
这里的主要逻辑就是检查路径中是否包含冒号 : 字符,如果存在就截取 ':'
之前的字符串作为新的 path
为请求路径,并将 ':'
之后的部分保存为 handler
路径参数。
从上述流程可以大概感觉到,就是这个特性,导致了鉴权逻辑中的路径和实际访问时的路径的差异,最终造成了鉴权的绕过。
要点-3: SolrAuth header
但还有一个疑问,为什么必须得加上 SolrAuth
头才可以利用。
这个得看回到鉴权前的流程,当我们的代码走到 org.apache.solr.servlet.SolrDispatchFilter#authenticateRequest 时,我们可以在这里看到进行了判断,只有当 header 为 PKIAuthenticationPlugin.HEADER 或 PKIAuthenticationPlugin.HEADER_V2 时,且cores.getPkiAuthenticationSecurityBuilder() 不为空,当前的鉴权插件才是 PKIAuthenticationPlugin,不然就是BasicAuthPlugin.
这里headerV2
和 headerV1
的值即为 "SolrAuth" 和 "SolrAuthV2"
SolrAuth header 的新发现
在进一步跟进的时候,可以看到 PKIAuthenticationPlugin#doAuthenticate 中也有一段校验的逻辑,从这里看到这里的逻辑其实headerV2
或 headerV1
都是可以通过的,而并不影响的我们
所以验证一下,也确实能达到一样的效果
小结
简单总结一下,也就是说由于插件中校验的路径和负责解析url的逻辑产生了差异,导致只要结尾是:/admin/info/key
,并且再加上"SolrAuth" header强制使用PKIAuthenticationPlugin, 就可以绕过鉴权直接访问操作API。
调用过程大概如下:
先是通过 org.apache.solr.servlet.SolrDispatchFilter#dispatch 来分发请求,然后经过
- org.apache.solr.servlet.SolrDispatchFilter#authenticateRequest
- org.apache.solr.security.AuthenticationPlugin#authenticate
- org.apache.solr.security.PKIAuthenticationPlugin#doAuthenticate
最终来到 PKIAuthenticationPlugin,进行鉴权。
最后在 org.apache.solr.servlet.HttpSolrCall#call 去解析响应被分割前的路径。