几乎每一个Web应用程序都使用数据库来保存操作所需的各种信息。例如,网上零售商所用的Web应用程序使用数据库保存以下信息:
用户账户、证书和个人信息;
所销售商品的介绍与价格;
订单、账单和支付细节;
每名应用程序用户的权限。
数据库中的信息通过SQL(Structured Query Language,结构化查询语言)访问。SQL可用于读取、更新、增加或删除数据库中保存的信息。
SQL是一种解释型语言,Web应用程序经常建立合并用户提交的数据的SQL语句。因此,如果建立语句的方法不安全,那么应用程序可能易于受到SQL注入攻击。这种缺陷是困扰Web应用程序的最臭名昭著的漏洞之一。在最严重的情形中,匿名攻击者可利用SQL注入读取并修改数据库中保存的所有数据,甚至完全控制运行数据库的服务器。
随着Web应用程序安全意识的日渐增强,SQL注入漏洞越来越少,同时也变得更加难以检测与利用。许多主流应用程序采用API来避免SQL注入,如果使用得当,这些API能够有效阻止SQL注入攻击。在这些情况下,通常只有在无法应用这些防御机制时,SQL注入才会发生。有时,查找SQL注入漏洞是一项艰难的任务,需要测试员坚持不懈地在应用程序中探查一两个无法应用常规控制的实例。
随着这种趋势的变化,查找并利用SQL注入漏洞的方法也在不断改进,通常使用更加微妙的漏洞指标以及更加完善与强大的利用技巧。我们首先分析最基本的情况,然后进一步描述最新的盲目检测与利用技巧。
有大量广泛的数据库可为Web应用程序提供支持。虽然对绝大多数数据库而言,SQL注入的基本原理大体相似,但它们之间也存在着许多差异,包括语法上的细微变化以及可能影响攻击者所使用的攻击类型的巨大行为与功能差异。受篇幅和个人经验所限,在下面的示例中,我们仅讨论3种最常用的数据库,即Oracle、MS-SQL和MySQL。在适当的情况下,我们将主要讨论这3种平台之间的区别。掌握这些技术后,就可以通过其他一些研究,迅速确定并利用任何其他数据库中的SQL注入漏洞。
提示
许多时候,访问和目标应用程序所使用数据库相同的、在本地安装的数据库会有极大帮助。通常,只需修改一个语法或者参考一个内置表或功能就可实现自己的目的。从目标应用程序收到的响应一般并不完整或者含义模糊,需要猜测才能理解。如果能交叉参考相同数据库的一个完全“透明”运行的版本,理解起来就容易得多。
如果这种方法不可行,最好找一个可以进行测试的适当交互式在线环境,如SQLzoo.net中的交互式在线教程。
下面以一个书籍零售商使用的Web应用程序为例,该应用程序允许用户根据作者、书名、出版商等信息搜索产品。完整的书籍目录保存在数据库中,应用程序使用SQL查询、根据用户提交的搜索项获取各种书籍的信息。
当一名用户搜索由Wiley出版的所有书籍时,应用程序执行以下查询:
该查询要求数据库检查书籍表的第一行,提取每条publisher列为Wiley值的记录,并返回所有这些记录。然后应用程序处理这组记录,并通过一个HTML页面将结果显示给用户。
在这个查询中,等号左边的词由SQL关键字、表和数据库列名称构成。这个部分的全部内容由程序员在创建应用程序时建立。当然,表达式Wiley由用户提交,它是一个数据项。SQL查询中的字符串数据必须包含在单引号内,与查询的其他内容分隔开来。
现在思考一下,如果用户搜索所有由O'Reilly出版的书籍,会出现什么情况。应用程序将执行以下查询:
在这个示例中,查询解释器以和前面一个示例相同的方式到达字符串数据位置。它解析这个包含在单引号中的数据,得到值O。然后遇到表达式Reilly',这并不是有效的SQL语法,因此应用程序生成一条错误消息:
如果应用程序以这种方式运行,那么它非常容易遭到SQL注入。攻击者可提交包含引号的输入终止他控制的字符串,然后编写任意的SQL修改开发者想要应用程序执行的查询。例如,在这个示例中,攻击者可以对查询进行修改,通过输入以下搜索项,返回零售商目录中的每一本书籍。
应用程序将执行以下查询:
这个查询对开发者查询中的WHERE子句进行修改,增加了另外一个条件。数据库将检查书籍表的每一行,提取publisher列值为Wiley或其中1等于1的每条记录。因为1总是等于1,所以数据库将返回书籍表中的所有记录。
攻击者的输入中的双连字符在SQL中是一个有意义的表达式,它告诉查询解释器该行的其他部分属于注释,应被忽略。在一些SQL注入攻击中,这种技巧极其重要,因为它允许忽略由应用程序开发者建立的查询的剩余部分。在上面的示例中,应用程序将用户提交的字符串包含在单引号中。因为攻击者已经终止他控制的字符串并注入其他一些SQL,他需要处理字符串末尾部分的引号,避免出现和O'Reilly示例中相同的语法错误。攻击者通过添加一个双连字符达到这一目的,将查询的剩余部分以注释处理。在MySQL中,需要在双连字符后加入一个空格,或者使用“#”符号指定注释。
原始查询还将访问仅限于已出版的书籍,因为它指定and published=1。通过注入注释序列,攻击者获得未授权访问权限,可以返回所有书籍(包括已出版及其他书籍)的详细信息。
提示
有些时候,可以不使用注释符号处理字符串末尾部分的引号,而用一个需要引号包含的字符串数据结束注入的输入,以此“平衡引号”。例如,输入以下搜索项:
将生成以下查询:
这个查询完全有效,可得到和1=1攻击相同的结果。
很明显,前面的示例不会造成严重的安全威胁,因为用户使用完全合法的方法就可以访问全部书籍信息。但是,稍后我们将描述如何利用这种SQL注入漏洞从各种数据库表中提取任何数据,并提升在数据库和数据库服务器中的权限。为此,不管出现在哪个应用程序功能中,任何SQL注入漏洞都应被视为极其严重的威胁。
SQL语言中包含许多可能出现在语句开头的动词。由于SELECT是最常用的动词,因此绝大多数的SQL注入漏洞出现在这种语句中。的确,当讨论SQL注入时,因为所举的示例全部属于这种类型,所以我们常常会产生这样的印象,即SQL注入漏洞只出现在SELECT语句中。然而,任何类型的语句都可能存在SQL缺陷,必须了解一些与其有关的重要问题。
当然,当与一个远程应用程序交互时,通常情况下不可能提前知道用户输入的一个特殊数据项将由哪种类型的语句处理。但是,可以根据使用的应用程序功能进行合理的猜测。下面说明最常用的SQL语句及其用法。
1.SELECT语句
SELECT语句被用于从数据库中获取信息。它们常用于应用程序响应用户操作而返回信息的功能中,如浏览一个产品目录、查看一名用户的资料或者进行一项搜索。根据数据库中的数据核对用户提交的信息的登录功能也经常使用这种语句。
如在前面的示例中说明的,SQL注入攻击的进入点(entry point)通常是查询中的WHERE子句,它将用户提交的数据传送给数据库,以控制查询结果的范围。因为WHERE子句一般在SELECT语句的最后,攻击者就可以使用注释符号将查询截短到其输入的结束位置,而不会使整个查询的语法失效。
SQL注入漏洞偶尔也会影响SELECT查询的其他部分,如ORDER BY子句或表和列名称。