Beyond
This is the beyond page that an additional post enumeration and assessment are conducted as the root
user after compromising the target system.
crontab
root@soccer:~# crontab -l | grep -v '^#'
*/3 * * * * /bin/rm /var/www/html/tiny/uploads/*
*/3 * * * * /bin/rm /usr/local/share/dstat/*
*/3 * * * * /bin/rm -rf /home/player/.dstat
*/10 * * * * /usr/bin/mysql < /root/run.sql
The root
user has 5 cronjobs;
- For every 3 mins, wipe the
/var/www/html/tiny/uploads/
directory - For every 3 mins, wipe the
/usr/local/share/dstat/
directory - For every 3 mins, remove the
/bin/rm -rf /home/player/.dstat
directory - For every 10 mins, run mysql with a SQL script;
/root/run.sql
/root/run.sql
root@soccer:~# cat /root/run.sql
delete from soccer_db.accounts where id != 1324;
Basically restoring the soccer_db.accounts
table to contain only the player
user with id=1324
Updated Soccer Club Web app
root@soccer:~# ll app
total 108
drwxr-xr-x 5 root root 4096 Dec 12 2022 ./
drwx------ 10 root root 4096 Dec 13 2022 ../
-rwxr-xr-x 1 root root 4979 Nov 28 2022 app.js*
-rw-r----- 1 root root 489 Nov 17 2022 ecosystem.config.js
drwxr-xr-x 92 root root 4096 Dec 1 2022 node_modules/
-rw-r--r-- 1 root root 66511 Nov 28 2022 package-lock.json
-rw-r--r-- 1 root root 401 Nov 17 2022 package.json
-rwxr-xr-x 1 root root 1179 Nov 29 2022 server.js*
drwxr-xr-x 5 root root 4096 Nov 28 2022 static/
drwxr-xr-x 2 root root 4096 Dec 12 2022 views/
It runs off /root/app
app.js
root@soccer:~/app# cat app.js
const ejs = require('ejs');
const mysql = require('mysql');
const express = require('express');
const flash = require('connect-flash');
const session = require('express-session');
const path = require('path');
const connection = mysql.createConnection({
host: 'localhost',
user: 'player',
password: 'PlayerOftheMatch2022',
database: 'soccer_db'
});
const app = express();
app.use(session({
secret: 'aVeryG00dS3cr3t',
resave: false,
saveuninitialized: false
}));
app.use(flash());
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.use(express.static(path.join(__dirname, 'static')));
app.set('views', path.join(__dirname, '/views'));
app.set('view engine', 'ejs');
function setAuth(request) {
if (typeof request.session.authenticated === 'undefined') {
request.session.authenticated = false;
}
}
app.get('/', function(request, response) {
setAuth(request);
response.render('index', {
title: "Soccer", isLoggedIn: request.session.authenticated
})
});
app.get('/match', function(request, response) {
setAuth(request);
response.render('match', {
title: "Soccer - Matches", isLoggedIn: request.session.authenticated
})
});
app.use('/signup', function(request, response) {
setAuth(request);
if (request.session.authenticated) {
response.redirect('/check')
} else {
if (request.method == 'POST') {
let username = request.body.username;
let password = request.body.password;
let email = request.body.email;
if (username && password && email) {
connection.query('SELECT * FROM accounts WHERE email = ?',[email], function(error, results, fields) {
if (error) throw error;
if (results.length > 0) {
response.send('Account Already Exists');
} else {
connection.query('INSERT INTO accounts (id, email, username, password) VALUES (FLOOR((RAND() * (99999-51891+1))+51891), ?, ?, ?)',[email, username, password], function(error, results, fields) {
if (error) throw error;
response.redirect('/login')
});
}
});
} else {
response.end();
}
} else {
response.render('signup', {
title : "Soccer - Signup", isLoggedIn: request.session.authenticated
});
}
}
});
app.use('/login', function(request, response) {
setAuth(request);
if (request.session.authenticated) {
response.redirect('check');
} else {
if (request.method == 'POST') {
let email = request.body.email;
let password = request.body.password;
if (email && password) {
connection.query('SELECT * FROM accounts WHERE email = ? AND password = ?', [email , password], function(error, results, fields) {
if (error) throw error;
if (results.length > 0) {
var rows = JSON.parse(JSON.stringify(results[0]));
request.session.authenticated = true;
request.session.email = email;
response.redirect('/check')
} else {
response.send('Incorrect Email and/or Password!');
}
response.end();
});
} else {
response.end();
}
} else {
response.render('login', {
title : "Soccer - Login", isLoggedIn: request.session.authenticated
});
}
}
});
app.get('/logout', function(request, response) {
request.session.destroy();
response.redirect('/');
});
app.use('/check', function(request, response) {
setAuth(request);
(async ()=>{
if (request.session.authenticated) {
email=request.session.email;
const result = await new Promise((resolve) => {
connection.query("SELECT * FROM accounts WHERE email = ?", [email], (error, results) => {
if (error) throw error;
var row = JSON.parse(JSON.stringify(results[0]));
resolve(row);
});
});
request.session.sessionID = result.id;
request.flash('success', request.session.sessionID);
response.locals.message = request.flash();
response.render('check', {
title : "Soccer - Check", isLoggedIn: request.session.authenticated
});
} else {
response.send('Please login to view this page!');
}
response.end();
})()
});
app.listen(3000, '127.0.0.1');
console.log("Web Application Started");
ecosystem.config.js
root@soccer:~/app# cat ecosystem.config.js
module.exports = {
apps : [{
script: 'index.js',
watch: '.'
}, {
script: './service-worker/',
watch: ['./service-worker']
}],
deploy : {
production : {
user : 'SSH_USERNAME',
host : 'SSH_HOSTMACHINE',
ref : 'origin/master',
repo : 'GIT_REPOSITORY',
path : 'DESTINATION_PATH',
'pre-deploy-local': '',
'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production',
'pre-setup': ''
}
}
};
package.json
root@soccer:~/app# cat package.json
{
"name": "auth",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"ejs": "^3.1.8",
"express": "^4.18.2",
"express-flash": "^0.0.2",
"express-session": "^1.17.3",
"mysql": "^2.18.1",
"path": "^0.12.7",
"ws": "^8.9.0"
}
}
server.js
root@soccer:~/app# cat server.js
const mysql = require('mysql');
const serv = require('ws');
const express = require('express');
const server = express().listen(9091, '0.0.0.0')
const socket = new serv.Server({ server });
const connection = mysql.createConnection({
host : "localhost",
user : "player",
password : 'PlayerOftheMatch2022',
port: 3306,
database : "soccer_db"
})
connection.connect();
socket.on('connection', ws=> {
ws.on('message', function incoming(data) {
try {
var id = JSON.parse(data).id;
} catch (e) {
//console.log(e);
}
(async () => {
try {
const query = `Select id,username,password FROM accounts where id = ${id}`;
await connection.query(query, function (error, results, fields) {
if (error) {
ws.send("Ticket Doesn't Exist");
} else {
if (results.length > 0) {
ws.send("Ticket Exists")
} else {
ws.send("Ticket Doesn't Exist")
}
}
});
} catch (error) {
ws.send("Error");
}
})()
});
});
This is the WebSocket API server
with the vulnerable SQL query
/views/
root@soccer:~/app# ll views
total 48
drwxr-xr-x 2 root root 4096 Dec 12 2022 ./
drwxr-xr-x 5 root root 4096 Dec 12 2022 ../
-rw-r--r-- 1 root root 3317 Nov 28 2022 check.ejs
-rw-r--r-- 1 root root 5189 Nov 28 2022 index.ejs
-rw-r--r-- 1 root root 458 Nov 28 2022 layout.ejs
-rw-r--r-- 1 root root 1739 Dec 12 2022 login.ejs
-rw-r--r-- 1 root root 8508 Nov 28 2022 match.ejs
-rw-r--r-- 1 root root 1602 Nov 28 2022 nav.ejs
-rw-r--r-- 1 root root 2168 Dec 12 2022 signup.ejs
frontend