Web渗透之SQL注入-URL解码注入(URL Decode Injection)

本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行,任何未经授权使用文中技术的行为均与作者无关,请务必遵守法律法规,获得许可后方可进行渗透测试。

目录

一、概念

1、核心原理

2、常见场景

3、实际例子

4、更隐蔽的双重编码

5、检测与利用方法

6、防御措施

7、总结

二、URL解码原理

[1. URL编码简介](#1. URL编码简介)

[2. 解码过程](#2. 解码过程)

[3. 为什么需要URL解码?](#3. 为什么需要URL解码?)

[4. URL解码与注入的关系](#4. URL解码与注入的关系)

[5. 关键点总结](#5. 关键点总结)

三、URL解码注入原理

[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 应用会自动对 GETPOST 参数进行一次 URL 解码。如果开发者错误地手动额外调用了解码函数 (如 PHP 的 urldecode()rawurldecode()),或者应用服务器存在"二次解码"行为,攻击者就可以利用编码绕过输入过滤(如关键字黑名单、简单的转义)。

示例流程

  1. 正常输入:1' or '1'='1

  2. 攻击者 URL 编码为:1%27%20or%20%271%27=%271

  3. 服务器自动解码一次:1' or '1'='1

  4. 如果应用再次解码(或使用不当),则原始恶意 SQL 注入成功。

2、常见场景

  1. 双重 URL 解码 某些 Web 应用在获取参数后会调用 urldecode(),而 Web 服务器(如 Apache、IIS)已经自动解码过一次,导致二次解码。例如 %2527%25 解码为 %,再解码一次成为 ')。

    • 输入:1%2527 → 第一次解码得 1%27 → 第二次解码得 1'
  2. 绕过简单的黑名单过滤 如果应用拦截了 '"# 等字符,但未对 URL 编码形式做检查,攻击者可以用 %27 代替 ',在解码后注入。

  3. 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. 输入 1%27 观察是否触发数据库错误(如单引号未闭合)。

  2. 输入 1%2527 观察是否出现同样的错误。

  3. 使用 %23 代替 # 进行注释,%2d%2d 代替 -- 等。

6、防御措施

  1. 避免手动 URL 解码 :Web 服务器已经自动解码一次,程序不应再调用 urldecode()rawurldecode()

  2. 使用参数化查询:预编译语句完全不受注入影响。

  3. 输入验证 :采用白名单校验,或直接使用类型转换(如 intval())处理数字型参数。

  4. WAF 规则优化 :检测 URL 编码后的危险字符(如 %27%22%23)以及双重编码模式。

  5. 正确设置字符编码:确保 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%271'

  • 最终 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. 攻击者输入 1%2527--

    • %25% 的 URL 编码,%27' 的 URL 编码。
  2. Web 服务器自动解码一次 → 1%27--

  3. addslashes 处理 1%27--: 此时字符串中的 %27 只是普通字符 %27,没有单引号,所以 addslashes 不会添加反斜杠 。结果仍为 1%27--

  4. urldecode 再次解码:将 %27 转换为 ',得到 1'--

  5. 拼接到 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,仍不是特殊字符;二次解码后才成为 ',成功绕过。


五、防御措施

  1. 不要手动解码 URL 参数$_GET$_POST 已经自动解码,禁止再次调用 urldecode/rawurldecode

  2. 使用参数化查询:这是根本解决方案,与编码、转义无关。

  3. 如果必须处理编码,应先解码再转义,但最安全的是不用转义,直接用预编译。

  4. 输入验证 :数字型参数用 intval(),字符串用白名单。


六、总结

本次实战演示了URL解码注入(二次解码) 的经典利用方式:通过 %2527 双重编码,绕过 addslashes 转义,最终实现 SQL 注入。该漏洞的根源在于应用层多余的 urldecode 操作,使得攻击者能够"复活"被编码的单引号。

相关推荐
万能的知了1 小时前
WAF、高防IP、CC防护:安全产品到底怎么选
服务器·网络协议·安全
网络研究院2 小时前
2026 终极攻防变局:深度拆解 MITRE ATT&CK ER8 企业安全评估路线图与微观技术实战
网络·安全·网络研究观
小红卒11 小时前
mysql之udf提权
数据库·mysql·网络安全
王码码203511 小时前
多台服务器怎么统一看状态?Beszel 轻量监控,搭起来不费事
运维·服务器·后端·安全·阿里云·接口·web
ylscode11 小时前
Anthropic Claude Oceanus意外泄露:Mythos系列AI红队测试遭遇API代理滥用危机
网络·人工智能·安全·web安全·安全威胁分析
rockey62712 小时前
基于AScript的SQL脚本语言发布啦!
sql·c#·.net·script·expression·动态脚本
福建佰胜张工12 小时前
MAXON 机电高压油安全切断阀技术全解
安全·自动化
Rubin智造社13 小时前
Anthropic安全白皮书2|三级成熟度模型:你的AI智能体该配哪级安全?
大数据·安全·沙箱隔离·零信任成熟度模型·三级安全框架·jit权限·不可变审计
乐兮创想 小林15 小时前
企业官网的安全架构:从 HTTPS、WAF 到备份与应急响应的 7 层防御工程
安全·https·网站建设·安全架构·企业官网·北京网站建设公司