SQL注⼊命令
SQL注⼊天书
引 ⾔
随着B/S模式应⽤开发的发展,使⽤这种模式编写应⽤程序的程序员也越来越多。但是由于这个⾏业的⼊门门槛不⾼,程序员的⽔平及经验也参差不齐,相当⼤⼀部分程序员在编写代码的时候,没有对⽤户输⼊数据的合法性进⾏判断,使应⽤程序存在安全隐患。⽤户可以提交⼀段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注⼊。
SQL注⼊是从正常的WWW端⼝访问,⽽且表⾯看起来跟⼀般的Web页⾯访问没什么区别,所以⽬前市⾯的防⽕墙都不会对SQL注⼊发出警报,如果管理员没查看IIS*志的习惯,可能被⼊侵很长时间都不会发觉。
但是,SQL注⼊的⼿法相当灵活,在注⼊的时候会碰到很多意外的情况。能不能根据具体情况进⾏分析,构造巧妙的SQL语句,从⽽成功获取想要的数据,是⾼⼿与“菜鸟”的根本区别。
根据国情,国内的⽹站⽤ASP+Access或SQLServer的占70%以上,PHP+MySQ占L20%,其他的不⾜10%。在本⽂,我们从分⼊门、进阶⾄⾼级讲解⼀下ASP注⼊的⽅法及技巧,PHP注⼊的⽂章由NB联盟
的另⼀位朋友zwell撰写,希望对安全⼯作者和程序员都有⽤处。了解ASP注⼊的朋友也请不要跳过⼊门篇,因为部分⼈对注⼊的基本判断⽅法还存在误区。⼤家准备好了吗?Let'
⼊ 门 篇
如果你以前没试过SQL注⼊的话,那么第⼀步先把IE菜单=>⼯具=>Internet选项=>⾼级=>显⽰友好 HTTP 错误信息前⾯的勾去掉。否则,不论服务器返回什么错误,IE都只显⽰为HTTP 500服务器错误,不能获得更多的提⽰信息。
第⼀节、SQL注⼊原理
以下我们从⼀个⽹www.19cn开始(注:本⽂发表前已征得该站站长同意,⼤部分都是真实数据)。
在⽹站⾸页上,有名为“IE不能打开新窗⼝的多种解决⽅法”的链接,地址为: ,我们在这个地址后⾯加上单引号’,服务器会返回下⾯的错误提⽰:
Microsoft JET Database Engine 错误 '80040e14'
字符串的语法错误 在查询表达式 'ID=49'' 中。
/showdetail.asp,⾏8
从这个错误提⽰我们能看出下⾯⼏点:
1.⽹站使⽤的是Access数据库,通过JET引擎连接数据库,⽽不是通过ODBC。
2. 程序没有判断客户端提交的数据是否符合程序要求。
3. 该SQL语句所查询的表中有⼀名为ID的字段。
从上⾯的例⼦我们可以知道,SQL注⼊的原理,就是从客户端提交特殊的代码,从⽽收集程序及服务器的信息,从⽽获取你想到得到的资料。
第⼆节、判断能否进⾏SQL注⼊
看完第⼀节,有⼀些⼈会觉得:我也是经常这样测试能否注⼊的,这不是很简单吗?
其实,这并不是最好的⽅法,为什么呢?
⾸先,不⼀定每台服务器的IIS都返回具体错误提⽰给客户端,如果程序中加了cint(参数)之类语句的话,SQL注⼊是不会成功的,但服务器同样会报错,具体提⽰信息为处理 URL 时服务器上出错。请
和系统管理员联络。
其次,部分对SQL注⼊有⼀点了解的程序员,认为只要把单引号过滤掉就安全了,这种情况不为少数,如果你⽤单引号测试,是测不到注⼊点的
那么,什么样的测试⽅法才是⽐较准确呢?答案如下:
①
② ;;;and 1=1
③ ;;;and 1=2
这就是经典的1=1、1=2测试法了,怎么判断呢?看看上⾯三个⽹址返回的结果就知道了:
可以注⼊的表现:
① 正常显⽰(这是必然的,不然就是程序有错误了)
② 正常显⽰,内容基本与①相同
③ 提⽰BOF或EOF(程序没做任何判断时)、或提⽰不到记录(判断了rs.eof时)、或显⽰内容为空(程序加了on error resume next)
不可以注⼊就⽐较容易判断了,①同样正常显⽰,②和③⼀般都会有程序定义的错误提⽰,或提⽰类型转换时出错。
当然,这只是传⼊参数是数字型的时候⽤的判断⽅法,实际应⽤的时候会有字符型和搜索型参数,我将在中级篇的“SQL注⼊⼀般步骤”再做分析。
第三节、判断数据库类型及注⼊⽅法
不同的数据库的函数、注⼊⽅法都是有差异的,所以在注⼊之前,我们还要判断⼀下数据库的类型。⼀般ASP最常搭配的数据库是Access 和SQLServer,⽹上超过99%的⽹站都是其中之⼀。
怎么让程序告诉你它使⽤的什么数据库呢?来看看:
SQLServer有⼀些系统变量,如果服务器IIS提⽰没关闭,并且SQLServer返回错误提⽰的话,那可以直接从出错信息获取,⽅法如下: ;;;and user>0
这句语句很简单,但却包含了SQLServer特有注⼊⽅法的精髓,我⾃⼰也是在⼀次⽆意的测试中发现
这种效率极⾼的猜解⽅法。让我看来看看它的含义:⾸先,前⾯的语句是正常的,重点在anduser>0,我们知道,user是SQLServer的⼀个内置变量,它的值是当前连接的⽤户名,类型为nvarchar。拿⼀个nvarchar的值跟int的数0⽐较,系统会先试图将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环境下可正常读取。
在确认可以注⼊的情况下,使⽤下⾯的语句:
;;;and (select count(*) from sysobjects)>0
;;;and (select count(*) from msysobjects)>0
如果数据库是SQLServer,那么第⼀个⽹址的页⾯与原页⾯ 是⼤致相同的;⽽第⼆个⽹址,由于不到表msysobjects,会提⽰出错,就算程序有容错处理,页⾯也与原页⾯完全不同。
如果数据库⽤的是Access,那么情况就有所不同,第⼀个⽹址的页⾯与原页⾯完全不同;第⼆个⽹址,则视乎数据库设置是否允许读该系统表,⼀般来说是不允许的,所以与原⽹址也是完全不同。⼤多数情况下,⽤第⼀个⽹址就可以得知系统所⽤的数据库类型,第⼆个⽹址只作为开启IIS错误提⽰时的验证。
进 阶 篇
在⼊门篇,我们学会了SQL注⼊的判断⽅法,但真正要拿到⽹站的保密内容,是远远不够的。接下来,我们就继续学习如何从数据库中获取想要获得的内容,⾸先,我们先看看SQL注⼊的⼀般步骤:
第⼀节、SQL注⼊的⼀般步骤
⾸先,判断环境,寻注⼊点,判断数据库类型,这在⼊门篇已经讲过了。
其次,根据注⼊参数类型,在脑海中重构SQL语句的原貌,按参数类型主要分为下⾯三种:
(A) ID=49 这类注⼊的参数是数字型,SQL语句原貌⼤致如下:
Select * from 表名 where 字段=49
注⼊的参数为ID=49 And [查询条件],即是⽣成语句:
Select * from 表名 where 字段=49 And [查询条件]
(B) Class=连续剧 这类注⼊的参数是字符型,SQL语句原貌⼤致概如下:
Select * from 表名 where 字段=’连续剧’
注⼊的参数为Class=连续剧’ and [查询条件] and ‘’=’ ,即是⽣成语句:
Select * from 表名 where 字段=’连续剧’ and [查询条件] and ‘’=’’
(C) 搜索时没过滤参数的,如keyword=关键字,SQL语句原貌⼤致如下:
Select * from 表名 where 字段like ’%关键字%’
注⼊的参数为keyword=’ and [查询条件] and ‘%25’=’, 即是⽣成语句:
Select * from 表名 where字段like ’%’ and [查询条件] and ‘%’=’%’
接着,将查询条件替换成SQL语句,猜解表名,例如:
ID=49 And (Select Count(*) from Admin)>=0
如果页⾯就与ID=49的相同,说明附加条件成⽴,即表Admin存在,反之,即不存在(请牢记这种⽅法)。如此循环,直⾄猜到表名为⽌。表名猜出来后,将Count(*)替换成Count(字段名),⽤同样的原理猜解字段名。
有⼈会说:这⾥有⼀些偶然的成分,如果表名起得很复杂没规律的,那根本就没得玩下去了。说得很对,这世界根本就不存在100%成功的⿊客技术,苍蝇不叮⽆缝的蛋,⽆论多技术多⾼深的⿊客,都是因为别⼈的程序写得不严密或使⽤者保密意识不够,才有得下⼿。
有点跑题了,话说回来,对于SQLServer的库,还是有办法让程序告诉我们表名及字段名的,我们在⾼级篇中会做介绍。
最后,在表名和列名猜解成功后,再使⽤SQL语句,得出字段的值,下⾯介绍⼀种最常⽤的⽅法-Ascii逐字解码法,虽然这种⽅法速度很慢,但肯定是可⾏的⽅法。
我们举个例⼦,已知表Admin中存在username字段,⾸先,我们取第⼀条记录,测试长度:
;;;and (select top 1 len(username) from Admin)>0
先说明原理:如果top 1的username长度⼤于0,则条件成⽴;接着就是>1、>2、>3这样测试下去,⼀直到条件不成⽴为⽌,⽐如>7成⽴,>8不成⽴,就是len(username)=8
当然没⼈会笨得从0,1,2,3⼀个个测试,怎么样才⽐较快就看各⾃发挥了。在得到username的长度后,⽤mid(username,N,1)截取第N位字符,再asc(mid(username,N,1))得到ASCII码,⽐如:
id=49 and (select top 1 asc(mid(username,1,1)) from Admin)>0
同样也是⽤逐步缩⼩范围的⽅法得到第1位字符的ASCII码,注意的是英⽂和数字的ASCII码在1-128之间,可以⽤折半法加速猜解,如果写成程序测试,效率会有极⼤的提⾼。
第⼆节、SQL注⼊常⽤函数
有SQL语⾔基础的⼈,在SQL注⼊的时候成功率⽐不熟悉的⼈⾼很多。我们有必要提⾼⼀下⾃⼰的SQL⽔平,特别是⼀些常⽤的函数及命
令。
Access:asc(字符) SQLServer:unicode(字符)
作⽤:返回某字符的ASCII码
Access:chr(数字) SQLServer:nchar(数字)
作⽤:与asc相反,根据ASCII码返回字符
Access:mid(字符串,N,L) SQLServer:substring(字符串,N,L)
作⽤:返回字符串从N个字符起长度为L的⼦字符串,即N到N+L之间的字符串
Access:abc(数字) SQLServer:abc (数字)
作⽤:返回数字的绝对值(在猜解汉字的时候会⽤到)
Access:A between B And C SQLServer:A between B And C
作⽤:判断A是否界于B与C之间
第三节、中⽂处理⽅法
在注⼊中碰到中⽂字符是常有的事,有些⼈⼀碰到中⽂字符就想打退堂⿎了。其实只要对中⽂的编码有所了解,“中⽂恐惧症”很快可以克服。
先说⼀点常识:
Access中,中⽂的ASCII码可能会出现负数,取出该负数后⽤abs()取绝对值,汉字字符不变。
SQLServer中,中⽂的ASCII为正数,但由于是UNICODE的双位编码,不能⽤函数ascii()取得ASCII码,必须⽤函数unicode ()返回unicode值,再⽤nchar函数取得对应的中⽂字符。
了解了上⾯的两点后,是不是觉得中⽂猜解其实也跟英⽂差不多呢?除了使⽤的函数要注意、猜解范围⼤⼀点外,⽅法是没什么两样的。⾼ 级 篇
看完⼊门篇和进阶篇后,稍加练习,破解⼀般的⽹站是没问题了。但如果碰到表名列名猜不到,或程序作者过滤了⼀些特殊字符,怎么提⾼注⼊的成功率?怎么样提⾼猜解效率?请⼤家接着往下看⾼级篇。
第⼀节、利⽤系统表注⼊SQLServer数据库
SQLServer是⼀个功能强⼤的数据库系统,与操作系统也有紧密的联系,这给开发者带来了很⼤的⽅便,但另⼀⽅⾯,也为注⼊者提供了⼀个跳板,我们先来看看⼏个具体的例⼦:
① ;p_cmdshell “net user name password /add”--
分号;在SQLServer中表⽰隔开前后两句语句,--表⽰后⾯的语句为注释,所以,这句语句在SQLServer中将被分成两句执⾏,先是Select出ID=1的记录,然后执⾏存储过程xp_cmdshell,这个存储过程⽤于调⽤系统命令,于是,⽤net命令新建了⽤户名为name、密码为password的windows的帐号,接着:
② ;p_cmdshell “net localgroup name administrators /add”--
将新建的帐号name加⼊管理员组,不⽤两分钟,你已经拿到了系统最⾼权限!当然,这种⽅法只适⽤于⽤sa连接数据库的情况,否则,是没有权限调⽤xp_cmdshell的。
③ ;;;and db_name()>0
前⾯有个类似的例⼦and user>0,作⽤是获取连接⽤户名,db_name()是另⼀个系统变量,返回的是连接的数据库名。
④ ;backup database 数据库名 to disk=’c:\inetpub\wwwroot\1.db’;--
这是相当狠的⼀招,从③拿到的数据库名,加上某些IIS出错暴露出的绝对路径,将数据库备份到Web⽬录下⾯,再⽤HTTP把整个数据库就完完整整的下载回来,所有的管理员及⽤户密码都⼀览⽆遗!在不知道绝对路径的时候,还可以备份到⽹络地址的⽅法(如
\\\Share\1.db),但成功率不⾼。
⑤ ;;;and (Select Top 1 name from sysobjects where xtype=’U’ and status>0)>0
前⾯说过,sysobjects是SQLServer的系统表,存储着所有的表名、视图、约束及其它对象,xtype=’U’ and status>0,表⽰⽤户建⽴的表名,上⾯的语句将第⼀个表名取出,与0⽐较⼤⼩,让报错信息把表名暴露出来。第⼆、第三个表名怎么获取?还是留给我们聪明的读者思考吧。
⑥ ;;;and (Select Top 1 col_name(object_id(‘表名’),1) from sysobjects)>0
从⑤拿到表名后,⽤object_id(‘表名’)获取表名对应的内部ID,col_name(表名ID,1)代表该表的第1个字段名,将1换成2,就可以逐个获取所猜解表⾥⾯的字段名。
以上6点是我研究SQLServer注⼊半年多以来的⼼⾎结晶,可以看出,对SQLServer的了解程度,直接影响着成功率及猜解速度。在我研究SQLServer注⼊之后,我在开发⽅⾯的⽔平也得到很⼤的提⾼,呵呵,也许安全与开发本来就是相辅相成的吧。
第⼆节、绕过程序限制继续注⼊
在⼊门篇提到,有很多⼈喜欢⽤’号测试注⼊漏洞,所以也有很多⼈⽤过滤’号的⽅法来“防⽌”注⼊漏洞,
这也许能挡住⼀些⼊门者的攻击,但对SQL注⼊⽐较熟悉的⼈,还是可以利⽤相关的函数,达到绕过程序限制的⽬的。
在“SQL注⼊的⼀般步骤”⼀节中,我所⽤的语句,都是经过我优化,让其不包含有单引号的;在“利⽤系统表注⼊SQLServer数据库”中,有些语句包含有’号,我们举个例⼦来看看怎么改造这些语句:
简单的如where xtype=’U’,字符U对应的ASCII码是85,所以可以⽤where xtype=char(85)代替;如果字符是中⽂的,⽐如where name=’⽤户’,可以⽤where name=nchar(29992)+nchar(25143)代替。
第三节、经验⼩结
1.有些⼈会过滤Select、Update、Delete这些关键字,但偏偏忘记区分⼤⼩写,所以⼤家可以⽤selecT这样尝试⼀下。
2.在猜不到字段名时,不妨看看⽹站上的登录表单,⼀般为了⽅便起见,字段名都与表单的输⼊框取相同的名字。
3.特别注意:地址栏的+号传⼊程序后解释为空格,%2B解释为+号,%25解释为%号,具体可以参考URLEncode的相关介绍。
4.⽤Get⽅法注⼊时,IIS会记录你所有的提交字符串,对Post⽅法做则不记录,所以能⽤Post的⽹址尽量不⽤Get。
5. 猜解Access时只能⽤Ascii逐字解码法,SQLServer也可以⽤这种⽅法,只需要两者之间的区别即可,但是如果能⽤SQLServer的报错信息把值暴露出来,那效率和准确率会有极⼤的提⾼。
防 范 ⽅ 法
SQL注⼊漏洞可谓是“千⾥之堤,溃于蚁⽳”,这种漏洞在⽹上极为普遍,通常是由于程序员对注⼊不了解,或者程序过滤不严格,或者某个参数忘记检查导致。在这⾥,我给⼤家⼀个函数,代替ASP中的Request函数,可以对⼀切的SQL注⼊Say NO,函数如下:
function SafeRequest(ParaName,ParaType)
'--- 传⼊参数 ---
'ParaName:参数名称-字符型
sql语句替换表中内容'ParaType:参数类型-数字型(1表⽰以上参数是数字,0表⽰以上参数为字符)
Dim ParaValue
ParaValue=Request(ParaName)
If ParaType=1 then
If not isNumeric(ParaValue) then
Response.write "参数" & ParaName & "必须为数字型!"
End if
Else
ParaValue=replace(ParaValue,"'","''")
End if
SafeRequest=ParaValue
End function
转载于:wwwblogs/xiaoyuyu/archive/2012/11/10/2764539.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论