WF-cb02878a-2c85-4dcb-bdc0-e65addf9fb9c-telsender

TelSender <= 1.14.14 - Unauthenticated Stored Cross-Site Scripting via Telegram Chat Title

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

Description

The TelSender plugin for WordPress is vulnerable to DOM-Based Cross-Site Scripting in all versions up to, and including, 1.14.14. This is due to insufficient input sanitization when processing Telegram API responses containing attacker-controlled chat titles. This makes it possible for unauthenticated attackers to inject malicious scripts via Telegram chat titles that execute when an administrator opens the TelSender settings page and clicks the "Tested" button.

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.14.14
PublishedJanuary 27, 2026
Last updatedFebruary 3, 2026
Affected plugintelsender

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on the **TelSender** plugin vulnerability, where a Telegram chat title controlled by an unauthenticated user (on the Telegram side) can trigger Cross-Site Scripting (XSS) in the WordPress admin dashboard. ### 1. Vulnerability Summary The TelSender plugin (versions <= 1.14…

Show full research plan

This research plan focuses on the TelSender plugin vulnerability, where a Telegram chat title controlled by an unauthenticated user (on the Telegram side) can trigger Cross-Site Scripting (XSS) in the WordPress admin dashboard.

1. Vulnerability Summary

The TelSender plugin (versions <= 1.14.14) fails to sanitize Telegram API responses before rendering them in the administrative interface. Specifically, when an administrator uses the "Tested" (Connection Test) functionality, the plugin fetches data from the Telegram API (such as chat titles). An attacker can change a Telegram group/chat title to a malicious script. When the WordPress admin initiates the connection test, the plugin's JavaScript receives the malicious title and injects it into the DOM (typically using a method like .html()), leading to DOM-based XSS.

2. Attack Vector Analysis

  • Endpoint: WordPress Admin Settings Page (typically wp-admin/admin.php?page=telsender).
  • Vulnerable Action: Clicking the "Tested" / "Check Connection" button.
  • Payload Delivery: Via the Telegram API. The attacker modifies a Telegram chat/group title that the bot is associated with.
  • Authentication:
    • Payload Injection: Unauthenticated (on the WordPress side; requires access to the Telegram chat).
    • Payload Execution: Requires an Administrator to click the test button.
  • Preconditions:
    1. The TelSender plugin must be configured with a valid (or dummy) Telegram Bot Token.
    2. The bot must be a member of a chat where the attacker can change the title.

3. Code Flow (Inferred)

  1. Admin UI: The administrator navigates to the TelSender settings and clicks a button (e.g., #telsender-test-btn).
  2. AJAX Request: A JavaScript function (likely in assets/js/admin.js or admin/js/telsender.js) sends an AJAX request to admin-ajax.php with an action like telsender_test_connection.
  3. Server-Side Fetch: The PHP handler (likely in admin/class-telsender-admin.php) uses wp_remote_get() to call the Telegram API (https://api.telegram.org/bot<token>/getUpdates or getChat).
  4. Data Return: The PHP handler receives the Telegram JSON response, extracts the title field from the chat object, and returns it to the JavaScript as part of a JSON response.
  5. DOM Sink: The JavaScript success callback takes the returned title and updates a status message or log area:
    // Vulnerable Pattern
    success: function(response) {
        jQuery('#test-result-container').html("Connected to chat: " + response.chat_title); 
    }
    
    The use of .html() instead of .text() allows the payload in chat_title to execute.

4. Nonce Acquisition Strategy

The "Tested" button is located within the Admin Settings page. To perform the AJAX request as an administrator:

  1. Identify Page: Use wp_cli to find the plugin's menu slug:
    wp eval "print_r( $GLOBALS['menu'] );" or check the admin_menu hook in the source.
  2. Navigate: Use browser_navigate to reach the TelSender settings page.
  3. Extract Nonce: Look for localized script data or hidden input fields.
    • Common localization key: telsender_params or ts_ajax.
    • Command: browser_eval("window.telsender_params?.nonce") or browser_eval("jQuery('#telsender_nonce').val()").
    • Note: Since the exploit is triggered by a manual click in the browser, the nonce is handled automatically by the plugin's own scripts. The research goal is to ensure the payload is present when the button is clicked.

5. Exploitation Strategy

Since the test environment cannot connect to the real Telegram API, we will mock the Telegram API response using WordPress filters.

  1. Mock Telegram API: Create a small helper plugin or use wp eval to hook into pre_http_request. This hook will intercept calls to api.telegram.org and return a spoofed JSON object containing the XSS payload in the chat title.
  2. Configure Plugin: Set a dummy Bot Token in TelSender settings.
  3. Trigger XSS:
    • Navigate to the settings page as an Admin.
    • Click the "Tested" button.
    • Observe the execution of the payload (e.g., alert(document.domain)).

Payload:
Chat <img src=x onerror="alert('XSS_SUCCESS_DOMAIN_'+document.domain)">

Mock Script (Conceptual):

add_filter('pre_http_request', function($pre, $args, $url) {
    if (strpos($url, 'api.telegram.org') !== false) {
        return [
            'response' => ['code' => 200, 'message' => 'OK'],
            'body' => json_encode([
                'ok' => true,
                'result' => [
                    [
                        'message' => [
                            'chat' => [
                                'id' => 123,
                                'title' => 'Test Group <img src=x onerror=alert(1)>'
                            ]
                        ]
                    ]
                ]
            ])
        ];
    }
    return $pre;
}, 10, 3);

6. Test Data Setup

  1. Plugin Activation: wp plugin activate telsender.
  2. Settings Initialization:
    wp option update telsender_settings '{"bot_token":"123456:ABC-DEF", "chat_id":"12345"}' --format=json
    
  3. Mock Deployment: Deploy the pre_http_request filter (via a drop-in or wp eval) to simulate the malicious Telegram response.

7. Expected Results

  1. The AJAX request to the connection test handler succeeds.
  2. The response contains the string: Test Group <img src=x onerror=alert(1)>.
  3. The browser's console or an alert box shows 1, confirming that the HTML was rendered directly into the DOM without escaping.

8. Verification Steps

  1. Observe DOM: Use browser_eval to check if the malicious <img> tag exists in the result container.
    browser_eval("document.querySelector('#test-result-container img') !== null")
  2. Check for Execution: Use a console.log or a global variable in the onerror payload and check for its existence.

9. Alternative Approaches

  • Stored XSS via Webhook: If the plugin supports webhooks, send a POST request directly to the webhook endpoint (usually unauthenticated) containing the malicious chat title. If the plugin stores this title in the database (e.g., in a "Recent Logs" option), the XSS will execute whenever the admin views the main plugin page, without needing to click "Tested".
  • Search for Sink: Use grep -r ".html(" . in the plugin's assets/ directory to find the exact line in JavaScript responsible for rendering the API response.
Research Findings
Static analysis — not yet PoC-verified

Summary

The TelSender plugin for WordPress is vulnerable to DOM-based Stored Cross-Site Scripting due to insufficient sanitization of Telegram chat titles retrieved via the Telegram API. An attacker can set a malicious script as a Telegram chat title, which is then executed in the administrator's browser when they perform a connection test in the plugin settings.

Vulnerable Code

// admin/class-telsender-admin.php (Inferred server-side handler)
public function telsender_test_connection() {
    $token = get_option('telsender_bot_token');
    $response = wp_remote_get("https://api.telegram.org/bot$token/getUpdates");
    $body = json_decode(wp_remote_retrieve_body($response), true);
    
    // The chat title is taken directly from the API response without sanitization
    $chat_title = $body['result'][0]['message']['chat']['title'];
    
    wp_send_json_success(array('chat_title' => $chat_title));
}

---

// assets/js/admin.js (Inferred client-side sink)
$.ajax({
    url: ajaxurl,
    type: 'POST',
    data: { action: 'telsender_test_connection', ... },
    success: function(response) {
        if (response.success) {
            // Vulnerable sink using .html() instead of .text()
            $('#test-result-container').html("Connected to chat: " + response.data.chat_title);
        }
    }
});

Security Fix

--- admin/js/telsender.js
+++ admin/js/telsender.js
@@ -10,1 +10,1 @@
-        $('#test-result-container').html("Connected to chat: " + response.data.chat_title);
+        $('#test-result-container').text("Connected to chat: " + response.data.chat_title);

--- admin/class-telsender-admin.php
+++ admin/class-telsender-admin.php
@@ -25,1 +25,1 @@
-    wp_send_json_success(array('chat_title' => $chat_title));
+    wp_send_json_success(array('chat_title' => sanitize_text_field($chat_title)));

Exploit Outline

1. Attacker gains access to a Telegram group or chat that the target WordPress site's bot is monitoring. 2. Attacker changes the Telegram chat title to a malicious XSS payload (e.g., `<img src=x onerror=alert(document.domain)>`). 3. Attacker waits for a site administrator to visit the TelSender settings page in the WordPress admin dashboard. 4. The administrator clicks the "Tested" or "Check Connection" button to verify the bot's functionality. 5. The plugin sends an AJAX request to the server, which fetches the malicious title from the Telegram API. 6. The server returns the payload to the administrator's browser, where the plugin's JavaScript renders it directly into the DOM using a vulnerable sink, executing the script.

Check if your site is affected.

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