前言
SQL 注入是比较常见的攻击类型,之前一直听说过,也尝试看过一些教程,但其中的单引号,字符串拼接等感觉有点抽象,不知道为什么要这么做。这次就使用 DVWA 的环境来演练一下吧。
在这里我们的目标是获取所有的用户名和密码(虽然不是明文)
打开 SQL Injection 页面,将级别调整为 Low。
1. 情况分析
正常情况下,我们输入 1,点击 submit 按钮,就会得到这个用户的用户名信息:
此时我们输入 1 AND 1 = 1,再次点击 Submit 按钮,发现并没有报错。而我们输入 1' 再点击按钮后,会提示错误:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''' at line 1
说明网页程序将我们的输入内容放入了数据库的查询语句中,即存在可以注入的地方。
SQL 注入分为 数字型注入,字符型注入 等等多种类型。数字型注入 意思为查询条件为数字类型,同样 字符型注入 意思为查询条件是字符(串)类型的。在我们的场景中,是输入单了引号才报错,说明是 字符型注入。
2. Low 难度实操
A. 大概猜测
根据网页上的业务可以大致猜测一下查询语句为:
SELECT * FROM 用户表 WHERE 用户ID = '传入的值';
为了验证猜想,我们传入 1' OR 1 = 1 #,第一个 ' 用于闭合之前的引号,后面的 # 号 用于注释SQL语句后面的内容(原查询语句中的 ' 和 可能的后面内容)。实际执行的 SQL 语句即为:
SELECT * FROM 用户表 WHERE 用户ID = '1' OR 1 = 1 #';
可以看到执行成功,符合我们的预期。
B. 判断字段数量
我们需要判断下返回当前结果的这条查询语句的实际查询字段情况。拼接上 1' UNION SELECT 1, 2 #,并尝试往后逐步增加列数。这里利用的是 Union 操作要求查询的字段数量相等。实际执行的 SQL 语句为:
SELECT f1, f2 ...... FROM 用户表 WHERE 用户ID = '1' UNION SELECT 1, 2 #';
如果拼接上 Union 查询语句没报错,则可以确定查询字段的数量。根据结果中1,2的顺序(我们拼接进去的),可以确定返回字段的顺序。此处只有两个,比较简单。
此处也可以通过 Order By 来判断。Order By 后面加数字,表示按照查询出来的第几列排序,如 Order By 2,意思为按照查询出来的第 2 列排序,而如果没有这么多列,就会有报错信息。据此可以判断查询出的列数量。
C. 获取敏感信息
由于我们的目标是查询用户表中的用户名和密码,因此我们需要找到对应的数据库,对应的表,对应的字段。对于 MySQL 数据库,这些内容存储在 information_schema 这个数据库中。
首先利用 Union,查出当前数据库中所有的表。输入 ' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #。MySQL 内置的group_concat 函数可以将多个列组合起来。(此时不在 ' 前输入既有的 id 的值 1,这样可以避免查出我们不需要的数据,只需一行就得到了结果)
可以看到当前数据库中有 guestbook 和 users 这两张表。显然我们的目标就是 users 表了,继续查询这个表中有哪些列。输入 ' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' # ,得到结果:
距离目标已经很近了!继续输入 ' union select 1,group_concat(concat(user, '-', password)) from users #,得到结果:
Low 的目标达成(怎么感觉怪怪的)!接下来探索 Medium 难度吧!
3. Medium 难度准备工作
A. 引入 BurpSuite
将难度调整为 Medium,可以看到已经没有了输入框,变成了下拉选择:
其实对于 WEB 页面来说,无论是输入框,还是下拉选择,或者是其它方式,最终反映到与后端服务器的交互,都是通过 HTTP 请求(当然还有 WebSocket)。如果我们可以拦截这些请求,并尝试修改,不是一样可以达到注入的目的吗。我们可以使用 Burp Suite 来实现这个操作(Burp Suite 有很多其它的功能,这里用得比较简单)。
因为还有很多攻击的形式没有尝试,考虑到操作便捷性和以后的使用,这里使用了另一台安装了 Kali Linux 的虚拟机,其中包含了很多其它的工具。请务必在法律允许的范围内使用!!!
从官方网站下载 Kali Linux 针对 Vmware 虚拟机的镜像,并启动即可。默认的用户名和密码都是 kali。
B. Burp Suite 拦截相关配置
我们需要先设置浏览器,让所有的流量都通过 BurpSuite (默认 8080 端口),这样才能拦截的到。打开 Firefox 浏览器(火狐大法好,破音),在 General 中找到 Network Settings,并设置代理为 localhost,端口号为 8080,如图:
在 Firefox 浏览器中打开我们的 DVWA 站点,调整 DVWA Security 中的难易度为 Medium,并打开我们要操作的 SQL Injection 页面。之后打开 BurpSuite,在 Proxy 一栏点击 Intercept is off 以打开拦截器。随后在 Firefox 浏览器中的流量都会被 BurpSuite 拦截。
4. Medium 难度实操
点击 DVWA 页面的 submit 按钮,可以在 Burp Suite 中看到如图所示的结果:
可以看到我们选择的参数被放在了红框所示的位置,接下来像在 Low 里面一样将注入的内容拼接到此处即可。
A. 注入类型判断
将 id=1 修改为 id=1' OR 1 = 1 #,并点击 Forward 按钮放行流量,可以看到 Firefox 浏览器中提示报错:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' OR 1 = 1 #' at line 1
这说明该注入漏洞不再是字符型注入了。尝试数字型注入,将 id=1 修改为 1 OR 1 = 1 #,再次放行流量,发现没有报错,网页展示了很多用户的信息,验证了 数字型注入 的猜测。
之后的思路和在 Low 里面的基本一样,唯一的不同是输入的内容在 BurpSuite 中修改。
B. 判断字段数量
将 id=1 修改为 0 UNION SELECT 1, 2 #,没有报错,确定查询语句查询的字段数量是 2 个,同时确定字段顺序。
C. 获取敏感信息
查找数据库中所有的表,替换为:
0 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
需要注意的是,前面必须要带有值 0。 因为字符型注入可以直接用 ' 闭合为空字符串,而数字型注入如果没有值,我们替换后实际的执行语句就变成了: SELECT * FROM 表名 WHERE id = union select 1 ........ 这样,会造成语法错误。
查看表中所有的列。替换为:
0 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' #
此时发生了错误。
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'users\' #' at line 1
可我们并没有传入 \'users\' 这样的东西进去。通过分析或者查看下DVWA的源代码(/var/www/html/vulnerabilities/sqli/source/medium.php)发现,此时将单引号转义了:
此时可以通过将字符串转换为十六进制来绕过转义。通过在线工具将字符串 users 转换为 7573657273,修改我们的拼接为:
0 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273 #
可以看到此时得到了我们想要的结果:
获取用户名和密码。替换为:
0 union select 1,group_concat(concat(user, 0x2d, password)) from users #
原来的 '-' 也替换为了对应的十六进制 0x2d。获取到了结果:
Medium 的目标也达成了!!!
5. High 难度实操
查看页面和源代码发现,实际与之前的 Low 几乎一样,只是通过弹出页面,利用 Session 传值。而且多了一个 LIMIT 1,限制了返回数量只有1条而不是多条,如图:
而按照我们之前的注入操作都是一行返回,因此我们输入:
' union select 1,group_concat(concat(user, '-', password)) from users #
即可达成结果。
6. Impossible 难度实操?
Impossible 难度旨在为我们提供一个比较安全的代码示例:
调整为 Impossible 难度,实际是很难注入的了。
参考
DVWA-------简单的SQL注入
DVWA之SQL注入
DVWA SQL Injection SQL注入全等级分析与实践
DVWA-7.4 SQL Injection(SQL注入)-Impossible