SQL注入
Last updated
Last updated
在本节中,我们将讲解什么是SQL注入(SQLi),描述一些常见示例,解释如何发现和利用各种SQL注入漏洞,并总结如何防范SQL注入。
LAB
如果你已经熟悉SQLi漏洞背后的基本概念,并且只想在一些真实的、故意易受攻击的目标上练习利用它们,你可以从下面的链接中访问该主题中的所有实验。
SQL注入(SQLi)是一种Web安全漏洞,允许攻击者干扰应用程序对其数据库进行的查询。它通常允许攻击者查看他们一般无法检索到的数据。可能包括属于其他用户的数据,或应用程序本身能够访问的任何其他数据。在许多情况下,攻击者可以修改或删除这些数据,从而导致对应用程序的内容或行为产生持久性的更改。
在某些情况下,攻击者可以升级SQL注入攻击以破坏基础服务器或其他后端基础设施,或者执行拒绝服务攻击。
一次成功的SQL注入攻击可能导致对敏感数据的未授权访问,例如密码、信用卡详细信息或个人用户信息。近年来,许多知名数据泄露事件都是SQL注入攻击的结果,导致了声誉受损和监管罚款。在某些情况下,攻击者可以获得对组织系统的持久性后门访问,从而导致长期的威胁,可能在很长一段时间内不被察觉。
在不同情况下会出现各种各样的SQL注入漏洞、攻击和技术。一些常见的SQL注入示例包括:
检索隐藏数据,可以修改SQL查询以返回额外的结果。
破坏应用程序逻辑,可以更改查询以干扰应用程序的逻辑。
UNION攻击,可以从不同的数据库表中检索数据。
检查数据库,可以提取有关数据库版本和结构的信息。
SQL盲注,即在应用程序的响应中不会返回你控制的查询结果。
考虑一个显示不同类别产品的购物应用程序。当用户点击Gifts类别时,他们的浏览器会请求以下URL:
这将导致应用程序对数据库进行SQL查询,以检索相关产品的详细信息:
该SQL查询要求数据库返回:
所有字段数据(*)
来自products表
产品类别是Gifts
并且已发布的产品(released = 1)。
released = 1
的限制是为了将未发布的产品隐藏起来。对于未发布的产品,想必是released = 0
。
由于应用程序没有实施任何SQL注入攻击的防御措施,因此攻击者可以构造以下攻击:
这将导致如下SQL查询:
这里的关键是,双破折号--
在SQL中是一个注释符,意味着剩余的查询条件被注释。这实际上删除了查询的其余部分,因此不再包含AND released = 1
这个条件。也就意味着所有的产品都会被显示,包括未发布的产品。
更进一步,攻击者可以造成应用程序显示任意类别的所有产品,包括他们不知道的类别:
这导致如下SQL查询:
修改后的查询将返回类别为Gifts或1等于1的所有商品。由于1 = 1
始终为真,因此查询将返回所有商品。
警告
在向SQL查询中注入条件
OR 1=1
时要小心。尽管在你注入的初始上下文中可能是无害的,但应用程序常常在多个不同的查询中使用来自单个请求的数据。如果你的条件达到了UPDATE
或DELETE
语句,可能会导致数据意外丢失。
LAB
考虑一个允许用户使用用户名和密码登录的应用程序。如果用户提交了wiener
的用户名和bluecheese
的密码,应用程序会执行以下的SQL查询来检查凭据:
如果查询返回了用户的详细信息,就会成功登录。否则就登录失败。
在这里,攻击者可以通过使用SQL注释序列--
从查询的WHERE
子句中删除密码检查,从而不用密码登录任意用户。例如,提交用户名administrator'--
和空密码将执行如下查询:
该查询返回用户名为administrator
的用户,并成功地将攻击者作为该用户登录。
LAB
在应用程序的响应中返回了SQL查询结果的情况下,攻击者可以利用SQL注入漏洞从数据库的其他表中检索数据。这是通过UNION
关键字实现的,它允许你执行一个额外的SELECT
查询,并将结果附加到原始查询中。
例如,如果一个应用程序执行如下包含用户输入“Gifts”的查询:
然后攻击者提交如下输入内容:
这将导致应用程序返回所有的用户名和密码以及产品的名称和描述。
阅读更多
在初步识别出SQL注入漏洞后,获取有关数据库本身的一些信息通常特别有用。这些信息通常可以为进一步的利用铺平道路。
你可以查询数据库版本的详细信息。完成此操作的方式取决于数据库的类型,因此可以通过任一技术手段推断出数据库类型。例如,在Oracle中你可以执行:
还可以确定哪些数据库表存在以及包含哪些列。例如,在大多数数据库上,你可以执行以下查询列出表:
阅读更多
很多SQL注入实例都是盲注漏洞。意味着应用程序不会在其响应中返回SQL查询的结果或任何数据库错误的细节。盲注漏洞仍然可以被利用来访问未经授权的数据,但所涉及的技术通常更复杂且难以执行。
根据漏洞的性质和所涉及的数据库,可以使用以下技术来利用SQL盲注漏洞:
你可以改变查询的逻辑,根据单个条件的真假来触发应用程序响应中可检测到的差异。这可能涉及到在一些布尔逻辑中注入一个新的条件,或者有条件地触发一个诸如除以零之类的错误。
你可以有条件地触发查询处理中的时间延迟,然后从应用程序响应时间上来判断条件是否生效;
你可以使用OAST技术触发带外网络交互。该技术非常强大,可以在其他技术无法做到的情况下发挥作用。通常,可以通过带外通道直接泄露数据,例如,将数据放入你控制的域的DNS查询中。
阅读更多
使用Burp Suite的Web漏洞扫描器可以快速可靠地检测到大多数SQL注入漏洞。
可以通过使用系统化的一套测试针对应用程序中的每个入口点来手动检测SQL注入漏洞。通常包括:
提交单引号字符'
,并查找错误或其他异常。
提交一些特定于SQL的语法,以评估入口点的基本(原始)值和不同值,并在生成的应用程序响应中寻找系统差异。
提交诸如OR 1=1
和OR 1=2
之类的布尔条件,并查找应用程序响应中的差异。
提交旨在在SQL查询中执行时触发时间延迟的有效载荷,并寻找应时间的差异。
提交旨在在SQL查询中执行时触发带外网络交互的OAST有效载荷,并监视任何由此产生的交互。
大多数SQL注入漏洞都出现在SELECT
查询的WHERE
子句中。这类SQL注入通常被经验丰富的测试人员很好地理解。
但是SQL注入漏洞原则上可以发生在查询的任何位置,也可以发生在不同的查询类型中。发生SQL注入最常见的其他位置包括:
在UPDATE
语句中,更新的值或WHERE
子句中。
在INSERT
语句中,插入的值中。
在SELECT
语句中,表名或列名中。
在SELECT
语句中,ORDER BY
子句中。
到目前为止,你在所有实验中都使用查询字符串来注入恶意的SQL有效载荷。但是,请务必注意,你可以使用被应用程序处理为SQL查询的任何可控输入来执行SQL注入攻击。例如,一些网站以JSON或XML格式接收输入,并使用它来查询数据库。
这些不同的格式甚至可以为你提供替代方法,来混淆由于WAF和其他防御机制而被阻止的攻击。弱实现通常仅在请求中查找常见的SQL注入关键字,因此你可以通过简单地对被禁用的关键字中的字符进行编码或转义来绕过这些过滤。例如,以下基于XML的SQL注入使用XML转义序列对SELECT
中的S
字符进行编码:
这将在服务器端进行解码,然后传递给SQL解释器。
LAB
一阶SQL注入发生在应用程序从HTTP请求获取用户输入的情况下,并且在处理该请求的过程中,以不安全的方式将输入合并到SQL查询中。
在二阶SQL注入(也称为存储型SQL注入)中,应用程序从HTTP请求中获取用户输入并将其存储以备将来使用。通常,这是通过将输入放入数据库中来完成的,但在存储数据时不会出现漏洞。之后,在处理其他HTTP请求时,应用程序将以不安全的方式检索存储的数据并将其合并到SQL查询中。
二阶SQL注入通常出现在这样的情况下,开发人员意识到了SQL注入漏洞,从而安全地处理输入放入数据库的初始过程。当以后对数据进行处理时,由于先前已将其安全地放置到数据库中,因此认为该数据是安全的。此时数据的处理方式是不安全的,开发人员错误地认为数据是可信任的。
在主流的数据库平台上,SQL语言的某些核心功能以相同的方式实现,因此,检测和利用SQL注入漏洞的许多方式在不同类型的数据库上均相同。
但是,常见数据库之间也存在许多差异。意味着用于检测和利用SQL注入的某些技术在不同平台上的工作方式会有所不同。例如:
字符串拼接的语法。
注释。
批处理(或堆叠)查询。
特定于平台的API。
错误信息。
阅读更多
通过使用参数化查询(也称为预编译语句)而不是在查询中进行字符串拼接,可以防范大多数SQL注入实例。
由于用户输入直接被拼接到查询中,因此以下代码容易受到SQL注入的攻击:
可以轻松地重写此代码以防范用户的输入干扰查询结构:
参数化查询可用于不受信任的输入作为查询中显示为数据的任何情况,包括WHERE
子句和INSERT
或UPDATE
语句中的值。它们不能用于处理查询其他部分中不可信任的输入,例如表、列名或ORDER BY
子句。将不受信任的数据放入这些查询部分的应用程序功能需要采用不同的方法,例如将允许的输入值列入白名单,或者使用不同的逻辑来实现所需的行为。
为了使参数化查询能够有效地防范SQL注入,查询中使用的字符串必须始终是一个硬编码常量,绝不能包含来自任何来源的任何变量数据。不要试图逐个决定数据项是否受信任,并在被认为安全的情况下继续在查询中使用字符串拼接。这样会很容易就数据的可能来源犯错误,或者其他代码的更改而违反有关数据受到污染的假设。
阅读更多