本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行,任何未经授权使用文中技术的行为均与作者无关,请务必遵守法律法规,获得许可后方可进行渗透测试。
目录
[1. URL编码简介](#1. URL编码简介)
[2. 解码过程](#2. 解码过程)
[3. 为什么需要URL解码?](#3. 为什么需要URL解码?)
[4. URL解码与注入的关系](#4. URL解码与注入的关系)
[5. 关键点总结](#5. 关键点总结)
[1. 正常处理流程(无漏洞)](#1. 正常处理流程(无漏洞))
[2. URL解码注入的核心------二次解码](#2. URL解码注入的核心——二次解码)
[3. 为什么二次解码会导致注入?](#3. 为什么二次解码会导致注入?)
[4. 绕过 WAF 和黑名单](#4. 绕过 WAF 和黑名单)
[5. 利用场景](#5. 利用场景)
[6. 防御措施](#6. 防御措施)
[7. 总结一句话](#7. 总结一句话)
[1. 正常请求](#1. 正常请求)
[2. 尝试普通注入(失败)](#2. 尝试普通注入(失败))
[3. 双重编码绕过(成功闭合引号)](#3. 双重编码绕过(成功闭合引号))
[4. 联合查询提取数据](#4. 联合查询提取数据)
一、概念
URL解码注入是一种利用 URL 编码(百分号编码)与应用程序解码顺序不一致 导致的注入技术。攻击者将恶意 SQL 语句中的特殊字符(如单引号 '、双引号 "、井号 # 等)进行 URL 编码(例如 %27、%22、%23),如果应用程序在接收参数后先进行 URL 解码再拼接到 SQL 语句中,这些原本被编码的字符就会恢复原样,从而改变 SQL 语义,形成注入。
1、核心原理
大多数 Web 应用会自动对 GET 和 POST 参数进行一次 URL 解码。如果开发者错误地手动额外调用了解码函数 (如 PHP 的 urldecode()、rawurldecode()),或者应用服务器存在"二次解码"行为,攻击者就可以利用编码绕过输入过滤(如关键字黑名单、简单的转义)。
示例流程:
-
正常输入:
1' or '1'='1 -
攻击者 URL 编码为:
1%27%20or%20%271%27=%271 -
服务器自动解码一次:
1' or '1'='1 -
如果应用再次解码(或使用不当),则原始恶意 SQL 注入成功。
2、常见场景
-
双重 URL 解码 某些 Web 应用在获取参数后会调用
urldecode(),而 Web 服务器(如 Apache、IIS)已经自动解码过一次,导致二次解码。例如%2527(%25解码为%,再解码一次成为')。- 输入:
1%2527→ 第一次解码得1%27→ 第二次解码得1'。
- 输入:
-
绕过简单的黑名单过滤 如果应用拦截了
'、"、#等字符,但未对 URL 编码形式做检查,攻击者可以用%27代替',在解码后注入。 -
WAF 绕过 部分 Web 应用防火墙(WAF)对请求进行检测时可能只识别原始字符串,而忽略了 URL 编码形式。攻击者通过编码可以绕过规则。
3、实际例子
假设 PHP 代码如下:
$id = urldecode($_GET['id']); // 额外的一次解码 $sql = "SELECT * FROM users WHERE id='$id'";
攻击者访问:
http://example.com/page.php?id=1%27%20or%20%271%27=%271
-
URL 编码部分:
%27→',%20→ 空格。 -
经过
urldecode()后,$id的值为1' or '1'='1。 -
最终 SQL 变为:
SELECT * FROM users WHERE id='1' or '1'='1'导致注入成功,返回所有用户。
4、更隐蔽的双重编码
http://example.com/page.php?id=1%2527%2520or%2520%25271%2527=%25271
%25解码为%,%27解码为',二次解码后得到1' or '1'='1。
5、检测与利用方法
-
输入
1%27观察是否触发数据库错误(如单引号未闭合)。 -
输入
1%2527观察是否出现同样的错误。 -
使用
%23代替#进行注释,%2d%2d代替--等。
6、防御措施
-
避免手动 URL 解码 :Web 服务器已经自动解码一次,程序不应再调用
urldecode()、rawurldecode()。 -
使用参数化查询:预编译语句完全不受注入影响。
-
输入验证 :采用白名单校验,或直接使用类型转换(如
intval())处理数字型参数。 -
WAF 规则优化 :检测 URL 编码后的危险字符(如
%27、%22、%23)以及双重编码模式。 -
正确设置字符编码:确保 UTF-8 等一致性,避免编码漏洞。
7、总结
URL 解码注入的本质是应用程序对用户输入进行了不必要的二次 URL 解码 ,导致原本被编码的 SQL 元字符恢复活性,从而利用注入漏洞。防御的核心是不要手动解码 + 参数化查询。如果必须解码,应确保在安全处理之后(如先解码再转义,但最佳实践是不解码)。
二、URL解码原理
URL解码(也称为百分号解码)是 URL 编码(百分号编码)的逆过程。其核心原理是:将 % 后跟两位十六进制数(如 %20、%27、%E4%BD%A0)转换回原始字符。
1. URL编码简介
URL中只允许包含 ASCII 字符集 中的一部分安全字符(字母、数字、-、_、.、~),其他字符(如汉字、空格、#、&、=、' 等)必须编码为 % 后跟该字符的 ASCII 码(或UTF-8字节序列) 的十六进制表示。
-
空格 → ASCII 码 32(0x20) →
%20 -
单引号
'→ ASCII 码 39(0x27) →%27 -
井号
#→ ASCII 码 35(0x23) →%23 -
中文"你" → UTF-8 编码为三个字节
0xE4 0xBD 0xA0→%E4%BD%A0
2. 解码过程
解码时,从左到右扫描字符串:
-
遇到普通字符(非
%),直接原样输出。 -
遇到
%,则读取其后的 两个字符(必须是合法的十六进制数字 0-9、A-F、a-f),将它们组合成一个字节(0~255),然后输出该字节对应的 ASCII 字符或 UTF-8 序列的一部分。
示例:
输入:Hello%20World%21 - %20 → 0x20 → 空格 - %21 → 0x21 → '!' 输出:Hello World!
示例(UTF-8多字节):
输入:%E4%BD%A0 - %E4 → 0xE4 - %BD → 0xBD - %A0 → 0xA0 这三个字节组合成 UTF-8 编码的“你”字。 输出:你
3. 为什么需要URL解码?
-
浏览器会自动将 URL 中的百分号编码解码后显示(地址栏看到的是原始字符,但实际传输的是编码)。
-
服务器收到请求后,Web 服务器(如 Apache、Nginx、IIS)会 自动进行一次 URL 解码 ,将
%XX还原为对应字符,然后将解码后的参数交给 Web 应用(如 PHP、Java)。 -
开发人员通常无需手动解码,因为
$_GET、$_POST等超全局变量已经是解码后的值。
4. URL解码与注入的关系
-
正常情况 :应用只依赖 Web 服务器的自动解码,不存在二次解码,因此
%27会被还原为',可能引发 SQL 注入(如果应用本身有注入漏洞)。 -
危险情况 :开发者错误地调用
urldecode()或rawurldecode()对已解码的输入再次解码,造成 二次解码 。攻击者可以利用双重编码(如%2527)绕过输入过滤或 WAF。
二次解码示例:
原始输入:%2527 服务器第一次解码:%25 → %,%27 → ',结果变为 %27 开发者手动解码:%27 → ' 最终得到单引号,实现注入。
5. 关键点总结
-
URL解码就是将
%XX序列还原成原始字节/字符的过程。 -
解码算法简单:识别
%,取后两位十六进制数,转为十进制 ASCII 码,输出对应字符。 -
浏览器和 Web 服务器都进行 一次 解码。应用层不应再手动解码,否则可能引入双重解码漏洞。
-
防范 URL 解码注入的最佳方法是 参数化查询,而不是依赖过滤或转义。
三、URL解码注入原理
URL解码注入本质上是应用程序对用户输入进行了多余的、额外的URL解码,导致原本安全的编码字符在多次解码后还原成SQL元字符,从而改变SQL语句结构,实现注入。
1. 正常处理流程(无漏洞)
浏览器/客户端 → Web服务器(自动解码一次)→ 应用层(直接使用解码后数据) 例如: 用户输入 1%27 → 服务器自动解码为 1' → PHP 的 $_GET['id'] 得到 1'。 如果应用存在SQL注入漏洞,此时 1' 就会触发错误。这是普通注入,不是URL解码注入。
2. URL解码注入的核心------二次解码
当应用在获取参数后再次手动调用解码函数 (如 PHP 的 urldecode()、rawurldecode()),就会产生二次解码。
典型错误代码:
$id = urldecode($_GET['id']); // 已经解码过一次,又解码第二次 $sql = "SELECT * FROM users WHERE id='$id'";
攻击过程:
-
攻击者输入:
1%2527其中%25是%的编码,%27是'的编码。 -
第一次解码(Web服务器自动):
%25→%,%27→',得到1%27。 -
第二次解码(
urldecode手动):1%27→1'。 -
最终 SQL:
SELECT * FROM users WHERE id='1''→ 语法错误或注入成功。
更典型的注入 payload:
1%2527%20or%20%271%27=%271
经过二次解码后变成:
1' or '1'='1
实现经典万能密码注入。
3. 为什么二次解码会导致注入?
-
正常情况下,单引号
'在第一次解码后就已经还原,如果应用有转义(如addslashes),它会被转义成\',从而失效。 -
但是二次解码时,
%2527经过第一次解码变成%27,这个%27不是单引号字符 ,而是一个百分号和数字串,因此不会被转义函数处理 (因为转义函数只作用于字符',不作用于字符串%27)。 -
第二次解码将
%27变成',此时'才出现,但转义过程已经结束,从而成功注入。
4. 绕过 WAF 和黑名单
许多 WAF 或输入过滤器会检查 '、"、# 等危险字符,但可能忽略 %2527(双重编码)。服务器解码两次后,WAF 只看到了第一次解码后的 %27(不是危险字符),而第二次解码在 WAF 检测之后发生,因此绕过。
5. 利用场景
-
PHP 中使用
urldecode()、rawurldecode()处理 GET 或 POST 参数。 -
某些框架或中间件(如 Tomcat、IIS)在特定配置下会产生额外解码。
-
开发人员错误地将已经解码的数据再次解码。
6. 防御措施
-
不要手动解码 URL 参数 :
$_GET、$_POST已经是解码后的值,无需再调用urldecode。 -
使用参数化查询:无论是否解码,预编译语句都能彻底防御 SQL 注入。
-
输入验证 :对数字型参数使用
intval();对字符串使用白名单。 -
WAF 规则 :检测双重编码特征(如
%25后跟十六进制)。
7. 总结一句话
URL解码注入是利用应用程序对用户输入进行多余的二次URL解码,使得原本被编码的SQL元字符在解码后才出现,从而绕过转义和过滤,实现SQL注入的攻击技术。
四、靶场渗透示例
一、漏洞代码

$id = addslashes($_GET['id']); // 转义特殊字符 $id = urldecode($id); // 再次URL解码 ← 危险操作 $sql = "select * from userinfo where id='{$id}'";
关键点:
-
addslashes会在单引号'、双引号"、反斜杠\、NULL 前加反斜杠。 -
正常情况下,用户输入
1'会被转义为1\',无法注入。 -
但代码中二次调用
urldecode,使得攻击者可以利用双重 URL 编码绕过转义。
二、注入原理(双重编码)
-
攻击者输入
1%2527--%25是%的 URL 编码,%27是'的 URL 编码。
-
Web 服务器自动解码一次 →
1%27--。 -
addslashes处理1%27--: 此时字符串中的%27只是普通字符%、2、7,没有单引号,所以addslashes不会添加反斜杠 。结果仍为1%27--。 -
urldecode再次解码:将%27转换为',得到1'--。 -
拼接到 SQL:
... where id='1'-- ',成功闭合引号并注释掉后面的内容,实现注入。
关键 :addslashes 作用于未解码的 %27 字符串,此时它不是单引号,故不被转义;二次解码后才变成真正的单引号,从而绕过防护。
三、注入过程
1. 正常请求
http://127.0.0.1/demo5.php?id=1
页面显示:
select * from userinfo where id='1' Array ( [0] => 1 [1] => dsqaf [2] => sadfsaf )

2. 尝试普通注入(失败)
http://127.0.0.1/demo5.php?id=1' -- -
SQL 变成 id='1\' -- -',单引号被转义,无法注入,仍返回原数据。

3. 双重编码绕过(成功闭合引号)
http://127.0.0.1/demo5.php?id=1%2527--
页面显示 SQL:
select * from userinfo where id='1'-- '
说明 %2527 成功变为 ' 并注释掉了后面的内容。此时查询返回原数据(因为 id=1 存在),但证明注入点可用。

4. 联合查询提取数据
http://127.0.0.1/demo5.php?id=1%2527 union select 1,2,3 limit 1,1--
页面显示:
select * from userinfo where id='1' union select 1,2,3 limit 1,1-- ' Array ( [0] => 1 [1] => 2 [2] => 3 )

成功通过联合查询获取了 1,2,3,说明可以任意执行 SQL 语句。
四、漏洞根本原因
-
错误的处理顺序 :先转义(
addslashes)后解码(urldecode),导致二次解码产生的新特殊字符未被转义。 -
双重 URL 编码 :
%2527在一次解码后变为%27,仍不是特殊字符;二次解码后才成为',成功绕过。
五、防御措施
-
不要手动解码 URL 参数 :
$_GET、$_POST已经自动解码,禁止再次调用urldecode/rawurldecode。 -
使用参数化查询:这是根本解决方案,与编码、转义无关。
-
如果必须处理编码,应先解码再转义,但最安全的是不用转义,直接用预编译。
-
输入验证 :数字型参数用
intval(),字符串用白名单。
六、总结
本次实战演示了URL解码注入(二次解码) 的经典利用方式:通过 %2527 双重编码,绕过 addslashes 转义,最终实现 SQL 注入。该漏洞的根源在于应用层多余的 urldecode 操作,使得攻击者能够"复活"被编码的单引号。