Unlocking any car from X brand in most of the world

Hacker News by 13 min read 504x views
Unlocking any car from X brand in most of the world

Share Post

November 7 2026

I've continually had a gentle place for this particular car brand. Our family has owned a few of them complete the years, and they've served us incredibly well. In 2023, I bought a new four rotor drive/SUV from them, and it was the archetypal period I had admission to their connected car platform.

Having looked at platforms akin this before (previous works), I was sceptical concerning how safe they really were. I suspected there was item there, but existence kept getting in the way of really looking. This July, I collaborated alongside Sam Curry to obtain a appearance at the safety of a important automaker's connected car systems. Throughout this post, I'll mention to them merely as "the automaker".

When you typically think of car hacking, it's frequently uncomplicated to jump into the hardware flank of things, but my backdrop sits beautiful squarely in compromising internet-facing web applications and infrastructure. Regardless of all the hardware bits and bobs, it all connects rear to several assistance that, by design, have to be reachable from the internet.

The journey started alongside a uncomplicated question: what infrastructure does the app conversation to whenever I remotely unlock my car? To activity this out, I downloaded the APK and decompiled it, eventually reconstructing the API requests that were being made.

Going through the code, the chief item of communication seemed to be resources under *.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" ...

Almost all part of the person use called rear to redacted-telematics.com.au. What alternatively existed under this domain?

Running connected car infrastructure can be fairly complex, and managing it requires a mix of admin tools, staging, dev, and QA environments, to create certain that customers get a spotless experience. Whether all of that have to be sitting on the community internet, accessible to anyone consenting to do several essential reconnaissance, is a distinct story.

One asset caught my notice additional than the others. admintool.redacted-telematics.com.au returned a leaf titled TEST HARNESS. Who can oppose a subdomain alongside admintool in it?

Visiting this subdomain presented a extremely plain and gray login screen, which didn't exactly ooze quality, stated it presented a logout clasp before we'd logged in.

I affection that most single-page applications container their complete JavaScript bundle equal whenever all they need to render is a login page. Sure, several additional contemporary frameworks divided out JavaScript records so that they lone assist what is necessary, but in this case, we could comb through the ample JavaScript bundle to comprehend what this "admin tool" really let you do.

Oozing quality
Who equal says JavaScript bundles?

Turns out this admin tool let you do fairly a lot. It had a affluent API that let the automaker's workforce engage alongside client accounts and manufacturing vehicles alongside extremely few restrictions. Reconstructing the requests, we established that a lot of these APIs needed no authentication beyond the hardcoded API keys sitting in the JavaScript bundle.

For example, we could appearance up person accounts by email address:

But that's fair not fine enough. If we desire to unlock any of their cars from fair knowing the figure plate, we're going to need to do improved than that.

In Australia, a figure dish gets you the VIN (vehicle acknowledgment number) fairly effortlessly through state-based registration lookup sites.

This isn't really a safety flaw. There are plentifulness of lawful reasons to desire a VIN, akin checking a car's true repair and coverage former before purchasing second-hand. If we could go from VIN to a subscriber or email, we'd complete the chain.

Fortunately, there was an API that let you do exactly that:

With the subscriber GUID, you could drag the complete document particulars (including the user's email via the /v1/account endpoint) akin so:

Well, I conjecture we have the archetypal part of our sequence sorted. Plate -> VIN -> Subscriber GUID -> User Account PII (email) registered on the automaker's character phase (ForgeRock OpenIDM).

Now, how do we dispatch commands to any car we want? Surely this admin tool would let us do that? Unfortunately, no. It let us provision features to a car, add ourselves to a user's document as a secondary user, and equal create distant admission codes. But for all that functionality, none of it really got us distant authority of a vehicle.

The character platform

Sam and I afterward spent two weeks digging into how the automaker manages their chief character system: the application they use, the way commands are issued, and how consumers authenticate.

When a person goes through the authentication stream for the automaker's mobile app, requests are made to login.redacted-identity.com.au, and following going through an MFA flow, a person auth token is eventually issued. This auth token is afterward used for consequent requests to api.redacted-telematics.com.au (both REST APIs and GraphQL). Once you have that auth token, it's beautiful much equivalent over. You can authority a user's car (remote unlock/lock, horns, engine, etc).

So if we could mint a token for any user, we'd eventually be capable to authority anyone's connected car. This is the viewpoint we went for.

The automaker is a dense person of ForgeRock's character application (OpenAM/OpenIDM). This is the cornerstone of their authentication mechanisms. Pulling the subdomains for *.redacted-identity.com.au, we quickly established their OpenIDM case at openidm.redacted-identity.com.au. This is the scheme that manages all character registered on the automaker's connected car platform.

This all clicked together whenever we were combing through the JavaScript records under *.redacted-telematics.com.au and we came throughout an entirely cursed vendor JavaScript bundle that embedded admin credentials for OpenIDM 😱:

Even although these admin credentials were meant to be for their staging environment, stated the hygiene we'd seen so far, we were beautiful certain they'd activity in manufacturing too.

Fighting the WAF

Looking nearer at OpenIDM, we couldn't inform whether the automaker knew how susceptible it was. One item was apparent though: they'd invested heavily in WAF rules to obstacle admission to the delicate APIs that ForgeRock typically lets you call via /openidm/*.

Even alongside the base IP, any petition to /openidm/ came direct rear as an Nginx WAF block. Someone had put genuine attempt into hardening these resources (despite them being externally accessible).

Fair enough, and we regard the hardening. But that much WAF funding normally method bandaids complete item known to be bad. They likely understood how bad it was to have this asset exposed at all, and several endeavor case kept it there anyway.

I'm not certain a WAF regulation has always stopped us whenever we're motivated enough, so we invested days into finding a way out of these protections, using our trusty way traversal tricks. How difficult could it be?

Validating the admin credentials we established inner the JavaScript bundle was not that tough. The Australian OpenIDM case had a few paths whitelisted akin /XUI/ and /UI/, and way traversal characters were not blocked, so item as uncomplicated as /XUI/..%2fopenidm/endpoint seemed to at smallest partially work:

At smallest we cognize the credentials activity in production

But it can never be as uncomplicated as that, right? The way traversal motionless didn't get us to the endpoints under /openidm/, gratitude to quirks in how OpenIDM handles routing. Every endpoint would error out akin so:

Gosh, we're so close?!

In OpenIDM's world, there were no servlets (or resources) registered against paths that included the way traversal inside them. So it would fair error out, equal although it issued us an admin JWT.

We were incredibly annoyed by this. A uncomplicated quirk in routing mechanisms (probably not equal designed as a safety consideration) was blocking the complete assault sequence from working. We sat disappointed for a few days, and we idea that the way traversal viewpoint fair wasn't going to work. We did discover a few another endpoints (user upload, for example) that looked akin they power work, but we hit another quirk, a servlet way check, that prevented us from using them meaningfully.

All of this made us ask a distinct question: why disclose these OpenIDM instances externally at all? Surely there had to be several lawful use case for these resources to be exposed on the internet? We went rear to the distinct JavaScript bundles we had collected from their resources and searched them for communications to OpenIDM. We turned to the decompiled Android use and established the following:

Maybe there is a whitelist for several endpoints?

These were plainly endpoints inside their OpenIDM asset that were contactable from the Android app. So, perchance these were accessible without the WAF getting in the way?

  • /openidm/endpoint/aaa -> 403 WAF blocked
  • /openidm/endpoint/pinService -> 404, no WAF block

Interesting!

What another tradition endpoints existed? With several extremely ray brute forcing and guessing, we quickly established a getUser endpoint. Guessing at parameters, we established it accepted mail, guid, and phone. Combined alongside our admin credentials, what did this really let us do?

Eep.

The reply came rear alongside the password hash, PIN code hash, mobile shove tokens, and PII including mobile numbers and complete names. There was no WAF obstacle on this endpoint, so no way traversal was needed and the accurate servlet responded.

Don't get me wrong, this was already beautiful impactful. But the passwords were hashed alongside PBKDF2-SHA512, and needing period on a hash cracking rig for all unlock, alongside no justify of success, felt a bit lacklustre.

Back to the example board. How could we admission the /openidm/managed/user servlet alongside our way traversal, and motionless have the way be recognised by OpenIDM? We grabbed the OpenIDM origin closest to the type we were evaluation against and let GPT 5.6 Sol Ultra expend a fine six hours on it.

It succeeded. Even although Sam and I had the two spent period trying to manually bypass this juvenile limitation ourselves, we hadn't tried all imaginable combination.

When Sam and I tried /XUI%2f..%2fopenidm/, we got a difficult 403, and being human, we assumed that applied to all prefix. What GPT 5.6 established was that /static%2f..%2fopenidm/ wasn't covered by the WAF at all, and it gave us the OpenIDM APIs without the servlet disturbance issue.

Taking complete accounts

With this bypass, taking complete an document became trivial. We could PATCH a new password for any person profile:

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!"}]

Which responded with:

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

OK, so we could overwrite the password for any user. But what concerning multi-factor authentication?

Skipping MFA

Well, the automaker's Australian surroundings had one additional misconfiguration: the multi-factor stage could be skipped entirely. First, we requested an authentication token from login.redacted-identity.com.au alongside a Resource Owner Password Credentials grant, which returned a spotless id_token we could toggle for item usable in the person app:

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 > }

We took that and moved on to stage two, exchanging the archetypal token for a session:

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" > }

Lastly, we performed the token toggle for a mobile app admission token:

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 overview write", > "expires_in": 3599 > }

It's excellent convoluted, I know. OAuth is annoyingly difficult sometimes. But these steps let us bypass multi-factor authentication for any person of the automaker's connected car app.

Unlocking the car

At this point, we were so damn near to unlocking any of the automaker's cars in Australia. All that was remaining was issuing the command API requests alongside the app token we could now mint for anyone.

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}

And magic. The doors unlocked.

We evidently did all of this on cars we explicitly owned or had approval to engage with.

But let's obtain a stage rear and think concerning what we achieved here. We could dispatch commands to any of the automaker's vehicles in Australia, throughout the two of their brands, by lone knowing the figure dish of the vehicle. The final sequence looked akin this:

Plate -> VIN -> Subscriber GUID -> OpenIDM admin credentials in JS vendor bundle -> Path traversal to bypass the WAF -> MFA bypass via ROPC stream -> Minting a person app token -> Remote command API.

At 12AM, whenever the complete sequence eventually came together, I took a abbreviated video of the manuscript in action. It's not polished, but it motionless felt magical seeing it all work. I certainly live for difficult challenges akin these.

0:00

/0:57

You power be wondering: Shubs, you stated any of their cars in the world. This lone covers Australia. What gives?

There's a much additional complex sequence that Sam and I worked on together, which he's published on his blog here. It required a lot additional activity than the steps we took for Australia, as the automaker had spent much additional period securing the US, Canadian, and European OpenIDM environments.

At the end of the day, alongside the two writeups combined, we could dispatch distant commands specified as unlocking and turning the engines on, for any of the automaker's vehicles in Australia, the United States, and Canada.

Other Article Hacker News
↑
Close Right Ads
Close Left Ads