请选择 进入手机版 | 继续访问电脑版
查看: 1687|回复: 7

正则表达式的学习与实例运用

[复制链接]
  • TA的每日心情
    开心
    2017-10-1 06:59
  • 签到天数: 308 天

    [LV.8]以坛为家I

    发表于 2016-6-4 23:31:07 | 显示全部楼层 |阅读模式
          以后少写渗透实战类的帖子了,多点安全分析、知识介绍类的。

          首先,要真正了解并应用正则表达式,需要你具备一点脚本编程、脚本分析的能力。如果你没有一点脚本基础并且也志不在此的话,那么这篇文章就可以略过了。本文仅同对脚本编程、脚本渗透有兴趣的人分享。本文内容包括基础介绍和实例分析两部分:前面的基础介绍有参考网上的相关资料,有自己的分析总结,后面将会结合实际运用举出三个实例,相信可以让不熟悉正则表达式的人能基本了解这个东东。

          在开始正文之前建议大家边看边准备一个正则测试工具,也可以使用在线正则测试,网上搜一下很多,这玩意儿和编程一样需要多动手,不同的是它简单多了,正常人都能懂。
          正则表达式的重要性,不懂的人可能觉得就是个匹配规则、无关紧要。但就我本人来看非常重要,它的作用是非常大的,实际编程中,正则表达式的使用频率非常高,正则是一个程序员必备的技能。在渗透测试领域,waf的过滤、exp的编写、CMS的过滤等,一般都是使用正则来匹配过滤的。

          用一句比较抽象的话来说:正则表达式就是一种描述匹配规则的工具。很多人应该都用过windows操作系统的搜索功能,对windows搜索里的通配符(*)、(.)应该不陌生,比如(*)匹配任意数量字符,比如你要搜索磁盘中全部的php文件,那么搜索(*.php)就OK了,这里的通配符本质上和正则表达式是一样的,也是描述一种匹配规则,只不过后者要更加丰富、更加复杂,当然匹配精准度也更高。

          首先来一个简单的例子,让大家对正则表达式有个最基本的认识。
          待匹配内容:I come from ihonker!
          匹配目标:ihonker
          这里要匹配出ihonker这个单词,那么最简单的就是直接把ihonker这个单词作为正则表达式即可了,看实际测试:

        01.jpg

          上面是正则表达式最简单的应用,聪明的你还能发散一下:如果待匹配目标是from的话,那么正则表达式换成from即可,同理可以推测其他单词比如come等等。但是在实际运用中待匹配的内容样式百出,匹配目标也是千奇百怪,仅仅使用单词来匹配就远远不够了。

          我把上面的内容稍微调整一下:
          待匹配内容:
          I come from ihonker! Our website is www.ihonker.org.
          If we remove point of the URL,it becomes as wwwihonkerorg.
    待匹配目标:ihonker(单独的单词)
        我们看看实际的匹配结果,可以看到匹配出了3个结果,它把前后两个url中的ihonker也匹配出来了。但我们目的是仅仅匹配出单个存在ihonker单词,也就是第一个ihonker,所以这个时候就需要深入的了解正则表达式的语法了。(当然我这里可以提前告诉你,正确的匹配规则是:\sihonker\b。它是怎么得出来的?请搬个小凳子慢慢看)
           02.jpg
    当然,上面这段字符也还是非常简单的,我们再来一个更加复杂点的例子。待匹配文本是下面这段代码,总共创建了4个函数,现在需要从这4个函数中匹配出所有参数都为可控参数的函数,把符合规则的函数所有内容都匹配出来,也就是说需要匹配出第2个和第4个函数的全部内容。
        这个就需要动点脑筋了,我也花了点时间才搞出来(水平同样很次),当然这里也提前把结果给出来:\bfunction\b[^{]*?\(\$([^\),]*?\)|[^\)]*?\){2})[^}]*?}
        就是上面这个表达式,不懂的阶级兄弟千万不要被上面的各种符号唬倒了,其实只要你看了下面的内容,相信会很容易理解,或许还能整出更高效的正则式(正则表达式本身比较灵活,理论上匹配同样的内容可以写出无限种匹配规则)。实际编程中对正则表达式应用难度只在其上不在其下,所以要学的东西还是很多的~
    [PHP] 纯文本查看 复制代码
    <?php
        function GetVar(a=2,b=3)
        {
            global $_vars;
            return isset($_vars[$k]) ? $_vars[$k] : '';
        }
        function Control                      //测试函数
        ($a)
                                              //This is a test!
        {
            global $dsql;
            $this->tpl = isset($this->tpl)? $this->tpl : new DedeTemplate();
            $sqltype = "DedeSql";
            if ($GLOBALS['cfg_mysql_type'] == 'mysqli' && function_exists("mysqli_init")) $sqltype = "DedeSql";
            else $sqltype = "DedeSqli";
            $this->dsql = isset($dsql)? $dsql : new $sqltype(FALSE);
        }
        function SetTemplate               //测试函数
        
        ($tplfile,                        //测试函数
        
        b=3)
        {
            $tplfile = DEDEAPPTPL.'/'.$this->style.'/'.$tplfile;
            $this->tpl->LoadTemplate($tplfile);
        }
        function                         //测试函数
                                         //This is a test!
        mytest($tplfile,contest($_GET["id"]))
        {
            $tplfile = DEDEAPPTPL.'/'.$this->style.'/'.$tplfile;
            $this->tpl->LoadTemplate($tplfile);
        }
    ?>


    元字符

    学习正则表达式首先要理解元字符,元字符是正则表达式的一个很重要的内容。我本人对它的理解:元字符是用来匹配某一特定类型的字串,它的作用是缩小正则表达式的体积,减轻我们的工作量。在没有元字符的情况下,如果你要匹配一个文本中所有的汉字,那么你可能需要准备一张汉字码表,然后把所有的汉字都放到正则表达式中去,我想这样的规则一定会让所有人都叹为观止!
        常用的元字符都在下面这张表列出来了,还有更多的就不一一列出,有人可能一看到这么多的符号就头晕了。我要告诉你的是我和你一样,我刚开始的时候也是很头疼记不住,但多次使用练习后,就慢慢记住了。所以,别担心,慢慢来。
           03.jpg
    下面挑几个典型的元字符做示例分析
        元字符:\b
    \b匹配一个单词的边界,换句话说它不匹配任何字符,它只匹配一个位置。在编程中,一个单词通常由空格、换行符以及其他特殊符号(比如”(”、”)”等)分隔开来,\b正是对应这些分割符号,但它又不会把这些符号匹配出来。如果不好理解的话,下面用实例来说明。
    有一行代码如下:function test($str,$_GET[“id”],utest($act),$testus=2),我们要匹配出它的函数名也就是test。先观察这个字串,里面包括3个test,其中后两个test分别包含在utest($act)和$testus=2两个字串中,要剔除这两个字串,我们的匹配规则就需要区隔掉字符’u’、’(‘和’$’。其中u是正常的小写字母,而’(‘和’$’则是特殊符号。\b不会对应’u’,但是会对应’(‘和’$’两个特殊符号!@#¥看到这里,你会不会想既然\b会对应’(‘和’$’,哪该怎么剔除呢?
    仔细分析一下:第一个test前后都是符号(前面是空格,后面是’(‘),后两个test只有一边是符号,那么这样的话匹配规则就呼之欲出了:\btest\b。这个正则的意思就是匹配单词test,test前后都为符号时即满足此规则,若前后有有任一个不为符号即不满足。你可以试一下\btest或者test\b这两种匹配结果。
           04.jpg
    如果把代码改为如下:function test($str,$_GET[“id”],utest($act),$test=2)。那么前面的规则就不太灵光了,它也会把第三个test匹配出来,这时候就需要另外的匹配规则了,可以自己思考一下。
    顺便说一下,看了\b的作用,再结合上面的那张表,我想你可以试着理解前面的第2个例子了。
    元字符:\w
    \w匹配任意数字、大小写字母、下划线(\w匹配汉字是.net环境下的正则表达式引擎提供的,本文不涉及)
    字符串:我a是C#7_おは0مرحبا。这段字符包括了汉字、大小写字母、数字、下划线、日语、希伯来语和#(特殊符号),我们看看它的匹配结果。这个比较容易理解就不多解释了。可以和\b对比一下,你会发现\w有自己的匹配内容,但\b并没有。
           05.jpg
    元字符之数量匹配符:*+?{n}{n,m}
    数量匹配符本身并没有匹配内容,它跟在匹配值的后面,使该匹配值重复匹配指定次数。(*)重复匹配0到任意次,(+)重复匹配1到任意次,(?)重复匹配0或1次,{n}重复匹配n次,{n,m}重复匹配n到m次。
    注:如果匹配值后面没有跟数量匹配符的话,那么它只能匹配一次。
    列出几个简单示例
    \d+:匹配任意数字1至任意次
    \w*:匹配任意大小写字母、数字、下划线0至任意次
    \s{5,8}:匹配任意空白符5到8次
    A?:A字母重复匹配0次或1次
    元字符:[^n]
         [^n]匹配除了n以外的任意一个字符,[^a-z]匹配任意一个非小写字母(其他可联想A-Z0-9)
         [^n]里的^和单独的^使用情况要区分开来,单独的^对应输入字符串的开头,而方括号中的^则是反义的意思。
    元字符:|
         |的作用是将该符号前后的两个规则都适用。
         \d{2,5}|[a-z]{5,8}:匹配任意2到5位的数字,或者匹配任意5到8位的小写字串。
       待匹配内容:<div>test1</div>bb<div>test2</div>
       匹配目标:所有的尖括号及其里面的内容
       匹配规则:\W\w+\W|\W{2}\w+\W(当然也可以精简为\W{1,2}\w+\W,还有其他无数种方法这里就不一一列举了)
    元字符:()
        (\d{5}):匹配并获取任意一个5位数字。
       待匹配内容:<div>test1</div>bb<div>test2</div>
       匹配目标:所有的尖括号里面的内容
       匹配规则:<(\W?\w+)>
         ()在实际编程中经常用到,用上面的例子来运行一下:
       从运行结果还可以看出来,()内的匹配规则是重复匹配的,直到把所有符合规则的内容匹配出来,并返回一个数组。并列的规则,通常也用()括起来。
          06.jpg
    元字符:/i/g/e/m
         /i:忽略大小写
        /g:
        /e:
        /m:
    贪婪模式与非贪婪模式
    数量匹配符后面跟?的匹配模式就是非贪婪模式,不跟?的模式是贪婪模式。贪婪模式在同等情况下匹配尽可能多的结果,而非贪婪模式则匹配尽可能少的结果。
    待匹配内容:asdabaecdbadefdlsdbsdlfbdalfdbdfadfdb
    匹配规则:分别用a\w*b和a\w*?b两个正则匹配
    结果1:asdabaecdbadefdlsdbsdlfbdalfdbdfadfdb
    结果2:
    asdab
    aecdb
    adefdlsdb
    alfdb
    adfdb
    在爬虫程序中,(.*?)这个正则出现的概率非常高,就是运用了非贪婪模式。
    好了,正则表达式的常见的基础应用差不多就是这些了,我写的比较简单,真正要记住还是得以点带面。
    到了这里,我想你也可以试着去读懂前面的第三个例子了。可能还是有点不好理解,不过没关系,看了下面的实例分析相信你就可以了。

    实例分析
    实例运用一:利用正则表达式编写自己的expdedecms路径泄漏漏洞
    在开始之前,还是要啰嗦一下:正则表达式是一个工具,它是用来辅助我们其他工作的。比如在本例的exp编写,需要的基础是python脚本编程和dedecms路径泄漏漏洞原理研究,然后再辅之以正则匹配,那么一个exp就可以成型了。
    首先简单介绍一下dedecms的路径泄漏:在url地址后面加上member/inc/config_pay_alipay.php可以爆出网站物理路径(当然如果网站配置得当就爆不出来了),那么我们exp的目的有二个:一是检测网站是否存在路径泄漏;二是把物理路径匹配出来。
        看看下面的这个例子。
           07.jpg
       它的源码如下:
    [HTML] 纯文本查看 复制代码
    <br />
    <b>Warning</b>:  require_once(DEDEMEMBER/paycenter/alipay/alipay_config.php) [<a href='function.require-once'>function.require-once</a>]: failed to open stream: No such file or directory in <b>/home/seo3c5s5e4o93dc/wwwroot/member/inc/config_pay_alipay.php</b> on line <b>11</b><br />
    <br />
    <b>Fatal error</b>:  require_once() [<a href='function.require'>function.require</a>]: Failed opening required 'DEDEMEMBER/paycenter/alipay/alipay_config.php' (include_path='.:/usr/local/lib/php') in <b>/home/seo3c5s5e4o93dc/wwwroot/member/inc/config_pay_alipay.php</b> on line <b>11</b><br />
    现在开始第一步:如何检测网站存在路径泄漏?我们知道采用CMS程序的网站,主体代码都是一致的,具体到这里可以判断两个报错提示:”Warning”和” Fatal error”是一定会出现的。So,我们可以判断页面的返回内容是否包含这两个单词,若包含则可推断存在路径泄漏,否则没有。
    正则表达式的任务就是从文本中匹配出这两个单词,再来观察一下这两个单词的特征,发现都被<b></b>一对标签框住,Warning是纯字母,Fatal error包含字母和空格,这样的话我们用一个基准匹配值加上数量匹配符,基准匹配值要同时能匹配字母和空格([\w\s]意为匹配任意一个字母、数字、下划线或空白符),这样也会把<b>11</b>匹配进来,但后面再接数量限制就可以排除了,数量匹配符在7和11之间({7,11})。完整的的正则表达式:<b>([\w\s]{7,11})</b>
    里面加了(),是为了后期让程序从中取出匹配的单词内容(回过头去看看()的作用),加以验证是否和预设单词一致,从而判断网站是否存在路径泄漏。这样第一步工作就完成了。
    开始第二步:如何获取网站物理路径?从源码可知物理路径为字符串”/home/seo3c5s5e4o93dc/wwwroot/member/inc/config_pay_alipay.php”,它同样包含在一对<b></b>中,字符内容包括反斜杠、字母、数字、下划线和小数点(别的网站也可能会包含其他特殊字符、空白符、中文、日文、火星文等,网站物理路径总是千奇百怪的)。聪明的你可能会想到把基准匹配值规则设定为匹配任意字符,然后后面加上数量匹配符就可以了。但我这里的回答是正确但又不合理的,说正确是因为这个匹配规则的确可以匹配出物理路径,说不合理是因为这个规则也会把前面的warning和fatal error这两个单词也匹配出来,这样就产生混淆了。
    这里的匹配规则要能匹配出物理路径,同时区别于前面的报错单词。物理路径的一大特点就是它包含斜杠,我们可以用这个反斜杠进行区隔。本例的物理路径是linux下的,它的首字符固定是一个斜杠,但windows下的物理路径类似D:/phpweb/www/include/test.php,反斜杠前面有字母和特殊符号。这里可以试着先写出一点:[^/]{0,10}?,意思是以非贪婪模式匹配任意非/字符0次到10次,如果路径的/字符前面存在其他字符,那么就匹配n次,否则就不匹配(0次)。然后再加一个/,正则式就变成了[^/]{0,10}?/,意思是匹配/字符一次,因为源码中只有路径会必然包含这个字符,所以只要匹配一次,确定字符中存在/那么就可以判断该字符为路径了。再后面就匹配任意字符,当然须不包括<>,这样可以避免匹配到</b>后面的字符(代码中存在多个</b>,如果不加<>会匹配错误),这样我们的正则式就变成了这样:[^/]{0,10}?/[^<>]{0,150}?,新加上的部分意思是以非贪婪模式匹配任意非<>字符0次到150次,这样这个匹配路径的正则式就完成了。再加上前后的区隔判断,最终完整的正则式为:<b>([^/]{0,10}?/[^<>]{0,150}?)</b>
    好了,看完了这个实例分析,我想前面的第三个例子应该会有点头绪了吧。
    下面要说点题外话,这里的实例最终要以py脚本的形式完成。而在编程中有数组的概念,具体到这个例子就是正则表达式的()里面的内容,会以数组的形式返回给程序,那么在编写程序的时候可以取巧:我们只用匹配出任意<b></b>之间的内容,然后利用数组索引取出响应的值来进行判断。对应的正则式为:<b>[\s\S]{0,150}?</b>,[\s\S]的意思为匹配任意字符一次。
    看看它的匹配结果,返回一个包含6个元素的数组,程序中取出第1或第4个元素,进行判断就OK了。
        共找到 6 处匹配:
        <b>Warning</b>
        <b>/home/seo3c5s5e4o93dc/wwwroot/member/inc/config_pay_alipay.php</b>
        <b>11</b>
        <b>Fatalerror</b>
        <b>/home/seo3c5s5e4o93dc/wwwroot/member/inc/config_pay_alipay.php</b>
        <b>11</b>
        据此,我们最终的exp脚本为
    [Python] 纯文本查看 复制代码
    import re
    import requests
    
    #判断网站是否存在路径泄漏
    def check(WholeUrl):
        try:
            response = requests.get(url=WholeUrl,timeout=15)
            content = response.content
            global result
            result = re.findall(Judge,content)
            if len(result) == 6:                                #判断获取的数组是否包含6个元素,如果不是则判断不存在漏洞
                continue
            else:
                return
            if result[0] == 'Warning' and result[3] == 'Fatal error':     #判断数组的第0个和第3个元素是否对应上warning和fatal error
                return True
            else:
                return False
        except Exception as e:
            return False
    
    #获取网站物理路径
    def match(WholeUrl):
        Status = check(WholeUrl)
        if Status == True:
            WebPath = result[1]
            PathJudge = re.compile(r'/[\s\S]{0,20}?/')          #判断数组的第1个元素是否是物理路径,这一步也可以不要。不过编程的一大原则是考虑各种容错,所以还是判断一下保险。
            PathResult = re.findall(PathJudge,WebPath)
            if len(PathResult) != 0:
                print '%s 存在物理路径泄漏' % DomainUrl
                print '%s 物理路径为:%s' % (DomainUrl,WebPath)
            else:
                print '%s不存在物理路径泄漏' % DomainUrl
        else:
            print '%s不存在物理路径泄漏' % DomainUrl
               
    if __name__=="__main__":
        DomainUrl = raw_input('请输入待检测地址:')
        exp_url = '/member/inc/config_pay_alipay.php'
        Judge = re.compile(r'<b>([\s\S]{0,150}?)</b>',re.DOTALL)   #获取所有的<b></b>之间的字符,返回一个数组
        WholeUrl = DomainUrl + exp_url      #合并url生成http://localhost/member/inc/config_pay_alipay.php
        match(WholeUrl)

      看看执行效果:
       09.jpg
      如果能做到这一步的话,那么你就可以尝试向bugscan等在线漏洞扫描平台提交插件了,当然插件代码要根据平台的统一第三方库和其他规范进行调整,不过那就不是难题了。

    实例运用二:打造自己的代码审计规则库(基于seay代码审计软件)

      很多刚接触代码审计的人可能都用过seay代码审计软件,这款软件的一个最基础也是最有用的功能是它的自动审计。自动审计的原理是用正则表达式把web程序的最容易产生漏洞的地方匹配出来,比如上传函数move_uploaded_file()就有可能产生上传漏洞,那么就用正则表达式匹配出该函数,算是进行了初次审计,真正确定漏洞还需要后期的人工验证。
       10.jpg
      该软件的匹配规则位于config/rule.bin文件
        11.jpg
    可以看到软件自带了一些常见的简单漏洞,如果你对代码审计有研究,那么可以自己在文件下面添加新的匹配规则,我们这里来添加php的反序列化函数unserilize()来试一下。
    Php的反序列化漏洞原理这里就不多解释了,该函数接受1个参数,如果这个参数是变量的话,那么就有可能产生反序列化漏洞(当然还得结合其他条件,不过那就不是正则表达式的任务了)。这里就用正则表达式匹配出带变量的unserilize函数,然后再辅助人工验证。
    首先分析一下,我们需要匹配出两个东西:一是unserilize函数,二是它的变量参数。Unserilize函数很好匹配,直接匹配这个单词就可以了。变量参数则需要分成两种情况:一是单一的变量,比如unserilize($str)这类的;二是unserilize(test($str))这类的,也就是函数的执行结果作为变量参数,当然里面的函数也要接受至少一个变量参数。
    下面再进一步分析unserilize()函数可能的表现形式:按照Php的语法规范,一个函数的标准写法就是unserilize($str)这样的形式(函数名后紧跟括弧,括弧里面只包括一个变量),但同时还有其他很多形式也可以正常执行,下面把这些形式总结一下。
           12.jpg
    最后二种形式虽然也是合规的,但实已达到令人惨绝人寰的无语地步,能写出这种函数的人一定是从火星来的。我们是地球人,还是以地球人的思维习惯来解决问题,只考虑前面3种形式(其实第三种形式虽然不至于远到火星级别,但也达到月球级了)。
    首先匹配unseriize这个单词,很好匹配:\bunserilize\b就可以了。后面的变量部分根据前面的分析要分成两种可能,所以需要用并列匹配,也就是(pattern01|pattern02)这种形式。
    单一的变量匹配:\(\s{0,10}\$\w{0,30}\s{0,10}\)。简单说明一下,首先匹配左括弧,也就是\(,然后考虑括弧和变量之间可能存在空白符(没办法,有时候程序员手误就多打了一个空格),所以用\s{0,10},匹配10个空白符(绰绰有余了),然后匹配变量,php变量在开头都有一个$符号,所以用\$匹配变量符号,然后匹配变量的单词部分也就是\w{0,30}(应该不会出现超过30个字母的变量单词吧),再然后再一次匹配空白符\s{0,10},最后匹配右括弧\)。
    函数变量匹配:\(\s{0,10}\w{0,30}\s{0,10}\(\s{0,10}[^\$]{0,30}?\$[^\)]{0,50}\)\s{0,10}\)。简单说明一下,首先匹配左括弧\(,然后匹配可能存在的空白符\s{0,10},再匹配函数名\w{0,30},再匹配函数名和左括弧之间可能的空白符\s{0,10},接着匹配左括弧\(,再匹配空格\s{0,10},再匹配函数内部的参数,因为里面的这个函数参数可能不止一个,也可能不全是变量,所以先以非贪婪模式匹配变量之前的可能的非$字符[^\$]{0,30}?,接着匹配变量符号\$,然后匹配后面的字符(只要确定不是右括弧就行了)[^\)]{0,50},再匹配里面函数的右括弧\),匹配最后两个右括弧之间的空格\s{0,10},最后匹配最后一个外面函数右括弧\)。
    那么完整的正则表达式就是:\bunserilize\b\s{0,10}(\(\s{0,10}\$\w{0,30}\s{0,10}\)|\(\s{0,10}\w{0,30}\s{0,10}\(\s{0,10}[^\$]{0,30}?\$[^\)]{0,50}\)\s{0,10}\))
    可以把这个玩意儿添加到匹配规则里面,下一次自动审计的时候就可以挖掘可能的反序列化漏洞了。到这里,再回头去看前面的第三个例子,是不是就感觉没什么难度了。

    实例运用三:代码审计之某CMS正则表达式漏洞挖掘
    前面说过,正则表达式非常灵活,同样的匹配内容理论上有无限种匹配规则。它的灵活性一方面可以极大方便编程人员写出高效的正则式,另一方面也可以极大的方便漏洞的产生~!@#。
    来看看下面这段正则表达式。
    [^\\{\\s]{1}(\\s|\\b)+(?:select\\b|update\\b|insert(?\\/\\*.*?\\*\\/)|(\\s)|(\\+))+into\\b).+?(?:from\\b|set\\b)|[^\\{\\s]{1}(\\s|\\b)+(?:create|delete|drop|truncate|rename|desc)(?\\/\\*.*?\\*\\/)|(\\s)|(\\+))+(?:table\\b|from\\b|database\\b)|into(?\\/\\*.*?\\*\\/)|\\s|\\+)+(?:dump|out)file\\b|\\bsleep\\([\\s]*[\\d]+[\\s]*\\)|benchmark\\(([^\\,]*)\\,([^\\,]*)\\)|(?:declare|set|select)\\b.*@|union\\b.*(?:select|all)\\b|(?:select|update|insert|create|delete|drop|grant|truncate|rename|exec|desc|from|table|database|set|where)\\b.*(charset|ascii|bin|char|uncompress|concat|concat_ws|conv|export_set|hex|instr|left|load_file|locate|mid|sub|substring|oct|reverse|right|unhex)\\(|(?:master\\.\\.sysdatabases|msysaccessobjects|msysqueries|sysmodules|mysql\\.db|sys\\.database_name|information_schema\\.|sysobjects|sp_makewebtask|xp_cmdshell|sp_oamethod|sp_addextendedproc|sp_oacreate|xp_regread|sys\\.dbms_export_extension)
    初看这个表达式,会让人涌起一种深深的无力感,难到无从下手。但是既然是研究技术,那就得迎难而上。这里说一下面对这种很长的表达式的分析方法:1.先分解,再分析;2.直接用正则测试工具,各种情况测试。这里既然是教程,那么就先拆解一下这个正则吧,其实我也很头疼。
    根据正则中常见的分隔符|符号和()符号,我们把上面这段正则拆分成几个部分:
    [^\\{\\s]{1}(\\s|\\b)+(?:select\\b|update\\b|insert(?\\/\\*.*?\\*\\/)|(\\s)|(\\+))+into\\b).+?(?:from\\b|set\\b)|   第一段正则
    [^\\{\\s]{1}(\\s|\\b)+(?:create|delete|drop|truncate|rename|desc)(?\\/\\*.*?\\*\\/)|(\\s)|(\\+))+(?:table\\b|from\\b|database\\b)| 第二段正则
    into(?\\/\\*.*?\\*\\/)|\\s|\\+)+(?:dump|out)file\\b|  第三段正则
    \\bsleep\\([\\s]*[\\d]+[\\s]*\\)|   第四段正则
    benchmark\\(([^\\,]*)\\,([^\\,]*)\\)|  第五段正则
    (?:declare|set|select)\\b.*@|    第六段正则
    union\\b.*(?:select|all)\\b|     第七段正则
    (?:select|update|insert|create|delete|drop|grant|truncate|rename|exec|desc|from|table|database|set|where)\\b.*(charset|ascii|bin|char|uncompress|concat|concat_ws|conv|export_set|hex|instr|left|load_file|locate|mid|sub|substring|oct|reverse|right|unhex)\\(|  第八段正则
    (?:master\\.\\.sysdatabases|msysaccessobjects|msysqueries|sysmodules|mysql\\.db|sys\\.database_name|information_schema\\.|sysobjects|sp_makewebtask|xp_cmdshell|sp_oamethod|sp_addextendedproc|sp_oacreate|xp_regread|sys\\.dbms_export_extension)  第九段正则
    这样拆分一下,大概有点思路了,这个正则就是上面9个短正则用|合并的,|作为并列合并符号,只要符合前后其中一个正则,那么就成立了。
    为了便于分析,我们先写一个简单的php脚本做正则测试,对于这种超长的正则还是边分析边测试的好。
    [PHP] 纯文本查看 复制代码
    <?php
        $value="[^\\{\\s]{1}(\\s|\\b)+(?:select\\b|update\\b|insert(?:(\\/\\*.*?\\*\\/)|(\\s)|(\\+))+into\\b).+?(?:from\\b|set\\b)|[^\\{\\s]{1}(\\s|\\b)+(?:create|delete|drop|truncate|rename|desc)(?:(\\/\\*.*?\\*\\/)|(\\s)|(\\+))+(?:table\\b|from\\b|database\\b)|into(?:(\\/\\*.*?\\*\\/)|\\s|\\+)+(?:dump|out)file\\b|\\bsleep\\([\\s]*[\\d]+[\\s]*\\)|benchmark\\(([^\\,]*)\\,([^\\,]*)\\)|(?:declare|set|select)\\b.*@|union\\b.*(?:select|all)\\b|(?:select|update|insert|create|delete|drop|grant|truncate|rename|exec|desc|from|table|database|set|where)\\b.*(charset|ascii|bin|char|uncompress|concat|concat_ws|conv|export_set|hex|instr|left|load_file|locate|mid|sub|substring|oct|reverse|right|unhex)\\(|(?:master\\.\\.sysdatabases|msysaccessobjects|msysqueries|sysmodules|mysql\\.db|sys\\.database_name|information_schema\\.|sysobjects|sp_makewebtask|xp_cmdshell|sp_oamethod|sp_addextendedproc|sp_oacreate|xp_regread|sys\\.dbms_export_extension)";
        $str=$_GET["id"];
        
        if (preg_match("/".$value."/is",$str)==1||preg_match("/".$value."/is",urlencode($str))==1)
        {
            print "参数不合法,重新输入!"."<br/>"."注入参数为:".$str;
            exit();
        }
        else
        {
            print "参数合法,成功绕过!"."<br/>"."注入参数为:".$str;
        }
    ?>
    Preg_math()函数利用了/is,其中i表示大小写同时匹配,s模式下.匹配所有的字符,包括换行符,同时对待匹配参数也用了urlencode()函数编码匹配。这段php脚本的作用是,当我们输入的参数触发了正则表达式时,返回参数不合法,否则参数合法。
    这段正则存在的问题还是比较多的,看了一下绕过的姿势多的让人乍舌。这里简单的分析几个吧。
    先从简单的开始分析,从最后一个开始。这是个防sqlserver数据库注入的正则,天知道怎么会放在Mysql+php环境的正则里面……另外解释一下里面的两个符号(?,之前说过()会把匹配的内容获取并返回,也就是会默认放进指定的缓存中,待后来调用,而?:的作用就是不保存缓存,这样的话就能不被调用了。\\表示匹配\。其他就不用多解释了,这个正则匹配sqlserver的关键词,这里不用多看。
    (?:master\\.\\.sysdatabases|msysaccessobjects|msysqueries|sysmodules|mysql\\.db|sys\\.database_name|information_schema\\.|sysobjects|sp_makewebtask|xp_cmdshell|sp_oamethod|sp_addextendedproc|sp_oacreate|xp_regread|sys\\.dbms_export_extension)
    看第8个正则。整个正则有两个()中间用\\b.*串联(匹配任意符号边界),最后再加一个左括弧(,也就是说同时满足select ascci(、select concat(、update char(,这样的即视为触发正则。
    (?:select|update|insert|create|delete|drop|grant|truncate|rename|exec|desc|from|table|database|set|where)\\b.*(charset|ascii|bin|char|uncompress|concat|concat_ws|conv|export_set|hex|instr|left|load_file|locate|mid|sub|substring|oct|reverse|right|unhex)\\(
    我们来试试,果然过滤了。
    13.jpg
    但如果我们绕一下呢?比如用select concat (也就是在cancat和左括弧之间加上空格呢(同样可以被mysql解析)?看看执行结果,这就绕过了!!!!就是这么简单!!!@#¥%……
    14.jpg
    再看第七个正则:union\\b.*(?:select|all)\\b。这个就是匹配Union select、union all这两个语句,其中每个单词后面都加了边界界定符。这个正则在单词的前后加参数绕过均不能成功,为什么?自己分析一下就知道了,这里就不再细说。但是有一种方法可以绕过,就是往单词中间加参数!对sql注入比较了解的应该都知道了,这里不方便细说,因为这个方法已经可以把这几个正则全部绕过了,有兴趣自己研究吧。
    第六个正则有点莫名其妙:(?:declare|set|select)\\b.*@。原意是要过滤select|declare|set这三个关键词,但因为后面加了@的缘故,导致这个正则没有什么用。随便加上任何一个关键词都可以通过。
    15.jpg
    其他的几个正则显然也存在漏洞,但是这里就不多说了,要写完的话篇幅太大,写了这么多也挺累的,并且这个漏洞目前还没有公开,写的太多容易造成一些不必要的破坏。有兴趣自己去研究绕过吧。实际中要利用这些漏洞,还要结合具体的输入参数处理流程,辅之以人工审计才可,但确定是存在漏洞的,不止一处。
    最后我再告诉你,这个正则表达式出自于国内一个使用非常广泛、非常知名的CMS。今天是2016年6月4日,就是现在,它仍然用在其最新版本的程序中。你会不会觉得学习正则表达式非常有必要了?
    写这篇文章就是两个目的:一是作为正则表达式的教程供大家参考;二是说明正则表达式非常重要,掌握了它可以干很多事情。


    正则表达式的学习与实例运用.pdf

    1.49 MB, 下载次数: 21, 下载积分: i币 -1

    评分

    参与人数 1i币 +5 收起 理由
    小圈圈 + 5 支持原创

    查看全部评分

    回复

    使用道具 举报

  • TA的每日心情

    3 天前
  • 签到天数: 928 天

    [LV.10]以坛为家III

    发表于 2016-6-5 01:35:16 | 显示全部楼层
    可以的,知识面越来越宽了

    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2018-11-6 18:37
  • 签到天数: 358 天

    [LV.8]以坛为家I

    发表于 2016-6-5 10:44:14 | 显示全部楼层
    就喜欢这样的帖子!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    2017-9-11 15:40
  • 签到天数: 425 天

    [LV.9]以坛为家II

    发表于 2016-6-5 14:45:51 | 显示全部楼层
    终于在论坛看到有点意思的文章了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    郁闷
    2016-7-1 14:57
  • 签到天数: 251 天

    [LV.8]以坛为家I

    发表于 2016-6-5 16:14:30 | 显示全部楼层
    这文章还是30分钟的正则
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2019-10-17 06:41
  • 签到天数: 182 天

    [LV.7]常住居民III

    发表于 2016-6-6 10:07:20 | 显示全部楼层
    就喜欢这种原理文章
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2017-3-9 13:28
  • 签到天数: 36 天

    [LV.5]常住居民I

    发表于 2016-9-24 19:19:38 | 显示全部楼层
    感谢分享,学习了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2016-11-17 09:25
  • 签到天数: 12 天

    [LV.3]偶尔看看II

    发表于 2016-10-19 13:52:13 | 显示全部楼层
    楼主辛苦了
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    快速回复 返回顶部 返回列表