CTF中preg_match函数的利用技巧

18 人参与

CTF赛场上,preg_match往往是把握关键权限的刀锋。它的匹配结果直接决定了后端逻辑的分支,稍有不慎就会让payload化为乌有。于是,熟悉其正则语法、PCRE特性以及PHP对输入的预处理方式,成为选手们在逆向题目中抢占先机的必修课。

正则表达式的核心要点

PCRE引擎支持的断言((?=...)(?!...))以及字符类的细粒度控制,是构造“看似合法、实则匹配”模式的基石。忽略大小写可通过i修饰符实现,而s则让点号匹配换行,常被用来跨行捕获。

常见的绕过技巧

  • 利用x00插入空字符,迫使trimstrlen误判长度。
  • 在字符类中混入[:punct:][:graph:]等 POSIX 类,扩大匹配范围,却不破坏整体结构。
  • 用非捕获分组(?:...)隐藏冗余子表达式,防止$match数组膨胀导致后续检查失效。

实战案例分析

某题给出的代码片段如下:

<?php
$key = 'KEY{********}';
$IM = preg_match("/key.*key.{4,7}key://.//(.*key)[a-z][[:punct:]]/i", trim($_GET["id"]), $match);
if ($IM) { die('key is: '.$key); }
?>

要让$IM为真,只需让正则成功捕获任意子串。一个常用的payload是:

http://example.com/vuln.php?id=keyABCkey12345key:/x/foobarkeyb!

这里keyABCkey12345key满足key.*key.{4,7}key的长度要求,随后:/x/foobar填补.//的占位,最后的b!匹配[a-z][[:punct:]]。一旦匹配成功,die立即泄露KEY{...}

细节决定成败:若在浏览器地址栏直接输入#?id=后留空,trim会去掉所有空白,导致正则匹配失败;而在payload中加入%00则可以制造隐藏字符,使得strlen判断仍为非零,却让正则看到完整模式。

参与讨论

18 条评论
  • 话题吸尘器

    空字符截断现在还能用?PHP版本是不是得卡老一点?

    回复
  • 晨跑的风景

    key.*key.{4,7}key 这个长度限制卡得真死,试了好久才凑对

    回复
  • 渔翁老周

    感觉用x修饰符加注释也能绕,不过例子没提

    回复
  • 龙女泪

    trim吃掉空格太坑了,本地测通线上挂,气死

    回复
  • 暗码之主

    %00在高版本PHP里早被干掉了,这题环境估计是5.6?

    回复
  • 赛博酷玩

    非捕获分组确实省事,不然$match下标乱七八糟的

    回复
  • 刃影

    看不懂正则那段,有没有大佬画个匹配流程图?

    回复
  • 黑曜战魂

    之前搞过类似题,也是靠[:graph:]混进去的

    回复
  • 漠辰

    又是PCRE特性题,每次看都头大 😩

    回复
  • 爵士

    payload最后那个b!匹配[a-z][[:punct:]],感叹号算标点?

    回复
  • WhistleStop

    说白了就是钻正则和字符串处理的空子,挺脏但有效

    回复
  • 闪电小星星

    这题要是没给代码片段根本没法下手吧

    回复
  • 隐形人设

    i修饰符忽略大小写,那KeyKEYkey行不行?

    回复
  • 社恐小风扇

    实战案例这payload构造得真细,服了

    回复
  • 尘封往事

    这题我做过,payload里那个[:punct:]差点没绕过去

    回复
  • 石膏

    @元宝 这题的正则也太绕了吧,能看懂的都是神仙

    回复
    1. yuanbao

      @ 石膏 其实多拆解几次就明白了,先看key.*key这部分,后面跟上4-7个任意字符,最后用b!这样的字母加标点收尾就行。

      回复
  • 傲娇天鹅湖

    payload里那个空字符%00的利用挺巧妙

    回复