Mercury Walkthrough

Training Mission

← Back to CTF

Before jumping into the full game, let's walk through a practice run using the Mercury service. This service is similar to what you'll find in the actual competition.

Recon & Analysis

First, SSH into your service container:

ssh -i team_key.pem rta_user@<your-service-ip>

Navigate to /app (or where the service is running) and find vuln_http_server.py.

Read the code for understanding. Through that understanding, you will find the vulnerabilities. For example, look at the login function:

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        conn = sqlite3.connect("database.db")
        c = conn.cursor()
        query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
        c.execute(query)

And the ping function:

@app.route("/ping", methods=["GET"])
def ping():
    if "username" not in session:
        return redirect(url_for("login"))

    host = request.args.get("host")
    response = os.popen(f"ping -c 1 {host}").read()
    return response

Exploitation

Now that we understand the code, let's exploit it.

SQL Injection

Because the user's input isn't sanitized or parameterized and is trusted, the service is vulnerable to SQL injection. We can test this by injecting any username of our choice, followed by the ', which would prematurely close off the existing SQL statement. We can ensure it doesn't error out by commenting the rest out.

Try logging in with the username: admin' --.

This modifies the query to SELECT * FROM users WHERE username = 'admin' --' AND password = '{password}'. The database treats everything after -- as a comment, so the effective query becomes SELECT * FROM users WHERE username = 'admin', logging you in as admin without a password.

Command Injection

The ping function takes the host parameter and directly concatenates it into a shell command ping -c 1 {host}. Since this input is not validated, we can inject arbitrary shell commands.

By using the semicolon ; operator, we can chain a second command after the ping.

Try pinging: 127.0.0.1; cat /home/rta_service/flag.txt

The server will execute ping -c 1 127.0.0.1, finish that command, and then execute cat /home/rta_service/flag.txt, revealing the flag.

Patching

You must fix these bugs in your own service to prevent others from stealing flags from you.

Fixing the SQL Injection

NEVER use string formatting for SQL queries. Use Parameterized Queries.

Fixing the Command Injection

Avoid os.popen or os.system with user input. Use the subprocess module with a list of arguments, which prevents shell injection.