KICS 镜像被投毒后,团队现在该查什么?先按 digest 追一遍 CI 历史
KICS 镜像被投毒后,团队现在该查什么?先按 digest 追一遍 CI 历史
Article type: tutorial
Voice: operator
如果你的 CI 过去两天用过 checkmarx/kics,现在最该做的不是先重跑扫描,也不是先看 Docker Hub 页面,而是先按 digest 回查这段时间到底拉到了哪一个镜像,再决定要不要做凭证轮换和缓存清理。
短答案是:这次风险点不在 Docker 基础设施被攻破,而在攻击者拿到了合法发布凭证,用正常发布路径把恶意镜像推到了常用 tag 上。只要你的流水线在暴露窗口里拉过这些 tag,且扫描时能碰到云凭证、Terraform 变量、Kubernetes 配置或内部拓扑信息,就应该把它当成一次潜在泄露事件处理。
先澄清一个名字:本文说的 KICS,指的是 Checkmarx 开源的 IaC 扫描器 checkmarx/kics,用来扫 Terraform、CloudFormation、Kubernetes 等配置,不是某个 Ghost 站内旧文里泛指的“容器安全工具”。这次要追的也不是版本号表面有没有变化,而是 tag 在暴露窗口里有没有指向过恶意 digest。
复现摘要(2026-04-24,本机 Docker CLI)
docker buildx imagetools inspect checkmarx/kics:latest当前返回的 index digest 是sha256:3e5a268eb8adda2e5a483c9359ddfc4cd520ab856a7076dc0b1d8784a37e2602,已不同于 Docker 官方通报中的恶意latestdigestsha256:a0d9366f6f0166dcbf92fcdc98e1a03d2e6210e8d7e8573f74d50849130651a0。docker buildx imagetools inspect checkmarx/kics:v2.1.20当前也指向新的合法 digest,而不是通报里的恶意sha256:2588a44890263a8185bd5d9fadb6bc9220b60245dbcbc4da35e1b62a6f8c230d。- 直接检查恶意 digest:
docker buildx imagetools inspect checkmarx/kics@sha256:2588a44890263a8185bd5d9fadb6bc9220b60245dbcbc4da35e1b62a6f8c230d现在返回not found。这说明仓库当前状态已经被修复,但不能反推你当时没拉到过恶意镜像。
适合谁先做这轮排查
适合谁
- 用
checkmarx/kics:latest、v2.1.20、alpine、debian这类 tag 跑 IaC 扫描的团队 - 扫描任务会接触云凭证、Terraform state、Kubernetes 清单、内部域名或资源命名的团队
- 用共享 runner、pull-through cache、私有镜像代理的团队
不适合谁
- 你根本没用过
checkmarx/kics镜像,只是本地下载过仓库源码 - 你的扫描完全离线,且流程里没有任何凭证、配置、内部拓扑信息可被读到
- 你已经确认流水线全程用的是固定 digest,不依赖浮动 tag
这次事件真正改变了什么
Docker 在 2026-04-23 的官方通报里给出的核心信息,不是“某个漏洞导致 Docker Hub 被攻破”,而是:攻击者拿到合法 Checkmarx 发布凭证后,把恶意镜像推到了常用 tag 上。 受影响的 tag 包括:
latestv2.1.20v2.1.20-debianalpinedebian- 新增的
v2.1.21 - 新增的
v2.1.21-debian
更麻烦的是,这些镜像不是简单“跑不起来”的假货。按 Docker 通报,恶意二进制保留了原本扫描能力,同时加了一条安静的外传路径,会把扫描输出加密后发往攻击者控制的地址 audit.checkmarx[.]cx,并带上 KICS-Telemetry/2.0 这个 User-Agent。
这也是为什么这篇文章不把角度写成“这次 KICS 事故值不值得关注”。如果你用 KICS 扫的正是 IaC 配置,那扫描结果本身就经常带着:
- 明文或半明文凭证线索
- 云资源命名规则
- 内网拓扑
- 集群命名空间
- 策略例外和安全基线偏差
换句话说,这不是“工具被换包”那么简单,而是你的防守视角要从“有没有误报”切到“扫描产物有没有被带走”。
在 TG Hubs 之前写过的 GitHub 如何用 eBPF 提前拦住高风险部署:一份更实用的借鉴清单 里,我们讨论的是发布前怎么多看一层运行时异常;这次 KICS 事件提醒的是另一层:只盯运行时不够,供应链入口的 tag 漂移本身也要被当成审计对象。
我更建议先做的,不是重跑,而是三步回查
第一步:先确认你当时拉到的是 tag,还是 digest
如果你在 CI 里写的是:
image: checkmarx/kics:latest
或者:
docker run --rm checkmarx/kics:v2.1.20 scan ...
那你现在首先要回答的问题不是“现在这个 tag 正不正常”,而是:在暴露窗口里,这个 tag 当时指向了什么 digest。
这也是本次原创核查里最关键的一点:我在本机执行 docker buildx imagetools inspect 时,今天看到的是修复后的 digest;而直接检查 Docker 通报里的恶意 digest 已经 not found。这说明事后看仓库当前状态,并不能替代事中查历史拉取记录。
你至少要补查这些地方:
- CI job 日志里真实拉取到的 digest
- runner 本地镜像缓存
- 私有代理仓库 / pull-through cache 的历史记录
- SBOM、镜像签名或构建元数据里记录的镜像来源
第二步:如果暴露窗口里确实拉过受影响 tag,把它当成“凭证接触过恶意执行体”处理
Docker 官方给出的动作非常明确:如果你的 CI 在暴露窗口里运行过 KICS,并且扫描时有凭证在作用域内,先轮换凭证,再补日志。
这一步别倒过来。原因很现实:
- 如果外传已经发生,晚几个小时补日志不会比先止血更重要
- 很多团队把云凭证、registry token、IaC backend token 一起塞进扫描作业里
- 同一个 runner 往往还会复用工作目录和缓存,影响面可能比单次 job 更大
优先处理对象通常包括:
- 云账号访问密钥
- Terraform backend 相关凭证
- Kubernetes service account / kubeconfig
- CI secret store 中被该作业读取过的 token
- 私有镜像仓库读写凭证
第三步:清缓存,不要只看“现在 pull 是好的”
这次事件特别容易让人误判的一点是:今天再 pull 一次,看到的是已经恢复后的合法镜像,于是误以为风险已经过去。
但 Docker 通报里已经明确提醒:干净的重新拉取,不会自动清掉你 runner、缓存代理、镜像镜像站里之前存过的恶意 digest。
所以最少要清掉:
- CI runner 本地镜像缓存
- 共享构建机上的层缓存
- pull-through registry 或企业镜像代理的缓存条目
- 内部构建脚本里写死的旧 digest 或导出归档
这和我们在 GitHub Agentic Workflows 安全架构公开后,团队最该补的不是模型能力,而是可控执行层 里强调的是同一件事:真正危险的不是一次异常本身,而是异常对象还能不能继续被自动化流程无声复用。
KICS 镜像投毒后的最小处置清单
如果你只要一份可以马上执行的清单,先按这个顺序做:
- 先列出过去 48 小时内所有用到
checkmarx/kics的 pipeline、runner、仓库和缓存节点 - 查 CI 日志或镜像代理日志,确认是否拉过
latest、v2.1.20、v2.1.20-debian、alpine、debian、v2.1.21、v2.1.21-debian - 如果拉取记录只有 tag、没有 digest,优先回查镜像缓存和 runner 本地历史
- 只要命中过 Docker 官方列出的恶意 digest 或暴露窗口内的受影响 tag,就先轮换该 job 接触过的凭证
- 查出站日志里是否有到
audit.checkmarx[.]cx的连接,或带KICS-Telemetry/2.0的流量 - 清掉 runner、缓存代理、镜像镜像站里的旧镜像层与索引
- 把后续 CI 改成固定 digest,而不是继续吃浮动 tag
- 给 IaC 扫描任务单独发短生命周期 token,不要复用开发者长期凭证
更适合长期改的,不是“换工具”,而是换默认发布策略
这次事件最容易把团队带偏的一种反应,是马上讨论“要不要换掉 KICS”或者“Trivy/KICS 谁更安全”。
这类问题当然可以讨论,但它不是眼下最先决定损失大小的变量。真正该立刻改掉的是三个默认动作:
1)别再把安全扫描镜像当成“用 latest 也没关系”的低风险组件
很多团队对业务镜像会 pin digest,对安全工具镜像却图省事直接用 latest。这次事件证明,安全工具本身就处在最容易接触敏感信息的位置,风险并不比业务 sidecar 低。
2)让扫描任务的凭证作用域更小
如果一次 KICS 扫描既能读 Terraform 仓库、又能访问 state backend、还能把结果上传回平台,那它天然就是高敏作业。更稳的做法是把读取、扫描、上传拆开,避免一个执行体拿走整套上下文。
3)把“异常 tag 漂移”接进日常审计
Docker 这次能较快定位问题,一个关键信号就是新 tag 没有对应上游 release,来源仓库也不对。这说明团队自己也应该补一层简单的对账:
- tag 发布时间是否异常
- 是否有对应 release / changelog / commit
- 镜像 provenance 是否来自预期仓库
- digest 是否突然整体漂移
在 Cloudflare 把内部 AI 工程栈公开后,团队真正该补的不是再换模型,而是把身份、路由、上下文和审查链路接成一个系统 里,我们说过工程团队需要把身份、路由和审查串起来;放到这次 KICS 事故上,同样成立:镜像发布、凭证作用域、缓存复用、出站流量,本来就该被当成一条链看。
什么团队最该现在补这道门槛,什么团队可以先不扩大战情
最该立刻升级处置的团队
- 使用浮动 tag 跑 KICS 的团队
- 扫描作业能接触生产或准生产凭证的团队
- 使用共享 runner / 缓存代理的团队
- 没有保存历史拉取 digest 的团队
可以先按较低等级处理的团队
- 全程固定 digest,且 digest 不命中通报列表
- 扫描环境完全隔离,没有敏感凭证与内部网络出口
- 有镜像代理审计、eBPF/网络日志和 runner 取证能力,能快速证明未发生命中与外传
这里的关键不是“你是不是大公司”,而是这条扫描流水线到底接触了多少敏感上下文。
FAQ
KICS 镜像现在恢复正常了,还需要排查历史 CI 吗?
需要。因为现在的 tag 已经恢复正常,不代表你在暴露窗口里没有拉到过恶意 digest。今天看到的“正常”只能说明仓库当前状态被修复了,不能替代历史追溯。
如果我们只用了 checkmarx/kics:latest,没有记录 digest,怎么办?
先查 runner 缓存、镜像代理日志、CI 原始 job 日志和构建元数据。没有 digest 记录时,最重要的是尽快确认暴露窗口内有没有拉取过受影响 tag,并按高风险路径先做凭证轮换。
这次应该先换掉 KICS,还是先修 CI 策略?
先修 CI 策略。是否换工具可以后评估,但眼前决定风险上限的是:有没有固定 digest、有没有缩小凭证作用域、有没有清缓存、有没有保留拉取历史。
如果我们用的是 Trivy,也要做同样的动作吗?
如果你的流程同样依赖浮动 tag、共享 runner 和高权限凭证,原则上要。Docker 这篇通报把 Trivy 和 KICS 放在同一攻击模式下讨论,提醒的是一类发布链问题,不只是单个项目名字。
KICS 镜像投毒这件事,对不碰云资源的小团队也重要吗?
重要,但优先级不同。如果你的扫描作业只读公开仓库、没有凭证、没有内部拓扑信息,风险面会小很多;但即便如此,也值得把浮动 tag 改成固定 digest,避免下次继续靠运气。
结论
这次 KICS 镜像投毒最值得记住的,不是“某个安全工具出事了”,而是:一旦安全扫描镜像本身能看到凭证和配置,它就该被当成高敏执行体管理。
所以 KICS 镜像被投毒后,团队现在最该做的不是等供应商给总结,也不是今天重新 pull 一次就安心,而是立刻按 digest 回查 CI 历史、轮换暴露凭证、清掉缓存,并把 checkmarx/kics 这类扫描镜像从浮动 tag 切到固定 digest。对这类供应链事件来说,真正拉开损失差距的,通常不是谁先看到新闻,而是谁先把历史拉取记录和凭证边界补齐。