公司动态

npmPyPI恶意包72小时排查实战:供应链投毒应急完整SOP

📅 2026/8/15 21:50:43
npmPyPI恶意包72小时排查实战:供应链投毒应急完整SOP
前言近几年开源供应链攻击已经从小众漏洞测试变成黑产常态化牟利的核心手段。不同于传统外网渗透、业务漏洞攻击npm、PyPI 投毒攻击的核心特点是无差别渗透、长链路潜伏、全资产污染。攻击者不需要突破企业防火墙、不需要破解业务账号只需要注册一个拼写相似的包、篡改小众依赖版本、劫持废弃包维护权限就能通过开发者日常的安装、构建、部署动作直接入侵开发终端、CI/CD流水线、生产服务器。绝大多数企业的应急短板非常统一只扫业务代码直接依赖忽略嵌套间接依赖只排查生产环境放过开发本地机器和构建节点发现恶意包只做删除操作不清理后门、不轮转密钥响应流程混乱各团队权责模糊导致短短几KB的恶意依赖造成密钥泄露、源码外泄、服务器被控、数据拖库等重大事故。本文完全基于真实攻防复盘编写摒弃通用化的安全模板从攻击本质出发拆解72小时完整应急流程包含可直接落地的排查命令、批量扫描脚本、资产梳理方法、漏洞修复规范、长期加固方案。所有流程均经过对抗式校验覆盖攻击者常用的潜伏、绕过、持久化手段适配中小企业、互联网团队、研发安全团队的实战落地场景。一、核心原理重新理解npm/PyPI供应链投毒第一性原理拆解所有开源供应链投毒底层逻辑只有一个利用研发信任机制让合法的依赖安装动作执行非法的恶意代码。无论是npm还是PyPI官方仓库本身没有入侵能力漏洞全部来自于包开发者的权限失控、包内容恶意篡改、用户的无信任安装行为。1.1 两类平台核心作恶机制npm 的攻击触发点集中在安装阶段核心依托配置脚本。preinstall、install、postinstall 三个钩子脚本会在执行npm install时自动运行不需要开发者手动调用包内接口。这也是为什么很多团队只检查代码import依赖却依然被入侵的核心原因——恶意代码在安装完成的瞬间就已经执行和业务运行无关。PyPI 的作恶逻辑更隐蔽。除了安装阶段的setup.py、setup.cfg 恶意代码执行还支持在模块导入时触发恶意逻辑。部分高级投毒包会做条件判断本地开发环境不触发恶意行为仅在CI构建、生产服务器启动、云端容器运行时执行窃密、外联、落后门操作极大提升排查难度。1.2 主流投毒攻击类型对抗式梳理站在攻击者视角目前黑产最常用、企业最容易漏报的四种攻击方式全部纳入本次SOP排查范围第一拼写混淆攻击Typosquat。攻击者注册和主流热门包名字极度相似的包比如将 axios 写成 axois、requests 写成 requestss。开发者手输安装命令、复制错误命令就会直接安装恶意包。这类包下载量极大是批量入侵企业的主要手段。第二依赖劫持攻击。大量开源小众包长期无人维护、开发者弃坑攻击者通过官方渠道申请接管包权限随后推送带恶意代码的新版本。企业项目长期引用该依赖自动更新版本后全员中招。第三版本投毒攻击。正规包的正常低版本安全最新版本被植入恶意代码。团队开启依赖自动更新、模糊版本匹配^、~ 版本符号会静默拉取恶意版本无任何告警。第四嵌套依赖投毒。这是危害最大、排查漏报率最高的攻击方式。恶意包不作为项目直接依赖而是被热门正规包嵌套依赖。项目本身不写入恶意包名但安装主依赖时会自动嵌套安装恶意子包全网资产批量污染。1.3 攻击完整链路架构流程图下面的流程图完整还原从投毒到入侵的全链路也是本次72小时排查的核心依据所有排查动作均围绕该链路设计篡改包版本/注册混淆包npm安装钩子/PyPI导入执行攻击者投毒公网npm/PyPI仓库开发者/CI流水线执行安装触发恶意代码本地/构建节点落地恶意程序窃取环境变量/AK/密钥植入持久化后门外联C2服务器源码、数据、密钥泄露长期控制服务器/开发机批量横向渗透内网资产1.4 传统排查方案的致命漏洞对抗式审查结论我复盘了近30起企业供应链投毒应急案例90%的团队都会踩三个致命坑这也是本文SOP重点补齐的短板只扫描package.json、requirements.txt 直接依赖不解析lock锁文件。嵌套恶意依赖完全无法检出这是最高发的漏报场景。只排查生产服务器忽略开发本地终端、CI构建节点、测试环境。开发机存储大量全局密钥、Git凭证、云服务AK被入侵后风险远高于生产环境。仅删除恶意包完成修复不做凭证轮转、后门查杀、镜像重建。大部分恶意包会落地持久化文件、写入系统计划任务、修改配置文件单纯删包无法清除后门。二、应急前置规范角色权责与72小时时间轴总览供应链应急最大的问题不是技术排查难度而是团队协同混乱。无明确权责会导致有人重复工作、核心环节无人负责、黄金响应时间被浪费。本文统一固定企业通用应急架构适配中小团队、专职安全团队、研发运维一体化团队。2.1 固定应急角色与权责无重叠、无空缺应急总负责人统筹整体事件审批隔离、停机、回滚、密钥轮转操作对外同步风险协调跨团队资源最终复盘定责。安全团队负责恶意包情报研判、IOC特征提取、扫描脚本开发、全量资产风险定级、攻防对抗验证、输出最终报告。研发团队负责所有代码仓库修复、依赖版本锁定、代码合并审核、业务功能回归、Git历史脏数据清理。运维团队负责私有制品仓库管控、CI/CD流水线冻结与恢复、容器镜像清理、主机隔离与重建、日志留存取证。云平台/运维审计负责全链路日志审计、资产快照备份、异常访问溯源、云凭证风控拦截。业务负责人评估业务停机影响、确认修复后业务可用性、配合风险定级与对外公示。2.2 72小时阶段拆分实战时间轴不可倒置0-2小时黄金阻断期。完成告警核验、IOC提取、全网紧急阻断、现场取证备份杜绝污染范围扩大。2-24小时全域排查期。完成代码、制品、镜像、主机、终端、CI节点全量扫描输出完整受影响资产清单。24-48小时修复验证期。完成代码修复、制品清理、节点处置、密钥轮转、业务回归验证。48-72小时复盘加固期。完成根因分析、漏洞加固、机制落地、输出正式应急报告杜绝二次复发。2.3 应急红线规则全程强制执行禁止在生产、办公终端本地复现恶意包所有行为分析必须在隔离沙箱、离线虚拟机完成。发现疑似感染资产优先隔离留存现场禁止直接重启、重装、清理日志破坏取证链路。所有修复操作必须留痕禁止私下手动修改依赖、推送代码、恢复流水线。只要存在任意一台感染节点必须全员轮转相关密钥不接受“未发现异常”侥幸判断。三、0-2小时黄金应急告警确认与全网阻断实战操作前2小时的核心目标不是排查是止损。只要阻断及时就能90%避免后续数据泄露、横向渗透风险。很多团队本末倒置优先溯源排查导致恶意代码持续外联、窃取数据风险无限放大。3.1 告警真实性快速核验接收告警后第一时间排除误报、舆情噪音锁定精准攻击信息固定四项核心数据恶意包精准名称、受影响版本区间、权威漏洞通告链接、恶意代码核心行为。同步完成IOC特征提取这是后续全量扫描的唯一依据核心提取项恶意包版本号、文件哈希值、C2外联IP/域名、恶意落地文件路径、恶意执行命令、特征字符串。同时快速判定作恶时机区分安装阶段触发、运行阶段触发、双阶段触发以此判断污染范围。仅安装触发的包未执行安装动作的资产无风险运行触发的包只要代码引入过依赖无论是否启动业务都需要排查。3.2 全网紧急阻断操作可直接落地第一步私有制品仓库拦截。企业研发必须依托私有npm、PyPI代理源禁止直连公网。发现恶意包后立即在私有源后台拉黑对应包名全部恶意版本下架已缓存的恶意制品关闭公网同步通道防止新的资产被污染。第二步CI/CD流水线全局冻结。暂停所有自动构建、自动部署、自动依赖更新任务锁定流水线配置。禁止所有构建节点访问公网开源仓库强制内网私有源解析阻断恶意包继续拉取通道。第三步高危节点紧急隔离。筛选近72小时执行过npm install、pip install 的构建机、CI runner、测试服务器、开发高配终端全部做网络隔离保留网络连接、进程状态、文件快照不做任何清理操作。3.3 取证备份不可逆必须优先执行阻断完成后立即备份所有现场数据防止后续排查修复覆盖日志证据。需要备份的核心文件所有项目lock锁文件、构建全量日志、容器镜像快照、主机进程快照、终端命令历史、网络连接日志、系统定时任务配置。取证核心原则只读备份不修改、不删除任何原始文件保证后续可溯源、可复盘、可追责。四、2-24小时全域排查全资产扫描与风险定级核心实战环节这个阶段是整个应急的核心核心目标是找出所有被污染的资产零遗漏。我将资产分为六层逐一排查覆盖企业所有研发、运维、生产资产配套完整的扫描脚本和命令解决传统排查漏报问题。4.1 第一层代码仓库全量扫描解决嵌套依赖漏报所有代码仓库的package.json、requirements.txt 只能作为辅助判断依据lock锁文件是唯一精准判定标准。lock文件会记录所有直接、嵌套依赖的精准版本、下载地址、文件哈希是排查嵌套投毒的核心依据。我提供两套完整可运行的批量扫描脚本支持批量扫描本地所有项目仓库适配Windows、Linux、Mac环境。4.1.1 npm lock文件批量扫描脚本Pythonimportjsonimportosimportsys# 在此配置恶意包清单按需更新MALICIOUS_NPM{test-mal-pkg:[1.0.0,1.1.0],fake-axios:[0.0.1,0.0.2]}defscan_npm_lock(file_path):try:withopen(file_path,r,encodingutf-8)asf:datajson.load(f)exceptExceptionase:print(f[异常] 解析失败{file_path}:{str(e)})return[]hit_list[]packagesdata.get(packages,{})forpkg_key,pkg_infoinpackages.items():pkg_namepkg_info.get(name,)pkg_versionpkg_info.get(version,)ifnotpkg_nameornotpkg_version:continueifpkg_nameinMALICIOUS_NPMandpkg_versioninMALICIOUS_NPM[pkg_name]:hit_list.append({file:file_path,pkg_name:pkg_name,pkg_version:pkg_version,pkg_path:pkg_key})returnhit_listdeftraverse_scan(root_path):total_hits[]forroot,dirs,filesinos.walk(root_path):forfileinfiles:iffilepackage-lock.json:full_pathos.path.join(root,file)resscan_npm_lock(full_path)ifres:total_hits.extend(res)returntotal_hitsif__name____main__:# 传入需要扫描的代码根目录scan_rootsys.argv[1]iflen(sys.argv)1else./resulttraverse_scan(scan_root)ifresult:print(*80)print(检测到恶意NPM依赖风险资产清单)foriteminresult:print(f文件{item[file]})print(f恶意包{item[pkg_name]}{item[pkg_version]})print(-*40)else:print(未检测到恶意NPM依赖)4.1.2 PyPI lock文件批量扫描脚本importreimportosimportsys# 恶意PyPI包配置MALICIOUS_PYPI{fake-requests:[2.20.0,2.21.0],py-mal-tool:[0.1.0]}# 匹配poetry.lock、requirements.txt版本VERSION_REGre.compile(r([a-zA-Z0-9_-])\s*\s*([0-9.]))defscan_py_lock(file_path):hit_list[]try:withopen(file_path,r,encodingutf-8)asf:linesf.readlines()except:return[]forlineinlines:resVERSION_REG.findall(line)ifnotres:continuepkg_name,pkg_verres[0]ifpkg_nameinMALICIOUS_PYPIandpkg_verinMALICIOUS_PYPI[pkg_name]:hit_list.append({file:file_path,pkg_name:pkg_name,pkg_version:pkg_ver})returnhit_listdeftraverse_scan(root_path):total_hits[]scan_files[requirements.txt,poetry.lock]forroot,dirs,filesinos.walk(root_path):forfileinfiles:iffileinscan_files:full_pathos.path.join(root,file)resscan_py_lock(full_path)ifres:total_hits.extend(res)returntotal_hitsif__name____main__:scan_rootsys.argv[1]iflen(sys.argv)1else./resulttraverse_scan(scan_root)ifresult:print(*80)print(检测到恶意PyPI依赖风险资产清单)foriteminresult:print(f文件{item[file]}|{item[pkg_name]}{item[pkg_version]})else:print(未检测到恶意PyPI依赖)4.2 第二层制品与容器镜像排查代码无风险不代表制品无风险。很多分支代码已修复但历史构建镜像、缓存制品依然保留恶意依赖部署后会持续带毒运行。核心排查动作遍历镜像仓库所有Tag镜像导出SBOM物料清单批量比对恶意包名和版本。同时扫描镜像内部node_modules、site-packages 目录校验文件哈希是否匹配恶意IOC。所有命中恶意包的镜像全部标记为高危废弃禁止后续部署、复用、缓存统一记录进风险资产清单。4.3 第三层CI/CD构建节点排查CI构建节点是供应链投毒的核心传播源头。构建机一旦中毒所有打包产出的镜像、安装的依赖全部带毒会批量污染生产、测试环境。排查核心项检查节点安装日志、缓存目录确认是否存在恶意包缓存核查近72小时构建任务统计带毒构建次数检查节点进程、定时任务、外联记录确认是否被植入后门。只要执行过带毒构建任务的CI节点全部标记为待重置不允许继续参与构建工作。4.4 第四层生产/测试主机容器排查批量在线核查运行中容器、服务器的依赖安装情况提供可直接复制的Linux排查命令# NPM环境快速核查npmls恶意包名npmlist-g恶意包名# PyPI环境快速核查pip list|grep恶意包名 pip freeze|grep恶意包名# 查看近24小时安装记录ls-lt~/.npm/ls-lt~/.cache/pip/同时核查系统异常行为陌生外联连接、新增未知文件、修改的系统配置、异常定时任务匹配前期提取的IOC特征精准定位已入侵资产。4.5 第五层开发本地终端排查绝大多数企业完全忽略开发机风险但开发终端存储的Git私钥、云AK、数据库密码、业务Token 权限远大于生产账号。一旦中毒攻击者可以直接拿下企业代码仓库、云资源权限。统一排查要求所有研发人员本地执行上述核查命令自查全局依赖上报异常安装记录、陌生进程、网络外联情况安全团队统一复核。4.6 风险等级定级标准统一落地标准高危资产已安装恶意包、触发过恶意代码执行、存在外联记录、存在后门落地存在数据泄露风险。中危资产代码/lock文件存在恶意依赖完成构建但未部署运行无执行记录。低危资产仅代码分支存在恶意依赖未构建、未安装、未执行。五、24-48小时修复清理彻底根除污染恢复业务排查完成后禁止简单删包重启必须按照「代码修复-制品清理-节点重置-密钥轮转-业务验证」的固定流程操作彻底清除所有污染痕迹杜绝残留后门。5.1 代码层彻底修复直接依赖污染升级官方安全版本彻底移除恶意包依赖重新生成lock锁文件。嵌套依赖污染核心难点npm 使用overrides 强制全局覆盖子依赖版本PyPI 通过版本约束锁定安全版本禁止间接依赖拉取恶意版本。所有修复代码必须经过安全评审、自动化扫描、人工复核确认无恶意依赖后允许合并禁止直接推送主干分支。5.2 制品与镜像彻底清理删除所有带毒镜像Tag、缓存制品、私有源恶意包版本清空CI构建缓存。所有业务必须基于修复后的代码全新构建镜像禁止复用任何历史缓存、旧镜像。私有制品仓库持续拉黑恶意包永久拦截该包名及恶意版本不随应急结束解除拦截。5.3 感染节点分级处置对抗式最优方案高危感染节点有执行、外联、后门痕迹直接重装系统/重置容器不做局部清理。恶意代码大概率已经落地持久化文件、篡改系统配置局部清理无法彻底根除。中危节点仅下载未执行卸载恶意包清空npm/pip全局缓存、本地项目缓存校验系统文件完整性。CI构建节点全员重置运行环境清空所有缓存文件重新初始化构建镜像。5.4 凭证全量轮转重中之重只要本次投毒包存在窃取环境变量、密钥的行为无论是否检测到泄露痕迹必须全量轮转所有相关凭证。攻击者大概率存在延迟窃取、延迟外联机制肉眼无法检出潜在泄露行为。轮转清单云服务AccessKey、数据库账号密码、Redis密码、CI/CD流水线Token、Git仓库密钥、接口签名密钥、内部系统账号。轮转完成后审计所有密钥历史访问日志排查异常登录、异常调用、异常外联行为。5.5 业务回归与监控验证解除流水线冻结使用全新修复镜像灰度发布完成业务功能回归测试。发布后持续监控24小时重点观测异常进程、陌生外联、日志报错、接口异常访问确认无残留风险。