Web Directory Free <= 1.7.12 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Web Directory Free plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.7.12 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.7.12Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-69018 (Web Directory Free) ## 1. Vulnerability Summary **CVE-2025-69018** is a Stored Cross-Site Scripting (XSS) vulnerability in the **Web Directory Free** plugin (versions <= 1.7.12). The vulnerability allows authenticated attackers with **Contributor-level*…
Show full research plan
Exploitation Research Plan: CVE-2025-69018 (Web Directory Free)
1. Vulnerability Summary
CVE-2025-69018 is a Stored Cross-Site Scripting (XSS) vulnerability in the Web Directory Free plugin (versions <= 1.7.12). The vulnerability allows authenticated attackers with Contributor-level permissions or higher to inject malicious JavaScript into directory listings. This occurs because the plugin fails to properly sanitize user-supplied metadata fields during storage and fails to escape those fields when rendering them on the front-end or in the admin dashboard.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/post.php(standard WordPress post processor) or a custom AJAX handler (inferred). - Action:
editpostorwmd_save_listing(inferred). - Vulnerable Parameter: Custom metadata fields associated with the directory listing (e.g., Address, Phone, Website, or custom text fields).
- Authentication: Requires a user with
Contributorrole or higher. Contributors can typically create and edit their own listings. - Preconditions: The plugin must be active, and a directory listing (custom post type) must be created or edited by the attacker.
3. Code Flow (Inferred)
- Entry Point: An authenticated user (Contributor) submits a form to create or update a directory listing (Post Type:
wmd-listing- inferred). - Processing: The request is handled by a function hooked to
save_postor a specific AJAX action (e.g.,Wmd_Listings_Manager::save_listing_data- inferred). - Storage: The plugin retrieves data from
$_POST(e.g.,$_POST['wmd_address']) and stores it usingupdate_post_meta()without passing it throughsanitize_text_field()orwp_kses(). - Sink: When a user (e.g., an Admin) views the listing, the plugin retrieves the data using
get_post_meta()and outputs it in a template file (e.g.,single-listing.phpor an admin table) using a rawechoorprintfwithout context-specific escaping likeesc_html()oresc_attr().
4. Nonce Acquisition Strategy
Since the attacker is a Contributor, they can access the WordPress dashboard to retrieve a valid nonce.
- Navigate to Listing Creator: Use
browser_navigateto go to the "Add New Listing" page.- URL:
http://[target]/wp-admin/post-new.php?post_type=wmd-listing(inferred).
- URL:
- Extract Nonce:
- If using the standard Post Editor: Use
browser_evalto get the value of the_wpnoncefield.browser_eval("document.querySelector('#_wpnonce')?.value")
- If the plugin uses a custom AJAX interface: Look for a localized script object.
browser_eval("window.wmd_admin_settings?.nonce")(inferred variable name).
- If using the standard Post Editor: Use
- Post ID: For updates, the
post_IDis also required, found in the URL or a hidden input field.
5. Exploitation Strategy
The goal is to inject a stored XSS payload into a directory listing field that will execute when an Administrator views the listing.
Step-by-Step Plan:
- Identify Target Field: Through the Contributor dashboard, identify text inputs for a listing (e.g., "Street Address").
- Capture Request: Observe the POST request when saving a listing to identify the exact parameter name (e.g.,
wmd_listing_meta[address]). - Prepare Payload:
<script>alert('XSS_POC_CVE-2025-69018')</script>. - Execute Injection: Use the
http_requesttool to send a forged POST request mimicking the save action.
Example Request (Inferred):
POST /wp-admin/post.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
action=editpost
&post_ID=[POST_ID]
&_wpnonce=[NONCE]
&post_title=Malicious Listing
&wmd_listing_address=<script>alert(document.domain)</script>
&save=Save
6. Test Data Setup
- Plugin Installation: Install and activate
web-directory-freev1.7.12. - User Creation: Create a user with the
Contributorrole. - Directory Configuration: Ensure at least one directory category is created so a listing can be assigned.
- Shortcode Placement: Create a page with the directory shortcode to view the front-end output:
wp post create --post_type=page --post_status=publish --post_title="Directory" --post_content="[web_directory]"(inferred shortcode).
7. Expected Results
- The POST request returns a
302 Foundredirecting back to the post editor. - The payload is saved in the
wp_postmetatable for the correspondingpost_id. - When an Administrator navigates to the directory listing on the front-end or views the listing in the "All Listings" admin page, the browser executes the script, and an alert box displaying the domain appears.
8. Verification Steps
- Database Check: Use WP-CLI to verify the payload is stored unsanitized:
wp post meta get [POST_ID] wmd_listing_address(inferred key).
- Front-end Verification: Use the
browser_navigatetool to visit the URL of the created listing and check for the alert. - Source Code Inspection: Inspect the page source via
browser_eval("document.body.innerHTML")to confirm the<script>tags are present and not escaped (e.g., not converted to<script>).
9. Alternative Approaches
- Payload Variation: If
<script>is filtered by a generic firewall, try attribute-based injection:"><img src=x onerror=alert(1)>
- Location Variation: If the "Address" field is sanitized, try other fields like "Zip Code", "Phone", or custom field labels/values if the plugin allows Contributors to define them.
- Admin-Specific XSS: Target fields that only appear in the admin "Edit Listing" view to specifically target Administrative users.
Summary
The Web Directory Free plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to and including 1.7.12. This is due to the plugin failing to properly sanitize and escape listing metadata fields, allowing authenticated attackers with Contributor-level permissions to inject malicious scripts into directory listings that execute when viewed by other users.
Exploit Outline
An authenticated attacker with Contributor-level access or higher logs into the WordPress dashboard and navigates to the directory listing creation or editing interface. The attacker injects a malicious script payload (e.g., <script>alert(document.domain)</script>) into one of the listing's metadata fields, such as the Street Address, Phone, or Website field. When the listing is saved, the payload is stored unsanitized in the database. The script then executes in the context of any user, including administrators, who views the listing on the front-end or via the admin management pages.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.