• 首页
  • 博客
  • 网站
  • 分享
  • 数码

当前位置:首页 » 个人博客 » 正文

不同的 URL 解析错误是如何发生的,以及哪些 URL 解析器库受到影响

 人参与  2022年11月26日 09:58  分类 : 个人博客  点这评论

在对 Apache2 的身份验证模块进行安全研究期间,我和我的团队发现了一个由 HTTP 服务器 Apache2 和现代 Web 浏览器如何以不同方式解析 URL 所引入的问题。虽然差异 URL 解析的一般问题已被公开记录,但我认为它没有得到应有的关注。它会影响范围广泛的软件,并在身份验证流程和对内部服务的请求等关键功能中引入漏洞。

5a0918db8211c739481510545627.jpg

在这篇博文中,我详细介绍了差异 URL 解析错误是如何发生的,以及哪些 URL 解析器库受到影响。我将使用我们最近在流行的 Apache2 模块 mod_auth_openidc 中发现的错误来为您提供此模式的真实示例,然后向您展示如何通过差异测试轻松检测应用程序中的类似错误。有了这个,我希望提高对这些细微错误的认识,并为您的工具箱添加一个新项目!

差异 URL 解析示例

要了解差异化 URL 解析,让我们看一下 mod_auth_openidc,这是 Zmartzone 开发的第三方 Apache2 模块。它充当OpenID Connect 依赖方,允许用户针对OpenID Connect 提供者进行身份验证和授权。 

例如,您可以在公共 Web 资产之前部署此模块,并且只允许用户对他们公司的 Google 帐户进行身份验证。如果您想进一步了解这些技术,Okta 发布了关于Oauth2OpenID Connect的图文并茂的指南

由于OpenID Connect 提供者很可能出现在另一个来源(在 HTTP 意义上)而不是托管应用程序的地方,因此需要将用户重定向到它们之间以传递重要信息。此信息通常还包括将客户端重定向到的 URL;验证这些值以避免将客户端重定向到意外目的地至关重要:这种不安全的行为称为Open Redirect。  

人们普遍认为,Open Redirect 漏洞与安全性无关,需要用户交互才能对自身产生影响(例如,网络钓鱼)。与 OAuth 流等应用程序的其他功能相结合,它们可以让攻击者窃取访问令牌并获得受害者在应用程序上的特权。

CVE-2021-32786:在 Mod_auth_openidc 中打开重定向

在本节中,我记录了我在 mod_auth_openidc 中发现的一个开放重定向问题,该问题是由 Apache2 的内部 URL 解析方法与 Web 浏览器有效使用的方法之间的解析差异引起的。

在验证 URL 以将用户重定向到(例如,在刷新令牌请求或注销步骤期间)时,将oidc_validate_redirect_url()调用名为的方法。它的实现依赖于apr_uri_parse(),在 [1] 中,从用户控制的参数中提取相关信息并填写apr_uri_t结构的成员:

src/mod_auth_openidc.c

Cpool, url, &uri) != APR_SUCCESS) {  // [1]         *err_str = apr_pstrdup(r->pool, "Malformed URL");         *err_desc = apr_psprintf(r->pool, "not a valid URL value: %s", url);         oidc_error(r, "%s: %s", *err_str, *err_desc);         return FALSE;     }" data-lang="text/x-csrc" style="box-sizing: border-box;">1个

2个

3个

4个

5个

6个

7

8个

9

10

11

12

13

围绕对 oidc_validate_redirect_url() 的调用执行进一步检查,例如:

  • 如果未明确配置为匹配“安全”重定向 URL 的允许列表,则匹配主机名(例如,当前请求的主机必须与从参数中提取的主机匹配);

  • 防止使用不带斜杠或以 开头的 URL //\\以防止像 CVE-2019-3877 这样的漏洞;

  • 防止在参数中使用 CR 和 LF 字符以避免换行注入(并最终避免打开重定向和跨站点脚本错误)。

但是,apr_uri_parse()基于RFC2396RFC3986拆分 URL (具有一些自定义行为,例如 userinfo 解析),同时浏览器尝试遵循WHATWG 生活标准每个 URL 解析器的实现方式都略有不同,但这里我们讨论的是两种不同的规范。 

正如 WHATWG 的 授权状态部分所述,遇到反斜杠会将状态设置为主机状态 (就像处理斜杠一样)。该函数apr_uri_parse()将简单地将其视为用户信息的一部分,因为它位于最后一个的左侧@

C= hostinfo && *s != '@')" data-lang="text/x-csrc" style="box-sizing: border-box;">1个

2个

3个

4个

5个

6个

由于这种解析差异,mod_auth_openidc 可能会被欺骗以为 URL 是“安全的”(例如,指向正确的域),而浏览器将遵循重定向到非预期主机。此行为可以在端点上演示,例如/oauth2/callback,将参数注销设置为https://evil.destination.tld\@host.tld/:此参数成功通过所有验证步骤,并将用户重定向到https://evil.destination.tld这不是预期的行为,攻击者可能会滥用它来执行高级网络钓鱼攻击,利用受害者对运行 mod_auth_openidc 的域的信任。

修补

由于迁移到符合 WHATWG 的 URL 解析器需要进行重大更改,mod_auth_openidc 的维护者决定添加一个特殊情况,用斜杠 ( 69cb206 ) 替换任何反斜杠: 

差异pool, redirect_to_url, OIDC_MAX_URL_LENGTH); + +    // replace potentially harmful backslashes with forward slashes +    for (i = 0; i < strlen(url); i++) +        if (url[i] == '\\') +            url[i] = '/';       if (apr_uri_parse(r->pool, url, &uri) != APR_SUCCESS) {         *err_str = apr_pstrdup(r->pool, "Malformed URL");" data-lang="text/x-diff" style="box-sizing: border-box;">1个

2个

3个

4个

5个

6个

7

8个

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

我的解析器中有什么?

我查看了每个生态系统中最常见的生态系统,并根据它们是遵循 WHATWG 还是其中一个 RFC(由下表中的 RFC 3986 简化)对它们进行分类。请记住,即使他们声称遵循这些标准,他们的实现也可能略有不同,内置函数可以使用不同的解析器。

解析器声称要遵循……HTTP://A.TLD\@B.TLD
PHPcURL
RFC 3986(添加)b.tld
PHPparse_url
RFC 3986,但不完全b.tld
节点JSurl.parse
工作组a.tld
爪哇java.net.URL
RFC 3986b.tld
net/url
RFC 3986无效的用户信息
红宝石uri
RFC 3986例外
蟒蛇3urllib
RFC 3986a.tld\@b.tld 
蟒蛇3urllib3/requestsRFC 3986a.tld 

我对其中一些结果感到惊讶:

  • NodeJS 选择符合 WHATWG 以与浏览器兼容,如果开发人员想要“旧”行为,请参考其Legacy API ;

  • Ruby 和 Go 不接受二义性数据;他们反而提出了一个错误; 

  • Pythonurllib脱颖而出urllib3。 

这种风险在微服务架构中更为明显,不同的语言可以交换数据或放置在彼此的前面(例如,在 Python 后端之前的 Go 反向代理)。彻底验证数据并不总是有帮助 — 毕竟,它们都是“有效”的 URL。

比较 URL 解析器

让我们尝试使用差异测试重新发现这个怪癖,即使这种方法是有偏见的,因为我们已经知道我们正在比较两个不同的规范。这个想法是我们将生成随机测试用例并使用我们的两个解析器解析这些数据: 

  1. libapr,由 mod_auth_openidc 使用;

  2. 一个遵循 WHATWG 来复制网络浏览器的行为。例如,Python 包 whatwg-url 避免了以引入新怪癖为代价来连接其庞大代码库的这个组件的麻烦。

如果两个库对相同输入的输出不同,我们将面临解析差异。唯一的缺点是这可能导致结果并不总是与安全相关,并且可能需要逐步实施精确的启发式方法以减轻分类步骤的负担。

我决定使用 GitLab 的pythonfuzz来简化我们测试工具的创建。在这种情况下,覆盖率指导不是很有用,两个字节的简单 for 循环就足够了。 

测试解析差异错误在现代架构中很重要,因为它们通常涉及针对相同规范的多个解析器。例如,反向代理可以根据传入请求做出决定,但它背后的应用程序可能会以不同的方式理解它——Joern Schneeweisz 记录了类似错误对 GitLab 影响的一个很好的例子(“如何利用解析器差异” ).

正如您可能已经预料到的那样,libapr 是一个 C 库,而 whatwg-url 是用 Python 编写的:我们需要使用 CFFI 在测试工具中连接这两个库。apr_uri_parse我们生成了使用所需的正确结构bindgen,然后添加了简单的启发式方法来检测任何与安全相关的差异,并在出现这种情况时引发异常。 

例如,该团队仅在预期域和非预期域之间插入随机有效负载,如果 libapr 提取了正确的域但 whatwg-url 提取了错误 的域,则会引发异常:

Python1个

2个

3个

4个

5个

6个

7

8个

9

10

11

12

13

14

15

16

运行此线束几秒钟会发现与我们在本文第一部分中执行的序列相同的序列。

1个

2个

3个

4个

5个

6个

7

8个

9

10

这绝对是一个过度设计的用于解析差异的模糊测试示例,但它仍然足够简单,可以在开发或安全研究期间在几分钟内应用。

时间线

日期行动
2021-07-22我们向 mod_auth_openidc 的维护者报告了两个错误。
2021-07-22供应商承认存在漏洞。
2021-07-22mod_auth_openidc 2.4.9 发布,GitHub 为该问题分配了 CVE-2021-32786。

在本文中,我展示了一个非常常见且易于在应用程序中识别的解析差异错误的示例。此外,我查看了常用的 URL 解析器库以及此类错误如何影响它们。我了解到拒绝模棱两可的输入比试图错误地解析它更安全。

投稿内容不要求原创性,所指观点属原作者所有!

本文链接:http://www.ziti66.com/net/html/173.html

本文标签:博客  Apache    

微信公众号:升级中

<< 上一篇下一篇 >>
为祖国加油
疫情防护,人人有责。祖国加油...
为祖国加油
疫情防护,人人有责。祖国加油...

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

搜索

网站分类

Tags列表

最新留言

++发现更多精彩++

    祖国加油!!!

Copyright ziti66.com on 2022. Some Rights Reserved.