15 minute read

Today NeverLAN CTF concluded with my team being somewhere in the top 1/4 out of 1600+ teams. I have learned a bunch about SQL and JavaScript, so to me it was time well spent. I spent most of it on Web Application challenges, as those seem to be the thing that interests me most and i would like to explain how they are all solved. Since this CTF was aimed at middle schoolers, that will be the level that i will explain the solutions on. The writeups will go as a narrative, with me explaining concepts as the challenges go. One of the challenges requires that you use Kali Linux, as it is an operating system created for those who test and implement network and computer security and it is good if you learn it. These writeups assume a certain level of computer knowledge and as much as i would like to, i can’t explain everything, as this article would become endless. Some of the stuff is linked for you to read up on in different sources, some of it you can google. With that said, let’s begin.

Cookie_monster:

This web page greets us with the following text:

He’s my favorite Red guy

That of course is referring to Sesame Street’s character Elmo, but how do you submit his name with no input fields? Very simple, open up Burp Suite from the Web Application Analysis category in your Linux Applications drop-down menu. Open the Proxy tab, now have your intercept on and visit the challenge page. If you forward all the packets that you got and open up your HTTP history, you’ll see a request to the challenge URL with the method GET. That simply means you are getting a web page from the server. Now look at the raw request:

GET / HTTP/1.1
Host: challenges.neverlanctf.com:1110
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: Red_Guy's_name=NameGoesHere
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

Nearly everything here is irrelevant to us in this challenge. Right click the request and send it to the repeater. Repeater is simply the tab that lets us make HTTP requests repeatedly to get or send out data. The little bit that is not irrelevant to us is the Cookie. A cookie is a piece of information a website stores on your device to identify and serve you better. Our cookie is the following:

Red_Guy's_name=NameGoesHere

Knowing the name of the red guy and having access to the cookie we can simply edit the raw request and put his name in like this:

Red_Guy's_name=Elmo

Now simply press Go to send out your request and look for the response on the right.

<!DOCTYPE html>
<html>
    <head>
        <title>Cookie_monster</title>
    </head>
    <body>
    <p>You got it! flag{YummyC00k13s}</p></body>
</html>

There’s your flag and the challenge is solved.

Things are not always what they seem:

As the title of the challenge says, don’t blindly trust what you see. Right click anywhere in the page and see it’s source. This is written in Hypertext Markup Language, or HTML for short. It’s not a programming language and it’s really simple to understand. It is made out of tags. Tags have an opening and are closed. An opening tag looks like this: <html>; and a closing tag looks like this: </html>. Tags can be placed in other tags and the tag that everything is placed in is the <html> tag. Inside of <html> there most likely is <head> for header and <body> for the main body of the page. Most stuff you see on a web page is in the <body> tag. The most common tag to use inside of the body is the <p> tag that stands for paragraph. We have a <p> tag inside of the <section> tag in the body of our page. Right after it is a tag, which is normally used when you have a bunch of text, but you want a part of it to have unique properties. Tags can be modified with styles that come from Cascading Style Sheets or CSS for short, which is also not a programming language. our span tag looks like this span style=”color:white;” which means the text inside of it is colored white on a white background, so you won’t see it unless you look into the source or try to select the text. Inside of the tag is the flag that you need:

<span style="color:white;">Well, look at you go! You can see that sometimes everything isn't as it appears... flag{Whale_w0u1d_y0u_l00k3y_th3r3}</span>

Here’s your flag. Challenge solved.

Dirty Validate:

Welcome to your first login page challenge. You will see multiple in this CTF. This one is simple. One thing about HTML is certain: it’s static. All it does is show you documents. It can’t interact with them in any way. You can’t make a login page with something that can only display things. That is where JavaScript comes in. It is a programming language that works with the page and your browser to make the pages dynamic and make them interact with what you do. JavaScript code is included in HTML with the script tag. If you inspect the page source, you’ll see there is JavaScript code for the website’s authentication. That is really, really bad because JavaScript does not operate on the server. It operates on the side of the user and the user may not have noble intentions, so don’t trust what the user gives you. Make sure it’s safe first. Making the user input safe solely with JavaScript is not a good idea, because they can see the code and trick their way through it. This page’s developers were nice enough to leave comments for us, that explain what the lines of code do. Comments in JavaScript start with //. This is the first comment of interest to us:

// make an ajax request to get the expected username

AJAX is Asynchronous JavaScript And XML. This comment is a fancy way of saying “checking if the username is right”. This is the statement the comment refers to:

$.ajax('webhooks/get_username.php',{})

As you can see, this clearly shows us a path of the page that the username is checked against. .php simply is the scripting language that page is written in. PHP is server-side, as opposed to JavaScript, which is on the user-side of things.

webhooks/get_username.php

Lets see if we can open this page ourselves by appending it to the url we have:

https://challenges.neverlanctf.com:1135/webhooks/get_username.php

This gives us a list of 4 usernames:

{"usernames":[ "AdamsFamily","Mr. Clean","Dr. Whom","JimmyOneShoe" ] }

Half of the job is done, now we need to get the passwords for them. Back to the source of the login page. This is the second comment of interest to us:

// make an ajax request to get the expected password for the given username

It refers to the following statement:

$.ajax('webhooks/get_password.php?user='+encodeURIComponent($('#name').val()),{})

It again shows us a path of the page that the password is checked against. This time it is different though, because there is more stuff after the ? sign. What goes after the ? sign in the URL is the parameters. We can see that the code uses a “user=’+encodeURIComponent($(‘#name’).val()),{}” parameter. This might look complicated, but it’s not. URL’s cannot contain special characters, so they need to be represented in a different way. That representation is called encoding. Why do we need this for passwords? Because some of our usernames have special characters like a space. encodeURIComponent encodes our input. That means that we can mess with this parameter by modifying it right in the address bar. We know 4 usernames, but i’ll tell you right away, the only one we need is Dr. Whom. The rest are simply there to distract you. Now let’s encode this username using any online encoder and we get this: Dr.%20Whom. %20 is how this encoding represents a space.

Now let’s make our http request to this path with the URL encoded parameter that we got:

https://challenges.neverlanctf.com:1135/webhooks/get_password.php?user=Dr.%20Whom

We get a strange looking string with an = sign at the end:

ZmxhZ3tEMG4ndF83cnVzN19KU30=

The equals sign should be a red flag to you instantly: this is most likely base64, which is a way of encoding different from URL encoding. base64 is a favorite of most CTF creators and you will see it a lot. You can use any online decoder to see what this string is:

flag{D0n't_7rus7_JS}

Challenge solved. Don’t trust JavaScript.

React To This:

This challenge is based on a website built with the popular React JavaScript library. This challenge is also very simple and only requires you to look around the source for the website. It’s more than one page though. Open up the developer tool of your browser and go to the Sources tab. Notice that at the root we have the static folder and inside of it we have css, js, node_modules, statis/js and webpack folders. Let’s go exploring in order. CSS is styling, so it’s of no interest to us. js has a bunch of interesting stuff though: Components, Pages and store folders, as well as app.js, index.js, main.237378d2.js and registerServiceWorker.js JavaScript files. Let’s look at app.js. It is nicely commented for us and there is something we may be interested in:

// Pages
import Home from './Pages/Home.js'
import Admin from './Pages/Admin.js'

Now unlike with the previous challenge, we can’t access this path through the URL to see what page comes up, but what we can do is open their source code, which is even better. Let’s go to the Pages folder that we have in the js folder. As a hacker it’s always better to attack whoever has the most power, so let’s go for the admin page:

class AdminClass extends React.Component {
  render(){
    var admin = 'You are not an admin.';
    var flg = 's3cur3_y0ur_s3ss10ns';

This page does a whole lot of stuff, but most importantly it creates a function with two variables with one of them being the flag. Challenge solved.

Console:

The only thing this challenge has is a text box for input, but don’t be fooled and follow the advice from all the previous challenges: don’t trust JavaScript. This console is a distraction and the real console you need is the one in your browser’s developer tools. But first let us look at the page source. These two functions interest us most:

function what(){
	var input = document.getElementById("pass").value;
	if( md5(input) == "7b1ece53a46f4a5a2995b9cf901bf457" ){
	   getThat('Y');
	}else{getThat('N')}
}
function getThat(strg){
	if(strg == 'Y'){
		// Note: There is no data sent to the key.php file...
		// jquery ajax reference: https://api.jquery.com/jQuery.ajax/
		$.ajax({
			type: 'GET',
			url: '1/key.php',
			success: function (file_html) {
				// success
			   foo.innerHTML=(file_html)
			}
		});
	}else{
		foo.innerHTML = "Nope, try again";
		
	}
}

The first function checks to see if the user input when hashed equals to 7b1ece53a46f4a5a2995b9cf901bf457. Hashing is like encryption that you don’t need or want to reverse. Most websites and organizations don’t actually keep your password in plaintext, they keep the hash of it. It’s gibberish that can’t be reversed. But given the same input, the hash will produce the same string, also called a digest. That means we can check to see if your password is correct without knowing your password, which is great for your privacy. The digest we have does not appear to be a real one and hash analyzers can’t confirm it is MD5, which stands for Message Digest. If it was MD5, then your job would be much simpler, because MD5 has been broken a long time ago and is not considered secure, because you can reverse it.

if( md5(input) == "7b1ece53a46f4a5a2995b9cf901bf457" ){
	getThat('Y');
}else{getThat('N')}

As stated by this condition in the first function, if the hash of your input equals the 7b1ece53a46f4a5a2995b9cf901bf457 digest, then we call the second function called getThat and pass it the argument ‘Y’ (presumably for Yes) and if it doesn’t, then we call getThat with the argument ‘N’ (presumably for No).

We don’t need to go through the getThat function too much. All we need to know is that it has a condition that is true when it’s given ‘Y’ and is false when ‘N’. If this condition is true, then it shows us an HTML page for success.

$.ajax({
type: 'GET',
url: '1/key.php',
success: function (file_html) {
	// success
   foo.innerHTML=(file_html)
}

So now the question is, what is stopping us from calling the getThat function directly, instead of supplying input for the first function and having it call it, depending on what we supply? Nothing really, so let’s do that. Let’s make the condition true by calling getThat with the argument ‘Y’. Open the console tab in your developer tools and enter getThat(‘Y’)

As expected, this works and adds the following to our page:

Password: flag{console_controls_js}

Challenge solved. That was the last JavaScript challenge. Don’t trust it.

SQL Fun 1:

This page greets us with a text box to input SQL code. SQL is Structured Query Language. It is a programming language for databases and it has a relatively simple syntax. This is how we normally write a query (request) for a SQL database:

SELECT * FROM table WHERE field = value

SELECT just states what to get from the database, in this case it’s an asterisk sign, which is a wildcard meaning everything. FROM sets the table we will query. WHERE defines what field we are querying with what value. If we were looking for Jimmy in the database, his name would be the value. Nicely enough, we are provided with the rest we need to complete our SQL statement. “Fname” is the field name where Jimmy’s name is stored and the table we need is called “users”, so let us create the SQL statement:

SELECT * FROM users WHERE Fname = 'Jimmy'

Our query works and gives us this information in a table:

idUsernameFnameLnameEmailPassword
2JimWillJimmyWillmanSQL@example.comflag{SQL_F0r_Th3_W1n}

His password is your flag:

flag{SQL_F0r_Th3_W1n}

Challenge solved.

SQL Fun 2:

The sequel to SQL Fun 1 has multiple simple steps in it. Firstly, we know there is a users table and a passwd table. We know the field we need in users is “Lname” and the value is “Miller”. Let us look at these tables separately. This will show us everything in the users table:

SELECT * FROM users

We can see that every user has an id and the table drops a hint by suggesting we JOIN another table. Let us check out the passwd table before we do that:

SELECT * FROM passwd

This table has an id field and a user_id field. We can already see the base64 string with the flag, but let’s refrain from being direct for now, we’re learning SQL after all. The common field between the two tables would be the id field from users and the user_id field from passwd. The hint earlier suggested we join the tables, which refers to a sql command of course. There are multiple ways of joining tables, but lets use the most basic one:

SELECT * FROM table1 INNER JOIN table2 ON table1.commonfield = table2.commonfield;

Plug in everything we know and we get this:

SELECT * FROM users INNER JOIN passwd ON users.id = passwd.user_id;

Now you see the base64 string corresponds to Tom Miller (which we already knew), so lets decode it and get the flag:

flag{W1ll_Y0u_J01N_M3?}

Challenge solved.

Das Blog:

Johnny has a blog it seems, however he doesn’t clean the input he gets from users. Open up his login page and we have a username field and a password field. Lets try using Johnny as a username and leave the password blank.

Sorry, That Username / Password is incorrect.

Alright then, let’s try something different. Let’s try Johnny’:

There was a problem when looking for user. Please try again.

Very interesting. This indicates to us that the username field is vulnerable to a SQL injection, one of the most famous vulnerabilities ever discovered. Now let’s try to guess and visualize what his SQL query looks like for the login page. Remember the basic statement we used?

SELECT * FROM table WHERE field = value

The difference with this blog is that we have 2 values, but it doesn’t change much. We still control the username value and we can insert something malicious in it to mess with the SQL code. This is most likely how the code looks like:

SELECT * FROM Users WHERE username ="Whatever we input" AND password ="whatever"

If this SQL statement returns anything, it is true. If it doesn’t, it’s false. We only successfully log in when it is true, so let us make it true without knowing the password or username. If we can input this into the username, it should make the SQL statement true. – is simply a one-line comment.

whatever' OR True -- '

The SQL statement would look something like this on the inside:

SELECT * FROM Users WHERE username ="whatever' OR True -- '" AND password ="whatever"

And unsurprisingly the authentication process breaks and tells us this:

You are now logged in as Johnny with permissions admin

Proceed to the main page and claim your flag in the top post:

flag{3sc4pe_Y0ur_1npu7s}

Challenge solved.

Das Blog 2

In the sequel to our SQL injection adventures from Das Blog 1 this same payload works on the username field and lets us bypass authentication:

whatever' OR True -- '

The catch is that we no longer have admin priviliges and therefore can’t see the post with the flag.

You are now logged in as Johnny with permissions user

This challenge is really hard to complete without a hint. The hint tells you this:

Try the basic injection technic where permissions=’admin’

Looks like “permissions” is a field name and “admin” is a value for it, so let us modify our SQL payload from Das Blog 1:

whatever' OR permissions='admin' -- '

You are now logged in as JohnnyAdmin with permissions admin

Let us go visit the main page and pick up our last Web Application flag:

flag{Pwn3d_W1th_SQL}

Challenge solved.

In conclusion, NeverLAN CTF was a great brush-up on the basics for me and was very appropriate for middle-schoolers where difficulty is concerned. Thanks to my team Interplanetary Ion Cannon for solving aside me and thanks to the NeverLAN CTF team for great challenges and effort.

Categories:

Updated: