XSS Vulnerability on Pressable/Atomic Hosting Platform via unescaped admin notices leads to code execution
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 HackerOneReport Stats
- Report ID: 3447021
- State: Closed
- Substate: resolved
- Upvotes: 4