Practical Website Hacking CTF: Exercise 1


Note: This post is part of a series related to the InfoSec Institute's Practical Website Hacking CTF. If you're interested check out the whole series.

Stated Mission Goal

People want you to store your favorite links here. However, you are not into that, you just want to do some XSS magic to the page. Add an alert with the message 'Ex1' to the page (My Sites:)

Stated Mission Vulnerability

Cross-Site Scripting (XSS) - OWASP Vulnerability Information


We'll be going over the Mission in great detail, and I'll be pointing out things that you might want to be familiar with as we go. Much of the content in this post is aimed at the person who, like me, is just starting out. I'm going to walk you through the mission and explain 3 differnt ways to complete it.

If you are just looking for a quick answer, you've come to the wrong place, my friend; we're going to really dig in.

Note: There's also a little game along the way regarding some of the content in each post. It doesn't do much just yet, but there will be something tying it all together at the end of this series and I'll come up with some sort of prize for sending the solutions to all the puzzles.

Let's get to it, then.

More than Mnemonics

First, it's always helpful to know what a form is doing and how the data is being sent before we really start working on anything. It's a good habit to start forming, so let's start with a basic test entry to see what happens when we submit this form.

Enter site in the Site Name input and url into the Site URL input and then submit the form.

Hmm, we've gotten an error stating we need to provide a URL. Obviously the form is doing some sort of validation of our URL text. This could pose a problem for our XSS attack, so let's see if there is any hint at what they are looking for in the code.

Right-click on the Site URL input and choose Inspect Element with Firebug or Inspect Element from the menu. If you've never used the Developer Tools of your browser before, now would be a good time to click around and get a sense for what's in there and what you can see. I'll be assuming you are vaguely familiar with the interface going forward.

Looking at the markup for this element, we can see the <input /> is set to have a type of url. This means the browser is actually enforcing URL validation on the field and not necessarily any code provided by the site. Just so we know what's going on with both fields, let's check out the Site Name field by either following the same process of right-clicking and inspecting it, or by opening the <label> element that comes before the parent <label> element of our Site URL input.

We can see a pattern attribute that is set to a value of [A-Za-z]+ which is a regular expression meaning any upper or lower case letters. So, we can reasonably assume that we are only allowed to enter letters into this field, but let's test to make sure. Try entering the <script> tag we'll need to satisfy the mission briefing and any URL.

Script Tag: <script>alert('Ex1');</script>

No dice. We get an error message asking us to "match the requested format." We could get into the poor user experience of having an input with specific requirements but not telling your user what those requirements are, but this is a simulated mission and that's a topic for another blog and another day.

We'll need to dig deeper.

Darkly Scanning

Choose the Scripts tab in Firebug, Degubber tab in Firefox's default Developer Tools, or Sources in Chrome (I'm going to start to use common language to refer to all of these when needing to from this point foward). Here we can see all of the JavaScript that the page is loading.

Never forget: Source code is your friend.

You can peruse the files are your leisure, but when you are ready to move on, choose the ex1.js file and continue reading.

Looking at the script we can see the initialize() method that sets up the exercise, including binding an event listener for the form submission: $("form.ex1").submit(function(evt) {. This event is bound using the popular jQuery library.

If you aren't familiar with it, it would be well worth your time to learn a bit about it as you'll find it on a large swathe of the Internet. Essentially the above code says, execute this anonymous function when the form with class ex1 is submitted.

Now, if you look just a bit below that you will see two variables, siteName and siteURL, being assigned. If you look closely at what the variable declarations you'll notice that both form inputs have any < or >, which we'll definitely need to create our <script> tag, replaced with their HTML entity equivalents, &lt; and &gt;.

It might also be worth making note of the jQuery selector, that's the CSS selector looking part inside the $() function, for each input. We can see that Site Name is selected via (".ex1 input[type='text']") and Site URL is selected via (".ex1 input[type='url']"). This tells us if the type of either input changes, the code will break because the value that is supposed to be assigned to one of these variables will no longer exist.

After the values have been retrieved and sanitized of any angled brackets, a paragraph tag, <p>, is added to the links section in the page's sidebar displaying our site name and site url.

Then a function called testForScript() is run, which we can assume checks to see if we have successfully injected the requested XSS attack to the page. If we were successful, the levelCompleted() function is executed.

Let's dig into this testForScript() function a bit more and see what the mission is actually looking for.

It appears that testForScript() generates a regular expression by calling spitRegex() and then checks the siteName and siteURL values we saw earlier for that regular expression.

Note: A lot of CTF/Wargame-style sites don't give you any access to the Regular Expressions that are used to check your answers. In this case we can see exactly what is expected, which is helpful.

A Most Excellent Adventure

Okay, so, we have established a few things:

  1. The Site URL input expects a URL
  2. The Site Name input expects only letters
  3. < and > will be replaced by their html entity equivalents
  4. The completion criteria involves having <script>alert('Ex1');</script> in one of the inputs
  5. Input type must not change

You might be wondering where we go from here. Take a moment to think through all the things we've looked at and test a few ideas of your own if you have them. The most important things you can learn from these kinds of exercises are how to explore possible options via research and your own curiosity and intuition.

If you can't get this to work, or want to know if you are on the right track, read on.

Warning: Mission Spoilers Below!

Enter the Matrix

Remember how we said the browser was performing the input validation? Do you also remember how we said Source code is your friend? Well, when we combine those two things, we can view and edit the source code that the browser is using to validate the inputs.

Inspect the Site Name input and change the pattern by double-clicking the pattern attribute's value and then replacing the current regular expression with a more lenient one, like [\S\s]+ (You could also double-click the pattern attribute and then delete the whole attribute from the element).

Now you can pretty much enter whatever you want into the Site Name field. Let's try our XSS script from before in our new, more open input:

Script Tag: <script>alert('Ex1');</script>

It looks like we've been foiled by the angled bracket replacement filter we made note of earlier. You'll see the script on the page, but it's being rendered as content inside the HTML tag because the angled brackets have been turned into HTML entities.

This means we're going to need to evade this filter somehow. There are many different forms of filters and many different types of evasion. Some are easy, some are very hard. In this case, we actually wouldn't really be able to run our script because all angle brackets will be converted into their HTML entity equivalents.

So, since we can't really beat the filter here, we have three options: 1) we can "cheat" a bit and finish this mission by basically tricking it into working, 2) we can "cheat" a lot and finish this mission by totally ignoring everything about it, or 3) we can do things the way the mission creators probably intended for us to and eliminate the filter from the equation.

I'll go over the two non-standard ways first and then delve into the "right" way to complete the mission. We'll go through the harder of the two first because I feel it will introduces you to a tool that most people who aren't web developers aren't all that familiar with. Then I'll mention the easier method and you'll wonder why I made you go through all of this (If you had been very observant when we first started looking at the ex1.js file, you might have noticed it yourself).

Then I guess I'll talk about the way we were supposed to handle this one, even though it's boring comparitively and requires a browser that lets you edit JavaScript files live (Hint: Firefox does not seem to like this).

Breaking Point

Note: The method I will be showing you for completing this mission will not work every where or for every circumstance, but I very rarely see it mentioned and think it's a great tool to add to your arsenal.

This is the unfortunate part where you learn that everything we've done up until this point was all just to familiarize you with the mindset you'll need to suceed at these kinds of tasks. Sorry about that.

So, let's go back to the Debugger tab of our Developer Tools and get back to the ex1.js file. Modern browser Developer Tools come with a really great feature to help web developers debug their JavaScript called Breakpoints. These allow you to set a point in the script where execution will pause until you tell it to continue. During this time, you have access to the current scope, which is really helpful in situations like this where the values we'll need to modify exist inside a closure.

If you are interested in JavaScript, I highly suggest you learn more about scope and closures if this is the first time you're really hearing about them.

Find line number 56, which should look like this: if (regex.test(variablesToCheck[i])) { and click in the margin to the left of the line number. You should see some sort of icon on that line, depending on which browser you are using it could be many different things, but it will commonly be a circle or a tag-like (rectangluar with an arrow at one end) object.

Refresh the page so that the browser will load the site with your breakpoint ready to go (This step isn't always necessary, it really depends on which browser and Developer Tools you are using).

Now when the code is executing, the browser will stop at that line and we will have full access to every variable in the scope of the testForScript() function. Go ahead and fill out the form and hit submit.

Execution should stop just before the code checks to see it's script regex is found. Click on the icon to toggle your Console, which should look like square with a > in it somewhere, and let's get "hacking."

If you start to type the name of the array we are looping through, you will notice the browser tyring to autocomplete it for you. Go ahead and type: variablesToCheck into the console and hit the Enter key. You should see the items you entered into the boxes as an array. Now, you get to replace either of the items in this array with the <script> tag we've been trying to enter this whole time.

I'm going to replace the first item in the array by running the following command in the Console:

variablesToCheck[0] = "<script>alert('Ex1');</script>";

Now if you type variablesToCheck and hit Enter again, you'll see that the first item in the array has been replaced with our <script> tag. Progress!

You may have noticed the sidebar on the right of your Debugger tab, which should show all the variables in the current scope. You may also notice the next line in the script checks to see if lastAlert is equal to patternText. In the sidebar, we can see that patternText is equal to Ex1, but lastAlert is equal to null. Looks like we need to update lastAlert if we want to keep going.

Enter the following command into the Console:

lastAlert = 'Ex1';

Now, we need to tell the script to continue executing. You can do this by clicking on the play button located in the top right or top left, depending on your browser and developer tools, of your Debugger tab.

The mission success text should show up and now you can celebrate and move on to Exercise 2, or you could keep reading and learn the "easy" way or skip ahead to the "right" way.

Going for Speed

Have any ideas what the easy way might be? I'll give you a hint, it involves the JavaScript file we've been looking at this whole time.

Open up the Console tab of your developer tools. Remember in the "hard" way where I told you if we started typing the variable name, the browser would try to autocomplete it for us because we had access to it? Try typing in the name of the function that runs when the mission parameters are satisfied: levelComplete(). Notice the browser trying to autocomplete that function name for you?

That means we can just call that function whenever we want. It also means we can bypass this mission. We don't necessarily learn about the XSS vulnerability that the CTF wanted us to, but we learn something more valuable. We learn to break things. We are now in control.

If you look at the parameters the function expects, it appears to take an integer, which we can assume corresponds to the level you are on.

Enter the following command into your console:


There seems to be a slight delay of about 10 seconds on the redirect this method triggers, so you won't see anything right away, but you will be redirected to level two shortly.

Spoiler Alert: This may be the only mission that awards you the Green menu item when using this method. And even if it wasn't, you shouldn't beat them all this way.

A Bogus Journey

Now, you may be saying to yourself, "But, we totally ignored the mission parameters and 'cheated' to complete this."

In some ways I agree with you, and I'm sure the InfoSec Institute CTF creators might agree with you, too. In other ways, I whole-heartedly disagree. Exploring systems and finding ways to break them and secure them is exactly what I think we should be learning about when we are studying InfoSec, Cyber Security, hacking, or whatever you'd like to label it.

Gotta Give Something

Okay, now that we've totally broken the mission, let's go about this the way the designers probably intended.

So, what we'll need to do is somehow prevent those pesky .replace()'s from running, and go back to that point earlier when we tricked the Site Name input into letting us enter anything we wanted.

Load this page up in a browser that will let you easily live-edit the ex1.js JavaScript file. I had luck with Chromium on Linux and Chrome on OS X, I'm sensing a theme here and think you might also have luck with Chrome on Windows.

Without futher ado, here's what you want to do to beat the mission. Right-click on the Site Name input and choose Inspect Element from the menu. Double-click on the pattern attribute's value and replace the regex, [A-Za-z]+ with our open regex [\s\S]+, which will let us input as many characters that are either spaces or non-spaces as we want.

Now, go to the Sources tab in your developer tools and find the ex1.js file in the sidebar. We need to edit lines 18 and 19 to remove the replace() function calls. Here are the altered lines for reference:

Line 18: var siteName = $(".ex1 input[type='text']").val().trim();

Line 19: var siteURL = $(".ex1 input[type='url']").val().trim();

Now, this part might have just been a bug with my Chromium install, but I needed to right-click on the ex1.js file in the sidebar and choose the Save option. I didn't actually save it anywhere, I just hit the Cancel button, but for some reason this made things work.

After closing the save dialog, right-click on the ex1.js file again and then choose Local Modifications from the menu. This should open a History pane that shows you have modified the file. If the pane is empty, you may need to refresh and try editing and pretending to save the file again.

Now it's time to enter our script tag into the Site Name input for the last time. You can enter any valid URL into the Site URL input.

Script Tag: <script>alert('Ex1');</script>

When you submit the form, the alert should show up. Click OK to see the success message and then wait to be redirected to Exercise 2.

Note: Save your answers to the game. There will be something at the end of the series tying the game in each post together.

comments powered by Disqus