Website LLMs.txt <= 8.2.6 - Reflected Cross-Site Scripting
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:NTechnical Details
<=8.2.6What Changed in the Fix
Changes introduced in v8.2.7
Source Code
WordPress.org SVN$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
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
@@ -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.