Share
## https://sploitus.com/exploit?id=5C0532FA-D147-5D9F-82F8-088C29473859
# Description
* **Name** : CVE-2024-11972
* **CVSSv3 Score** : 9.8
* **Affected Versions** : Hunk Companion < 1.9.0
* **Published** : 30/12/24

CVE-2024-11972 is a critical vulnerability in the Hunk Companion WordPress plugin versions prior to 1.9.0. This flaw allows unauthenticated attackers to exploit improperly authorized REST API endpoints, enabling them to install and activate plugins from the WordPress.org repository, including those that are outdated or have known vulnerabilities. Exploiting this vulnerability can lead to severe security risks, such as remote code execution, SQL injection, XSS or administrative backdoors.

An common example for abusing this vulnerability is with the ```WP Query Console``` plugin. Once installed using this exploit, the plugin provides a console interface within WordPress where users can run SQL queries against the site's database, 
enabling data theft, backdoor creation, and complete database compromise.

# Exploitation
### Dependancies
Needed Python libraries -> ```argparse```, ```requests```, ```urljoin```
### Usage
```cmd
options:
  -h, --help            show this help message and exit
  -u URL, --url URL     Base URL of the WordPress site (default: http://localhost/wordpress/).
  -p PLUGIN, --plugin PLUGIN
                        Plugin name to install (default: classic-editor).
```
1. Download the exploit.py
2. Run it with the correct arguments - python exploit.py -u ```<insert wordpress URL>``` -p ```<insert plugin name>```

# Source Code Review
The vulnerable endpoint, ```/wp-json/hc/v1/themehunk-import```, was initially identified by Daniel Rodriguez during an analysis of access logs as part of an ongoing investigation.
Using this information we can look inside the source code of the import endpoint.
Inside the ```/import/core/class-installation.php``` file we can see the following line: 
```php
204 |  $temp_file = download_url('https://downloads.wordpress.org/plugin/'.$slug.'.zip');
```
The ```HUNK_COMPANION_SITES_BUILDER_SETUP``` class manages the installation and activation of WordPress plugins and themes, handling both free and premium types based on input parameters. 
It dynamically checks if a plugin or theme is installed or active, downloads and unzips the necessary files if missing, and activates them using WordPress core functions.
This hardcoded functionality permits the plugin to download any plugin from the WordPress repository, even those that are removed or discontinued, giving attackers the opportunity to leverage vulnerable plugins for exploitation.

To delve deeper into the issue, we can look inside ```/import/app/app.php```, 
lets compare between the different fixes introduced in versions 1.8.0 | 1.8.7 | 1.9.0 since each introduced another layer of security:
## 1.8.0 (All versions below 1.8.7) 
```php
register_rest_route( 'hc/v1', 'themehunk-import', array(
          'methods' => 'POST',
          'callback' => array( $this, 'tp_install' ),
          'permission_callback' => '__return_true',
      ) );
```
The ```permission_callback``` always returns ```true```, this allows **any user** or unauthenticated actor to access the endpoint using a POST request.
Since there is no type of Nonce verification of any type of authentication process, an attacker could bypass the needed permissions and directly install the wanted plugin. 

## 1.8.7
In version 1.8.7, several security improvements were introduced to address the flaws present in earlier versions, 
but the endpoint ```/hc/v1/themehunk-import``` and the overall implementation still have significant flaws:
```php
public function register_routes() {

    register_rest_route( 'hc/v1', 'themehunk-import', array(
      'methods' => 'POST',
      'callback' => array( $this, 'tp_install' ),
      'permission_callback' => function () {
// Check if the user is logged in
if ( ! is_user_logged_in() ) {
    return new WP_REST_Response( 'Unauthorized: User not logged in', 401 );
}

// Debug: Log the user role and capabilities to see what they have
$current_user = wp_get_current_user();
// error_log( 'Current user: ' . $current_user->user_login );
// error_log( 'User roles: ' . implode( ', ', $current_user->roles ) );
// error_log( 'User capabilities: ' . print_r( $current_user->allcaps, true ) );

// Ensure the user has the 'install_plugins' capability
if ( ! current_user_can( 'install_plugins' ) ) {
    return new WP_REST_Response( 'Unauthorized: Insufficient capabilities', 401 );
}

  // Get the nonce from the request header
        $nonce = $request->get_header('X-WP-Nonce');

        // Verify the nonce
        if ( ! wp_verify_nonce( $nonce, 'hc_import_nonce' ) ) {
            return new WP_REST_Response( 'Unauthorized: Invalid nonce', 401 );
        }

return true; // Permission granted
```
While this version includes checks to ensure the user is logged in (```is_user_logged_in()```) and verifies that they have the appropriate capabilities (```current_user_can('install_plugins')```), sending a ```401 Unauthorized``` response if they do not, it still overlooks the main issue that remains unaddressed.
Wordpress Callback logic when it comes to ```permission_callback``` is evaluated like this: 

![image](https://github.com/user-attachments/assets/418862a5-e7d2-4631-adad-ef404537c83b)

Meaning a correct response would be one of the following - ```true``` | ```false``` | ```WP_Error```.
The issue stems from the fact that the returned ```WP_REST_Response``` is not a boolean or a WP_Error, since Wordpress does not interpret it as a denial (```false```) nor an error (```WP_Error(```), it might implictily accept it and grant access to an unauthenticated user, essentially not solving the main issue. 

## 1.9.0
Version 1.9.0 successfully resolved the issue, rendering the vulnerability no longer exploitable:  
```php
public function register_routes() {

    register_rest_route( 'hc/v1', 'themehunk-import', array(
      'methods' => 'POST',
      'callback' => array( $this, 'tp_install' ),
      'permission_callback' => function () {
      // Check if the user is logged in
      if ( ! is_user_logged_in() ) {
          return false;
      }

// Debug: Log the user role and capabilities to see what they have
$current_user = wp_get_current_user();
// error_log( 'Current user: ' . $current_user->user_login );
// error_log( 'User roles: ' . implode( ', ', $current_user->roles ) );
// error_log( 'User capabilities: ' . print_r( $current_user->allcaps, true ) );

// Ensure the user has the 'install_plugins' capability
if ( ! current_user_can( 'install_plugins' ) ) {
    return false;
}

  // Get the nonce from the request header
        $nonce = $request->get_header('X-WP-Nonce');

        // Verify the nonce
        if ( ! wp_verify_nonce( $nonce, 'hc_import_nonce' ) ) {
            return false;
        }

return true; // Permission granted
```
In this version, we can see a proper false value is returned if the user is not logged in or lacks the correct permissions, 
ensuring the permission_callback correctly denies unauthorized access, making the exploit no longer viable.