Daniel Lee was a summer intern in MWR's New York office. Below he writes about his experience with MWR and some of the cool stuff he got to work on.
Working as an Intern at MWR was a great experience. During my time, I was able to explore different areas of security that I did not know before. I am glad that I was able to work with passionate and talented security experts who loved what they do.
For the first week or so, I played around with “The Bazaare”, a purposely vulnerable web application that was developed for training. This was intended to introduce some of the vulnerabilities that can be found on various web applications. To help point out the vulnerabilities in “The Bazaare”, I also did various internal web application training modules.
Once I was done with “The Bazaare”, I spent two weeks or so tackling a vulnerable Windows Server, which was supposed to model some infrastructure vulnerabilities. It was running many different vulnerable services and my goal was to break into the server and get system level privileges in various ways. In the process, I got to learn a lot about possible uses of the Metasploit framework.
For the remainder of my internship, I worked on my own research project, which was to hack an Android game. I picked out one of the game applications from the Google Play Store and was able to find several vulnerabilities. In the process, I learned how to use Drozer, an Android testing framework, and I also learned how to write Xposed modules to assist in exploiting the vulnerabilities.
There are many games on the Google Play Store but it is questionable whether all of these apps are actually secured. For my research project, I chose a random game from the Google Play store to test its security. I ended up choosing a mobile game called “Legion Hunters”, which was created by the developer MainGames. The main goal of the game is to defeat the monsters. It contains in-app purchases, which will help the player progress further into the game. The game is written in Java, Lua and Cocos2dx (this was discovered through de-compiling the application, more information on this is below).
Lua is a powerful and lightweight-scripting language that many games on the Google Play store use. It is very common to write a game engine in C/C++ and the game itself in Lua because it will avoid any issues with separation and provide more flexibility. For example, the Lua scripts can be edited to alter the game without the need to recompile the entire game engine. More importantly, Lua is already supported in many existing game engines.
Cocos2dx is a cross platform game engine that provides common functionality in the game. It has a simplified API and rich features that developers can use without spending too much time. It does support Lua, which is most likely the reason that the developers for this game chose Cocos2dx for the engine.
As a first step, I played around and noted any particularly interesting features in the game. This helped me take note of entry points for possible security vulnerabilities. The following are some of the features I noted:
Server (tap to select)
There are two available login services:
Once the user logs in, it is not necessary to login again next time since the application remembers all the previously logged in accounts. To pull up the list of those accounts, the user can simply tap on “PlayNow” as shown below:
If the player does not want to create an account, the player can choose
‘Create New Account’ to create a temporary account.
Once in the game itself, the following features caught my eye:
Out of all these interactive features, I set my target as the login process since that is the first step the new user has to go through. Specifically, I wanted to find out what the game login process looks like when the player logs in through MainGames.
Hoping to find any API calls to MainGames, I decompiled the app and traced all the Java classes and methods responsible for making the request to their server. Eventually, I found a Java class called “MgSdkRequestFactory.”
This class defines all URLs for MainGames’ API and handles all main requests such as payment and authorization. When I saw these URLs, I immediately noticed two things:
To see exactly what data is sent over to MainGames’ API server, I did a Man-in-the-Middle (MITM) attack on the game and found that the login process goes through the following steps.
First, it sends a POST request to “/v1/member/auth” with the following data:
In this payload, the “sign” parameter is used to check validity of the request. It is calculated by taking the MD5 hash of username + password + timestamp + app_secret. Since the app secret was initially unknown, I wrote an Xposed module that logs this information. Xposed is a modular framework that can be used to hook and alter methods and variables in Android applications and the Android OS itself, this requires root access.
In order to verify I had correctly reversed the sign parameter, I checked it against multiple test accounts and it worked for all of them. More importantly, I found that the ”app_secret” parameter did not change across users. This indicated that this is a key specific to the application.
If the user’s login was successful, it would return following JSON data:
If the user’s login was not successful however, it would return following JSON data:
Once the user logs in, the application sends a second POST request to “/v1/game/server/login” with the following data:
The “sign” parameter is still used as a way to validate the request but this time it was calculated by taking the MD5 hash of server_id + product_id + user_token + timestamp + app_secret.
If the login was successful, the server would return following JSON data:
To put this all together, I wrote a simple Python script to login to the game:
The developers of this game have stated on the Google Play Store that I need to grant the application read/write permissions to the device’s external storage in order to “save credentials & images to support multiple accounts login”. This indicates that there is a file on the device that contains the list of logged in accounts.
Starting from Android 6.0, apps no longer have an access to the device’s external storage by default but instead they request the permission when they need it at runtime. This means I have to look for this file somewhere under “/mnt/runtime/read” and “/mnt/runtime/write”. To make the process easier, I wrote an Xposed module that hooks onto the method “loadSharedAccount” and logged the parameters.
This resulted in discovering the following files:
I extracted the file and realized that the content was based64 encoded. I decoded the content but it was all useless data as seen below:
I searched through the decompiled source code for mentions of this file and it turns out AES encryption was used to encrypt the content before it was written to this file. Fortunately, the developers were nice enough to hard code the key into the application and I was able to write a simple Python script that decrypts this file.
#decrypting creds from loaded file
f = open("shared_acc.json","r")
payload = f.read()
payload = payload.encode('utf-8')
payload = payload.decode('base64')
account = payload[0:len(payload)-16]
aes = AES.new(self.key,AES.MODE_CBC,"0"*16)
f = open("shared_acc_decrypt.json","wb")
This script decrypts the content and saves the result into a new file called “shared_acc_decrypt.json” which contains the full list of previously logged in accounts as shown below:
This is how the app keeps track of previously logged in accounts. To login as one of the users in the list, it goes through following steps:
First, the application sends a POST request to “/v1/member” with the following payload:
Similar to the previous “sign” parameter, it is used as a way to validate the request and this is calculated by taking the MD5 hash of user_token + timestamp + app_secret.
If the login was successful, the server would return the user data in JSON format.
However, if the login was not successful, the server would return an error code “-21”.
If the user does not have an account or accidentally deletes the list of
previous accounts, the user can create a temporary user for logging in.
First, a username and password combination are generated based on the following formulas:
Once a username and password combination are generated, the application sends a POST request to “/v1/member/register” with the following data:
This time, the “sign” parameter is calculated by taking the MD5 hash of username + password + email + ref_product + timestamp + app_secret.
If the login was successful, it would return the user’s data in JSON format.
Using this information I improved my script to automatically generate new accounts and login to the game.
When an account is created, the user receives the following data from the server:
The user_id and user_token are both used to authorize with the game server after the successful login through one of the available services (Facebook or MainGames). This is handled by the method called “API_AUTHORIZED” which takes the user_id, user_token and the flag for login service as the parameters.
Hoping to get some error response, I created a temporary account and through an Xposed module I changed this user id to one of the user ids from different accounts in the following list:
These are some of the user ids I extracted from my test accounts and these all have the following properties:
Instead of getting an error response, I was logged in as the other user to which the user id belonged to. Since the validation was done on the server side, I could not pinpoint exactly how the server authorizes the user but some possibilities include:
To confirm this vulnerability, I attempted to login as various test accounts from a temporary account. Since I was successful in all attempts, I can theoretically retrieve all user ids and login as any user.
To wrap up, the following summarizes the login process through MainGames’ service:
No Previous Accounts
In the end, this app was not as secure as it could have been. Perhaps most of the popular games are not as vulnerable as this one. However, the fact that such vulnerabilities in this game exist says a lot about security in mobile gaming. There are plenty of good security practices and guidelines online that Android developers can follow. These are my suggestions to the developers of “Legion Hunters”.
SSL and SSL pinning
Hard Coded Keys
MWR first contacted the vendor on August 12th 2016 and again on August 26th 2016, however no response was received.