CTF 技巧:preg_match() 绕过实战与深度解析
1. preg_match()函数基础解析在CTF比赛中preg_match()函数是PHP中用于正则表达式匹配的核心函数理解它的工作原理是绕过防御的关键。这个函数的基本语法是preg_match($pattern, $subject)其中$pattern是正则表达式模式$subject是要检查的字符串。我刚开始接触CTF时最头疼的就是这个函数。记得有一次做题明明payload看起来没问题但就是被preg_match()拦截了。后来才发现原来这个函数对字符串的处理有很多隐藏特性。比如它默认只匹配字符串的开头部分除非你在正则表达式里加上/m修饰符。这个函数有几个重要特性值得注意返回值匹配成功返回1失败返回0出错返回false默认行为只匹配第一次出现的结果匹配方式默认区分大小写除非使用/i修饰符特殊字符点号(.)默认不匹配换行符除非使用/s修饰符理解这些基础特性非常重要因为很多绕过技巧都是基于这些特性发展出来的。比如数组绕过就是利用了preg_match()对数组处理的特殊行为。2. 常见绕过方法实战2.1 数组绕过技巧数组绕过是最简单直接的preg_match()绕过方法。原理很简单preg_match()处理数组时会报错并返回false而false在条件判断中会被当作0处理。来看个实际例子?php $a [flag.php]; if(!preg_match(/flag/, $a)) { echo 绕过成功; } else { echo 被拦截了; } ?这里的关键在于我们传入的是一个数组而不是字符串。preg_match()在处理数组时会触发警告并返回false而!false在条件判断中等同于true所以成功绕过了检查。我在实际做题中发现这种绕过方式有几个变种直接数组赋值$a [flag.php];使用array()函数$a array(flag.php);空数组追加$a[] flag.php;需要注意的是一定要确保传入的是真正的数组。我曾经犯过一个错误写成了$a [flag.php]这实际上是个字符串无法绕过检查。2.2 回溯次数绕过回溯次数绕过是利用了PHP正则引擎的回溯限制特性。PHP默认设置了100万次回溯限制不同版本可能不同超过这个限制会导致匹配失败。实现方法很简单$a str_repeat(a, 1000000).flag.php;这个payload会先创建100万个a然后拼接上flag.php。当正则引擎尝试匹配时会因为回溯次数过多而失败返回false。不过这个方法有个限制它只能让preg_match()失败但并不能保证后续代码能正常执行。我在测试中发现如果直接在后面拼接敏感字符串还是可能被拦截$a str_repeat(a, 1000000).flag.php; // 可能还是会被拦截更有效的方法是结合其他技巧比如先触发回溯限制再通过其他方式传递payload。2.3 换行符绕过有些WAF规则编写不严谨可能只检查单行内容。这时可以通过换行符来绕过$a anything\nflag.php;原理是正则表达式默认以$匹配行尾而很多简单的黑名单规则可能没有考虑多行情况。不过这个方法成功率不高因为现代WAF通常都会处理多行输入。我在测试中发现URL编码的换行符%0a有时也能起作用$a %0aflag.php%0a;但正如原始文章作者提到的这个方法在实际中效果有限需要结合具体题目环境尝试。3. 高级绕过技巧3.1 编码混淆技术当基础绕过方法失效时可以尝试各种编码混淆技术。PHP对字符串的处理非常灵活这给了我们很多操作空间。十六进制编码绕过$a \x66\x6c\x61\x67\x2e\x70\x68\x70; // flag.php的十六进制表示Unicode编码绕过$a fl\x{0061}g.php; // 使用Unicode编码的a这些编码方式可以绕过简单的字符串匹配但对使用了/u修饰符的正则表达式无效。3.2 正则表达式特性利用深入理解正则表达式特性可以找到更多绕过方法。比如利用正则的最左匹配原则$a flflagag.php; // 中间插入干扰字符或者利用零宽断言$a fl(?ag)ag.php; // 使用正向预查这些方法需要对正则表达式有深入理解在特定题目中可能非常有效。3.3 多段匹配绕过有时候可以尝试将敏感字符串拆分到多个参数中$a fl; $b ag.php;然后在服务端拼接时触发漏洞。这种方法在代码审计类题目中特别有用。4. 防御与对抗4.1 如何防御这些绕过作为开发者了解这些绕过方法后可以更好地防御它们严格类型检查if(!is_string($input)) { die(Invalid input); }多重过滤$filtered preg_replace(/[^a-zA-Z0-9]/, , $input); if(preg_match(/flag/, $filtered)) { die(Detected malicious input); }限制输入长度if(strlen($input) 100) { die(Input too long); }4.2 CTF中的实战思路在CTF比赛中遇到preg_match()过滤时我的常规思路是先尝试最简单的数组绕过测试各种编码方式检查是否有长度限制可以触发回溯分析正则表达式模式寻找可能的绕过点尝试多段拼接或参数污染最重要的是保持耐心很多时候需要尝试多种方法的组合才能成功绕过。