Reversing Genymotion to recover my stored password


2022-06-25

Curious as to why, when I launched Genymotion on an uneventful Thursday morning at work, I saw my credentials fly by through Burp I decided to investigate.

This blog post is a follow-up to a lightning talk I gave at HitchHack 2022’s and Le Hack 2022’s Rump Sessions.

TL;DR

Genymotion stores the user’s Genymotion account password locally. The password is XOR-encrypted with a static key. It can be decrypted and retrieved with the following script :

# https://pypi.org/project/PyQt5/
from PyQt5.QtCore import QSettings
# https://docs.pwntools.com/en/stable/install.html
from pwn import xor

qs = QSettings('Genymobile', 'Genymotion')

username = qs.value('customer.email')

enc_pass = bytes(qs.value('credentials.password'))
xor_key  = b'@f,ml;vcxw,x!xc<'

password = xor(enc_pass, xor_key)

print(f'Creds = {username}:{password.decode()}')

Preliminary background

Genymotion is a freemium Android emulator based on Virtualbox. It’s similar to Bluestacks’ and Android Studio’s emulator but is geared towards software companies looking to automate tests and do Continuous Integration (CI). Genymobile also offers hosted Android virtual devices through their “Android as a Service” (AaaS) solution.

Genymotion is coded in C++ and uses Qt for the GUI.

On startup, Genymotion makes two POST requests to cloud.genymotion.com firstly to log the user in (/launchpad/login/) and secondly to check if the user’s license has expired (/licenses/activation/). Rather than doing this the right way and using a session cookie Genymotion sends the user’s email address and password to the server, which then responds with a session cookie for the activation check.

File system reconnaissance

Genymotion stores configuration data with the QSettings class using the INI format. The location of the file is OS-dependent :

  • Linux/MacOS (configuration file) : $HOME/.config/OrgName/AppName.conf
  • Windows (registry key) : HKEY_CURRENT_USER\Software\OrgName\AppName

Here’s an extract of my own configuration file, it contains my email and password but the latter is probably encrypted and encoded in Qt’s esoteric encoding scheme. This isn’t an issue, we can just use Qt’s own functions, through PyQt for example, to decode the QSettings for us. The encrypted data however means we’ll have to do some reversing (yay!) to figure out the encryption scheme.

$ cat ~/.config/Genymobile/Genymotion.conf
[General]
configuration_screencast_save_folder=/home/----/Pictures
credentials.password=@ByteArray(\x13\x13\\\b\x1eh\x13\0\r\x5I(@\v\x10K/\x14H)
customer.email=--------@-----.com
...

Reversing the binary

Opening the binary in IDA confirms, from the mangled function names, that it’s indeed coded in C++ and that it uses Qt for the GUI (from the function names starting with Q...).

The string credentials.password is referenced by two functions, sub_11A780 and sub_11A9B0. Both functions call sub_11A590 ; sub_11A780 is called to store the credentials and sub_11A9B0 is called as a wrapper to decrypt the password before logging in to the API. This suggests sub_11A590 is both the encryption and decryption, heavily hinting at the usage of XOR encryption.

The function reallocates several variables before entering the following loop :

XOR encryption! The function loads a static key (shown below) and performs repeated key XOR encryption on the password.

“See how close the key was to the credentials.password ? Guessing could have made this whole project way easier”

Running the Python credential extractor with the key successfully retrieves the password.

Flex time

As I said at the beginning of the blog post, this is a follow-up to a conference I gave at Le Hack 2022. From it came my favourite photo of myself.

The photograph saw the opportunity and took it, what a lad