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 官方通报中的恶意 latest digest sha256: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:latestv2.1.20alpinedebian 这类 tag 跑 IaC 扫描的团队
  • 扫描任务会接触云凭证、Terraform state、Kubernetes 清单、内部域名或资源命名的团队
  • 用共享 runner、pull-through cache、私有镜像代理的团队

不适合谁

  • 你根本没用过 checkmarx/kics 镜像,只是本地下载过仓库源码
  • 你的扫描完全离线,且流程里没有任何凭证、配置、内部拓扑信息可被读到
  • 你已经确认流水线全程用的是固定 digest,不依赖浮动 tag

这次事件真正改变了什么

Docker 在 2026-04-23 的官方通报里给出的核心信息,不是“某个漏洞导致 Docker Hub 被攻破”,而是:攻击者拿到合法 Checkmarx 发布凭证后,把恶意镜像推到了常用 tag 上。 受影响的 tag 包括:

  • latest
  • v2.1.20
  • v2.1.20-debian
  • alpine
  • debian
  • 新增的 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 日志或镜像代理日志,确认是否拉过 latestv2.1.20v2.1.20-debianalpinedebianv2.1.21v2.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。对这类供应链事件来说,真正拉开损失差距的,通常不是谁先看到新闻,而是谁先把历史拉取记录和凭证边界补齐。

Read more

Hugging Face Docker Space 能跑 Arm64 吗?先查这 3 个兼容性卡点

Hugging Face Docker Space 能跑 Arm64 吗?先查这 3 个兼容性卡点 Article type: tutorial Voice: reviewer 如果你打算把一个 Hugging Face Docker Space 部署到 Apple Silicon、Jetson,或者后面准备迁到 Graviton 这类 Arm64 环境,最省时间的做法不是先把镜像拉下来硬跑一遍,而是先做 3 个检查:基础镜像有没有 arm64 manifest、依赖里有没有写死 x86_64 wheel、仓库里有没有把平台假设藏在 requirements 或启动脚本里。 这篇文章的短答案是:能不能跑,不取决于它是不是 Hugging Face Docker Space,

By One AI
GitHub 如何用 eBPF 提前拦住高风险部署:一份更实用的借鉴清单

GitHub 如何用 eBPF 提前拦住高风险部署:一份更实用的借鉴清单

GitHub 如何用 eBPF 提前拦住高风险部署:一份更实用的借鉴清单 先说结论 如果把这篇 GitHub 官方工程文只读成“又一个 eBPF 用例”,就低估了它的价值。GitHub 这次真正公开的,不是一个更酷的内核技巧,而是一种新的部署安全思路:把“会不会在事故里自断修复路径”这件事,从人工 review 清单,前移成部署时就能拦截的运行时闸门。 我的判断很明确:只有那些已经在管理有状态主机、滚动发布、脚本化运维、而且最怕“出事时修不了自己”的团队,才值得借鉴 GitHub 这套 eBPF 做法;如果你现在还是普通 stateless Web 服务、发布链路很短、依赖边界也不复杂,先把依赖盘点、回滚资产、预发验证补齐,收益通常比直接上 eBPF 更大。 先给一个可提取的短答案: * 适合借鉴这套

By One AI
OpenAI Agents SDK 值得现在上吗?先分清 Responses API、SandboxAgent 和 MCP 的边界

OpenAI Agents SDK 值得现在上吗?先分清 Responses API、SandboxAgent 和 MCP 的边界

OpenAI Agents SDK 值得现在上吗?先分清 Responses API、SandboxAgent 和 MCP 的边界 先说结论 如果你现在在看 OpenAI Agents SDK,最重要的不是先问“它是不是又一个 agent 框架”,而是先分清三件事:Responses API 是模型调用层,Agents SDK 是运行时编排层,SandboxAgent 是给长任务和真实工作区准备的执行层。 这三层边界分清了,你才知道该不该上、先上哪一层、哪些团队现在上会省事,哪些团队现在上只会把复杂度提前引进来。 我的短答案是:如果你的任务已经不止一次模型调用,而是开始涉及工具调用、状态延续、交接、多步执行,OpenAI Agents SDK 已经值得认真评估;如果你的需求还停留在“一问一答 + 少量函数调用”,直接用 Responses API

By One AI
Cloudflare 把内部 AI 工程栈公开后,团队真正该补的不是再换模型,而是把身份、路由、上下文和审查链路接成一个系统

Cloudflare 把内部 AI 工程栈公开后,团队真正该补的不是再换模型,而是把身份、路由、上下文和审查链路接成一个系统

Cloudflare 把内部 AI 工程栈公开后,团队真正该补的不是再换模型,而是把身份、路由、上下文和审查链路接成一个系统 先说结论 如果你团队这两个月也在推 AI 编码工具、MCP Server、代码 Agent,Cloudflare 这篇内部 AI 工程栈 复盘最值得看的,不是“又一家大厂全面上 AI 了”,而是它把一个很多团队迟早都会撞上的问题说透了:模型接进来只是开始,真正决定 AI 能不能规模化落地的,是身份认证、请求路由、上下文供给、代码审查和执行隔离这几层能不能被接成一个系统。 Cloudflare 公布的数据非常夸张,但也非常有启发:过去 30 天,Cloudflare 有 3,683 名内部用户在用 AI 编码工具,占全公司约 60%,占

By One AI
Follow @Fuuqius