I recently tried to buy a new game from TG which unfortunately was only available to Premium members (TG has two membership levels, Basic and Premium), and the Premium membership was expensive as hell. I bit my nails for a while and wondered what I could do since my future happiness seriously depended on playing several hours of said game as soon as possible. Since I’m a software engineer myself, I decided to see if I could just play around with TG’s services for a while and just see how things worked.

I reloaded TG’s game purchase page with my Chrome DevTools open in order to see what requests the website makes. First thing I noticed was that TG uses AJAX to load some of its Javascript assets, about a dozen of them. Two of these scripts caught my eye: user.js and purchase.js , and I swiftly opened them up to see what treasures were buried within.

// snippet from https://theseus-games.co.uk/assets/js/user.js componentDidMount() {

this.retrieveContactInformation();

this.retrieveUserInfo();

}



retrieveUserInfo() {

axios.get(this.appUrl + '/user').then(

function (response) {



this.setState({

application: response.data.application,

active: response.data.active,

isLoaded: true

});



}.bind(this)

);

}

Of course, I had built several apps with React and React Native and I immediately realized that the web frontend was built with React, communicating with a Laravel backend (I got this from the session cookies). If you know a little Javascript (or pretty much any web language haha), you can work your way through these snippets.

// s nippet from https://theseus-games.co.uk/assets/js/purchase.js purchaseGame = (evt) => {

evt.preventDefault();



if (this.state.active == 2 || this.state.active == 33) {

this.setState({

isLoaded: false

});



var newGame = {

gameId: this.state.gameId,

session: this.state.session

};



axios({

method: 'post',

url: this.appUrl + "/purchaseGame",

data: newGame

}).then(

function (response) {

this.setState({

isLoaded: true

});

}.bind(this)

);

} else {

this.setState({

message: 'Sorry! This game is only available to our premium members.'

});

}

}

Apparently, user membership information is indicated through an active field in a User entity, and only users where active is either 2 or 33 can be allowed to get the game. Of course the numbers must represent membership levels, and either 2 or 33 indicated Premium membership, and since I was getting the message: ‘Sorry! This game is only available to our premium members’, my active field most definitely wasn’t 2 or 33.

Further down the line in the DevTools request tab, I saw the actual GET /user API request, and I found this response:

// response for GET https://theseus-games.co.uk/application/user {

"application":"182718048",

"active":22,

"userId":60088

}

Apparently, my active field was 22, which is how the web app knows to deny me access.

Immediately, I fired up Postman in order to make API requests to update my active field. From DevTools, I copied out my current session cookie and the X-XSRF-TOKEN header value as well into Postman, and I tried to make a PUT request to https://theseus-games.co.uk/application/user/60088 with payload:

// payload to PUT https://theseus-games.co.uk/application/user {

"application":"182718048",

"active":2,

"userId":60088

}

But I immediately received a 405 (Method Not Allowed) error, meaning users are updated in a different route. In turn, I tried POST /application/updateUser , POST /application/updateUserInfo and got 404s for both. At this point, I decided to abandon this approach entirely and try something else. (If I wanted to continue, I’d have inspected the other *.js files that were downloaded via AJAX to get the specific route for updating user, or just go to my account page and try to update my account, then retrieve the route; but there are many ways to skin an elephant haha.)

I have this cool Chrome extension called Tamper Chrome which is used to (surprise!) tamper with requests in the browser, and modify request headers, payloads, etc. The next approach I tried involved me tampering with the actual API requests, and there were two things I could easily try:

1. Download a copy of https://theseus-games.co.uk/assets/js/purchase.js , and modify the if condition checking the active field and then place it in my local server (e.g. /var/www/html/web/purchase.js ), then modify the outgoing request to https://theseus-games.co.uk/assets/js/purchase.js and change the request path to http://localhost/web/purchase.js , so it serves my local version instead.

For my local modification, I could simply change:

if (this.state.status == 2 || this.state.status == 33) {...}

to:

if (true) {...}

2. In a similar manner to (1), I could serve a local version of https://theseus-games.co.uk/application/user and simply return a user object with the active field conveniently modified, then modify the AJAX request path to my local version. I mean something like: