不偷密码,直接伪造身份:Golden SAML 云上身份攻击实战

安全从业者 | 2026-08-02

一、凌晨两点的"正常登录"

凌晨两点,SOC 大屏弹出一条审计日志:

时间:2026-08-4 02:13:41
用户:admin@company.com
动作:AWS 控制台登录
方式:SSO(单点登录)
结果:成功

值班同事扫了一眼,继续刷手机。SSO 嘛,每天几千条,太正常了。

但如果把日志背后的事讲清楚,你大概率会坐直:登录这个人,根本不是 admin。他没有密码,没有 MFA,却以 admin 的身份进了控制台,而且全程没触发任何暴力破解告警。

这就是云上身份联合(Federated Identity)的暗面。企业上云之后,密码 + MFA 越来越难打,攻击者的火力正在往身份链路集中——他们不偷密码,直接伪造身份。今天这篇,我把 SAML 伪造、OIDC 混淆、云上 STS 滥用这三条路从头拆一遍,最后给检测与防御清单。

二、为什么攻击者盯上了"身份"

先说结论:云上安全边界已经从"网络"转移到了"身份"

以前攻企业,要么打 Web 漏洞,要么钓鱼拿密码。现在呢?大部分公司上了 MFA,密码爆破基本废了;云平台自带 WAF 和漏洞管理,0day 越来越贵。但有一层东西几乎人人都信、却很少有人审计——身份提供商(IdP)

企业做 SSO,一般会引入 Okta、Azure AD(Entra ID)或者自建的 Keycloak 作为 IdP,所有员工先登录 IdP,再由 IdP"发令牌"给云平台(SP)。这套机制下,云平台不认识你的密码,只认 IdP 签发的令牌

这意味着:谁控制了 IdP 的信任,谁就控制了所有接入的云账号。攻击链上存在一个"单点信任"——而这正是攻击者最喜欢的地方。

近几年的真实事件,全是这条线的翻车现场:

  • 2020 SolarWinds:攻击者通过供应链后门拿到 Azure AD 的 SAML 签名证书,伪造任意用户令牌,横穿微软云生态;
  • 2023 Storm-0558:微软披露,攻击者拿到 MSA(个人账号)签名密钥,直接伪造 Azure AD 令牌访问多家政府机构的 O365 邮箱;
  • 2022 Okta:LAPSUS$ 通过支持后台拿到超级用户权限,摸到了客户的身份管理面板。

这三起事件没有一个是靠"爆破密码"打进去的。身份链路上的信任,才是当代云上攻防的主战场。

三、Golden SAML:一张证书就能伪造一切

3.1 先花 30 秒看懂 SAML 流程

SAML 2.0 是 Web 上最老牌的 SSO 协议,流程长这样:

用户 → 访问云平台(SP)
     → SP 说:请先去 IdP 登录
     → 用户登录 IdP(输密码 + MFA)
     → IdP 生成 SAML 断言(Assertion),用 IdP 私钥签名
     → 返回给 SP
     → SP 用 IdP 的公钥验证签名 → 放行

关键就在最后两步:SP 只认"IdP 的签名",至于断言里写的用户名、角色、会话时长,SP 全都信。这套信任模型本身没问题,但有一个致命前提——IdP 的私钥必须永远安全

3.2 Golden SAML 攻击原理

Golden SAML 的思路非常直接:

拿到 IdP 的 SAML 签名私钥
        ↓
攻击者自己构造任意 SAML 断言
        ↓
断言里写上"我是 admin,角色是 Administrator,会话 8 小时"
        ↓
用偷来的私钥签名
        ↓
直接发给 SP → 登录成功

整个过程不碰密码、不碰 MFA、不触发任何暴力破解告警。更狠的是,因为令牌本身签名有效,SP 侧看这就是一次"合法"登录,时间戳、来源 IP 都随你编。

私钥怎么丢的?SolarWinds 已经演示过标准剧本:后门打进身份管理服务器 → 导出签名证书(含私钥)。Okta/微软那两起则是管理后台失守。总之,私钥一旦泄露,整个身份体系等于白给

3.3 实操:攻击者怎么伪造断言

真实攻击中,工具链已经很成熟。最常见的是 shimit(Go 写的 Golden SAML 工具)和 AADInternals(PowerShell):

# AADInternals:拿到 Azure AD 签名证书后伪造 admin 令牌
Import-Module AADInternals
$cert = Get-AADIntSigningCertificate
New-AADIntSAMLToken -ImmutableID "7f9c..." `
    -Issuer "https://sts.windows.net/tenant-id/" `
    -SigningCertificate $cert | Out-File token.xml

然后拿伪造的 SAML 响应去登录:

POST /SAML/SSO  HTTP/1.1
Host: login.microsoftonline.com
Content-Type: text/xml

<samlp:Response ...>
  <saml:Assertion>
    <saml:NameID>admin@company.com</saml:NameID>
    <saml:Attribute Name="Role">
      <saml:AttributeValue>Global Administrator</saml:AttributeValue>
    </saml:Attribute>
    <ds:Signature>(用窃取私钥签名)</ds:Signature>
  </saml:Assertion>
</samlp:Response>

从 SP 的角度看,这是"IdP 亲笔签名"的合法请求,验证签名、放行。攻击者要做的唯一一件事,是让私钥别被怀疑地拿到手。

3.4 现实世界的样本

Storm-0558 是近年最值得复盘的一例:攻击者不是入侵某个客户,而是拿到了微软自己 MSA 体系的签名密钥,然后用它签 Azure AD 令牌。客户这边密码没丢、MFA 没关、0day 没用——照样被翻了邮箱。这说明什么?信任链上任何一环的密钥泄露,都会让下游所有防护形同虚设。

3.5 为什么这么难发现

Golden SAML 最阴的地方在于"不可观测性"。常规入侵总会在某个环节留下痕迹:钓鱼邮件有 IoC、Webshell 有流量特征、横向移动有异常登录 IP。但伪造的 SAML 令牌在 SP 侧就是一次"通过 IdP 的正常登录"——日志里写的是 admin、时间戳正常、来源 IP 是办公网段,蓝队按传统思路根本无从下手。

我参与过的攻防演练里,这类攻击多数是靠行为基线偏差暴露的:比如某个从不加班的账号凌晨三点登录、或者同一会话里出现了平时从未用过的管理操作。也就是说,检测 Golden SAML 不能只看"登录成不成功",要看"这次登录合不合常理"——这也是后面第五节检测清单的重点。

四、OIDC 与 STS:不偷密钥,靠配置错误也能登堂入室

SAML 是"偷私钥",而现代云原生更流行 OIDC(OpenID Connect)——比如 GitHub Actions 通过 OIDC 直接换取 AWS 临时凭证。这条路不用偷私钥,光靠配置错误就能打,实战中遇到的比例更高。

4.1 正常流程:OIDC 换 AWS 临时凭证

GitHub Actions 构建
   ↓
GitHub 的 OIDC IdP 签发 ID Token(含 repo、ref、aud 等信息)
   ↓
调用 AWS sts:AssumeRoleWithWebIdentity
   ↓
AWS 校验 token 签名 + 检查信任策略 → 返回临时凭证

这套机制本身是好的:CI 里不用再放长期 AK/SK。但信任策略写得不严谨,等于把门焊死了还不锁

4.2 攻击面一:信任策略太宽松

很多团队的 Role 信任策略长这样:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Federated": "accounts.google.com"},
    "Action": "sts:AssumeRoleWithWebIdentity"
  }]
}

没有 Condition 限制 aud(受众)和 sub(主体)。这意味着:任何能用 Google 账号 OIDC 登录的人,都能 assume 这个角色拿云上权限——包括攻击者的个人 Gmail。攻击者只需要三步:

# 1. 用自己 Google 账号拿 ID Token
# 2. 直接 assume 目标角色
aws sts assume-role-with-web-identity \
  --role-arn arn:aws:iam::123456789012:role/dev-power \
  --role-session-name attacker \
  --web-identity-token <攻击者自己的ID Token>

# 3. 用返回的临时凭证干活
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
aws s3 ls s3://production-backup

如果目标角色权限是 AdministratorAccess,这一步就直接接管账号了。

4.3 攻击面二:OIDC Token Confusion

另一种高级玩法是"令牌混淆":同一个 IdP(比如 GitHub)被多个应用共用时,如果信任策略只校验了 iss(签发者)却没校验 aud,攻击者可以把签发给其他应用的 token 拿来 assume 你的角色。防御侧必须用 StringEqualsaud 锁死到自己的应用 ID。

4.4 自查脚本:你的信任策略到底安不安全

这个脚本我在红队演练里用过,帮你快速扫出"裸奔"的 OIDC 信任策略:

#!/usr/bin/env python3
"""检查 AWS Role 的 AssumeRoleWithWebIdentity 信任策略是否缺少限制"""
import json
import sys

def check_trust_policy(policy_doc: str) -> list:
    problems = []
    try:
        policy = json.loads(policy_doc)
    except json.JSONDecodeError as e:
        return [f"JSON 解析失败: {e}"]

    for stmt in policy.get("Statement", []):
        actions = stmt.get("Action", [])
        if isinstance(actions, str):
            actions = [actions]
        if "sts:AssumeRoleWithWebIdentity" not in actions:
            continue
        cond = stmt.get("Condition", {})
        string_equals = cond.get("StringEquals", {})
        # 缺少 aud 限制 = 任何该 IdP 用户都能 assume
        if not any("aud" in k for k in string_equals):
            problems.append("❌ 缺少 aud 限制:任意 OIDC 应用都能 assume 此角色")
        # 缺少 sub 限制 = 任意仓库/用户都能用
        if not any("sub" in k for k in string_equals):
            problems.append("⚠️ 缺少 sub 限制:建议锁定到具体仓库/主体")
        # 如果连 Condition 都没有,直接最高风险
        if not cond:
            problems.append(" 完全没有 Condition:任何人可 assume 此角色")
    return problems or ["✅ 未发现明显 OIDC 信任策略问题"]

if __name__ == "__main__":
    # 用法: python3 check_oidc_trust.py policy.json
    if len(sys.argv) < 2:
        print("用法: python3 check_oidc_trust.py <policy.json>")
        sys.exit(1)
    with open(sys.argv[1], encoding="utf-8") as f:
        for p in check_trust_policy(f.read()):
            print(p)

拿上面那段"裸奔"策略一跑,会立刻报出 和 ❌。凡是红队看到这种策略,第一反应都是"白捡的权限"。

五、检测与防御清单

层面检测/防御措施说明密钥SAML 签名证书强制轮换 + 硬件安全模块(HSM)托管私钥不进服务器文件系统,杜绝 SolarWinds 式导出密钥定期审计 IdP 证书指纹,变更立即告警检测"幽灵证书"OIDC信任策略必须加 `Condition` 锁 `aud` + `sub`用上方脚本做 CI 卡点OIDCRole 权限最小化,禁止直接给 AdministratorAccessAssumeRole 也应遵守最小权限检测监控 `sts:AssumeRoleWithWebIdentity` / `AssumeRoleWithSAML` 的异常调用关注陌生 repo/外部 IdP 主体检测对登录日志做"会话指纹"分析(时间、IP、UA 与历史偏差)Golden SAML 令牌常露马脚在行为异常管理身份管理员账号单独隔离 + 强 MFA + 特权访问管理(PAM)防 Okta 式后台沦陷

三个立即能落地的动作:

1. 今天:用脚本扫一遍所有 Role 的 OIDC 信任策略,加锁
2. 本周:把 SAML 签名证书迁入 KMS/HSM 托管,开启轮换
3. 本月:把 AssumeRole 类调用接进 SIEM,建基线告警

六、写在最后

云上攻防打到最后,打的不是漏洞,是信任。密码可以改、MFA 可以补,但签名私钥一旦伪造过,整个身份体系就必须重建信任——这比任何一次密码泄露都贵。

记住一句话:当攻击者不再需要密码的时候,你还在监控"密码错误次数",就已经输在起跑线上了。 把身份链路的密钥、策略、行为基线当核心资产来管,才是在新战场活下来的姿势。


关注「安全值班室」公众号

每天网络安全早报 + 实战攻防案例 + 安全干货

关注安全值班室

posted on 2026-08-05 21:51  明.Sir  阅读(0)  评论(0)    收藏  举报

导航