XSS Vulnerability on Pressable/Atomic Hosting Platform via unescaped admin notices leads to code execution

Disclosed: 2026-02-18 14:19:07 By georgestephanis To automattic
Unknown
Vulnerability Details
## Summary: XSS Vulnerability: Unescaped text output in admin notice on `atomic-platform.php` ## Platform(s) Affected: Pressable / Atomic Hosting Platform ## Steps To Reproduce: 1. Update or set the `atomic_single_option_limiter_notices` option on a site to `a:1:{s:34:"<script>alert('test')</script>test";a:1:{s:7:"expires";i:1893456000;}}` 2. Visit `/wp-admin/plugins.php` 3. The XSS payload will execute in the admin notice area This vulnerability is contingent on getting the XSS payload into the options table first. Once injected, it will be executed when other users view the affected admin page. ## Supporting Material/References: Vulnerable code in `atomic-platform.php`: ```php function add_admin_notice( $message, $class = 'notice notice-error is-dismissible' ) { $this->admin_notices[] = array( $message, $class ); } function print_admin_notices() { foreach ( $this->admin_notices as $notice ) { ?> <div class="<?php echo esc_attr( $notice[1] ); ?>"> <p><?php echo $notice[0]; ?></p> </div> <?php } } ``` The notices are echoed without any escaping -- missing `esc_html()` around the `$notice[0]`. This is likely done because there can be some markup in the notice -- `<strong>` tags, for example: ```php function single_option_limiter_notices() { $admin_notices = get_option( $this->single_option_limiter_notices_key ); if ( empty( $admin_notices ) || ! is_array( $admin_notices ) ) { return; } $changed = false; foreach ( $admin_notices as $option => $data ) { if ( $data['expires'] > time() ) { $this->add_admin_notice( "Excess updates to option <strong>$option</strong> blocked." ); } else { unset( $admin_notices[ $option ] ); $changed = true; } } if ( $changed ) { update_option( $this->single_option_limiter_notices_key, $admin_notices ); } } ``` However, even here, when they are set from being pulled in from the options table, they aren't escaped when interpolated into the string. Recommended fix: Escape the output in the `print_admin_notices()` method, or earlier using something like: ```php $this->add_admin_notice( sprintf( 'Excess updates to option <strong>%s</strong> blocked.', esc_html( $option ) ); ); ``` Note: The admin notice comes with `is-dismissible` class, but dismissing it does nothing as it reappears on page refresh.
Actions
View on HackerOne
Report Stats
  • Report ID: 3447021
  • State: Closed
  • Substate: resolved
  • Upvotes: 4
Share this report