以后少写渗透实战类的帖子了,多点安全分析、知识介绍类的。
首先,要真正了解并应用正则表达式,需要你具备一点脚本编程、脚本分析的能力。如果你没有一点脚本基础并且也志不在此的话,那么这篇文章就可以略过了。本文仅同对脚本编程、脚本渗透有兴趣的人分享。本文内容包括基础介绍和实例分析两部分:前面的基础介绍有参考网上的相关资料,有自己的分析总结,后面将会结合实际运用举出三个实例,相信可以让不熟悉正则表达式的人能基本了解这个东东。
在开始正文之前建议大家边看边准备一个正则测试工具,也可以使用在线正则测试,网上搜一下很多,这玩意儿和编程一样需要多动手,不同的是它简单多了,正常人都能懂。
正则表达式的重要性,不懂的人可能觉得就是个匹配规则、无关紧要。但就我本人来看非常重要,它的作用是非常大的,实际编程中,正则表达式的使用频率非常高,正则是一个程序员必备的技能。在渗透测试领域,waf的过滤、exp的编写、CMS的过滤等,一般都是使用正则来匹配过滤的。
用一句比较抽象的话来说:正则表达式就是一种描述匹配规则的工具。很多人应该都用过windows操作系统的搜索功能,对windows搜索里的通配符(*)、(.)应该不陌生,比如(*)匹配任意数量字符,比如你要搜索磁盘中全部的php文件,那么搜索(*.php)就OK了,这里的通配符本质上和正则表达式是一样的,也是描述一种匹配规则,只不过后者要更加丰富、更加复杂,当然匹配精准度也更高。
首先来一个简单的例子,让大家对正则表达式有个最基本的认识。
待匹配内容:I come from ihonker!
匹配目标:ihonker
这里要匹配出ihonker这个单词,那么最简单的就是直接把ihonker这个单词作为正则表达式即可了,看实际测试:
上面是正则表达式最简单的应用,聪明的你还能发散一下:如果待匹配目标是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。它是怎么得出来的?请搬个小凳子慢慢看)
当然,上面这段字符也还是非常简单的,我们再来一个更加复杂点的例子。待匹配文本是下面这段代码,总共创建了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);
}
?>
元字符
学习正则表达式首先要理解元字符,元字符是正则表达式的一个很重要的内容。我本人对它的理解:元字符是用来匹配某一特定类型的字串,它的作用是缩小正则表达式的体积,减轻我们的工作量。在没有元字符的情况下,如果你要匹配一个文本中所有的汉字,那么你可能需要准备一张汉字码表,然后把所有的汉字都放到正则表达式中去,我想这样的规则一定会让所有人都叹为观止! 常用的元字符都在下面这张表列出来了,还有更多的就不一一列出,有人可能一看到这么多的符号就头晕了。我要告诉你的是我和你一样,我刚开始的时候也是很头疼记不住,但多次使用练习后,就慢慢记住了。所以,别担心,慢慢来。
下面挑几个典型的元字符做示例分析 元字符:\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这两种匹配结果。 如果把代码改为如下:function test($str,$_GET[“id”],utest($act),$test=2)。那么前面的规则就不太灵光了,它也会把第三个test匹配出来,这时候就需要另外的匹配规则了,可以自己思考一下。 顺便说一下,看了\b的作用,再结合上面的那张表,我想你可以试着理解前面的第2个例子了。 元字符:\w \w匹配任意数字、大小写字母、下划线(\w匹配汉字是.net环境下的正则表达式引擎提供的,本文不涉及) 字符串:我a是C#7_おは0مرحبا。这段字符包括了汉字、大小写字母、数字、下划线、日语、希伯来语和#(特殊符号),我们看看它的匹配结果。这个比较容易理解就不多解释了。可以和\b对比一下,你会发现\w有自己的匹配内容,但\b并没有。 元字符之数量匹配符:*、+、?、{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+)> ()在实际编程中经常用到,用上面的例子来运行一下: 从运行结果还可以看出来,()内的匹配规则是重复匹配的,直到把所有符合规则的内容匹配出来,并返回一个数组。并列的规则,通常也用()括起来。 元字符:/i、/g、/e、/m /i:忽略大小写 /g: /e: /m: 贪婪模式与非贪婪模式 数量匹配符后面跟?的匹配模式就是非贪婪模式,不跟?的模式是贪婪模式。贪婪模式在同等情况下匹配尽可能多的结果,而非贪婪模式则匹配尽可能少的结果。 待匹配内容:asdabaecdbadefdlsdbsdlfbdalfdbdfadfdb 匹配规则:分别用a\w*b和a\w*?b两个正则匹配 结果1:asdabaecdbadefdlsdbsdlfbdalfdbdfadfdb 结果2: asdab aecdb adefdlsdb alfdb adfdb 在爬虫程序中,(.*?)这个正则出现的概率非常高,就是运用了非贪婪模式。 好了,正则表达式的常见的基础应用差不多就是这些了,我写的比较简单,真正要记住还是得以点带面。 到了这里,我想你也可以试着去读懂前面的第三个例子了。可能还是有点不好理解,不过没关系,看了下面的实例分析相信你就可以了。
实例分析 实例运用一:利用正则表达式编写自己的exp之dedecms路径泄漏漏洞 在开始之前,还是要啰嗦一下:正则表达式是一个工具,它是用来辅助我们其他工作的。比如在本例的exp编写,需要的基础是python脚本编程和dedecms路径泄漏漏洞原理研究,然后再辅之以正则匹配,那么一个exp就可以成型了。 首先简单介绍一下dedecms的路径泄漏:在url地址后面加上member/inc/config_pay_alipay.php可以爆出网站物理路径(当然如果网站配置得当就爆不出来了),那么我们exp的目的有二个:一是检测网站是否存在路径泄漏;二是把物理路径匹配出来。 看看下面的这个例子。 它的源码如下: [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)
看看执行效果: 如果能做到这一步的话,那么你就可以尝试向bugscan等在线漏洞扫描平台提交插件了,当然插件代码要根据平台的统一第三方库和其他规范进行调整,不过那就不是难题了。
实例运用二:打造自己的代码审计规则库(基于seay代码审计软件)
很多刚接触代码审计的人可能都用过seay代码审计软件,这款软件的一个最基础也是最有用的功能是它的自动审计。自动审计的原理是用正则表达式把web程序的最容易产生漏洞的地方匹配出来,比如上传函数move_uploaded_file()就有可能产生上传漏洞,那么就用正则表达式匹配出该函数,算是进行了初次审计,真正确定漏洞还需要后期的人工验证。 该软件的匹配规则位于config/rule.bin文件 可以看到软件自带了一些常见的简单漏洞,如果你对代码审计有研究,那么可以自己在文件下面添加新的匹配规则,我们这里来添加php的反序列化函数unserilize()来试一下。 Php的反序列化漏洞原理这里就不多解释了,该函数接受1个参数,如果这个参数是变量的话,那么就有可能产生反序列化漏洞(当然还得结合其他条件,不过那就不是正则表达式的任务了)。这里就用正则表达式匹配出带变量的unserilize函数,然后再辅助人工验证。 首先分析一下,我们需要匹配出两个东西:一是unserilize函数,二是它的变量参数。Unserilize函数很好匹配,直接匹配这个单词就可以了。变量参数则需要分成两种情况:一是单一的变量,比如unserilize($str)这类的;二是unserilize(test($str))这类的,也就是函数的执行结果作为变量参数,当然里面的函数也要接受至少一个变量参数。 下面再进一步分析unserilize()函数可能的表现形式:按照Php的语法规范,一个函数的标准写法就是unserilize($str)这样的形式(函数名后紧跟括弧,括弧里面只包括一个变量),但同时还有其他很多形式也可以正常执行,下面把这些形式总结一下。 最后二种形式虽然也是合规的,但实已达到令人惨绝人寰的无语地步,能写出这种函数的人一定是从火星来的。我们是地球人,还是以地球人的思维习惯来解决问题,只考虑前面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)\\( 我们来试试,果然过滤了。 但如果我们绕一下呢?比如用select concat (也就是在cancat和左括弧之间加上空格呢(同样可以被mysql解析)?看看执行结果,这就绕过了!!!!就是这么简单!!!@#¥%…… 再看第七个正则:union\\b.*(?:select|all)\\b。这个就是匹配Union select、union all这两个语句,其中每个单词后面都加了边界界定符。这个正则在单词的前后加参数绕过均不能成功,为什么?自己分析一下就知道了,这里就不再细说。但是有一种方法可以绕过,就是往单词中间加参数!对sql注入比较了解的应该都知道了,这里不方便细说,因为这个方法已经可以把这几个正则全部绕过了,有兴趣自己研究吧。 第六个正则有点莫名其妙:(?:declare|set|select)\\b.*@。原意是要过滤select|declare|set这三个关键词,但因为后面加了@的缘故,导致这个正则没有什么用。随便加上任何一个关键词都可以通过。 其他的几个正则显然也存在漏洞,但是这里就不多说了,要写完的话篇幅太大,写了这么多也挺累的,并且这个漏洞目前还没有公开,写的太多容易造成一些不必要的破坏。有兴趣自己去研究绕过吧。实际中要利用这些漏洞,还要结合具体的输入参数处理流程,辅之以人工审计才可,但确定是存在漏洞的,不止一处。 最后我再告诉你,这个正则表达式出自于国内一个使用非常广泛、非常知名的CMS。今天是2016年6月4日,就是现在,它仍然用在其最新版本的程序中。你会不会觉得学习正则表达式非常有必要了? 写这篇文章就是两个目的:一是作为正则表达式的教程供大家参考;二是说明正则表达式非常重要,掌握了它可以干很多事情。
|