文章源自【字节脉搏社区】-字节脉搏实验室
作者-叶子
在基于PCRE实现的正则引擎中,我们常使用“m表示multi-line、s表示single-line”。
multi-line表示按行来匹配正则,可以理解为以换行符为切割,对每行进行正则匹配然后进行or运算出结果。 中single-line的意思是将待匹配的文本视为一行,换行符不再作为“换行”的标志。
这在日常使用中也经常会出现由首尾界定符产生的小bug,比如下面这一个。
<?php
if(preg_match('/^a[a-z]+z$/m', $_GET['input'])) {
echo $_GET['input'];
}
正则匹配中我们常使用`^`和`$`来界定正则匹配的首尾,但这两个的符号本身的含义其实是代表了“行的开头和结尾”,也就意味着如果你输入一个%0a换行,后面就可以输入任何字符也能完成匹配。
比如上面的代码本身的含义可能是:用户输入一个以a开头,以z结尾的中间可以有若干字母的字符串。但因为指定了m修饰符,导致用户可以传入:
input=abcz%0a<svg%20onload=alert(1)>
来绕过检测。
但其实m修饰符的出现频率并不高,所以下面这个例子你有可能见过。
<?php
if(preg_match('/SELECT.+FROM/i', $_GET['input'])) {
echo 'SQL Injection: ' . $_GET['input'];
} else {
echo 'Bypass: ' . $_GET['input'];
}
上面代码本身的含义可能是正则匹配SQL注入的payload,但同理可以用一个换行符绕过
input=SELECT%20password%0aFROM%20database
解决的办法也很简单,使用s修饰符即可,这样的话`.“可以匹配上所有字符,自然也就包含了换行符。
那如果我们不加修饰符,结果如何呢?
不加s或m -> single line,但 `.` 不能匹配换行符
加s -> single line,且 `.` 匹配包括换行符在内的所有字符
加m -> multi line
同时加s和m -> multi line,且 `.` 匹配包括换行符在内的所有字符
PS:在第一个代码中你也会发现如果input本身以`\n`结尾的话也是可以成功匹配的,所以严格来说也是可以算是一个漏洞,毕竟写代码的人本意是以z结尾的。导致这个结果的原因是,“行”这个事物本身就会存在两种情况:非最后一行的“行”,其结尾就是换行符或者最后一行,其结尾就是字符串结尾。 所以,`$`也就可以匹配两种情况:字符串结尾或换行符,即使在s模式下也是如此。这也就是”Apache HTTPd”。XD