Cracking Music Server Software

May 13, 2018
cracking reverse-engineering

tl;dr

Generating licenses using MD5(email) is bad.

Validating licenses using HTTP in plain text is bad.

The Software

This post covers reverse engineering and cracking the license validation process of a popular and closed source self hosted music server application. The developer has been notified about the findings, however no reply has been received. The validation issues still exist as of now and affects all issued licenses. Because of this, the name of the application won’t be disclosed.

Source Code

The server application consists of a software package written in java. This means that decompilation can easily be performed using jd-gui or similar tools. Afterwards simple greping for license yields these interesting methods:

Further analysis shows that licensed are validated in two steps:

  1. Local checks using isLicenseValid(String email, String license)
  2. Server side checks using validateLicense()`

Local Checks

After digging into the source code, this code snippet has been identified to be responsible for local license checks:

public boolean isLicenseValid(String email, String license) {
   if ((email == null) || (license == null)) {
      return false;
   }
   return license.equalsIgnoreCase(StringUtil.md5Hex(email.toLowerCase()));
}

That’s right: All licensed issued are basically MD5(email). Bypassing this check is done by generating a MD5 hash of a random email address. This also means that you can hijack another persons license only by knowing his/her email address.

Remote Checks

Bypassing local checks is easily possible - not let’s have a look at the API call which triggers server side checks:

private void validateLicense() {
   String email = getLicenseEmail();
   Date date = getLicenseDate();

   if ((email == null) || (date == null)) {
      licenseValidated = false;
      return;
   }
   try
   {
      String content = HttpUtil.httpGet("http://<domain>/<endpoint>?email=" + StringUtil.urlEncode(email) + "&date=" + date
            .getTime() + "&version=" + versionService.getLocalVersion(), 120, 120);

      licenseValidated = ((content != null) && (!content.contains("false")));
      if (!licenseValidated) {
         LOG.warn("License key is not valid.");
      }
      String[] lines = StringUtils.split(content);
      if (lines.length > 1) {
         licenseExpires = new Date(Long.parseLong(lines[1]));
      }
   }
   catch (Throwable x) {
      LOG.warn("Failed to validate license.", x);
   }
}

To bypass the second validation stage, one could perform an easy attack: Redirecting domain locally to an attacker provided server which always returns the string true. However, another approach seemed to require even less work.

By spawning a docker container with the name domain and linking it to the application server, this check seemed to be bypassed. This step basically redirects domain to a non existing server which tricks the validation process into being successfull all the time. Most likely this is intended behaviour because people don’t need an internet connection to validate licenses.

In-Process Fuzzing With Frida

October 24, 2019
frida exploiting fuzzing reverse-engineering

Dynamic Instrumentation: Frida And r2frida For Noobs

September 13, 2019
radare2 r2 frida r2frida ctf reverse-engineering

r2con 2019 CTF Writeups

September 2, 2019
r2 radare2 ctf reverse-engineering