ionCube Tester Plus <= 1.3 - Unauthenticated Arbitrary File Download
Description
The ionCube Tester Plus plugin for WordPress is vulnerable to Path Traversal in all versions up to, and including, 1.3. This makes it possible for unauthenticated attackers to read the contents of arbitrary files on the server, which can contain sensitive information.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:HTechnical Details
<=1.3What Changed in the Fix
Changes introduced in v1.4
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-69411 (ionCube Tester Plus Path Traversal) ## 1. Vulnerability Summary The **ionCube Tester Plus** plugin (version <= 1.3) includes a standalone utility script `loader-wizard.php`. This script is intended to help users install ionCube loaders by generating con…
Show full research plan
Exploitation Research Plan: CVE-2025-69411 (ionCube Tester Plus Path Traversal)
1. Vulnerability Summary
The ionCube Tester Plus plugin (version <= 1.3) includes a standalone utility script loader-wizard.php. This script is intended to help users install ionCube loaders by generating configuration files (like php.ini) and offering them for download. However, the script is directly accessible, does not load the WordPress environment (and thus lacks WordPress security controls), and contains a path traversal vulnerability in its file-handling logic. An unauthenticated attacker can use traversal sequences (../) to download arbitrary files from the server, such as wp-config.php.
2. Attack Vector Analysis
- Target File:
wp-content/plugins/ioncube-tester-plus/loader-wizard.php - Vulnerable Parameters:
fileordownload(inferred from common ionCube wizard vulnerabilities and the "Arbitrary File Download" description). - Authentication: None required (Unauthenticated).
- Preconditions: The plugin must be installed. The web server must allow direct execution of PHP files within the
wp-content/plugins/directory.
3. Code Flow
- Entry Point: A direct HTTP GET request is made to
loader-wizard.php. - Initialization: The script defines various constants and immediately calls the
run()function (Line 115 inloader-wizard.php). - Execution Path:
- The
run()function (logic contained in the truncated portion of the source) processes global variables like$_GET. - It identifies an action (likely via a
pageoractionparameter, or a directdownloadparameter). - When a download action is triggered, the script retrieves a file path from a user-provided parameter (e.g.,
$_GET['file']). - Vulnerable Sink: The script passes this unsanitized path directly to a file-reading function such as
readfile(),file_get_contents(), orinclude(). - Because the script is standalone and does not call
wp-load.php, no WordPress nonces or capability checks (current_user_can) are performed.
- The
4. Nonce Acquisition Strategy
No nonce is required.
The script loader-wizard.php is a standalone "wizard" designed to run even if the WordPress environment is not fully functional (e.g., during loader installation). It does not include wp-load.php or wp-config.php, meaning WordPress-specific functions like wp_create_nonce() and wp_verify_nonce() are unavailable and not used.
5. Exploitation Strategy
The goal is to read the wp-config.php file located at the WordPress root.
Step 1: Discover the Vulnerable Parameter
Since the source for the run() function logic is truncated, the agent must test the two most likely traversal patterns used in ionCube wizard scripts.
Step 2: Test Pattern A (Direct Parameter)
Request:
GET /wp-content/plugins/ioncube-tester-plus/loader-wizard.php?download=../../../../wp-config.php HTTP/1.1
Host: [TARGET_HOST]
Step 3: Test Pattern B (Action/Page Pattern)
Request:
GET /wp-content/plugins/ioncube-tester-plus/loader-wizard.php?page=download&file=../../../../wp-config.php HTTP/1.1
Host: [TARGET_HOST]
Step 4: Analysis of Response
- A successful exploit will return the raw contents of
wp-config.php. - Look for
DB_NAME,DB_USER,DB_PASSWORD, andAUTH_KEYin the response body.
6. Test Data Setup
- Install the
ioncube-tester-plusplugin (version 1.3) in the test environment. - Ensure the plugin directory is
ioncube-tester-plus. - Verify that
wp-config.phpexists in the standard location (ABSPATH).
7. Expected Results
- Success: The HTTP response contains the PHP source code of
wp-config.php. TheContent-Typeheader may beapplication/octet-streamortext/htmldepending on the exact sink implementation. - Failure: The response is a 404 (file not found), 403 (forbidden by server config), or the wizard's default HTML interface without the file content.
8. Verification Steps
After the HTTP request, verify the content retrieved:
- Use
http_requestto fetch the file. - Check the response body for the string:
define( 'DB_NAME'. - (Optional) Use
wp-clito verify the actual database name matches the one found in the downloaded file:wp config get DB_NAME --allow-root
9. Alternative Approaches
If the patterns above fail, try:
- LFI via
page:?page=../../../../wp-config.php(sometimes wizards useincludefor templating). - Log Download: Check if the wizard has a log viewing feature (e.g.,
?page=log&file=...). - Double Encoding: If a basic filter exists, try
%2e%2e%2finstead of../. - Different Root Depth: If the site is installed in a subdirectory, adjust the traversal depth (e.g.,
../../../../../wp-config.php).
Summary
The ionCube Tester Plus plugin includes a standalone script `loader-wizard.php` that is accessible without authentication and outside the WordPress security context. It fails to sanitize the 'ininame' parameter during file download operations, allowing unauthenticated attackers to use path traversal sequences to download arbitrary sensitive files from the server, such as 'wp-config.php'.
Vulnerable Code
// loader-wizard.php line 3243 in version 1.3 $download = get_request_parameter('download'); $ini_file_name = ''; if (!empty($download)) { $ini_file_name = get_request_parameter('ininame'); if (empty($ini_file_name)) { $ini_file_name = ini_file_name(); } }
Security Fix
@@ -1,5 +1,19 @@ <?php // -*- c++ -*- +// --- BEGIN SECURITY PATCH --- +// Load WordPress environment to check authentication +$wp_load = dirname(dirname(dirname(dirname(__FILE__)))) . '/wp-load.php'; +if ( file_exists( $wp_load ) ) { + require_once( $wp_load ); +} + +// Block access if not a logged-in Administrator +if ( ! defined( 'ABSPATH' ) || ! current_user_can( 'manage_options' ) ) { + header( 'HTTP/1.0 403 Forbidden' ); + die( 'Access Denied: You must be a logged-in Administrator to use this tool.' ); +} +// --- END SECURITY PATCH --- + /** * ionCube Loader install Wizard * @@ -3229,7 +3243,8 @@ $download = get_request_parameter('download'); $ini_file_name = ''; if (!empty($download)) { - $ini_file_name = get_request_parameter('ininame'); + // Security Fix: basename() prevents path traversal attacks (e.g. ../../) + $ini_file_name = basename( get_request_parameter('ininame') ); if (empty($ini_file_name)) { $ini_file_name = ini_file_name(); }
Exploit Outline
The exploit targets the standalone `loader-wizard.php` script located in the plugin directory. 1. Target Endpoint: `/wp-content/plugins/ioncube-tester-plus/loader-wizard.php` 2. Required Parameters: - `download`: Any non-empty value to trigger the download logic. - `ininame`: A path traversal payload (e.g., `../../../../wp-config.php`) pointing to the desired file relative to the plugin directory. 3. Authentication: None required. The script does not initialize the WordPress environment and lacks any capabilities checks in the vulnerable version. 4. Execution: The attacker makes a GET request with these parameters, and the server responds with the raw contents of the file specified in the `ininame` parameter.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.