# 章节12：DevOps



# Ansible

在这章节，我们将简要介绍在 DevOps 中所常涉及的应用以及这些应用可能带来的攻击面。通过对这些 DevOps 中涉及的应用进行利用与攻击，我们可以实现代码执行、横向移动、供应链污染、信息窃取等。DevOps 是一组结合了软件开发和 IT 运营的实践,，促进了一致性与自动化，适用于软件构建、系统变更、以及基础设施修改的管理。DevOps 使得传统基础设置和配置任务更加高效和线性。因为自动化的特性，以及能对基础设置的变更和部署，意味著 DevOps 中涉及的应用具有较高权限，对于攻击者来说是很高效的目标。

### **DevOps 简介**

在 DevOps 环境中可以使用各种工具，这些工具有助于**自动化部署**、**编排**、**版本控制**、**配置管理**、**测试和监控**。以下为分门别类的一些例子：

#### **版本控制系统**

##### **Git**

一种**分布式**版本控制系统，用于在软件开发过程中跟踪源代码的更改。**GitHub**、**GitLab** 和 **Bitbucket** 是流行的 Git 存储库托管平台。

##### **SVN**  


一个**集中式**版本控制系统，允许开发人员跟踪和管理项目中文件和目录的更改。它支持原子提交并使用“复制-修改-合并”模型，促进在单个代码库上的协作工作。

#### **工件存储库管理器**

##### **JFrog Artifactory**

一个通用的工件存储库管理器，完全支持使用任何语言或技术创建的软件包。Artifactory 提供端到端的工件自动跟踪，并具有用户友好的 UI。它与流行的 CI/CD 工具无缝集成并支持所有主要的包格式，有助于加快软件发布周期。

##### **Sonatype Nexus**

一个强大的存储库管理器，允许用户代理、收集和管理**依赖项**，同时促进软件开发团队之间的协作。Nexus 支持多种软件包格式，并与 CI/CD 管道顺利集成，有助于简化软件开发、部署和生命周期管理的过程。

#### **持续集成/持续部署 (CI/CD) 工具**

##### **Jenkins**

一种开源自动化服务器，可让开发人员可靠地构建、测试和部署他们的软件。

##### **GitLab CI/CD**

GitLab 平台的一部分，它提供了一个复杂的持续集成和部署系统。

#### **配置管理工具**

##### **Ansible**

一种开源软件置备、配置管理和应用程序部署工具。

##### **Puppet**

它提供了一种交付和操作软件的标准方式，无论它在何处运行。

##### **Chef**

一个强大的自动化平台，可将基础架构转换为代码。

#### **容器化和编排工具**

##### **Docker**

一个开源平台，用于在容器内自动部署、扩展和管理应用程序

##### **Kubernetes**

一个开源平台，旨在自动部署、扩展和操作应用程序容器。

#### **基础架构即代码 (IaC) 工具**

##### **Terraform**

由 HashiCorp 创建的开源 IaC 软件工具。 它使用户能够使用声明性配置语言来定义和提供数据中心基础设施。

##### **AWS CloudFormation**

为用户提供一种通用语言来描述和配置云环境中的所有基础设施资源。

#### **监控和记录工具**

##### **Grafana**

一个多平台开源分析和交互式可视化 Web 应用程序。

##### **ELK Stack**

用于搜索的 Elasticsearch，用于集中日志记录的 Logstash 和用于可视化的 Kibana。

##### **Splunk**

一种搜索、监控和分析机器生成的大数据的工具。

#### **测试和质量控制工具**

##### **Selenium**

一套用于**浏览器自动化**的工具。 它用于测试 Web 应用程序。

##### **SonarQube**

SonarSource 开发的开源平台，用于持续检查代码质量。

而在这一小节，我们讨论 **Ansible**。Ansible 是一款开源自动化工具或平台，我们可以将 Ansible 视为一个简单的自动化引擎，用于执行配置管理、应用程序部署、服务内编排和供应等 **IT 任务**。虽然 Ansible 可以在任何安装了 Python 的主机上运行，但它通常在类 Unix 系统上运行。Ansible 的显著优势之一是它通过连接到节点 (管理的主机) 并向它们推出称为**Ansible 模块的**特制化 **Python 脚本**来执行特定任务，例如搜集信息、配置设置、运行命令与应用等，Ansible 通过 SSH 执行这些模块，并在完成后**删除**它们，脚本的输出则会被返回给控制器。Ansible 管理的节点通常在**清单文件**中，这是一个通常位于 **/etc/ansible/hosts** 的文本文件，列出了要管理的节点，并可以选择在标签下分组。 节点可以是任何可以运行 Python 的设备：服务器、云实例、虚拟机，甚至是网络设备。

为了能与节点交互，Ansible 不需要在从属主机上安装代理之类的软件，也没有服务器、守护进程、数据库，而是使用 **SSH** 这个受控主机都会安装的服务。控制主机需要有**节点主机用户的密码**，或者节点主机存储有**控制主机 Ansible 账户的公钥**。在安全性方面，因为 Ansible 使用了SSH，所以意味着 Ansible 可以像 SSH 本身一样非常安全。 Ansible 使用一种称为 **YAML** 的语言，以 **Ansible Playbook** 的形式，以一种即使不精通特定脚本语言的 IT 人员也易于理解的方式定义自动化任务。 Playbook 具有高可读性，描述了用户想要远程系统执行的策略，或一般 IT 流程中的步骤。

### **枚举**

当我们攻陷了一台主机，我们首先需要对 Ansible 进行枚举，来确定该主机是否安装了 Ansible，以及是节点主机还是控制器主机，所有的节点主机信息等。在 Web01 上，执行以下枚举：

##### **检查 Ansible 是否安装**

通过查看 ansible 命令是否存在来判断当前主机是否安装了 ansible，如图所示，Web01 有安装 ansible。

```shell
which ansible
```

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/OABdMObo0URbhoYX-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/OABdMObo0URbhoYX-image.png)

##### **列举节点主机**

通过检查清单文件，即 /etc/ansible/hosts 来查看有哪些节点主机。

```shell
cat /etc/ansible/hosts
```

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/TjCQX0K9Jygmktbg-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/TjCQX0K9Jygmktbg-image.png)

我们看到，white-bird 域中的 **dev01** 是受控的节点主机，并且节点主机的受控用户是 **ansibleadm**。

##### **枚举存在的 Playbooks**

Ansible Playbooks 默认存放于 **/opt/playbooks**，查看该目录下是否有 Ansible Playbook。

```shell
ls -al /opt/playbooks
```

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/GPkMfb4pwf9IiWOp-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/GPkMfb4pwf9IiWOp-image.png)

我们能看到共有 5 个 Playbook。

### **利用**

如果攻陷了 Ansible 控制器，我们可以通过运行 Ad-hoc 命令或者 Playbooks 的形式对节点主机进行远程命令执行。前者是简单的 Shell 命令，后者是以脚本形式包含了一系列的任务被周期性地执行。

##### **对节点主机执行命令**

通过下述命令，我们可以对特定的节点主机执行命令。

```shell
ansible <节点主机> -a "<命令>"
```

因为只有 ansible 账户的公钥被存储于 ansibleadm 的 **authorized\_keys** 中，因此即便是 root 用户也不行，我们需要切换到 ansible 账户。此外，我们可以将主机更换为 **all** 以对所有节点执行该命令。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/IBqZP8IV4azSjYib-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/IBqZP8IV4azSjYib-image.png)

##### **以 root 权限执行命令**

在命令后面添加 **--become** 选项可以使得受控用户以 sudo 权限运行命令。

```shell
ansibnle <主机名> -a "<命令>" --become
```

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/IREl3MExiyVHLBbX-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/IREl3MExiyVHLBbX-image.png)

##### **执行 playbooks**

除了 Ad-hoc 命令外，还可以对节点主机运行 playbook，命令如下：

```shell
ansible-playbook <playbook 文件>
```

运行 **krb\_monitor.yml** Playbook，我们得到如下输出：

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/Sd5w1V7qUWYApuan-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/Sd5w1V7qUWYApuan-image.png)

该 Playbook 用于搜集 Linux 域主机上存在的 ccache 文件。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/4tDYsh1h4HqCiTg6-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/4tDYsh1h4HqCiTg6-image.png)

除了用这些原生命令执行脚本或命令外，我们还可以从**不当配置**上下手。如果我们对于 **Ansible 控制器**已经具有最高权限，那么我们自然可以轻松拿下所有节点主机，但如果我们尚未获得 **root** 或者 **ansible** 账户权限，又该怎么做呢？除了执行远程命令外，Playbooks 还可以储存加密后的节点主机凭证，当我们对 Ansible 控制器尚未具有 root 权限时，可以从可读的 Playbooks 中提取出加密后的凭证，尝试破解出节点主机的明文密码。

##### **包含明文密码的 Playbook**

如果控制器在节点主机上要模仿的用户并没有添加控制器账户的公钥，那么可以在 Playbook 中硬编码明文密码。例如查看 **krb\_monitor\_become.yml**，我们能看到该 Playbook 硬编码了 dev01 账户的明文密码。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/3wl1CruUENVD7SB3-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/3wl1CruUENVD7SB3-image.png)

##### **命令行中包含明文密码的 Playbook**

除了 Playbook 中可以硬编码明文密码，因为 Playbook 可以执行 Shell 命令，而一些 Shell 命令中同样可以包含明文密码，例如 mysql 命令。在 Playbook **message\_parser.yml** 中，我们根据代码可以知道这个 Playbook 的作用是以 alize 的身份向 chat.js 应用认证并拉取聊天记录。虽然 alice 的凭证我们已经知道了，留意 Playbook 中包含明文凭证的命令是很重要的。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/am2OhrqcO0mj1xZh-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/am2OhrqcO0mj1xZh-image.png)

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/68E1sQ1VsrRAc08U-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/68E1sQ1VsrRAc08U-image.png)

##### **可写的 Playbook**

我们发现 Playbook **account\_monitor.yml** 是全局可写的。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/GNGVpi0yxGEWFJBS-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/GNGVpi0yxGEWFJBS-image.png)

该 Playbook 以特权用户 ansibleadm 在 Dev01 上读取 **/etc/passwd** 文件的内容。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/is7VbPAEZRQYCt3S-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/is7VbPAEZRQYCt3S-image.png)

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/Gn1JhfQqT4Hn650k-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/Gn1JhfQqT4Hn650k-image.png)

任何用户可以修改这个 Playbook 所执行的 Shell 命令，不过要执行该 Playbook，需要 ansible 用户才行。因此，我们可以变更其中的 Shell 命令，然后等待下次该 Playbook 被执行。

##### **包含 Ansible Vault 字符串的 Playbook**

为了能以另外一个用户的身份在节点主机上执行 Playbook 中的任务，我们需要提供目标用户的凭证，但硬编码明文密码是很有安全隐患的。因此，Ansible 还支持在 Playbook 中以 **Vault 字符串**形式作为目标用户的凭证。其中，Vault 字符串是使用 **Vault 密码**加密后字符串，字符串可以是用户的明文密码。如 asroot.yml 为例，里面包含了这么一段 Vault 字符串。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/f2JhexkH0Z9LLrM2-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/f2JhexkH0Z9LLrM2-image.png)

把这段 Vault 字符串拷贝下来，从 **$ANSIBLE\_VAULT** 开始，如下图所示：

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/bnMMHXsYL1u1N51R-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/bnMMHXsYL1u1N51R-image.png)

使用 ansible2john 将其转换为可被 hashcat 破解的形式：

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/wlqY6XL1TaJKqEt3-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/wlqY6XL1TaJKqEt3-image.png)

将文件名以及冒号去掉，即从 **$ansible** 开始。使用 hashcat 的 16900 模式来字典破解：

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/76At0a1CO17NtM9N-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/76At0a1CO17NtM9N-image.png)

最后，使用命令 **cat ansible.txt | ansible-vault decrypt**，并输入破解得到的 vault 密码来获得明文凭证。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/PnwBWZTNssSp4w5M-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/PnwBWZTNssSp4w5M-image.png)

# 攻击供应链

### **存储库管理软件**

在企业环境中，我们常常遇见存储库管理软件，例如 **JFog Artifactory**, **Nexus Sonatype** 等。这些软件存储了企业内在用的软件包、项目文件、二进制文件等。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2022-11/scaled-1680-/ply5uYjSfnbmaJAa-image.png)](https://raven-medicine.com/uploads/images/gallery/2022-11/ply5uYjSfnbmaJAa-image.png)

[![image.png](https://raven-medicine.com/uploads/images/gallery/2022-11/scaled-1680-/FfEf8UreLH3mXAhO-image.png)](https://raven-medicine.com/uploads/images/gallery/2022-11/FfEf8UreLH3mXAhO-image.png)

Sonatype Nexus 或 JFrog Artifactory 等**工件存储库管理器软件**的沦陷会产生严重后果，并导致通常所说的**供应链攻击**或**供应链污染**。这是由于这些系统在软件开发和交付过程中发挥的关键作用。 原因如下：

**集中信任**：这些系统是组织内信任的中央来源。开发人员、CI/CD 系统和部署流程都从这些系统中提取工件，并期望这些工件是绝对可信的。如果攻击者可以破坏系统并将合法工件替换为恶意文件，恶意软件便会最终传播到多个系统。

**广泛影响**：根据组织的规模和工件存储库的使用范围，单个恶意文件最终可能会出现在大量系统上。这可能会导致终端系统遭到大规模破坏。

**持久性和版本控制**：因为工件存储库保留旧版本的工件，所以恶意文件在受损后很长时间内仍可供下载，除非及时发现并删除攻击者上传的恶意文件，否则系统可能会在工件存储库最初遭到破坏后很长时间内受到危害。

**自动化**：在 DevOps 环境中，新软件的构建和部署经常发生，而且通常是自动化的。这意味着攻击者分发的恶意文件可以快速分布到整个组织，并且很难追踪和删除工件的所有实例。

**访问机密**：通常，这些系统可以访问部署应用程序所需的机密，例如数据库凭证、API 密钥等。如果遭到沦陷，攻击者可以访问这些敏感信息。

由于这些原因，工件存储库的安全性至关重要。

### **匿名访问**

例如 Artifactory 和 Sonatype，是允许**匿名访问**的，即攻击者不需要凭证也可以访问存储库中的文件，甚至具有一定权限，例如更改文件或项目的属性。并且，如果存在更严重的配置失误，匿名状态下甚至可以部署文件，这种情况对于攻击者无疑是快捷的胜利。即便在匿名状态下没有任何写权限，我们应该枚举匿名访问的所有权限。除此之外，因为存储库管理软件同样可能存储了内部应用的源代码以及其他敏感文件，我们可以从中提取敏感信息，例如 **API Key**，**明文帐号密码**等。

### **供应链污染**

不管是**具有写权限的匿名访问**，还是获得了**存储库管理软件的有效凭证**，如果我们具备**写权限**，便可以**上传**、**替换**、**更改**文件。从攻击者的角度，将经常被下载或自动化安装的文件替换为植入了木马的程序，可以从而污染众多主机与用户。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/VVrT4t59ooxrbLLV-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/VVrT4t59ooxrbLLV-image.png)

如上图所示，该二进制文件已经被下载了 **73** 次了，说明无论是终端用户下载，还是服务器上的计划任务自动拉取文件并执行，只要该文件在供应链中被污染了，后果严重。但即便在授权的渗透或者红队行动中，对于此类攻击依旧要极为谨慎，因为要彻底清除污染十分麻烦。

### **案例：污染供应链实现在 Dev01 的提权**

能被污染的当然不止工件存储库管理软件。在 Dev01 的 **/opt** 目录下，我们发现了一个有意思的文本文件 **status\_report.txt**。查看其内容，看起来是一些用于搜集主机信息的命令的执行输出。根据文件权限，我们猜测是 root 用户在执行某个脚本后将输出保存在了这个文件里。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/6H5zX5qMUO1ZTmC7-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/6H5zX5qMUO1ZTmC7-image.png)

观察到这个文件在不断更改，意味着脚本可能在背景中以特定时间间隔执行，即有着类似于计划任务的活动在驱动着。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/E6v1Bcs3cxlDdz32-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/E6v1Bcs3cxlDdz32-image.png)

使用 pspy ([https://github.com/DominicBreuker/pspy](https://github.com/DominicBreuker/pspy)) 观察背景中的进程，我们发现了这些相关的内容：

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/1eo3a7X980jO2urq-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/1eo3a7X980jO2urq-image.png)

简单地说，有一个在 root 目录下的脚本 **status.sh** 在负责从仓库 [https://github.com/raven-medicine/Automation](https://github.com/raven-medicine/Automation) 克隆最新的项目到 /root/Automation 目录中，目录里的脚本 **cron.sh** 被执行。

回顾一下我们在课程早期的时候，在 **Web01** 的 **FTP 服务器**中发现了一个文本文件，包含了一对有效的 **Github 帐号**。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/e7FUjT5EZIxqcJYB-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/e7FUjT5EZIxqcJYB-image.png)

用该凭证得以成功登陆。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/MUc5dKaT3NDW5Rit-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/MUc5dKaT3NDW5Rit-image.png)

如我们所料，这个脚本被克隆到了 Dev01 的本地然后被 root 用户执行。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/3fVT6klaQPocnTch-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/3fVT6klaQPocnTch-image.png)

既然我们有了Github 凭证，那么可以更新该脚本，**插入恶意命令**，实现对这个简单供应链的污染。就 Dev01 上而言，因为是 **root** 用户执行的，如果我们尚未找到提权路径，我们可以用此方法来实现**本地特权提升**。

在拿到 root 权限后，观察一下自动化的脚本，跟我们猜想的一样。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/B0RZuLA1qkS3mmmF-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/B0RZuLA1qkS3mmmF-image.png)

# 第12章课后作业

### **练习**

1：了解一下 **DevOps 简介**中所提及到的这些软件。

2：复现针对 Ansible 的这些枚举与攻击方法。

3：查看网络安全方面的新闻，了解一下近几年的一些大规模供应链污染事件。

# 面试专题

1\. 谈一谈 Ansible 与 Terraform 的异同

2\. DevOps 对于攻击者/红队，可以有什么样的使用场景呢？

3\. 在使用供应链污染攻击的时候，有哪些因素与事情需要我们仔细考虑？

4\. 如果我们攻下了一台 Ansible 控制主机的服务器，但目前非 root 权限，我们有哪些可能的手段实现提权以及横向移动？