Introduction


Web Security.

The web is just clients and servers passing messages back and forth, and every piece in that exchange has to decide what to trust. Almost every web vulnerability begins in that decision.

This module is your on-ramp. You'll get to know the environment each challenge runs in, then start turning a server's own behavior against it, land your first shells, and see how a single byte in a URL can change what a request means. None of it is academic. These are the same small mistakes behind real breaches, and here you're the one on the other side of them.



Environment

Welcome to web security. Before you attack anything, get to know the ground you're standing on, because you have an advantage here that you won't get in the real world.

Every challenge runs inside a container, and you have a shell inside that same container. The web app isn't a distant black box, it's a local program: you can read its source at /challenge/server, find it in ps, and watch it open files your own user can't.

It isn't running yet. Start it like any other challenge, by running /challenge/server. It stays in the foreground while it runs, so open a second terminal to talk to it with curl -k https://challenge.internal. The -k lets curl accept the challenge's self-signed certificate. The flag is on the page it returns. There's no proxy in front of this one, so the Challenge interface can't reach it. If you'd rather use a browser, there's one in the Desktop workspace.

Inside the container, you could talk to the server directly. In the real world you can't, so this challenge makes you reach it the way an outside attacker would.

An nginx proxy now sits in front of the server on port 80, and the dojo exposes that port as the Challenge interface, in the bottom-left menu. That's your way in.

The direct route is closed: curl -k https://challenge.internal from a terminal just returns connection refused, because the server only answers the proxy now. Open it in the browser, and the page will show you how the server saw your request, with the flag alongside it.

The app wants a key, and it won't tell you which one.

It doesn't need to. The key was written directly into the server's code, and that code is sitting in your container in plain text. Hardcoded secrets are a common real-world mistake, and the source is the first place to look. Read it with cat /challenge/server. Find the value your input is compared against, bring it to the Challenge interface, and submit it for the flag. The proxy-only rule from the last challenge holds from here on.

When you load the page, the server sets a key cookie in your browser, and it won't hand over the flag until you send that value back. It never shows the value on the page.

Your browser has it, though. Open devtools with F12 and look: cookies are under Application in Chrome, Storage in Firefox. The cookie isn't HttpOnly, so the console can read it too with document.cookie. Send that value back through the page to get the flag.

The page gives you a key and stays locked. There's no form and no button, so there's nothing on the page you can submit it with.

So don't use the page. Put the key in the URL, where everything after the ? is a parameter you set yourself: add key= followed by the value shown on the page. The dojo serves this app under a path prefix, so add the parameter onto what's already in the address bar instead of replacing it. Load it, and the page unlocks.


Shell

You rarely break into a system and find a shell waiting for you. A bug gets your foot in the door, one command you can run or one file you can write, and from there you have to build your way up to real control of the box.

What you're building toward is a shell that talks back to you, and there are two ways to wire one up. A bind shell has the target open a port and wait for you to connect, which falls apart the moment a firewall blocks the way in, NAT hides the target, or someone notices the open port. A reverse shell flips the direction: the target connects to you. Outbound traffic is usually trusted, so the machine slips out past the firewall and hands you a shell from the inside.

The server is already up. Poke its /reverse endpoint and it fires off a reverse shell to localhost on port 1337. Be listening when it does.

A bind shell works the opposite way. The target opens a port and waits, and you connect to it.

That needs an open path from you to the target, which is the thing you usually don't have. So it's situational: useful when you're already inside the network, or when it's the outbound connections being watched and the inbound ones slip by.

The server is already up. Hit its /bind endpoint and it opens a shell on localhost port 1337. Connect to it and grab the flag.


URL & Encoding

A URL parser sorts every byte into one of two piles: content, or punctuation. & marks the end of one parameter, = divides a name from its value, and the moment the parser spots one it splits the string right there, whether or not that's what you meant.

That's fine until the value you actually need to deliver is built out of those very bytes. Send them raw and your input gets sliced apart before the server ever sees it whole.

The server is already up, and it reads a payload argument. Check /challenge/server to see what it expects. Your job is to smuggle your delimiter bytes all the way into payload without the parser prying them loose along the way.

Not every payload is a line of shell. When a server runs whatever you feed it, nothing says you have to feed it a script instead of a whole compiled program.

The server is already up. It pulls a URL-safe base64 blob out of an elf parameter, decodes it, and runs the ELF you sent. Read /challenge/server for the details. Write an ELF that goes after the flag, encode it, ship it over, and let the server run it for you.


30-Day Scoreboard:

This scoreboard reflects solves for challenges in this module after the module launched in this dojo.

Rank Hacker Badges Score