AWS - Nitro Enum

支持 HackTricks

基本信息

AWS Nitro 是一套 创新技术,构成了 AWS EC2 实例的基础平台。由亚马逊推出以 增强安全性、性能和可靠性,Nitro 利用定制的 硬件组件和轻量级虚拟机监控程序。它将传统虚拟化功能抽象到专用硬件和软件上,最小化攻击面并提高资源效率。通过卸载虚拟化功能,Nitro 使 EC2 实例能够提供 接近裸金属的性能,这对资源密集型应用特别有利。此外,Nitro 安全芯片专门确保 硬件和固件的安全性,进一步巩固其强大的架构。

Nitro Enclaves

AWS Nitro Enclaves 提供一个安全的、隔离的计算环境,专门设计用于处理高度敏感的数据。利用 AWS Nitro 系统,这些隔离区确保强大的 隔离和安全性,非常适合 处理机密信息,如个人身份信息或财务记录。它们具有极简的环境,显著降低数据暴露的风险。此外,Nitro Enclaves 支持加密证明,允许用户验证只有授权代码在运行,这对维护严格的合规性和数据保护标准至关重要。

Nitro Enclave 镜像是 在 EC2 实例内部运行 的,您无法从 AWS 网络控制台查看 EC2 实例是否在运行 Nitro Enclave 镜像。

Nitro Enclave CLI 安装

按照 文档中的所有说明。然而,以下是最重要的说明:

# Install tools
sudo amazon-linux-extras install aws-nitro-enclaves-cli -y
sudo yum install aws-nitro-enclaves-cli-devel -y

# Config perms
sudo usermod -aG ne $USER
sudo usermod -aG docker $USER

# Check installation
nitro-cli --version

# Start and enable the Nitro Enclaves allocator service.
sudo systemctl start nitro-enclaves-allocator.service && sudo systemctl enable nitro-enclaves-allocator.service

Nitro Enclave Images

您可以在 Nitro Enclave 中运行的镜像基于 docker 镜像,因此您可以从 docker 镜像创建您的 Nitro Enclave 镜像,例如:

# You need to have the docker image accesible in your running local registry
# Or indicate the full docker image URL to access the image
nitro-cli build-enclave --docker-uri <docker-img>:<tag> --output-file nitro-img.eif

如您所见,Nitro Enclave 镜像使用扩展名 eif(Enclave Image File)。

输出将类似于:

Using the locally available Docker image...
Enclave Image successfully created.
{
"Measurements": {
"HashAlgorithm": "Sha384 { ... }",
"PCR0": "e199261541a944a93129a52a8909d29435dd89e31299b59c371158fc9ab3017d9c450b0a580a487e330b4ac691943284",
"PCR1": "bcdf05fefccaa8e55bf2c8d6dee9e79bbff31e34bf28a99aa19e6b29c37ee80b214a414b7607236edf26fcb78654e63f",
"PCR2": "2e1fca1dbb84622ec141557dfa971b4f8ea2127031b264136a20278c43d1bba6c75fea286cd4de9f00450b6a8db0e6d3"
}
}

运行映像

根据 文档,为了运行一个 enclave 映像,您需要分配 至少是 eif 文件大小的 4 倍的内存。可以在文件中配置要分配给它的默认资源。

/etc/nitro_enclaves/allocator.yaml

请始终记住,您还需要为父 EC2 实例保留一些资源

在知道要分配给映像的资源并且甚至修改了配置文件后,可以使用以下命令运行一个 enclave 映像:

# Restart the service so the new default values apply
sudo systemctl start nitro-enclaves-allocator.service && sudo systemctl enable nitro-enclaves-allocator.service

# Indicate the CPUs and memory to give
nitro-cli run-enclave --cpu-count 2 --memory 3072 --eif-path hello.eif --debug-mode --enclave-cid 16

枚举隔离区

如果您攻陷了一个 EC2 主机,可以使用以下命令获取正在运行的隔离区映像列表:

nitro-cli describe-enclaves

不可能在运行的 enclave 镜像内获取 shell,因为这正是 enclave 的主要目的。然而,如果您使用参数 --debug-mode,则可以通过以下方式获取其 stdout

ENCLAVE_ID=$(nitro-cli describe-enclaves | jq -r ".[0].EnclaveID")
nitro-cli console --enclave-id ${ENCLAVE_ID}

终止隔离区

如果攻击者通过默认方式攻陷一个 EC2 实例,他将无法在其中获得 shell,但他将能够通过以下方式终止它们

nitro-cli terminate-enclave --enclave-id ${ENCLAVE_ID}

Vsocks

与运行图像的 enclave 进行通信的唯一方式是使用 vsocks

虚拟套接字 (vsock) 是 Linux 中的一种套接字系列,专门设计用于促进虚拟机 (VMs) 与其 hypervisors 之间,或虚拟机 之间通信。Vsock 使得高效的 双向通信 成为可能,而无需依赖主机的网络堆栈。这使得虚拟机即使在没有网络配置的情况下也能进行通信,使用 32 位上下文 ID (CID) 和端口号 来识别和管理连接。vsock API 支持流和数据报套接字类型,类似于 TCP 和 UDP,为虚拟环境中的用户级应用程序提供了一个多功能工具。

因此,vsock 地址看起来像这样:<CID>:<Port>

要找到运行图像的 CIDs,您可以执行以下命令并获取 EnclaveCID

nitro-cli describe-enclaves

[
{
"EnclaveName": "secure-channel-example",
"EnclaveID": "i-0bc274f83ade02a62-enc18ef3d09c886748",
"ProcessID": 10131,
    "EnclaveCID": 16,
    "NumberOfCPUs": 2,
"CPUIDs": [
1,
3
],
"MemoryMiB": 1024,
"State": "RUNNING",
"Flags": "DEBUG_MODE",
"Measurements": {
"HashAlgorithm": "Sha384 { ... }",
"PCR0": "e199261541a944a93129a52a8909d29435dd89e31299b59c371158fc9ab3017d9c450b0a580a487e330b4ac691943284",
"PCR1": "bcdf05fefccaa8e55bf2c8d6dee9e79bbff31e34bf28a99aa19e6b29c37ee80b214a414b7607236edf26fcb78654e63f",
"PCR2": "2e1fca1dbb84622ec141557dfa971b4f8ea2127031b264136a20278c43d1bba6c75fea286cd4de9f00450b6a8db0e6d3"
}
}
]

请注意,从主机上无法知道 CID 是否暴露任何端口!除非使用一些 vsock 端口扫描器,如 https://github.com/carlospolop/Vsock-scanner

Vsock 服务器/监听器

这里有几个示例:

简单的 Python 监听器

```python #!/usr/bin/env python3

From

https://medium.com/@F.DL/understanding-vsock-684016cf0eb0

import socket

CID = socket.VMADDR_CID_HOST PORT = 9999

s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) s.bind((CID, PORT)) s.listen() (conn, (remote_cid, remote_port)) = s.accept()

print(f"Connection opened by cid={remote_cid} port={remote_port}")

while True: buf = conn.recv(64) if not buf: break

print(f"Received bytes: {buf}")

</details>
```bash
# Using socat
socat VSOCK-LISTEN:<port>,fork EXEC:"echo Hello from server!"

Vsock 客户端

示例:

Last updated