EASYCTF - Nosource
21 Feb 2018
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 =)