Reversing Blizzard's Authenticator

Reversing Blizzard’s Authenticator #

When you sign up for Blizzard’s TOTP authenticator, they force you to use a Blizzard app to generate the TOTP codes. I wanted to keep backups in a standard format and just use my password manager. However, there does not seem to be an easy way to convert the Blizzard authenticator to a normal TOTP code. However, we can extract the secret key from the app’s shared preferences and decode the secret key.

Extracting the secret key #

The Blizzard app stores a hashed version of the key in a shared preferences file. On Android, this file can be easily extracted by looking at the “/data/data/com.blizzard.bma/shared_prefs/com.blizzard.bma.AUTH_STORE.xml” if you have root.

If you do not have root, then normally we would be out of luck. However, Blizzard set the “android:allowBackup=“true”” flag in the Blizzard authenticator apps application manifest. This offers a rootless way to get the value of the “com.blizzard.bma.AUTH_STORE.HASH” shared preference. Check this post for more details.

Unhashing the Key #

We can pull the Android apk using ADB to get the compiled APK. Then we can run the file through something like BytecodeViewer to get a nice Java version of the code. After reading through the token and TOTP generator classes, Blizzard is simply doing a standard SHA1 based implementation of TOTP with a 30 second period. One interesting difference from standard implementations is the addition of a time offset field that is added onto the server time. In my case however, the time offset was around 900 milliseconds so it didn’t really make a difference.

The “AUTH_STORE_HASH” that is stored in shared preferences is hashed using a hard-coded mask so the key is not stored directly in shared-preferences. But this is pretty weak since it’s hard coded and can be easily reversed as follows.

import java.util. * ;
import java.lang. * ;
import java.io. * ;

class Dehasher {
  private static final byte[] HASH_MASK = new byte[]{57, -114, 39, -4, 80, 39, 106, 101, 96, 101, -80, -27, 37, -12, -64, 108, 4, -58, 16, 117, 40, 107, -114, 122, -19, -91, -99, -87, -127, 59, 93, -42, -56, 13, 47, -77, -128, 104, 119, 63, -91, -101, -92, 124, 23, -54, 108, 100, 121, 1, 92, 29, 91, -117, -113, 107, -102};
  private static final String HASH = "00112233445566.. put your hash from AUTH_STORE in here";

  private static int hexCharToInt(char var0) {
    if (var0 >= '0' && var0 <= '9') {
      return var0 - 48;
    } else {
      byte var1 = 97;
      if (var0 < 'a' || var0 > 'f') {
        var1 = 65;
        if (var0 < 'A' || var0 > 'F') {
          return 0;
        }
      }

      return var0 - var1 + 10;
    }
  }

  private static byte[] applyMask(byte[] var0) {
    for (int var1 = 0; var1 < var0.length; ++var1) {
      var0[var1] = (byte)((byte)(var0[var1] ^ HASH_MASK[var1]));
    }

    return var0;
  }

  public static byte[] hexStringToBytes(String var0) {
    int var1 = var0.length() >> 1;
    byte[] var2 = new byte[var1];

    for (int var3 = 0; var3 < var1; ++var3) {
      int var4 = var3 * 2;
      char var5 = var0.charAt(var4);
      char var6 = var0.charAt(var4 + 1);
      var4 = hexCharToInt(var5);
      var2[var3] = (byte)((byte)(hexCharToInt(var6) & 15 | var4 << 4));
    }

    return var2;
  }

  public static void main(String[] args) throws java.lang.Exception {
    String data = new String(applyMask(hexStringToBytes(HASH)));

    System.out.println("Secret (hex): " + data.substring(0, 40));
    System.out.println("Serial: " + data.substring(40));
  }
}

Then we can simply convert the secret hex-string to base 32 with something like Cryptii. This base 32 string can be encoded into a standard OTP url and used in any TOTP generator.

Calendar September 26, 2020