XSS on account.leagueoflegends.com via easyXDM
A long chain of issues that leads to XSS in the league of legends (LoL) account subdomain via easyXDM, which is a developer focused JS library that provides an interface for doing cross-origin communication using various protocols. easyXDM consists of a producer-consumer setup, where a producer page exports functions for the consumer page to invoke. In LoL’s case, they exported methods to send requests and responses cross-origin, as well as get and set cookies via the pm.html
page. These are dangerous functionalities of course, so there’s some protections here. The document.referrer
is checked against an allowlist of domains owned by riot games or their partners. On top of that, the message origin reported by easyXDM is also verified against this same list.
Open redirect to bypass referrer check
They managed to bypass the referrer check via an open redirect issue in easyXDM’s FrameElementTransport.js
class, which is used for passing variables using the frameelement
property on gecko browsers. One of the things it allows you to do is set the window top location to this xdm_e
parameter (aka config.remote
). They could force FrameElementTransport
to be used by setting xdm_p
(aka config.protocol
to 5). They abused this via the apollo
consumer, which is an allowlisted consumer.
Bypassing origin check
Now they could get attacker controlled code loaded, but it was still subject to the origin check by easyXDM. The second bug was one in HashTransport
for communicating across iframes via the window location.hash
. It’s a hacky technique which is fundamentally flawed somewhat as it’s impossible for the parent page to know who updated the location.hash
, and so they assume messages came from config.remote
, which can be controlled by an attacker.
This puts the attacker in a catch-22 though. They can set xdm_e
/config.remote
to an attacker domain to get their code loaded, but they’ll fail the origin check. They could set it to an allowed domain to bypass the origin check, but then they forfeit loading attacker code. So they had to revisit their original referrer bypass and rework it a bit. They essentially used HashTransport
again to get their page loaded in a nested iframe under the apollo
consumer and switch the parent location source to the pm.html
page after the fact. This allowed them to send requests to pm.html
, though they couldn’t get responses back.
Bypassing signing/verification + getting XSS easyXDM additionally tries to sign and verify incoming messages to prevent iframes being abused in this way. The problem is, they sign using a secret that’s set by the iframe assuming an attacker doesn’t have control of the second level iframe from the start. So an attacker can simply set their own secret and still gain access to those methods mentioned earlier.
XSS was achieved by using the ability to make XHR requests, and the fact that that jquery would implicitly load a URL as a JSONP call if it ends in =?
(at least until jquery v4). This finally got them XSS in accounts.leagueoflegends.com
.