# 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)