
这道题非常经典考查的是 PHP 正则表达式修饰符Modifiers的特性差异以及换行符绕过。我们的目标是让代码执行到 else { echo $flag; }。也就是说我们需要让输入的参数a满足以下两个条件通过第一个检测pregmatch(′/phpa 满足以下两个条件 通过第一个检测preg_match(/^phpa满足以下两个条件通过第一个检测pregmatch(′/php/im’,a)必须为True。绕过第二个检测pregmatch(′/phpa) 必须为 True。 绕过第二个检测preg_match(/^phpa)必须为True。绕过第二个检测pregmatch(′/php/i’, $a) 必须为 False。核心代码与正则表达式分析我们来对比一下这两个 preg_match 的正则表达式有什么不同第一个正则表达式‘/^php$/im’/i忽略大小写。/m多行模式Multi-line mode。在多行模式下^ 不仅匹配字符串的开头还匹配每一行的开头即换行符 \n 之后的位置。$ 不仅匹配字符串的结尾还匹配每一行的结尾即换行符 \n 之前的位置。这意味着只要你的输入中有任意独立的一行内容完全是 “php”就能触发 True。例如 “abc\nphp\ndef” 就可以匹配成功。第二个正则表达式‘/^php$/i’/i忽略大小写。注意这里没有 /m 修饰符默认是单行模式。在默认模式下^ 只能匹配整个字符串的绝对开头。$ 匹配整个字符串的结尾或者是字符串末尾的换行符之前。这意味着它要求你的整个输入字符串极其严格除了 “php”或其大小写变体之外不能有其他多余的行或字符。绕过思路既然第一个正则支持多行匹配而第二个正则不支持我们就可以利用 换行符\nURL编码为 %0a 来构造攻击载荷Payload。我们需要构造一个字符串使得它包含多行其中有一行是 php构造结构换行符 php 或者是 php 换行符 任意内容分析原理比如我们输入 %0aphp即一个换行符后面跟着 php。第一个正则 (/im)因为有换行符它把字符串看作两行。第二行是 “php”完全符合 /^php/匹配成功进入内层循环。第二个正则(/i)因为它没有/m它把/匹配成功进入内层循环。 第二个正则 (/i)因为它没有 /m它把 %0aphp 当作一个整体。由于绝对开头是一个换行符而不是 p所以 ^php/匹配成功进入内层循环。第二个正则(/i)因为它没有/m它把匹配失败直接走到 else 分支输出 $flag。最终构造 Payload在 URL 中换行符 \n 需要进行 URL 编码对应的编码是 %0a。你可以尝试以下任意一种构造方式方式一开头加换行payload?cmd%0aphp方法二结尾加换行和任意内容payload为?cmdphp%0aa