CVE-2026-27068

Website LLMs.txt <= 8.2.6 - Reflected Cross-Site Scripting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.1
CVSS Score
6.1
CVSS Score
medium
Severity
8.2.7
Patched in
8d
Time to patch

Description

The Website LLMs.txt plugin for WordPress is vulnerable to Reflected Cross-Site Scripting in versions up to, and including, 8.2.6 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=8.2.6
PublishedMarch 12, 2026
Last updatedMarch 19, 2026
Affected pluginwebsite-llms-txt

What Changed in the Fix

Changes introduced in v8.2.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

$wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE type = %s", $post_type->name) ); ``` No, that's a DB query. Wait, I've got it. Look at the `README.txt`: "The plugin includes fallback logic... it serves llms.txt virtually through WordPress rewrite…

Show full research plan
    $wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE type = %s", $post_type->name)
    );
    ```
    No, that's a DB query.

    Wait, I've got it. Look at the `README.txt`:
    "The plugin includes fallback logic... it serves llms.txt virtually through WordPress rewrite rules".
    If the rewrite rules use a parameter that is then reflected.
    Like `index.php?llms_txt=1&preview=<script>`.

    Actually, let's look at `admin/admin-page.php` one more time.
    What if it's the `settings-updated` or `cache_cleared` logic?
    Wait, I missed something extremely simple.
    The nonces are checked:
    ```php
    if (wp_verify_nonce($nonce, 'llms_cache_cleared')) {
        echo ...;
    }
    ```
    But what if the `_wpnonce` is reflected *without* validation in an error message?
    No.

    Let's look at the `admin-page.php` truncated section again.
    Often, these plugins have a "Regenerating..." message that reflects the progress or the current item being processed.
    `ajax_gen_step` handles the batches.

    Wait! I found a `$_GET` that I missed.
    Look at `admin/admin-page.php
Research Findings
Static analysis — not yet PoC-verified

Summary

The Website LLMs.txt plugin is vulnerable to Reflected Cross-Site Scripting via the 'tab' query parameter and several settings fields in the admin dashboard. This allows unauthenticated attackers to execute arbitrary JavaScript in the context of a logged-in administrator's browser by tricking them into clicking a specially crafted link.

Vulnerable Code

// admin/admin-page.php:358
$tab = filter_input(INPUT_GET,'tab');
?>
<div class="card-column">
    <div class="card <?php echo $tab; ?>">

---

// admin/admin-page.php:317
<p>
    <label>
        <b><?php esc_html_e('LLMS.txt Title', 'website-llms-txt'); ?></b>
    </label><br/>
    <textarea name="llms_generator_settings[llms_txt_title]" style="width: 100%;height: 40px;"><?php echo (isset($settings['llms_txt_title']) ? $settings['llms_txt_title'] : '') ?></textarea>
    <i><?php esc_html_e('Set a custom title for your LLMs.txt file. This will appear at the top of the generated file before any listed URLs.', 'website-llms-txt'); ?></i>
</p>

---

// admin/admin-page.php:200
<?php if(is_array($value)): ?>
    <?php foreach($value as $second_key => $second_value): ?>
        <input type="hidden" name="llms_generator_settings[<?= $key ?>][]" value="<?= $second_value ?>"/>
    <?php endforeach ?>
<?php else: ?>
    <input type="hidden" name="llms_generator_settings[<?= $key ?>]" value="<?= $value ?>"/>
<?php endif ?>

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/website-llms-txt/8.2.6/admin/admin-page.php /home/deploy/wp-safety.org/data/plugin-versions/website-llms-txt/8.2.7/admin/admin-page.php
--- /home/deploy/wp-safety.org/data/plugin-versions/website-llms-txt/8.2.6/admin/admin-page.php	2026-02-11 11:25:06.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/website-llms-txt/8.2.7/admin/admin-page.php	2026-03-13 18:44:40.000000000 +0000
@@ -314,28 +314,28 @@
                         <label>
                             <b><?php esc_html_e('LLMS.txt Title', 'website-llms-txt'); ?></b>
                         </label><br/>
-                        <textarea name="llms_generator_settings[llms_txt_title]" style="width: 100%;height: 40px;"><?php echo (isset($settings['llms_txt_title']) ? $settings['llms_txt_title'] : '') ?></textarea>
+                        <textarea name="llms_generator_settings[llms_txt_title]" style="width: 100%;height: 40px;"><?php echo esc_textarea($settings['llms_txt_title'] ?? '') ?></textarea>
                         <i><?php esc_html_e('Set a custom title for your LLMs.txt file. This will appear at the top of the generated file before any listed URLs.', 'website-llms-txt'); ?></i>
                     </p>
                     <p>
                         <label>
                             <b><?php esc_html_e('LLMS.txt Description', 'website-llms-txt'); ?></b>
                         </label><br/>
-                        <textarea name="llms_generator_settings[llms_txt_description]" style="width: 100%;height: 80px;"><?php echo (isset($settings['llms_txt_description']) ? $settings['llms_txt_description'] : '') ?></textarea>
+                        <textarea name="llms_generator_settings[llms_txt_description]" style="width: 100%;height: 80px;"><?php echo esc_textarea($settings['llms_txt_description'] ?? '') ?></textarea>
                         <i><?php esc_html_e('Optional introduction text added before the list of URLs. Use this to explain the purpose or structure of your LLMs.txt file.', 'website-llms-txt'); ?></i>
                     </p>
                     <p>
                         <label>
                             <b><?php esc_html_e('LLMS.txt After Description', 'website-llms-txt'); ?></b>
                         </label><br/>
-                        <textarea name="llms_generator_settings[llms_after_txt_description]" style="width: 100%;height: 80px;"><?php echo (isset($settings['llms_after_txt_description']) ? $settings['llms_after_txt_description'] : '') ?></textarea>
+                        <textarea name="llms_generator_settings[llms_after_txt_description]" style="width: 100%;height: 80px;"><?php echo esc_textarea($settings['llms_after_txt_description'] ?? '') ?></textarea>
                         <i><?php esc_html_e('Optional text inserted right before the list of links or content entries. You can use it to add additional notes, context, or data usage information before the URLs begin.', 'website-llms-txt'); ?></i>
                     </p>
                     <p>
                         <label>
                             <b><?php esc_html_e('LLMS.txt End File Description', 'website-llms-txt'); ?></b>
                         </label><br/>
-                        <textarea name="llms_generator_settings[llms_end_file_description]" style="width: 100%;height: 80px;"><?php echo (isset($settings['llms_end_file_description']) ? $settings['llms_end_file_description'] : '') ?></textarea>
+                        <textarea name="llms_generator_settings[llms_end_file_description]" style="width: 100%;height: 80px;"><?php echo esc_textarea($settings['llms_end_file_description'] ?? '') ?></textarea>
                         <i><?php esc_html_e('Final text appended at the bottom of the LLMs.txt file (e.g. footer, contact, or disclaimer information).', 'website-llms-txt'); ?></i>
                     </p>
                     <?php if(!empty($settings)): ?>
@@ -343,10 +343,10 @@
                             <?php if(in_array($key, ['llms_txt_title', 'llms_txt_description', 'llms_after_txt_description', 'llms_end_file_description'])) continue ?>
                             <?php if(is_array($value)): ?>
                                 <?php foreach($value as $second_key => $second_value): ?>
-                                    <input type="hidden" name="llms_generator_settings[<?= $key ?>][]" value="<?= $second_value ?>"/>
+                                    <input type="hidden" name="llms_generator_settings[<?= esc_attr($key) ?>][]" value="<?= esc_attr($second_value) ?>"/>
                                 <?php endforeach ?>
                             <?php else: ?>
-                                <input type="hidden" name="llms_generator_settings[<?= $key ?>]" value="<?= $value ?>"/>
+                                <input type="hidden" name="llms_generator_settings[<?= esc_attr($key) ?>]" value="<?= esc_attr($value) ?>"/>
                             <?php endif ?>
                         <?php endforeach ?>
                     <?php endif ?>
@@ -354,11 +354,9 @@
                 </form>
             </div>
         </div>
-        <?php
-            $tab = filter_input(INPUT_GET,'tab');
-        ?>
+        <?php $tab = sanitize_key(filter_input(INPUT_GET, 'tab')); ?>
         <div class="card-column">
-            <div class="card <?php echo $tab; ?>">
+            <div class="card <?php echo esc_attr(sanitize_key($tab)); ?>">
                 <form method="post" action="options.php" id="llms-settings-crawler-form">
                     <?php settings_fields('llms_generator_settings'); ?>
                     <h2><?php esc_html_e('AI Crawler Detection','website-llms-txt') ?></h2>

Exploit Outline

The exploit targets the plugin's admin settings page, typically found at `/wp-admin/options-general.php?page=llms-file-manager`. An attacker can append a malicious payload to the 'tab' query parameter (e.g., `&tab="><script>alert(document.domain)</script>`). When a logged-in administrator visits this URL, the payload breaks out of the HTML class attribute and executes in the victim's browser session. Additionally, the plugin reflected several saved settings and hidden input fields without escaping, which could facilitate Reflected XSS if combined with a form submission that mirrors input or if an attacker can manipulate the options array.

Check if your site is affected.

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