Skip to content Skip to navigation

JavaScript jail

Category: 

We have an ip address and a port. Connected using netcat we got V8 JavaScript shell. print(Object.keys(this)) gives us all global objects available: print, quit, checker, check.

print(check) gives our pwn target:

function (rand) {
	function stage1() {
		var a = Array.apply(null, new Array(Math.floor(Math.random() * 20) + 10)).map(function () {return Math.random() * 0x10000;});
		var b = rand(a.length);

		if (!Array.isArray(b)) {
			print("You're a cheater!");
			return false;
		}

		if (b.length < a.length) {
			print("hmm.. too short..");
			for (var i = 0, n = a.length - b.length; i < n; i++) {
				delete b[b.length];
				b[b.length] = [Math.random() * 0x10000];
			}
		} else if (b.length > a.length) {
			print("hmm.. too long..");
			for (var i = 0, n = b.length - a.length; i < n; i++)
				Array.prototype.pop.apply(b);
		}

		for (var i = 0, n = b.length; i < n; i++) {
			if (a[i] != b[i]) {
				print("ddang~~");
				return false;
			}
		}

		return true;
	}

	function stage2() {
		var a = Array.apply(null, new Array((myRand() % 20) + 10)).map(function () {return myRand() % 0x10000;});
		var b = rand(a.length);

		if (!Array.isArray(b)) {
			print("You're a cheater!");
			return false;
		}

		if (b.length < a.length) {
			print("hmm.. too short..");
			for (var i = 0, n = a.length - b.length; i < n; i++) {
				delete b[b.length];
				b[b.length] = [Math.random() * 0x10000];
			}
		} else if (b.length > a.length) {
			print("hmm.. too long..");
			for (var i = 0, n = b.length - a.length; i < n; i++)
				Array.prototype.pop.apply(b);
		}

		for (var i = 0, n = b.length; i < n; i++) {
			if (a[i] != b[i]) {
				print("ddang~~");
				return false;
			}
		}

		return true;
	}

	print("stage1");

	if (!stage1())
		return;

	print("stage2");

	if (!stage2())
		return;

	print("awesome!");
	return flag;
}

The flag is contained in closure made by calling checker. Since there is no any legal method to take variables from closure we have to deceive check tests somehow. It's JavaScript baby, we can redefine everything. The simplest solution is to redefine Array.apply in a way it returns empty array and to return empty array from our rand function. Obviously two empty arrays are the same size and have same elements. Let's do it:

Array.apply = function() {return [];};
function rand() {return [];}
check(rand);

This is it.