Monday 25 June 2012

Mjölnir - Brute-forcing a keystore


Mjölnir Brute-force, or how to brute-force your own keystore password


If, like me, you've been in the unfortunate siutation where you have forgotten your own keystore (or certificate key) password then help may be at hand. After some fruitless searching I decide to write my own piece of software to brute force my keystore certificate.  As I am/have been a Java programmer I decided to write it in the language I knew best, and this is how I did it.

I have made the source code available for anyone who wants it

Loading the keystore


The Java API already has code for loading a keystore, java.security.KeyStore.  The keystore needs to be placed on the classpath (I just stuck it in the root on my Eclipse project)

// Load the keystore in the user's home directory
File file = new File(keystoreName);
is = new FileInputStream(file);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, keystorePassword.toCharArray());
return keystore;

The above code simply attempts to load a keystore with the given keystoreName (filename) and keystorePassword.  If the password is incorrect an exception will be thrown.  As I was in a hacky mood, I went with catching the exception and returning null if the password was at fault.  The full method looks like:

public KeyStore loadKeystore(String keystoreName, String keystorePassword) {
FileInputStream is = null;
try {
// Load the keystore in the user's home directory
File file = new File(keystoreName);
is = new FileInputStream(file);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, keystorePassword.toCharArray());
return keystore;
} catch (java.security.cert.CertificateException e) {
throw new KeystoreException(e);
} catch (NoSuchAlgorithmException e) {
throw new KeystoreException(e);
} catch (FileNotFoundException e) {
// Keystore does not exist
throw new KeystoreException(e);
} catch (KeyStoreException e) {
throw new KeystoreException(e);
} catch (IOException e) {
if (e.getCause().getMessage().contains("Password verification failed")) {
return null;
}
throw new KeystoreException(e);
} finally {
try {
is.close();
} catch (IOException e) {
// at least we tried
e.printStackTrace();
}
}
}

To check a keystore password this just needs to be called a LOT of times with different configurations of password.  However we will come back to that shortly as, on this occasion, I knew my keystore password but not the password for the certificate it contained.

Loading a keystore key

So I now needed to load the certificate.  Java also has an API for this in the java.security.Key class.  In a similar vein to before, we can wrap this call so it returns null if unsuccessful

public Key loadKey(KeyStore keystore, String keyAlias, String password) {
try {
// get my private key
Key key = keystore.getKey(keyAlias, password.toCharArray());
return key;
} catch (NoSuchAlgorithmException e) {
// let it return null
} catch (KeyStoreException e) {
// let it return null
} catch (UnrecoverableKeyException e) {
// let it return null
}
return null;
}

Assuming we could load the keystore, we can use that to try and load the key.  Either way, we now have methods for attempting to load a keystore of key.  Null will be returned if we are unsuccessful

Brute forcing


Next we need to be able to call one of these methods but alter the password attempt each time.  Firstly I defined the possible characters it might comprise:

// possible characters used
char[] charset = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z' };

You can add, or take away, any that you want.  We will also need to store the current guess so we can increment it on the next iteration. I've stored them like this

private char[] cs; // Character Set
private char[] cg; // Current Guess
public BruteForce(char[] characterSet, int guessLength) {
cs = characterSet;
cg = new char[guessLength];
Arrays.fill(cg, cs[0]);
}

Next we need to increment the current guess each time so we are trying a different password, making sure we try every combination

protected void increment() {
int index = cg.length - 1;
while (index >= 0) {
if (cg[index] == cs[cs.length - 1]) {
if (index == 0) {
cg = new char[cg.length + 1];
Arrays.fill(cg, cs[0]);
break;
} else {
cg[index] = cs[0];
index--;
}
} else {
cg[index] = cs[Arrays.binarySearch(cs, cg[index]) + 1];
break;
}
}
}

The above method increments the currentGuess array each time it is called, making it 1 larger if necessary.  This means we try every combination of every character in the array eg
aa
ab
ac
ad

and so on

The current guess can then be used to attempt to load the keystore, or key.  If null is returned the password was wrong and the next guess can be attempted eg

public synchronized String getNextAttempt() {
increment();
return String.valueOf(cg);
}
String attempt = attack.getNextAttempt();
boolean found = source.attempt(attempt);

Notice the use of the synchronized key word - I added this as I ran it in a multithreaded environment.  Making the method synchronized means that the incrementing and getting the next guess are atomic, so no other thread can access the next guess at the same time.  This ensures that none are missed

Putting this all together I managed to brute force a 7 character password in about 5 hours (using 100 threads on an i7 PC)

I've built a min-framework around it and also added an heuristic brute force attack (using keywords I might use in a password) until I fouind that it wasn't any of those, leaving me to have to brute force it. It can be found on our site.  Feel free to use it.  If anyone wants to take on developing it further let me know and I'll give you access to the repository