PHP-Preg_replace()正则替换绕过
PHP-preg_replace()正则替换绕过漏洞 #PHP #代码审计
preg_replace()默认是替换所有符号匹配条件的元素
其中正则表达式不再阐述
语法
mixed preg_replace( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count]])
参数说明
$pattern
:- 要搜索的模式,可以是字符串或者一个字符串数组
$replacement
:- 用于替换的字符串或者字符串数组
$subject
:- 要搜索替换的目标字符串或者字符串数组
其余为可选,详情自行百度;如果Subject 是个数组,那么返回的也是数组,其他情况返回一个字符串。
示例
<?php
$a = $_GET['a'];
echo preg_replace('/Preg_replace/',$a,'This is Preg_replace');
?>
// 这里我用了分隔符
运行这段代码
我给该脚本传参为aegis
,那么会将上述语法中的$pattern
(即/Preg_replace/
)匹配$subject
(即This is Preg_replace),是否存在Preg_replace,如果存在就替换成$replacement
。
模式修饰符
主要了解下,不是重点
- i
- 如果设置了这个修饰符,模式中的字母会进行大小写不敏感匹配
- m
- “行首”和“行末”就会匹配目标字符串中任意换行符之前或之后,另外, 还分别匹配目标字符串的最开始和最末尾位置。
- s
- 模式中的点号元字符匹配所有字符,包含换行符。如果没有这个 修饰符,点号不匹配换行符。
- x
- 模式中的没有经过转义的或不在字符类中的空白数据字符总会被忽略, 并且位于一个未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略。
- A
- 模式被强制为"锚定"模式,也就是说约束匹配使其仅从 目标字符串的开始位置搜索。
- D
- 模式中的元字符美元符号仅仅匹配目标字符串的末尾。如果这个修饰符 没有设置,当字符串以一个换行符结尾时, 美元符号还会匹配该换行符(但不会匹配之前的任何换行符)。 如果设置了修饰符m,这个修饰符被忽略
- S
- 当一个模式需要多次使用的时候,为了得到匹配速度的提升,值得花费一些时间 对其进行一些额外的分析。如果设置了这个修饰符,这个额外的分析就会执行。
- U
- 这个修饰符逆转了量词的"贪婪"模式。 使量词默认为非贪婪的,通过量词后紧跟
?
的方式可以使其成为贪婪的。
- 这个修饰符逆转了量词的"贪婪"模式。 使量词默认为非贪婪的,通过量词后紧跟
- X
- 这个修饰符打开了 PCRE 与 perl 不兼容的附件功能。模式中的任意反斜线后就 ingen 一个 没有特殊含义的字符都会导致一个错误,以此保留这些字符以保证向后兼容性。
- J
- 内部选项设置(?J)修改本地的
PCRE_DUPNAMES
选项。允许子组重名, (译注:只能通过内部选项设置,外部的 /J 设置会产生错误。) 自 PHP 7.2.0 起,也能支持J
修饰符。
- 内部选项设置(?J)修改本地的
- u
- 模式和目标字符串都被认为是 UTF-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 5 字节和 6 字节的 UTF-8 字符序列以无效字符序列对待。
示例
拿i模式演示吧
<?php
$a = $_GET['a'];
echo preg_replace('/preg_replace/',$a,'This is Preg_replace');
?>
// 没使用模式的情况下
<?php
$a = $_GET['a'];
echo preg_replace('/preg_replace/i',$a,'This is Preg_replace');
?>
//使用了i模式会忽略大小写
/e模式下的代码执行问题
/e
模式下,会使得preg_replace()将replacement参数当做PHP代码执行。PHP 7系列版本就不能用了。
// 我们给刚才的例子加上e模式
<?php
$a = $_GET['a'];
echo preg_replace('/preg_replace/ei',$a,'This is Preg_replace');
?>
直接将我们传入的phpinfo()进行了执行。
然后在看下一组代码:
<?php
$a = $_GET['a'];
//echo preg_replace('/preg_replace/ei',$a,'This is Preg_replace');
echo preg_replace('/(.*)/ei', 'strtolower("\\1")', $a);
?>
为什么会出现一个\\1
呢?而且为什么会传入{${phpinfo()}}
就会打印phpinfo呢
\\1
实际上就是 \1
,而 \1
在正则表达式中有自己的含义;在正则表达式中有个叫做反向引用的:
对一个正则表达式模式或部分模式 两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从
1
开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 '\n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
就是说 \1
实际上指定的是第一个子匹配项
可变变量
可变变量是一种PHP独特的变量,他允许动态改变一个变量的名称。
使用可变变量就是在变量的前面多加一个美元符号“$”。
<?php
$a = "hello";
$$a = 'world';
echo $a,$hello;
echo "\n";
echo $a,${$a};
?>
就是将$a
的值作为$$a
的变量名。
{${}}
而在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。{${phpinfo()}}
中的 phpinfo() 会被当做变量先执行,执行后,即变成 {1} , (phpinfo()成功执行返回true)。