診断ジェネレータ作成プラグイン <= 1.4.16 - Authenticated (Subscriber+) Stored Cross-Site Scripting via 'js' Parameter
Description
The 診断ジェネレータ作成プラグイン (Diagnosis Generator) plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'js' parameter in versions up to and including 1.4.16. This is due to missing authorization checks and insufficient input sanitization in the themeFunc() function. The function is hooked to 'admin_init' and processes theme update requests without verifying user capabilities, allowing any authenticated user (including subscribers) to save malicious JavaScript to theme files. Additionally, the save() function uses stripslashes() which removes WordPress's magic quotes protection. This makes it possible for authenticated attackers, with subscriber-level access and above, to inject arbitrary web scripts in theme files that will execute whenever a user accesses a page containing the diagnosis form shortcode.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.4.16# Exploitation Research Plan: CVE-2026-5293 ## 1. Vulnerability Summary **CVE-2026-5293** is a Stored Cross-Site Scripting (XSS) vulnerability in the **診断ジェネレータ作成プラグイン (Diagnosis Generator)** plugin (versions <= 1.4.16). The vulnerability exists because the `themeFunc()` function, which is hooked t…
Show full research plan
Exploitation Research Plan: CVE-2026-5293
1. Vulnerability Summary
CVE-2026-5293 is a Stored Cross-Site Scripting (XSS) vulnerability in the 診断ジェネレータ作成プラグイン (Diagnosis Generator) plugin (versions <= 1.4.16). The vulnerability exists because the themeFunc() function, which is hooked to admin_init, fails to perform authorization checks (e.g., current_user_can()) or nonce verification.
An authenticated user with Subscriber-level permissions or higher can send a request containing a malicious js parameter. The plugin processes this request and saves the input into theme-related configuration files using the save() function. Because the save() function employs stripslashes(), it bypasses WordPress's automatic magic quotes, allowing the injection of arbitrary JavaScript. This payload is subsequently executed in the context of any user (including administrators) who visits a page containing the diagnosis form shortcode.
2. Attack Vector Analysis
- Vulnerable Endpoint: Any WordPress admin path (typically
/wp-admin/admin-ajax.phpor/wp-admin/admin-post.php) triggers theadmin_inithook. - Vulnerable Function:
themeFunc()(inferred to be inside the main plugin file or a theme-handling class). - Trigger Parameter: A POST request containing a specific action or identifier to enter the
themeFunc()logic, plus thejsparameter. - Payload Parameter:
js - Authentication: Authenticated (Subscriber or higher).
- Preconditions: The plugin must be active. To trigger the XSS on the frontend, a page must contain the plugin's shortcode (typically
[os-diagnosis-generator]).
3. Code Flow
- Entry Point: An authenticated user sends a POST request to
/wp-admin/admin-ajax.php. - Hook Execution: WordPress triggers the
admin_inithook. - Vulnerable Function: The plugin's registered callback
themeFunc()executes. - Lack of Authorization:
themeFunc()fails to check forcurrent_user_can( 'manage_options' ). - Data Processing: The code checks for the presence of a 'theme update' trigger (e.g., a specific
$_POSTkey). - Sanitization Bypass: The
jsparameter is passed to asave()function. This function usesstripslashes($_POST['js']), removing any escaping applied by WordPress core. - Persistence (Sink): The content of the
jsparameter is written to a file (e.g., a template file inwp-content/uploads/os-diagnosis-generator/or similar) or stored in the database and then written to a script file. - Execution: When the diagnosis form is rendered via shortcode, the stored JavaScript is included in the page output.
4. Nonce Acquisition Strategy
The vulnerability description explicitly mentions "missing authorization checks," which strongly suggests that the admin_init hook lacks both capability checks and nonce verification (check_admin_referer or check_ajax_referer).
If a nonce is required for the "theme update" request, it is likely localized for the plugin's admin interface.
- Identify Script Handles: Look for
wp_localize_scriptcalls in the plugin source (e.g., searching foros-diagnosis-generator-admin). - Creation of Admin Page: Since the attacker is a Subscriber, they cannot normally see the plugin's settings page. However, because the hook is on
admin_init, the logic runs on any admin page they can access (likeprofile.php). - Bypass Check: If the plugin code uses
check_admin_referer( 'some_action', 'some_nonce', false )without checking the return value, the nonce is bypassed.
Assumed Strategy: Attempt the exploit without a nonce first. If it fails, search the plugin source for wp_create_nonce.
5. Exploitation Strategy
The goal is to inject a script that will execute when the diagnosis generator is loaded.
Step 1: Authentication
Log in as a Subscriber user.
- URL:
/wp-login.php - Tool:
http_request
Step 2: Identify Injection Trigger
The themeFunc() function likely looks for a specific POST parameter to initiate the save process. Based on the plugin name and description, we will look for keys like os_diagnosis_save, theme_update, or update_js.
(Inferred request parameters based on typical plugin patterns):
- Action:
os_diagnosis_generator_update_theme(inferred) - Parameter:
js
Step 3: Send Malicious Request
Send a POST request to /wp-admin/admin-ajax.php (or similar) to save the malicious JS.
- Request Method:
POST - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=themeFunc&js=</script><script>alert(document.domain)</script>&(trigger_param)=1
(Note: The exact 'action' and 'trigger_param' need to be verified by grepping the plugin source foradd_action('admin_init', ...))
Step 4: Trigger the XSS
Visit a page containing the plugin's shortcode.
- URL:
http://localhost:8080/diagnosis-page/
6. Test Data Setup
- Plugin Installation: Install and activate
os-diagnosis-generator<= 1.4.16. - User Creation: Create a Subscriber user.
wp user create attacker attacker@example.com --role=subscriber --user_pass=password
- Shortcode Page: Create a public page with the diagnosis shortcode.
wp post create --post_type=page --post_title="Diagnosis" --post_status=publish --post_content="[os-diagnosis-generator]"- (Note: Verify the exact shortcode slug by running
grep -r "add_shortcode" wp-content/plugins/os-diagnosis-generator)
7. Expected Results
- The POST request from the Subscriber user returns a success code (e.g., 200 OK or a redirect).
- When navigating to the page created in Step 6, a JavaScript alert box displaying the document domain appears.
- Inspecting the page source reveals the injected
<script>alert(document.domain)</script>payload.
8. Verification Steps
- Check Filesystem: Use WP-CLI to check if a new JS file was created or modified in the plugin's directory or uploads.
ls -R wp-content/uploads/os-diagnosis-generator/
- Check Options: Check if the JS is stored in the WordPress options table.
wp option get os_diagnosis_generator_custom_js(inferred option name)
- Verify Frontend Render: Use
http_requestto fetch the diagnosis page and grep for the payload.http_request(url='http://localhost:8080/diagnosis-page/')-> search foralert(document.domain)in the response body.
9. Alternative Approaches
- Parameter Polling: If the specific trigger parameter for
themeFuncis unknown, grep the plugin source for$_POSTinside the function hooked toadmin_init. - CSRF: If the plugin checks for authentication but lacks nonces, an administrator could be tricked into clicking a link that injects the JS payload.
- Shortcode Attribute XSS: If the
jsparameter injection fails, investigate if the shortcode handler itself reflects attributes without escaping.
Summary
The Diagnosis Generator plugin for WordPress (up to version 1.4.16) is vulnerable to Stored Cross-Site Scripting (XSS) via the 'js' parameter. This is caused by the themeFunc() function being improperly hooked to admin_init without capability checks or nonces, combined with the use of stripslashes() which bypasses WordPress's default magic quotes protection.
Vulnerable Code
/* In the main plugin file or theme-handling file */ add_action('admin_init', 'themeFunc'); function themeFunc() { // Vulnerability: No current_user_can() check or nonce verification // Any authenticated user reaching admin_init (including subscribers) can trigger this if (isset($_POST['js'])) { // Vulnerability: stripslashes() removes escape characters, allowing raw script tags $js_content = stripslashes($_POST['js']); $this->save($js_content); } }
Security Fix
@@ -10,6 +10,13 @@ function themeFunc() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'save_theme_js' ) ) { + return; + } + if ( isset( $_POST['js'] ) ) { - $js = stripslashes( $_POST['js'] ); + $js = sanitize_textarea_field( stripslashes( $_POST['js'] ) ); $this->save( $js ); } }
Exploit Outline
The attacker first authenticates as a low-privileged user (Subscriber or higher). They then construct a POST request to any endpoint that triggers the 'admin_init' hook (such as /wp-admin/admin-ajax.php). The payload includes the specific trigger parameter for the themeFunc() logic and the 'js' parameter containing a malicious script (e.g., <script>alert(document.domain)</script>). Because the function lacks authorization checks and uses stripslashes(), the payload is saved to the plugin's theme configuration. Finally, the attacker visits a page containing the plugin's shortcode to execute the stored script in the context of other users' browsers.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.