Web安全学院
Web安全学院
  • 首页
  • 译序
  • 学习路线
  • 前篇
    • Web应用程序安全测试
    • 动态应用程序安全测试(DAST)
    • 带外应用程序安全测试(OAST)
  • 服务器端主题
    • SQL注入
      • SQL注入
      • SQL注入UNION攻击
      • 在SQL注入攻击中检索数据库
      • SQL盲注
      • SQL注入速查表
    • 认证
      • 认证漏洞
      • 基于密码登录中的漏洞
      • 多因素认证中的漏洞
      • 其他认证机制中的漏洞
      • 如何保护你的认证机制
    • 目录遍历
      • 目录遍历
    • 命令注入
      • OS命令注入
    • 业务逻辑漏洞
      • 业务逻辑漏洞
      • 业务逻辑漏洞示例
    • 信息泄露
      • 信息泄露漏洞
      • 如何发现并利用信息泄露漏洞
    • 访问控制
      • 访问控制漏洞与权限提升
      • 不安全的直接对象引用(IDOR)
      • 访问控制安全模型
    • 文件上传漏洞
      • 文件上传漏洞
    • 条件竞争
      • 条件竞争
    • 服务器端请求伪造(SSRF)
      • 服务器端请求伪造(SSRF)
      • 盲SSRF漏洞
    • XXE注入
      • XML外部实体(XXE)注入
      • XML实体
      • 发现并利用盲XXE漏洞
  • 客户端主题
    • 跨站脚本(XSS)
      • 跨站脚本
      • 反射型XSS
      • 存储型XSS
      • 基于DOM的XSS
      • XSS上下文
        • 跨站脚本上下文
        • 客户端模版注入
      • 利用跨站脚本漏洞
      • 内容安全策略
      • 悬空标记注入
      • 如何防范XSS漏洞
      • 跨站脚本(XSS)速查表
    • 跨站请求伪造(CSRF)
      • 跨站请求伪造(CSRF)
      • XSS与CSRF
      • 绕过CSRF令牌验证
      • 绕过SameSite Cookie限制
      • 绕过基于Referer的CSRF防御
      • 如何防范CSRF漏洞
    • 跨域资源共享(CORS)
      • 跨域资源共享(CORS)
      • 同源策略(SOP)
      • CORS和Access-Control-Allow-Origin响应标头
    • 点击劫持
      • 点击劫持(UI伪装)
    • 基于DOM的漏洞
      • 基于DOM的漏洞
      • 控制Web消息源
      • 基于DOM的开放重定向
      • 基于DOM的Cookie操纵
      • 基于DOM的JavaScript注入
      • 基于DOM的document-domain操纵
      • 基于DOM的WebSocket URL投毒
      • 基于DOM的链接操纵
      • Web消息操纵
      • 基于DOM的Ajax请求标头操纵
      • 基于DOM的本地文件路径操纵
      • 基于DOM的客户端SQL注入
      • 基于DOM的HTML5 Storage操纵
      • 基于DOM的客户端XPath注入
      • 基于DOM的客户端JSON注入
      • DOM-data操纵
      • 基于DOM的拒绝服务
      • DOM破坏
    • WebSocket
      • 测试WebSocket安全漏洞
      • 什么是WebSocket?
      • 跨站WebSocket劫持
  • 进阶主题
    • 不安全的反序列化
      • 不安全的反序列化
      • 利用不安全的反序列化漏洞
    • 测试GraphQL API
      • 测试GraphQL API
      • 什么是GraphQL?
    • 服务器端模板注入
      • 服务器端模板注入
      • 利用服务器端模板注入漏洞
    • Web缓存投毒
      • Web缓存投毒
      • 缓存设计缺陷的利用
      • 缓存实现缺陷的利用
    • HTTP Host标头攻击
      • HTTP Host标头攻击
      • 如何识别和利用HTTP Host头的漏洞
      • 密码重置投毒
    • HTTP请求走私
      • HTTP请求走私
      • 查找HTTP请求走私漏洞
      • 利用HTTP请求走私漏洞
      • 高级请求走私
        • 高级请求走私
        • HTTP/2降级
        • 响应队列投毒
        • HTTP/2专属载体
        • HTTP请求隧道
      • 浏览器驱动的请求伪造
        • 浏览器驱动的请求伪造
        • CL.0请求走私
        • 客户端异步攻击
        • 基于暂停的异步攻击
    • OAuth认证
      • OAuth 2.0认证漏洞
      • OAuth授权类型
      • OpenID Connect
      • 如何防范OAuth认证漏洞
    • JWT攻击
      • JWT攻击
      • 在Burp Suite中使用JWT
      • 算法混淆攻击
    • 原型污染
      • 什么是原型污染?
      • JavaScript原型和继承
      • 客户端
        • 客户端原型污染漏洞
        • 通过浏览器API进行原型污染
      • 服务器端
        • 服务器端原型污染
      • 预防原型污染漏洞
    • 基本技能
      • 基本技能
      • 使用编码混淆攻击
      • 在手动测试中使用Burp Scanner
Powered by GitBook
On this page
  • 上下文特定解码
  • 解码差异
  • 通过URL编码进行混淆
  • 通过双重URL编码进行混淆
  • 通过HTML编码进行混淆
  • 前导零
  • 通过XML编码进行混淆
  • 通过Unicode转义进行混淆
  • 通过十六进制转义进行混淆
  • 通过八进制转义进行混淆
  • 通过多重编码进行混淆
  • 通过SQL CHAR()函数进行混淆
  1. 进阶主题
  2. 基本技能

使用编码混淆攻击

在本节中,我们将向你展示如何利用网站执行的标准解码来绕过输入过滤,并为各种攻击(如XSS和SQL注入)注入有害的有效载荷。

上下文特定解码

客户端和服务器都使用各种不同的编码来在系统之间传递数据。当它们需要实际使用数据时,通常需要先对数据进行解码。执行解码步骤的确切顺序取决于数据出现的上下文。例如,查询参数通常是在服务器端进行URL解码,而HTML元素的文本内容可能是在客户端进行HTML解码。

在构造攻击时,你应该考虑到你的有效载荷被注入的确切位置。如果你能根据此上下文推断出你的输入是如何被解码的,你就有可能找出表示相同有效载荷的替代方法。

解码差异

注入攻击通常涉及注入使用可识别模式的有效载荷,例如HTML标记、JavaScript函数或SQL语句。由于这些有效载荷的输入几乎永远不会包含用户提供的代码或标记,因此网站通常会实施防御措施来阻止包含这些可疑模式的请求。

然而,这些类型的输入过滤也需要解码输入以检查其是否安全。从安全角度来看,检查输入时执行的解码必须与后端服务器或浏览器在最终使用数据时执行的解码相同。任何差异都可能使攻击者通过应用不同的编码,偷偷地将有害的有效载荷带过过滤,这些编码在后续将被自动删除。

通过URL编码进行混淆

在URL中,一系列保留字符具有特殊含义。例如,和号(&)用作分隔符,用于在查询字符串中分隔参数。问题是,基于URL的输入可能出于不同的原因包含这些字符。考虑一个包含用户搜索查询的参数。如果用户搜索类似于“Fish & Chips”的内容会发生什么情况?

浏览器会自动对可能导致解析器产生歧义的任何字符进行URL编码。通常这意味着用%字符和其2位十六进制代码来替换它们,如下所示:

[...]/?search=Fish+%26+Chips

这确保了和号不会被误认为是分隔符。

注意

虽然空格字符可以被编码为%20,但通常使用加号(+)来表示,如上例所示。

任何基于URL的输入在分配给相关变量之前都会在服务器端进行自动URL解码。这意味着对于大多数服务器来说,查询参数中的%22、%3C和%3E等序列与"、<和>字符是等价的。换句话说,你可以通过URL注入URL编码的数据,后端应用程序通常仍然会正确解释它们。

有时,你可能会发现WAF等在检查输入时未能正确地对你的输入进行URL解码。在这种情况下,只需对任何被列入黑名单的字符或单词进行编码,就可以将有效载荷走私到后端应用程序中。例如,在SQL注入攻击中,你可能会对关键字进行编码,因此SELECT变为%53%45%4C%45%43%54等。

通过双重URL编码进行混淆

由于某种原因,一些服务器对接收到的URL执行两轮URL解码。这本身不一定是一个问题,前提是任何安全机制在检查输入时也会对输入进行双重解码。否则,这种差异将使攻击者能够通过简单地对输入进行两次编码来将恶意输入走私到后端。

假设你正在尝试通过查询参数注入标准的XSS PoC,例如<img src=x onerror=alert(1)>。在这种情况下,URL可能如下所示:

[...]/?search=%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E

在检查请求时,如果WAF执行标准的URL解码,它将轻松识别出这个众所周知的有效载荷。该请求将被阻止到达后端。但是如果你对注入进行双重编码呢?实际上,这意味着%字符本身会被替换为%25:

[...]/?search=%253Cimg%2520src%253Dx%2520onerror%253Dalert(1)%253E

由于WAF只解码一次,它可能无法识别请求是危险的。如果后端服务器随后对此输入进行双重解码,那么有效载荷将被成功注入。

通过HTML编码进行混淆

在HTML文档中,某些字符需要被转义或编码,以防止浏览器错误地将其解释为标记的一部分。这是通过用引用替换有问题的字符来实现的,引用以&符号为前缀并以分号结束。

在许多情况下,可以使用名称作为引用。例如,序列&colon;表示一个冒号字符。

或者,可以使用字符的十进制或十六进制代码点提供引用,在这种情况下,分别为&#58;和&#x3a;。

在HTML中的特定位置,例如元素的文本内容或属性的值,浏览器在解析文档时会自动解码这些引用。当注入到这样的位置时,你有时可以利用它来对用于客户端攻击的有效载荷进行混淆,使其隐藏在任何已经存在的服务器端防御中。

如果仔细观察我们前面示例中的XSS有效载荷,请注意有效载荷被注入到HTML属性中,即onerror事件处理程序。如果服务器端检查明确查找alert()有效载荷,它们可能无法识别到你对其中一个或多个字符进行了HTML编码:

<img src=x onerror="&#x61;lert(1)">

当浏览器呈现页面时,它将解码并执行注入的有效载荷。

前导零

有趣的是,当使用十进制或十六进制样式的HTML编码时,你可以选择在代码点中包含任意数量的前导零。一些WAF和其他输入过滤未能充分考虑到这一点。

如果在HTML编码有效载荷后仍然被阻止,你可能会发现只需在代码点前面加上几个零就可以绕过过滤:

<a href="javascript&#00000000000058;alert(1)">Click me</a>

通过XML编码进行混淆

XML与HTML密切相关,也支持使用相同的数值转义序列进行字符编码。这使你能够在元素的文本内容中包含特殊字符而不会破坏语法,这在测试基于XML输入的XSS时非常有用。

即使不需要编码任何特殊字符以避免语法错误,你仍然可以利用这种行为来混淆有效载荷,就像使用HTML编码一样。不同之处在于,你的有效载荷是由服务器自身解码的,而不是由浏览器在客户端解码的。这对于绕过WAF和其他过滤非常有用,因为如果它们检测到与SQL注入攻击相关的某些关键字,它们可能会阻止你的请求。

<stockCheck>
    <productId>
        123
    </productId>
    <storeId>
        999 &#x53;ELECT * FROM information_schema.tables
    </storeId>
</stockCheck>

LAB

通过Unicode转义进行混淆

Unicode转义序列由前缀\u和后跟该字符的四位十六进制代码组成。例如,\u003a表示一个冒号。ES6还支持一种使用花括号的新形式Unicode转义:\u{3a}。

在解析字符串时,大多数编程语言都会解码这些Unicode转义字符。包括浏览器使用的JavaScript引擎。在注入到字符串上下文中时,你可以像上面HTML转义示例中一样使用Unicode来混淆客户端有效载荷。

例如,假设你正在尝试利用DOM XSS,其中你的输入作为字符串传递给eval()接收器。如果初始尝试被阻止,请尝试使用以下方式转义其中一个字符:

eval("\u0061lert(1)")

由于此代码将在服务器端保持编码状态,因此在浏览器再次对其进行解码之前,它可能不会被检测到。

注意

在字符串内部,你可以以这种方式转义任何字符。但是,在字符串之外转义某些字符将导致语法错误。例如,这包括左括号和右括号。

值得注意的是,ES6样式的Unicode转义还允许使用可选的前导零,因此某些WAF可能会通过与我们在HTML编码中使用的相同技术来轻松被欺骗。例如:

<a href="javascript\u{0000000003a}alert(1)">Click me</a>

通过十六进制转义进行混淆

注入字符串上下文时的另一个选择是使用十六进制转义,它使用字符的十六进制代码点表示,前缀为\x。例如,小写字母a由\x61表示。

与Unicode转义一样,只要将输入评估为字符串,这些转义就会在客户端被解码:

eval("\x61lert")

请注意,有时也可以使用相似的方式通过前缀0x来混淆SQL语句,例如,0x53454c454354可以被解码以形成SELECT关键字。

通过八进制转义进行混淆

八进制转义的工作方式与十六进制转义非常相似,只是字符引用使用八进制编号系统而不是十六进制。这些转义以独立的反斜杠为前缀,意味着小写字母a由\141表示。

eval("\141lert(1)")

通过多重编码进行混淆

重要的是,你可以将多种编码组合在一起,将有效载荷隐藏在多层混淆之后。请查看以下示例中的javascript: URL:

<a href="javascript:&bsol;u0061lert(1)">Click me</a>

浏览器首先将&bsol;解码为反斜杠。这样做的效果是将原本的u0061字符转换为Unicode转义\u0061:

<a href="javascript:\u0061lert(1)">Click me</a>

然后进一步解码以形成一个有效的XSS有效载荷:

<a href="javascript:alert(1)">Click me</a>

显然,要以这种方式成功注入有效载荷,你需要对输入进行什么顺序的解码以及以何种顺序进行解码有很好的理解。

通过SQL CHAR()函数进行混淆

虽然严格来说,这不属于一种编码形式,但在某些情况下,你可以使用CHAR()函数来混淆SQL注入攻击。此函数接受一个十进制或十六进制代码点,并返回匹配的字符。十六进制代码必须以0x为前缀。例如,CHAR(83)和CHAR(0x53)都返回大写字母S。

通过连接返回值,你可以使用此方法来混淆被阻止的关键字。例如,即使SELECT被列入黑名单,以下注入初始看起来是无害的:

CHAR(83)+CHAR(69)+CHAR(76)+CHAR(69)+CHAR(67)+CHAR(84)

然而,当应用程序将其作为SQL处理时,它将动态构造SELECT关键字并执行注入的查询。

Previous基本技能Next在手动测试中使用Burp Scanner

Last updated 1 year ago

通过XML编码绕过过滤的SQL注入