网络安全 频道

用Snort巧妙检测SQL注入和跨站脚本攻击

脚本攻击是最近网络上最疯狂的攻击方法了,很多服务器配置了先进的硬件防火墙、多层次的安全体系,可惜最后对80端口的SQL注入和跨站脚本攻击还是没有办法抵御,只能看着数据被恶意入侵者改的面目全非而毫无办法——把你的Snort武装起来吧,用它来检测这样的攻击!
我们将以使用开放源代码的入侵检测系统IDS为例,编写基于规则的正则表达式来对这类攻击进行监测。这里顺便提一下,在Snort中默认的规则设置包含了检测跨站脚本的关键字,是不是会产生冲突呢?不用担心,这种冲突我们还是有办法解决的,我们可以将它们用ASCII码的十六进制形式表示出来。例如在Snort和脚本中都存在的关键字<script>,我们就可以用%3C%73%63%72%69%70%74%3E来代替。 
如果你想对每一种可能的SQL注入进行检测,那么你只要检查任何SQL元字符的出现,如像单引号(’)、分号(;)和双减号(--)。同样的,一个监测跨站脚本攻击的很偏执的方法,就是查看HTML标记的尖括号(<、>)。但是,前述的方法可能会引起大量的误检。为了避免误检,我们就必须将过滤规则设置的尽可能的准确,即使这样,我们仍然不能保证不会产生误检,只能尽力而为了。
本文中,我们将就使用Snort对Perl中的字符串进行检测为例,做一详细介绍。不熟悉Perl的读者,可要回头将Perl中的正则表达式部分仔细阅读一下了。 

1.针对SQL注入的正则表达式
在为SQL注入选择正则表达式的时候,需要记住的是:攻击者除了可以从Cookie的域中进行攻击之外,还可以通过表单(Form)的Input输入框。所以,你在对输入进行逻辑验证的时候,还应该对来自用户的每种输入信息都要考虑到,像表单域和Cookie信息都应该作为怀疑的对象。同样的,如果你发现许多针对单引号和分号的报警的时候,也可能是因为是由Web应用的Cookie中的信息引起的。因此,必须对特定的Web应用来评估这样的特殊符号。
如前所述,一个用来检测SQL注入攻击的最弱的正则表达式,就是通过检查上面提到的元字符(’;--)。要检测这样的元字符,或者它们的16进制的等价表示,我们可以使用如下的正则表达式:
/(\%27)|(\’)|(\-\-)|(\%23)|(#)/ix
这里,%27是单引号的16进制值,%23是’#’的16进制值。像单引号和短横杠都是MS SQL Server和Oracle中的特殊字符,如果我们使用的是MySQL,还必须检测’#’的出现。这里需要说明的就是“—”,不需要检查它的16进制值,因为它不是HTML元字符,不会被浏览器编码。同样的,如果攻击者试图手动修改“—”为它的16进制值%2D的话,SQL注入操作会失败。为了照顾一下不熟悉Perl的同志,接下来我将上面的这个正则表达式简要解释一下://是Perl中用来引起要进行模式匹配的代码,其完整形式为m//,通常,我们在使用双斜杠引起模式匹配代码的时候,可以将m省略不写。符号“|”是或的作用,如同在其他语言中or的通常意义。这里的小括号是为了更容易让读者看明白的情况下而写出来的,完全可以不用。第二个/后面的i表示对要匹配的字母不区分大小写。第二个/后面的x表示忽略模式中的空白。
我们可以将上面的这个表达式添加到Snort的规则里面去:alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"SQL Injection - Paranoid"; flow:to_server,established;uricontent:".pl";pcre:"/(\%27)|(\’)|(\-\-)|(%23)|(#)/i"; classtype:Web-application-attack; sid:9099; rev:5;) 在这个例子里面,uriconten关键字部分,我们使用了“.pl”,因为在我们的测试环境中,我们的CGI脚本是使用Perl写成的。这部分内容取决于你的特定应用,可能是“.php”、 “.asp”、“.jsp”等。Pcre关键字的内容,就是将要对.pl文件进行检查的模式。你可以从这个正则表达式受到启发,以便写出更多的Snort规则。
SQL查询中还有可能在Where语句中包含纯数字,像这样:
select value1, value2, num_value3 from database where num_value3=some_user_supplied_number 
在这个例子中,攻击者可能执行一条附加的SQL查询语句,像这样就可以:
3; insert values into some_other_table 
上面的那条Snort规则可以扩展一下,用来过滤分号(;)的出现。然而,分号也可能出现在正常的HTTP交互中。为了减少误检,上面的规则可以修改为从等号(=)之后的部分进行匹配。当用户使用GET或者POST作请求时,输入域一般会像这样出现:
username=some_user_supplied_value&password=some_user_supplied_value 
因此,对用户的SQL注入尝试的检测可以从等号(=)或者它的16进制值开始检测,修改后的SQL元字符检测的正则表达式如下:
/((\%3D)|(=))[^\n]*((\%27)|(\’)|(\-\-)|(\%3B)|(;))/ix
这个规则首先检查等号(=)或者它的16进制%3D。接下来,[^\n]*的含义就是匹配零个或者多个非换行符字符,其中,\n是Perl中的换行符,^是排除符号,也就是说非换行符,*的含义就是对它前面的字符进行零次或者多次匹配。接下来的部分,跟前面没有什么不同了。
一个典型的SQL注入尝试过程,通常会围绕着单引号来制造一下查询,来看一下返回的结果正确与否,从而确定是否存在SQL注入漏洞。大多数的例子都是使用了字符串1’or’1’=’1。然而,要对这类字符串进行检测可不是件容易的事情,因为攻击者可以轻易的构造出一些具有等价作用的字符串,如1’or2>1--。这样,唯一不变的部分也就剩下了一个常数和接下来的字符串’or’了。对于这类攻击,我们可以构造如下的正则表达式,来进行检测,典型的SQL注入攻击的正则表达式: 
/\w*((\%27)|(\’))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix
\w(注意,这里的w是小写的,Perl中的用于模式匹配的\w和\W意义截然不同)指的是大小写字母或者数字,加了*之后的意义就是用来匹配一个或者多个数字或者字母。(\%27)|\’用来匹配单引号或者它的16进制值。(\%6F)|o|(\%4F))((\%72)|r|(\%52),%6F和%4F分别为字母o的小大写16进制值,%72和%52分别为字母r的小大写16进制值。因为使用的i选项,所以没有必要将字母o和r的大小写形式都写出来。这样,单词or的各种形式,我们都已经考虑到了。 
同样的,在SQL注入中,我们也经常会用到union关键字,我们可以如下设置规则,针对UNION关键字的SQL注入的检测的正则表达式:
/((\%27)|(\’))union/ix
有了上面的基础,这句规则就不需要多作解释了。对于同样会经常碰到的select,insert,update,delete,drop等语句,我们可以举一反三,灵活运用规则进行设置了。 在SQL注入中,我们为了使得SQL服务器返回错误,然后通过错误来获得有用的信息,经常会出现诸如1=(select name from student where sno=’200021’)。这个子查询语句返回的值是字符类型,而1是数字,这时候就会有错误爆出,从而泄漏了name字段的内容。这样就引出了,我们必须能够对这种常见的注入进行监测。对于这类攻击,我们不能够从1开始做限制,因为这样可能导致大量的误检,显然,解决方法就是对select关键字进行监测,因为URL中提交的信息,正常情况下是不会包含select关键字的。
2.5对1=(select name from student where sno=’200021’)类似的查询进行监测的正则表达式:
/((\%27)|(\’))select/ix
在MS SQL Server中,经常被利用的还有扩展存储过程xp_cmdshell、xp_regread 、xp_regwrite等。这类存储过程在调用的时候,都要使用到exec关键字。另外,系统的存储过程通常都是以sp或者xp开头的,所以,我们针对这类SQL注入,可以如下配置正则表达式。针对MS SQL Server的SQL注入进行检测的正则表达式:
/exec(\s|\+)+(s|x)p\w+/ix
exec为调用存储过程的关键字。(\s|\+)代表空格或者它的HTTP编码的等价替换。+用于使前面的和后面的字符至少匹配一次,这样,(\s|\+)+(s|x)的意思就是使空格和s与x中的一个进行至少一次的匹配。\w+在前面已经解释过了,这里不多说了。
上面,我们仅仅是对SQL注入的一些最基本的方法进行监测,但是,熟悉SQL注入的人都知道,SQL注入中用的方法还有很多,我们在这里不可能一一写到,所以请读者体谅。

2.针对跨站脚本攻击的正则表达式 
在发动一次跨站脚本攻击之前,为了测试网站的漏洞,攻击者通常要做一个简单的HTML测试,这可能要涉及到HTML中的tag像<b>、<i>、<u>等。同样的,也可以使用一个简单的脚本如<script>alert("OK")</script>。这可能是因为大多数的关于CSS的讨论文档,都是使用了这样一个简单的脚本例子来判断一个站点是否可以进行CSS攻击。这类尝试,我们可以通过Snort进行检测。一些高级一些的入侵者可能会使用变换的方式进行测试,如将起对应的16进制值进行等价替换,如<script>可以使用%3C%73%63%72%69%70%74%3E进行替换。
下面的这个正则表达式就可以针对这类攻击进行检测。它将捕获使用<b>、<u>或者<script>的尝试。这个正则表达式也是不区分大小写的。我们必须对尖括号的符号和它的16进制值都进行匹配,左尖括号<的16进制值为%3C,右尖括号>的16进制值为%3E。简单CSS攻击的正则表达式:
/((\%3C)|<)((\%2F)|\/)*[a-z0-9\%]+((\%3E)|>)/ix
((\%3C)|<)是对左尖括号进行检测。((\%2F)|\/)*对表示tag标志结束的斜杠或者16进制值进行匹配。[a-z0-9\%]+对一个以上的小写字母或者阿拉伯数字进行匹配。((\%3E)|>)对右尖括号或者它的16进制值进行匹配。再将这个写成Snort规则就成了:alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"NII Cross-site scripting attempt"; flow:to_server,established; pcre:"/((\%3C)|<)((\%2F)|\/)*[a-z0-9\%]+((\%3E)|>)/i";classtype:Web-application-attack; sid:9000; rev:5;) 跨站脚本也可以通过使用<img src=>技术来实现,对于这一类攻击,我们可以这样配置,来使得CSS攻击不容易实现。针对<img src>的CSS攻击的正则表达式:
/((\%3C)|<)((\%69)|i|(\%49))((\%6D)|m|(\%4D))((\%67)|g|(\%47))[^\n]+((\%3E)|>)/i
左右尖括号不再解释。(\%69)|i|(\%49))((\%6D)|m|(\%4D))((\%67)|g|(\%47)是针对img的匹配。[^\n]+在前面也已经说过了。还有一种最偏执的针对CSS攻击的正则表达式:
/((\%3C)|<)[^\n]+((\%3E)|>)/i
这个正则表达式采用了最“恶毒”的办法,就是将所有出现<>的地方全都认为是CSS攻击,够毒吧,呵呵!但是,这种方法最容易引起误检,但是它却实实在在的能够有效防止CSS攻击。
从某种程度上来说,跨站脚本攻击似乎更好控制一些,其实不然,经常关注黑防的读者自有深刻的体会,我就不多说了。

3.自己动手编写规则
当编写Snort规则时,首先考虑的是效率和速度,好的规则要包含content选项。2.0版本以后,Snort改变了检测引擎的工作方式,在第一阶段就作一个集合模式匹配。一个content选项越长,这个匹配就越精确。如果一条规则不包含content选项,它们将使整个系统慢下来。
当编写规则时,尽量要把目标定位在攻击的地方(例如,将目标定位在1025的偏移量等等)而不仅仅是泛泛的指定(如,在这匹配脚本代码)。Content规则是大小写敏感的(除非你使用了nocase选项)。不要忘记content是大小写敏感的和大多数程序的命令都是大写字母。FTP就是一个很好的例子。考虑如下的规则:
alert tcp any any -> 192.168.1.0/24 21 (content: "user root"; msg: "FTP root login";)
alert tcp any any -> 192.168.1.0/24 21 (content: "USER root"; msg: "FTP root login";)
上面的第二条规则能检测出大多数的自动以root登陆的尝试,而第一条规则就不行。Internet 守护进程在接受输入时是很随便的。在编写规则时,很好的理解协议规范将降低错过攻击的机会。

4.结论
本文中,我们给出了各种各样的针对SQL注入和跨站脚本攻击的正则表达式。虽然有一些是很偏执和粗暴的,因为即使有一点发起攻击的企图,这样的正则表达式都会引起Snort的报警。这样的一些偏执的正则表达式最容易引起误检,为了尽量减少这样的误检,我们要尽量的精确设置正则表达式。同时,我们前面介绍的方法也只是针对现在比较常见的攻击做出的检测,黑客的对抗技术中,总是此起彼长,“道高一尺,魔高一丈”,当我们发现了新的入侵的方法时,要对某些规则做出适当的调整甚至添加新的规则,才能够适应新的安全要求。这点相信各位早都深有体会了,本文结合我的一点理解写出,希望和大家共同学习.

http://www.hack58.net/Article/60/61/2006/10858.htm

0
相关文章