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.