EASYCTF - Nosource

description: All you CTFers are sure getting on my nerves with your source-viewing and developer tools-ing! Alas, despite my best wishes, the experienced programmers on the wonderful website StackOverflow tell me that it’s impossible to keep you from looking at the HTML. But a disable right click script certainly won’t stop an experienced CTFer like you! So finding the flag in the source of this problem should be no trouble, right?

category: Web

When we launch the chall, a web page informs us that we have to use Chrome and enable Javascript. So I used Chrome and nothing happended. After playing with Javascript options in Chrome browser, I finally ended up to the ‘real’ chall page.

Here you can’t use ‘right click -> inspect element’ or ‘ctrl+u’ because it will redirect you to the first page and you won’t be able to get the source code like that.

The way I did it was using Wireshark: I use it to sniff the network when we get the challenge page.

Then we can get the source code by following the stream. Here is the source code:

<!-- Stop looking at the source code -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Stop looking at the source code</title>
<script class="delete">
	var timeout = setTimeout(function () {
		location.replace('/soupd?2');
	}, 1000);
</script>
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/main.css" rel="stylesheet">
</head>

<body>
<main role="main" class="container">
<div class="starter-template">
	<h1>Stop looking at the source code</h1>
	<p class="lead">
		Welcome to the problem page! Please enter the flag below.
	</p>
	<form id="flag-form">
		<div class="form-group">
			<input type="text" class="form-control" id="flag" placeholder="easyctf{">
		</div>
		<button type="submit" class="btn btn-success btn-lg">Check</button>
	</form>
</div>

<script class="delete">
	// Ah, old friend, we meet again...
	function process(a, b) {
		'use strict';
		var len = Math.max(a.length, b.length);
		var out = [];
		for (var i = 0, ca, cb; i < len; i++) {
			ca = a.charCodeAt(i % a.length);
			cb = b.charCodeAt(i % b.length);
			out.push(ca ^ cb);
			
		return String.fromCharCode.apply(null, out);
	}

	(function () {
		'use strict';

		function soupd() {
			document.documentElement.innerHTML = '';
			location.replace('/soupd?2');
			setInterval(function () {
				location.replace('/soupd?12');
			}, 100);
			

		try {
			let badNodes = document.getElementsByClassName('delete');
			for (let i = 0; i < badNodes.length; i++) {
				badNodes[i].parentNode.removeChild(badNodes[i]);
			}
		} catch (e) {}

		try {
			window.history.pushState({}, 'Stop looking at the source', '/');
		} catch (e) {}

		try {
			var element = new Image('/static/img/soup.png');
			Object.defineProperty(element, 'id', { get: function () {
			  soupd();
			}});
			eval("console.log('Stop looking at the source code%c', element);");
		} catch (e) {}

		var formEl = document.getElementById('flag-form');
		var inputEl = document.getElementById('flag');

		var func = "(function (e, v) { e.preventDefault() || " +
					"alert(inputEl.value === process(this.prototype.flag, " +
					"this.prototype.key) ? 'Your flag is correct!' : " +
					"'Incorrect, try again.'); })";
		var f = 'DQ4cJgsbCVofB18sNw4wRlhfCwAbXxpTC1wwKVlcGBIaUDAGJzowYDoqTiI=';
		var p = { prototype: { flag: atob(f), key: 'heheheh!' }};

		document.addEventListener('DOMContentLoaded', function () {
			formEl.addEventListener('submit', eval(func).bind(p));
			$('.delete').remove();
		});

	})();
</script>
</main>

<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="/static/js/jquery-3.2.1.slim.min.js"></script>
<script src="/static/js/popper.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script class="delete">clearTimeout(timeout);</script>

</body>
</html>

First let’s get rid of this html and build our javascript:

function process(a, b) {
	var len = Math.max(a.length, b.length);
	var out = [];
	for (var i = 0, ca, cb; i < len; i++) {
		ca = a.charCodeAt(i % a.length);
		cb = b.charCodeAt(i % b.length);
		out.push(ca ^ cb);
	}
	return String.fromCharCode.apply(null, out);
}

entry = "WeWriteHere"

var f = 'DQ4cJgsbCVofB18sNw4wRlhfCwAbXxpTC1wwKVlcGBIaUDAGJzowYDoqTiI=';

flag = atob(f);
key = 'heheheh!';

func()

function func() {
	alert(entry === process(flag, key) ? 'Your flag is correct!' : 'Incorrect, try again.'); 
}

It seems that all we have to do is to print process(flag, key) and write it as entry:

console.log(process(flag, key));
// -> ektCc~a{wb7I_kXg0:ces:rrc9XL19p3r5XcO_XARO&G

We try this in entry, but it didn’t work… We know the flag look like easyctf{…} And we can see: e??????{… Maybe the given key is not right. Let’s generate another one (we can see that the process methos only compute xor between entry and key):

# First character of the key is good (we want 'e' and we already have 'e')

# For the second character, we want 'a' in output, but we have 'k' and the actual second character key is 'e':
chr(ord('a')^ord('k')^ord('e'))
# -> o

# For the third character, we want 's' in output, but we have 't' and the actual third character key is 'h':
chr(ord('s')^ord('t')^ord('h'))
# -> o

# For the 4th character, we want 'y' in output, but we have 'C' and the actual 4th character key is 'e':
chr(ord('y')^ord('C')^ord('e'))
# -> _

# The 5th character of the key is good (we want 'c' and we already have 'c')

# For the 6th character, we want 't' in output, but we have '~' and the actual 6th character key is 'e':
chr(ord('t')^ord('~')^ord('e'))
# -> o

# For the 7th character, we want 'f' in output, but we have 'a' and the actual 6th character key is 'h':
chr(ord('f')^ord('a')^ord('h'))
# -> o

Okay let’s set our new key (‘hoo_hoo’) and print the flag:

function process(a, b) {
	var len = Math.max(a.length, b.length);
	var out = [];
	for (var i = 0, ca, cb; i < len; i++) {
		ca = a.charCodeAt(i % a.length);
		cb = b.charCodeAt(i % b.length);
		out.push(ca ^ cb);
	}
	return String.fromCharCode.apply(null, out);
}

var f = 'DQ4cJgsbCVofB18sNw4wRlhfCwAbXxpTC1wwKVlcGBIaUDAGJzowYDoqTiI=';
flag = atob(f);
key = 'hoo_hoo!';

console.log(process(flag, key));
// -> `easyctf{wh0s_a_g00d_s0urc3_v13w3r?_YOU_ARE!}`

We can validate the chall with this flag =)