On-PremisesIaCTHM
Introduction
在这个房间里,您将了解本地基础设施即代码 (IaC) 部署。虽然这在基于云的世界中似乎已经过时,但进行本地部署而不是云部署确实有一些很好的理由,尤其是在受到严格监管的行业中。
先决条件
- 基本的进攻性安全技术和技能
- IaC 简介
学习目标
- 了解本地 IaC 的原因
- 了解本地 IaC 的优点和缺点
- 理解并知道如何使用 Vagrant
- 理解并知道如何使用 Ansible
- 了解 Ansible 和 Vagrant 的安全问题
Why On-Premises
本地 IaC 是指使用基础设施即代码在内部网络上部署系统和服务。本质上,所有将托管系统和服务的基础设施都可以在组织的数据中心本地找到。
采用本地 IaC 的原因
在当今世界,我们拥有多家云提供商,使用本地 IaC 似乎违反直觉。但是,在某些情况下,本地 IaC 更适合您的需求。最大的好处是它可以让您更好地控制整个 IaC 管道。让我们使用另一个流行的例子,GitHub 与自托管 GitLab,作为案例研究。
GitHub 和 GitLab 是两个流行的源代码管理平台,由流行的版本控制软件 Git 支持,被组织广泛使用。然而,在两者之间做出选择往往成为一个控制问题。如果您使用 GitLab 自托管解决方案,则可以完全控制整个堆栈。您最终不仅要负责 GitLab 平台的管理,还要负责底层基础设施的管理。虽然这需要做更多的工作,但它也允许您显著地锁定访问权限。例如,您只能允许通过 VPN 访问您的 GitLab 实例,这意味着它不会向所有人公开。
相反,使用 GitHub 意味着你正在转移部分安全责任。重要的是要记住,GitHub 是一种软件即服务 (SaaS) 解决方案。这意味着,如果你使用 GitHub,你就无法控制托管源代码的基础设施。相反,你正在将基础设施管理的安全责任转移给 GitHub,后者成为你组织的第三方。虽然这些组织非常重视安全措施,但他们的安全性并非没有缺陷,这可能会使你组织的源代码暴露在外。每当你使用第三方时,你都必须接受这种风险。
从这个案例研究中推断,您应该明白为什么一些组织仍然喜欢本地 IaC,尤其是在监管严格的行业,例如金融或政府部门。虽然本地 IaC 肯定需要付出更多努力,但它为组织提供了对其 IaC 管道的完全控制和定制。
优点和缺点
为了总结为什么要使用内部部署 IaC 并确定它是否是针对您的特定问题的正确解决方案,让我们来看看它的优点和缺点。
本地 IaC 具有以下优势:
- 允许完全控制 IaC 管道
- IaC 管道可以根据您的具体需求进行完全定制
- 由于 IaC 管道不托管第三方系统上的资源,因此数据保护和监管更容易执行
- 虽然基础设施的初始投资成本很高,但托管 IaC 管道无需每月付费
- 小型部署通常更容易
本地 IaC 有以下缺点:
- 由于您托管自己的 IaC 管道,因此您对其安全性和配置负全部责任
- 部署的可扩展性不如基于云的 IaC 灵活,这可能导致部署资源不足或资源投资过度
- 本地 IaC 的初始投资明显高于基于云的 IaC
现在您了解了为什么可以使用本地 IaC,让我们深入了解一些可用于创建您自己的本地 IaC 的工具。虽然可以使用许多不同的工具和软件,但在这个房间里,我们将重点关注 Vagrant 和 Ansible 作为示例。
Vagrant Basics
在我们的本地 IaC 之旅中,我们将从 Vagrant 开始。Vagrant 是一种软件解决方案,可用于构建和维护可移植的虚拟软件开发环境。本质上,Vagrant 可用于从 IaC 管道创建资源。您可以将 Vagrant 视为 Docker 的老大哥。在 Vagrant 的上下文中,Docker 将被视为提供者,这意味着 Vagrant 不仅可用于部署 Docker 实例,还可用于部署托管它们的实际服务器。
术语
在深入使用 Vagrant 之前,让我们先了解一些术语。
| Term | Definition |
|---|---|
| Provider | Vagrant 提供程序是一种虚拟化技术,将用于配置 IaC 部署。Vagrant 可以使用不同的提供程序,例如 Docker、VirtualBox、VMware 甚至 AWS 进行基于云的部署。 |
| Provision | Provision 是指使用 Vagrant 执行操作。这可以是添加新文件或运行脚本来配置使用 Vagrant 创建的主机等操作。 |
| Configure | 配置用于使用 Vagrant 执行配置更改。可以通过向主机添加网络接口或更改其主机名来更改配置。 |
| Variable | 变量存储了一些将在 Vagrant 部署脚本中使用的值。 |
| Box | Box 指的是 Vagrant 将提供的图像。 |
| Vagrantfile | Vagrantfile 是 Vagrant 将读取和执行的配置文件。 |
Vagrant 示例
让我们看一个简单的 Vagrant 配置脚本。在我们的示例中,我们有以下文件夹结构:
. |
Vagrantfile 脚本包含以下代码:
Vagrant.configure("2") do |cfg| |
在此示例中,我们将配置两台服务器。这两台服务器都将从基础 Ubuntu Bionic x64 映像开始。与 Docker 一样,这些映像将从公共映像存储库中提取。但是,我们可以告诉 Vagrant 在哪里找到这些映像,这意味着它们也可以从私有映像存储库中提取。您可以看到两台服务器都配置了主机名以及每台主机将拥有多少个 CPU(1)和 RAM(4GB)。第一台服务器还将有两个附加了静态 IP 的网络接口。第二台服务器将上传一个名为“files.zip”的文件,并在主机上执行一个名为“script.sh”的脚本。
如果我们想要配置整个脚本,我们将使用命令“vagrant up”。这将按照 Vagrantfile 中指定的顺序配置两台服务器。我们还可以决定仅使用服务器名称配置其中一台服务器。例如,可以使用“vagrant up server”配置“testserver”。
如果您想使用 Vagrant 尝试 VirtualBox 配置,请查看此 repo。使用 VirtualBox 和 Vagrant,此 repo 将允许您创建自己的 Active Directory (AD) 网络,其中包含两个域控制器、一个服务器和一个工作站。阅读 Vagrantfile 以查看将在每个主机上执行哪些配置。
稍后我们将在此房间中创建我们自己的 IaC 部署脚本。
Ansible Basics
下一个要了解的 IaC 工具是 Ansible。与 Vagrant 一样,Ansible 是另一套允许您执行 IaC 的软件工具。Ansible 也是开源的,因此成为 IaC 管道和部署的热门选择。Ansible 和 Vagrant 之间的一个主要区别是 Ansible 对执行的步骤执行版本控制。这意味着 Ansible 与 Docker 类似,因为它只会对需要更新的步骤执行更新,而不是要求完全重新配置。
术语
在深入使用 Ansible 之前,让我们先复习一下一些术语。
| Term | Definition |
|---|---|
| Playbook | Ansible 剧本是一个 YAML 文件,包含一系列将要执行的步骤。 |
| Template | Ansible 允许创建模板文件。这些文件充当您的基础文件(如配置文件),其中包含 Ansible 变量的占位符,然后在运行时注入这些占位符以创建可部署到主机的最终文件。使用 Ansible 变量意味着您可以在单个位置更改变量的值,然后它将传播到配置中的所有占位符。 |
| Role | Ansible 允许创建一组模板和指令,这些模板和指令随后被称为角色。然后可以为要配置的主机分配一个或多个这些角色,为主机执行整个模板。这样,您就可以通过一行配置重复使用角色定义,其中指定必须在主机上配置该角色。 |
| Variable | 变量存储一些将在 Ansible 部署脚本中使用的值。Ansible 可以更进一步,使用变量文件,每个文件对相同变量具有不同的值,然后在运行时决定使用哪个变量文件。 |
Ansible 示例
Ansible 使用特定的文件夹和文件结构。结构中最重要的部分是剧本,这是一个 YAML 文件,它最终决定了将执行哪些命令进行配置。让我们看一下典型的文件夹结构:
. |
文件夹结构仅针对“common”角色进行了扩展。但是,同样的结构也适用于角色 2 至 3。
让我们深入了解一下每个文件的作用。让我们从 playbook.yml 文件开始:
--- |
我们可以看到,剧本指定应该将 common 和 role3 角色应用于将执行此 Ansible 剧本的所有主机。它还将使用 var.yml 文件覆盖角色内的任何默认变量。这允许我们仅在需要时更改默认变量,因为默认值仍将应用于所有其他变量。
要执行 common 角色,Ansible 将从 defaults/main.yml 文件中加载变量,并使用在 var.yml 文件中找到的任何新值覆盖这些变量。然后它将读取并执行 tasks/main.yml 文件。此文件看起来像这样:
--- |
Ansible 文件的第一部分将确定主机的操作系统发行版。然后,它将使用此信息来包含特定于操作系统的配置步骤。从我们的文件夹结构中可以看出,我们根据发行版是 Debian 还是 RedHat 制定了特定的配置步骤。剧本的第二部分将设置 root 用户的密码。在第三部分中,我们更新主机并安装一些软件包。但是,我们仅执行与操作系统发行版匹配的安装步骤。如果主机是 Debian,我们将执行 apt.yml 文件中指定的命令。如果主机是 RedHat,我们将执行 yum.yml 文件中指定的命令。这使我们的 Ansible 角色与操作系统发行版无关。最后,我们将执行 task1.yml 和 task2.yml 文件中包含的命令。这些命令可以是任何命令,例如向主机添加文件或进行配置更改。
这些信息需要掌握很多,但当你开始使用这些工具并了解脚本何时执行时,就会明白很多。
结合 Vagrant 和 Ansible
虽然你可以坚持使用一个 IaC 工具来处理整个管道,但结合使用工具往往是有益的。例如,Vagrant 可用于主机的主要部署,而 Ansible 可用于主机特定的配置。这样,你只在想要从头开始重新创建整个网络时才使用 Vagrant,但仍可以使用 Ansible 进行主机特定的配置更改,直到需要完全重建。Ansible 随后将在每个主机上本地运行以执行这些配置更改,而 Vagrant 将从虚拟机管理程序本身执行。为了做到这一点,你可以在 Vagrantfile 中添加以下内容,告诉 Vagrant 配置 Ansible 剧本:
config.vm.provision "ansible_local" do |ansible| |
如果您只想坚持使用单一 IaC 解决方案,那么还值得注意的是 Vagrant 和 Ansible 之间的一些差异:
| Feature/Aspect | Vagrant | Ansible |
|---|---|---|
| Configuration Language | Ruby (for Vagrantfiles). | YAML (for Playbooks). |
| Integration with Other Tools | 通常与 Chef、Puppet 或 Ansible 等配置工具一起使用。 | 可以独立使用或与其他 CI/CD 工具集成。 |
| Complexity | 建立开发环境相对简单。 | 更大的基础设施和更高级的配置具有更高的复杂性。 |
| Scalability | 更适合小规模、单独的开发环境。 | 高度可扩展,适合管理复杂的多层应用程序。 |
| Execution Model | 具有顺序执行步骤的程序化风格。 | 声明性模型,描述系统所需的状态。 |
现在我们已经了解了 Vagrant 和 Ansible,让我们利用这些知识构建我们自己的 IaC 管道!
Building an On-Prem IaC Workflow
Vagrantfile
让我们先来看看 Vagrantfile:
Vagrantfile
Vagrant.configure("2") do |config| |
在此 Vagrantfile 中,我们可以看到将配置两台机器。
DB 服务器
将配置的第一台机器是 dbserver。通过代码行,我们可以看到该机器将被添加到本地网络并接收 IP 172.20.128.3。我们还可以看到配置目录将作为共享挂载。最后,使用 Docker 作为提供程序,将启动 mysql 映像,并将 mysql 密码配置为 mysecretpasswd。
Web 服务器
将配置的第二台机器是 webserver。与 dbserver 机器类似,它将连接到网络并使用 Docker 作为其提供程序。但是,有一些细微的差别。首先,webserver 将公开 SSH。由于我们使用 Docker,我们必须更改一些默认的 Vagrant 配置以允许 Vagrant 通过 SSH 连接。这包括更改将用于连接的用户名和私钥。其次,我们可以通过查看以下行看到 Ansible playbook 将在容器上执行:
cfg.vm.provision "shell", inline: "ansible-playbook /vagrant/provision/web-playbook.yml" |
让我们看一下这个 Ansible 剧本会做什么。
Ansible 剧本
让我们首先查看“web-playbook.yml”文件:
- hosts: localhost |
这是一个简单的 Ansible 脚本,指示将在主机上配置 webapp 角色。
为了更好地理解 webapp 角色将包含哪些内容,我们可以先查看 ~/iac/provision/roles/webapp/tasks/main.yaml 文件:
- include_tasks: "db-setup.yml" |
这向我们展示了 Ansible 配置将有两个主要部分。此时,还值得查看 ~/iac/provision/roles/webapp/defaults/main.yml 文件中的默认值:
db_name: BucketList |
我们稍后会回顾这些变量,但请记住它们。
DB 设置
让我们看一下 db-setup.yml 文件:
- name: Create temp folder for SQL scripts |
从脚本中,我们可以看到将执行 7 项任务。阅读这些任务,我们可以看到将创建一个临时文件夹,SQL 脚本将被推送到该文件夹,然后针对数据库主机执行。
让我们看看 Ansible 如何从之前注入这些变量。看一下“Create DB”任务的 shell 命令:
shell: mysql -u {{ db_user }} -p{{ db_password }} -h {{ db_host }} < /tmp/sql/createdb.sql |
如您所见,将使用默认文件的值或覆盖的值(如果存在)注入 db_user、db_password 和 db_host 这三个变量。
Ansible 允许我们更进一步。让我们看一下实际的 createdb.sql 文件:
drop DATABASE IF EXISTS {{ db_name }}; |
我们可以看到,这些变量甚至被注入到将要使用的文件模板中。这使我们能够从单个集中位置控制将要使用的变量。当我们更改将用于连接数据库的用户或密码时,我们可以在一个位置进行更改,并且它将传播到角色的所有配置步骤中。
Web 设置
最后,让我们看一下 app-setup.yml 文件:
- name: Copy web application files |
此文件只有两个任务。第一个任务复制 Web 应用程序所需的工件,第二个任务复制 Web 应用程序文件作为模板。执行模板复制是为了确保变量(例如数据库连接字符串)也注入到脚本中。
我们不会深入研究将用于配置的其余文件,但是,建议您查看这些文件以更好地了解我们到底在配置什么。
运行 IaC 管道
现在我们已经了解了我们的管道,是时候启动它了!让我们使用“iac”目录中的“vagrant up”启动我们的管道和配置。管道需要一段时间才能启动,但请注意正在发生的事情。
注意:虽然在 Ansible 配置步骤运行时,您可能会在终端上看到一些红色,但只要这些行仅表示警告而不是错误,配置就会按预期完成。
一旦我们的管道配置了机器,我们就可以使用 docker ps 命令验证它们是否正在运行:
终端
ubuntu@tryhackme:~$ docker ps |
如果正在运行,我们可以使用以下命令启动我们的 Web 应用程序:
vagrant docker-exec -it webserver -- python3 /app/app.py |
加载后,您可以使用目标机器的浏览器导航到 Web 应用程序(http://172.20.128.2/):

恭喜!您已经执行了第一个 IaC 管道!
Security Concerns in On-Prem IaC
使用 IaC 时,无论部署是在本地还是基于云,都需要考虑一些安全问题。如果您正在寻找安全最佳实践,来自 OWASP 的这些 等备忘单是一个不错的起点。甚至 NIST 等框架也提供了有关如何保护 IaC 的信息。
但是,在这个房间里,我们将更多地关注 IaC 的实际安全性以及现实世界中常见的错误。这不是需要考虑的事项的完整列表,但它是一个很好的开始,无论是保护您的 IaC 管道免受现实世界的攻击,还是在红队交战中攻击 IaC 管道时要注意什么。总共有四个主要元素需要考虑。
依赖项
IaC 管道中需要考虑安全性的第一个元素是依赖项。与软件管道类似,我们的 IaC 管道将具有依赖项。在 IaC 管道中,最大的依赖项是用于构建基础架构的基础映像。如果这些依赖项存在任何安全问题,这些问题将通过管道传播。
一个常见的例子是映像的操作系统版本中的漏洞。如果映像本身未更新,则部署的主机将存在此漏洞。因此,确保对 IaC 管道中使用的资源进行充分的依赖管理非常重要。每当您使用第三方软件或系统时,都可能带来依赖风险。有关依赖项管理和潜在漏洞的更多信息,请查看 依赖项管理 房间。
默认值
IaC 管道中常见的安全漏洞是未更改默认值。当主机和服务最初通过 IaC 管道配置时,使用默认凭据或连接字符串配置这些系统是非常常见的。归根结底,这是有意为之,因为管道中的工具使用这些默认凭据来配置主机。因此,安全配置错误不是默认凭据,而是没有在 IaC 部署的最后一步删除或更改它们。
默认凭据的一个示例是 Vagrant 用于配置 Windows 主机的凭据。Vagrant 提供的 Windows 映像通常具有内置的默认帐户“vagrant”,其密码为“vagrant”。这是为了让虚拟机管理程序上运行的 Vagrant 脚本连接到主机进行文件传输和脚本执行。如果在部署结束时未删除此默认用户,威胁行为者可以使用它来连接并完全控制通过 IaC 管道部署的所有主机。
默认值的另一个问题是部署在主机上的服务。例如,假设我们的 IaC 管道用于部署 CI/CD 管道,因此将在其中一个主机上安装 Jenkins 角色。通常使用 Jenkins 的默认凭据安装此角色,即“jenkins:jenkins”。在这种情况下,我们无法在 IaC 部署的最后阶段真正更改这些凭据,但希望使用 CI/CD 基础架构的软件工程师能够更改凭据。在某些情况下,不会执行此操作,这会导致部署中的基础架构不安全。在其他情况下,即使软件要求用户在第一次登录事件时更改密码,由于从未使用过该软件,默认凭据仍然存在,从而允许威胁行为者破坏服务及其潜在的底层基础架构。
因此,在构建 IaC 管道时,重要的是要注意使用默认值的情况以及如果不更改默认值可能会带来风险的情况。然后,应将这些默认值的更改作为 IaC 管道中的最后步骤嵌入,或者在需要时在部署完成后手动执行。
强化不足
IaC 管道中经常出错的第三个元素是强化不足。IaC 的主要目标是快速部署基础设施。这通常意味着正在部署的基础设施尚未完全强化。强化不是自动发生的事情;它必须经过规划。因此,重要的是确保强化步骤嵌入 IaC 管道中或在部署后手动执行。
强化不足的一个常见例子是 IaC 管道用于部署的服务。例如,当 Vagrant 进行 Windows 主机的配置时,它通过 WinRM 连接进行此操作。碰巧的是,WinRM 是威胁行为者通常用于横向移动的服务。因此,重要的是,如果不再需要该服务,红色表示部署后正常运行,应将其禁用。在规划 IaC 管道时,不仅要包括一般的强化步骤,还要包括关闭管道本身使用的服务的那些步骤。
远程代码执行作为一项功能
最后一个要考虑的因素是要记住,最终,IaC 管道只不过是远程代码执行作为一项功能。本质上,管道正在有效地执行允许创建新基础设施并将其集成到现有网络中的代码。虽然这在合适的人手中非常强大,但也可能极其有害。
考虑到这一点,重要的是将安全性应用于 IaC 管道以防止未经授权的访问。这可以通过两项主要的安全措施来实现,即秘密管理和遵循最小特权原则。
通过秘密管理,我们可以确保敏感信息(例如最终凭证或密钥)得到安全存储,并且不能简单地从 IaC 源代码中读取以破坏部署。
遵循最小特权原则可确保仅向需要 IaC 管道的用户和服务提供访问权限。这包括实施 CI/CD 和构建安全 房间中教授的任何管道中都应有的相同安全控制。如果威胁行为者能够破坏管道,他们通常也能够破坏已部署的基础设施。
这些是保护您的 IaC 管道免受现实世界威胁所需的一些关键安全考虑因素。现在您知道要寻找什么,让我们利用这些知识来攻击易受攻击的 IaC 管道!
Attacking On-Prem IaC
ssh上到所给的机器上,查看Vagrantfile,能够看到开了一台数据库和一个web实例
Vagrant.configure("2") do |config| |
做一个代理隧道
┌──(mikannse㉿kali)-[~] |
浏览器访问172.20.128.2,在上面的sign in 功能下有个”Test DB”,抓包发现存在命令执行,进行反弹shell,拿到web server的shell
POST /api/testDB |
在这个实例的根目录有一个vagrant,在keys目录下找到一个私钥,尝试了一下是root的ssh私钥
┌──(mikannse㉿kali)-[~] |
在/tmp/datacopy中找到第三个flag
发现还有一个.ssh目录,并且所给的entry还有一个ubuntu用户
root@e485755d7773:/tmp/datacopy# ls -a |
Conclusion
在这个房间里,我们了解了本地 IaC。让我们回顾一下:
- 如今仍有理由在本地而不是云端执行 IaC
- Vagrant 和 Ansible 是两套可用于 IaC 管道的软件
- 有了 IaC 管道,我们面临更大的安全隐患,因为 IaC 管道本身就是远程代码执行,威胁行为者可以利用它来创建新的机器
- 虽然 IaC 管道为基础设施的部署提供了自动化,但我们仍然负责手动或通过使用自动化来强化已部署的基础设施
现在您已经了解了内部部署 IaC,现在是时候了解一下基于云的 IaC 了!
