Natas CTF - Levels 0 to 10

Posted by Tom on 2012-11-16 12:43

In the last year or so I've become quite enamoured with netsec capture the flag games, after stumbling across the first Stripe CTF earlier in the year. Since then I did the second Stripe CTF and have been chugging through Smash The Stack's IO. I always find myself learning a lot and enjoying the challenges, even if it means learning gdb from scratch pretty much every time. That stuff just doesn't seem to stick.

Anyhoo, the latest one I've been playing with is the Natas CTF from Over The Wire. The first levels are pitched at people looking to learn the basics, although shit gets real from level 11 and up, so I'll cover solutions for up to level 10 for now and fill in the others in a later post, since they're a lot more involved.

It goes without saying that here be spoilers . . .


Level 0

The password for the next level is in a comment of the HTML, so view source and we're good to go. Yeah, this stuff starts pretty gently.

The mistake: The key is right there in the HTML.

The solution: Don't put the key in the HTML. Srsly.

Level 1

Same again, but we can't right-click and get to the context menu to view the source. We can disable Javascript in the browser. We can snag the response from the wire with a proxy. We can use wget. Or just use the menus like your grandma. That's what I did.

The mistake: Relying on Javascript for security.

The solution: Don't send anything to the client that you don't want them to see. Trivial, but meh. Also, Javascript is easy to disable. Don't make it something your system depends upon. That includes data validation. By all means use it to report failures to the user early, but you need to replicate that validation on the server.

Level 2

There is nothing on this page

OK, now we're getting somewhere. Viewing the HTML will reveal a 1x1 pixel image with the path /files/pixel.png. Point your client at /files/ and we find that the directory is browsable. Open users.txt for the sweet, sweet passwordy goodness.

The mistake: Not browsable directories (although the use-case for them is pretty small - just turn them off), but rather putting sensitive information below the webroot.

The solution: Don't do it.

Level 3

<!-- No more information leaks!! Not even Google will find it this time... -->

Viewing source this time reveals this clue. The hint is the reference to Google. Browse to /robots.txt to see which URLs are explicitly blocked to web crawlers, and then refer to the previous level.

The mistake: robots.txt is for making things not appear in search engines (as long as they honour it), not hiding things. If your security depends entirely making it available for the world but assuming people won't find it, then you're bad and you should feel bad. Source: reality.

The solution: Same as above. Don't make your super squirrel secrets servable.

Level 4

Access disallowed. You are visiting from "" while authorized users should come only from ""

The most obvious way to establish this is to check the Referrer (or Referer, because HTTP can't spell), so we'll need to manually tweak the HTTP headers of the request. I ended up using Fiddler for this, although there's several Firefox plugins for modifying headers, and you can do the same with curl or wget. Set the Referer header to "", and open sesame!

wget --user=natas4 --password=PREVIOUS_LEVELS_PASSWORD

The mistake: Trusting what the client is sending you.

The solution: Don't do it. More specifically: if you really, really need to know where a user came from you'll need to put something in their session - session! not cookie! If it's across applications then it starts to get complicated and will involve links containing tokens or tracking Javascript in one or the other domain.

Level 5

Access disallowed. You are not logged in

Ruh roh.

There's not a lot to go on here, so let's check the usual suspects. Persistence generally means cookies, and the Web Developer addon for Firefox allows us to view and edit cookies for a page, among its many talents.

Edit, you say? Why yes, I think I shall. Set it to 1, reload the page and the key is yours.

The mistake: Trusting what the client is sending you.

The solution: Don't do it. More specifically: if you really, really need to know where a user came from you'll need to put something in their session - session! not cookie! If it's across applications then it starts to get complicated.

Level 6

This one is essentially the same as levels 2 and 3, with some PHP in the way to muddy the waters. Browse to the file specified in the source and then enter it as the password.

The mistake: Sharing your source code?

The solution: Know where your secrets are. I've checked passwords I shouldn't have into open source repositories before myself. Put it in config files or the database.

Level 7

The 'page' parameter in the querystring is vulnerable to local file inclusion. We know that the flags are stored in /etc/natas_webpass/natasX (where X is the level number). So by browsing to /index.php?page=/etc/natas_webpass/natas8 we get to see the next flag.

For shits and giggles you can also include anything else in the file system which you have read access to. /etc/passwd? You got it.

The mistake: Directory traversal.

The solution: At the very least normalise the path and make sure they're staying in the webroot, although it's better to make the user-supplied parameter a key into a lookup so they can only browse to fixed values. And forcing the extension of the parameter on the server side would help. Either which way, you're basically reinventing the CMS.

Level 8

For this one we know the mechanism by which the secret is encoded and all of the operations are reversible so we can simply do the inverse of those functions in the reverse order and we're good to go. I ended up using since most of the others didn't support 5.4 (which is necessary for the hex2bin function), but it seems to be on the fritz at the moment so try


Now we've got our password, enter it into the form to continue.

The mistake: See level 6.

The solution: See level 6.

Level 9

This level is passing user input almost directly to the command line. The parameter is not even quoted in the command sent to the OS, we can add a space and a second file to search through. We can use . to match everything and then comment out the rest of the line using #.

. /etc/natas_webpass/natas10 #

The mistake: Passing client input directly to the command line.

The solution: Find another way. You could mitigate the damage of this kind of thing by careful quoting and encoding and character filtering, but you've only got to miss one and you're screwed.

Level 10

Turns out my exploit for Level 9 works here as well. Which is nice.

The mistake: Same as the previous level.

The solution: If you're going to deny characters, consider whitelisting rather than blacklisting. It's easy to miss a few and it's a good idea to fail closed for security-sensitive code.


That'll do for now. Things get a little more involved from here on out, so I want to devote more time to each one. Stay tuned!