参考文章:
https://zhuanlan.zhihu.com/p/391439312
https://blog.csdn.net/loseheart157/article/details/109305380
https://blog.csdn.net/qq_45521281/article/details/105585709
0x00 前言
之前没有系统的整理过这方面的东西,因为本身做的题和接触到的都没有对这方面考察特别多,前几天做了几道专项考察的题目才发现对这个洞的掌握还不太扎实,特地回来补一补。
0x01 什么是RCE
RCE又称远程代码执行漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。
RCE可以细分为代码执行和命令执行。代码执行执行的是后端语言的代码,命令执行执行的是系统的命令,直接对系统进行操作。
0x02 RCE产生的原因
归根结底是对用户输入内容的过滤不完善,最终导致用户输入的恶意内容被执行。
0x03 危险函数或触发方式
php代码执行函数:
eval()assert(),create_function(),call_user_func(),call_user_func_array(),uasort(),preg_replace(),array_map()等
此外,${}
也可以执行代码(在双引号中倘若有${}
出现,那么{}内的内容将被当做php代码块来执行。)
比如:
<?php
${phpinfo()};
?>
php命令执行函数:
system() system — 执行外部程序(命令行),并且显示输出 这个函数会将结果直接进行输出 (注意:是直接输出区别于返回值,因为这个,我一般不用它),命令成功后返回输出的最后一行,失败返回FALSE
shell_exec() shell_exec — 通过 shell 环境执行命令 ( 这就意味着这个方法只能在 linux 或 mac os的shell环境中运行 ),并且将完整的输出以字符串的方式返回。如果执行过程中发生错误或者进程不产生输出,则返回 NULL。
exec() exec — 执行一个外部程序 返回命令执行结果的最后一行内容。不显示回显。如果想要获取命令的输出内容, 请确保使用 output 参数,或者利用这个函数来构建反弹shell。
passthru() passthru — 执行外部程序并且显示原始输出。
此外还有popen(),pcntl_exec()等函数。
0x04 一些基本的利用手法
执行命令的一些方式:
echo、ping等命令后添加
|
可以追加命令执行花括号也可以执行命令
反引号可以直接执行其中的命令,某些时候可以利用反引号来起到声东击西的绕过效果。
命令拼接符
windows或linux下:
command1 ; command2 : 先执行command1后执行comnand2
command1 & command2 : 先执行comnand2后执行command1
command1 && command2 : 先执行command1后执行comnand2
command1 | command2 : 只执行command2
command1 || command2 : command1执行失败, 再执行command2(若command1执行成功,就不再执行command2)
0x05 绕过手法
其实命令执行本身是没有什么好说的,直接去执行命令就可以了,一般实际运用中我们更应该关注的是如何找到RCE的点以及找到RCE注入点后如何绕过开发者所做的限制。关于如何找到这个问题,有点偏离本文的话题,所以我们这里需要去了解一下如何去对限制进行规避和绕过。
思路
我们进行绕过的核心思路就是,如何找到后端语言中没有但是系统语言(如shell,powershell,cmd等)中存在的特性。
一、一些通用的绕过姿势
1.空格被限制
可以用以下字符串代替:
< 、<>、%09(tab键)、%20、$IFS$9、$IFS$1、${IFS}、$IFS等,还可以用{} 比如 {cat,flag}
$1-9(数字)是shell编程中的一个位置变量,表示接受到的第多少个参数,如果不传入参数则为空
那么我们这里为什么要在$IFS之后再用$9呢?原因是我们如果直接在$IFS后面加文件名,会导致变量名判断错误,如下所示
它把$IFSflag视作一个变量名了,后面直接没输出了,所以我们后面加个$9作为分割即可。
二、关键字被过滤
比如ctf中常见的flag,php等关键字被过滤导致无法直接读取flag
1.使用转义符号
使用引号,反斜线等符号可以绕过关键字的限制
ca\t /fl\ag
cat fl''ag
2.字符拼接
a=fl;b=ag;cat$IFS$a$b
3.空变量绕过
shell编程中$@
是依次获取参数,如果没有参数则为空
绕过的原理就是这个空变量是shell的概念,在php中检测时依旧是fl$@ag
这个字符串,不会被正则匹配到
4.通配符绕过
可以使用通配符去绕过文件名中存在关键字的规则
比如以上这种payload就可以绕过cat限制,绕过空格限制,绕过flag关键字的限制。
二、代码执行中函数被过滤
比如存在eval(),我们想借助这个去转到命令执行来获取flag或者是进一步获取系统的操作权限,但是关键的命令执行函数system()等被过滤
1.反引号绕过
也称之为内敛执行。
在权限足够的情况下,php中的反引号可以直接执行系统命令(我愿称之为通杀,函数都不需要=。=)
<?php
if(isset($_GET["code"])){
$code = $_GET["code"];
if(preg_match("/system/i",$code)){
die("no hack!");
}else{
eval($code);
}
}else{
highlight_file(__FILE__);
}
?>
2.编码绕过
base64、16进制、8进制
三、命令执行中命令被过滤
比如过滤掉了cat这种读取命令
1.同功能替代
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
curl file://文件路径 读取文件内容
strings
bash -v 执行文件里的命令,看报错能看到
rev:反序输出
2.空变量绕过
原理同上
3.字符拼接
原理同上
4.从$PATH环境变量里取字符
如下,我们把ls给过滤了
<?php
if (isset($_GET['code'])) {
$code = $_GET['code'];
if (!preg_match("/\&|\|| |\/|ls/i",$code)){
system($code);
} else {
die("NO hack");
}
} else {
highlight_file(__FILE__);
}
?>
我们此时就可以看一下环境变量$PATH
一般都是这个
下面就可以截取其中的字符echo出来然后反引号执行
四、记一套应对过滤的组合拳
这里假设过滤了flag等直接读取flag的关键字,限制了输入长度,我们无法一次执行多条命令
我们就可以用如下方式:
这套组合的核心在于\
让命令可以连起来,因为它表示换行,也就是说命令没有断开,否则就会出现这种情况
ls -t
是将文本按时间排序输出
ls -t > shell
将输出输入到shell文件中
sh将其作为shell文件执行,shell中则是我们按照顺序构造好的读取flag的命令,所以就可以绕过限制去执行命令。
此外通过echo去不断进行追加从而写一个shell脚本去执行也可以
0x06 ctf实战
[GXYCTF2019]Ping Ping Ping
提示传参ip
猜测是命令执行,用|
执行多条命令试试
可以执行,查看flag.php看看
有过滤,不如先看看index.php,看看过滤的规则
还不能有空格,$IFS$1
绕过
拿到源码,可以看到过滤了很多东西,字符,关键字之类的
但是没有过滤反引号,直接内敛执行绕过
?ip=127.0.0.1|cat$IFS$1`ls`
拿到flag。