Client Hell
Cyber night 3 Client Hell challenge
Application Resources
Application source code
const express = require('express');
const cookieParser = require("cookie-parser");
const path = require('path')
const sessions = require('express-session');
const nunjucks = require('nunjucks');
const parser = require('url');
const { userTable, notesTable } = require('./database');
const { visit } = require('./bot');
const app = express();
app.use(express.urlencoded({ extended: true }));
const oneDay = 1000 * 60 * 60 * 24;
app.use(sessions({
secret: process.env.SECRET,
saveUninitialized: true,
sameSite: 'none',
cookie: { maxAge: oneDay },
resave: false
}));
app.use(cookieParser());
nunjucks.configure('views', {
autoescape: true,
express: app
});
app.set('views', './views');
app.use('/static', express.static(path.resolve('static')));
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader(
'Access-Control-Allow-Methods',
'OPTIONS, GET, POST, PUT, PATCH, DELETE'
);
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.get('/', (req, res) => {
if(req.session.loggedIn){
const query = "SELECT notes from notes where username = ?";
const param = [req.session.username];
let result = [];
notesTable.all(query, param, (err, rows) => {
if(err) console.log(err);
for(let i = 0; i < rows.length; i++){
result.push(rows[i].notes);
}
return res.render('home.html', { username: req.session.username, notes: result });
});
}else{
return res.redirect('/login')
}
});
app.get('/login', (req, res) => {
return res.render('login.html');
});
app.get('/register', (req, res) => {
return res.render('register.html')
});
app.post('/register', (req, res) => {
const { username } = req.body;
const { password } = req.body;
let msg;
const query = "SELECT username from user where username = ?";
const param = [username];
userTable.all(query, param, (err, rows) => {
if(err) console.log(err);
if(rows.length != 0){
msg = "username already exists";
return res.render('register.html', { msg: msg });
}else{
msg = "User have been created";
const query2 = "INSERT INTO user(username, password) VALUES (?,?)";
const param = [username, password]
userTable.run(query2, param);
return res.render('register.html', { msg: msg });
}
});
});
app.post('/login', (req, res) => {
const { username } = req.body;
const { password } = req.body;
let msg = ""
const query = "SELECT username, password from user where username = ? and password = ?";
const param = [username, password];
if(username && password){
userTable.all(query, param, (err, rows) => {
if(err) return;
if(rows.length != 0){
req.session.loggedIn = true;
req.session.username = username;
return res.redirect('/');
}else{
msg = "username or password is incorrect";
return res.render('login.html', { msg: msg });
}
})
}
});
app.post("/note", (req, res) => {
if(req.session.loggedIn){
const { note } = req.body;
const query = "INSERT INTO notes(username, notes) VALUES (?,?)";
const param = [req.session.username, note];
notesTable.run(query, param, () => {
return res.redirect('/');
});
}else{
return res.redirect('/login');
}
})
app.get('/report', (req, res) => {
if(req.session.loggedIn){
return res.render('report.html');
}else{
if(req.ip.includes('127.0.0.1')){
return res.render('report.html');
}else{
return res.redirect('/login');
}
}
});
app.post('/admin/review', async (req, res) => {
const { url } = req.body;
regex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*)/
if(decodeURIComponent(url).match(regex)){
const parse_url = parser.parse(url);
if(parse_url.host.split(':')[0] == "127.0.0.1"){
await visit(url).then(res => {
console.log(url);
}).catch(e => {
console.log(e);
});
return res.json({msg: "We sent your report to the admin"});
}else{
return res.json({msg: "Invalid url"});
}
}else{
res.json({msg: "please submit a url"});
}
});
app.get('/admin/note', (req, res) => {
if(!req.ip.includes('127.0.0.1')) return res.redirect('/');
return res.json({flag: process.env.FLAG});
});
app.get('/logout', (req, res) => {
req.session.destroy();
res.redirect('/');
});
app.listen(1337, () => {
console.log("Listening on port 1337")
});
report.html content
Exploitation
Quick analysis
Route
/admin/reviewrequires aurlin thePOSTrequest body. Line 2Regex could be bypassed by encoding the special characters with URLEncode. Line 3 to Line 4
The provided URL host must be equal to 127.0.0.1 Line 6
The bot (chromium) accesses the URL. Line 7
The flag can be obtained via
/admin/noteroute only if the client ipAddress is 127.0.0.1. Line 22 to 23
the developer used a vulnerable Adobe Dynamic Tag Management that is included in report.html (Client Side Prototype Pollution). [1]
Exploit
I developed a custom payload for the client-side prototype pollution that bypasses most regex special characters without encoding.
Note you must change VPS.IPAddress to your VPS IPAddress.
VPS.IPAddress to your VPS IPAddress.Payload
POST Request Payload
encodes
[]only.
file.js Content
server.py Content
Result
References
Last updated
Was this helpful?