美文网首页
从一道CTF的非预期解看PHP反斜杠匹配问题

从一道CTF的非预期解看PHP反斜杠匹配问题

作者: yzddMr6 | 来源:发表于2020-04-13 08:21 被阅读0次

    前言

    刷buuoj的时候遇到这样一个题,做一半看到他这个正则写的有点问题,就去翻wp。


    image

    找到了官方的wp发现果然是个非预期。

    image

    但是官方wp中并没有深入说明。后来看到评论去翻出题人的博客也没找到相关的信息,加上看到了其他wp中一些不准确的说法,所以今天就有了这篇文章来讲一讲自己的看法。

    正文

    题目源码

    <?php
    error_reporting(E_ALL || ~ E_NOTICE);
    header('content-type:text/html;charset=utf-8');
    $cmd = $_GET['cmd'];
    if (!isset($_GET['img']) || !isset($_GET['cmd'])) 
        header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
    $file = hex2bin(base64_decode(base64_decode($_GET['img'])));
    
    $file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
    if (preg_match("/flag/i", $file)) {
        echo '<img src ="./ctf3.jpeg">';
        die("xixi~ no flag");
    } else {
        $txt = base64_encode(file_get_contents($file));
        echo "<img src='data:image/gif;base64," . $txt . "'></img>";
        echo "<br>";
    }
    echo $cmd;
    echo "<br>";
    if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
        echo("forbid ~");
        echo "<br>";
    } else {
        if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
            echo `$cmd`;
        } else {
            echo ("md5 is funny ~");
        }
    }
    

    前面md5碰撞已经是老套路了,问题出在后面对shell命令的过滤上。

    if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
        echo("forbid ~");
        echo "<br>";
    } 
    

    熟悉php代码审计的同学应该都知道,在preg_match中要过滤\ 是需要四个\\\\才可以达到目的,原理如下:

    $str = '\/div';
    $pattern = '/\\\\\/div/';
    // '\\\\\/' 解析过程如下:
    // PHP解析:
    // 第1个'\'转义第2个'\',转义后为字符串'\'
    // 第3个'\'转义第4个'\',转义后为字符串'\'
    // 第5个'\'转义'/',转义后为字符串'/'
    // 字符合起来为'\\/' (则 \\/div 即为正则将要解析的内容,注意:正则解析的内容已经不包括正则标识符//)
    // 正则解析器解析:
    // 两个'\\' 正则表达式看做'\' (则正则最终解析为 \/div)
    $rs = preg_match($pattern, $str, $arr);
    if($rs) print_r($arr); // Array ( [0] => \/div )
    

    但是出题人似乎觉得不够,又在后面加了四个反斜杠的匹配,似乎本意是要过滤\\\

    理论来说已经出现了四个\\\\了,但是为什么还会造成非预期ca\t这种解呢?

    我们本地测试一下

    去掉其他的乱七八糟的东西,只留下对于反斜杠等的过滤

    image

    可以看到虽然正则中有\\\\,但是却无法过滤到反斜杠。

    反向思考其原因,应该是问题出在前面两个反斜杠的匹配部分。

    因为正则匹配中相当于要经过两层解析器解析,一层是php的,一层是正则表达式的。所以此处前面的两个反斜杠经过php解析器处理后应该是表示了一个转义号\,之后又与后面的表示逻辑或的|结合到一起,从而在正则表达式解析器中解析为\|。又因为|是正则中的保留符号,所以需要一个转义符来转义。所以最后的实现效果应为对于字符|的过滤。

    所以我们猜测这种写法真正被解析的结果应该是对于字符串|\的过滤,即不是单独的\的匹配。

    我们来验证一下猜想是否正确:


    image

    可以看到此时已经触发了正则匹配机制,输出了forbid。

    所以综上所述:非预期的原因是错误的正则写法匹配了|\,而非预期的\

    错误的一些说法

    第一个

    https://www.cnblogs.com/20175211lyz/p/12189515.html
    这篇文章中提到反斜杠有这么多种匹配方法,如果你做实验的话发现也确实会输出1234。事实真的是这样吗?

    image

    随便写个字符串,发现134照样可以匹配到。

    image
    原因是134条规则都在左右多加了个|,然而|左右为空,也就是说对于任意空字符串都可以匹配,而并非预期的目的。

    第二个

    这篇文章的解释是把\t当成tab,这个就更离谱了。
    https://blog.csdn.net/SopRomeo/article/details/104124545

    image

    第三个

    https://www.jianshu.com/p/21e3e1f74c08

    image
    这个同学自己调试了一番,离真相就差一点啦。

    最后

    纸上得来终觉浅,绝知此事要躬行。

    与君共勉。

    相关文章

      网友评论

          本文标题:从一道CTF的非预期解看PHP反斜杠匹配问题

          本文链接:https://www.haomeiwen.com/subject/wepwmhtx.html