SECURITY ADVISORY / 01

CVE-2025-12166 Exploit & Vulnerability Analysis

Complete CVE-2025-12166 security advisory with proof of concept (PoC), exploit details, and patch analysis for simply-schedule-appointments.

simply-schedule-appointments products NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

An unauthenticated attacker can inject arbitrary SQL into the field or field_where parameters of the plugin's database query methods by appending SQL operators and subqueries. No authentication or CSRF token is required.

GET /wp-admin/admin-ajax.php?action=td_get_appointment&field=id,1) UNION SELECT user_login,user_pass FROM wp_users WHERE (1=1 HTTP/1.1
Host: target-site.local

The response leaks database column names and schema information; a crafted payload using UNION SELECT against wp_users or wp_postmeta tables will extract password hashes, API keys, or customer PII stored in the database. The attacker observes valid rows from the injection subquery appearing in the JSON response where legitimate appointment data should be, confirming the query succeeded.

What the Patch Did

Before:

$field = esc_sql( $field );
return $wpdb->get_var( $wpdb->prepare( "SELECT $field FROM {$this->get_table_name()} WHERE $field_where = %s LIMIT 1;", $field_value ) );

After:

$sanitized_field = sanitize_key( esc_sql( $field ) );
return $wpdb->get_var( $wpdb->prepare( "SELECT $sanitized_field FROM {$this->get_table_name()} WHERE $sanitized_field_where = %s LIMIT 1;", $field_value ) );

The patch added a call to sanitize_key() on top of the existing esc_sql() call. sanitize_key() is a WordPress core function that strips all characters except alphanumerics and underscores, ensuring that only valid SQL identifiers (column names, table names) can be interpolated into the query string. This is the correct input filter for identifier sanitization in WordPress; esc_sql() alone is insufficient because it escapes quotes but does not restrict the character set.

Root Cause

CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

The attacker controls the field and field_where parameters via HTTP request parameters (likely GET or POST to an AJAX action handler). These values flow directly into the SQL query string via string interpolation:

"SELECT $field FROM {$this->get_table_name()} WHERE $field_where = %s"

Even though $field_value (the third parameter) is passed to wpdb->prepare() and safely parameterized, the identifier names themselves ($field, $field_where) are not parameterized — they are concatenated directly into the query string. The esc_sql() function escapes single and double quotes but does not restrict the character set; an attacker can still inject SQL operators, subqueries, and logical operators by crafting malicious field names such as id, (SELECT password FROM wp_users)--. The trust boundary is crossed the moment user input reaches these variables without sanitize_key().

Why It Works

The load-bearing line is $sanitized_field = sanitize_key( esc_sql( $field ) );. Removing only sanitize_key() while keeping esc_sql() would not prevent the injection because esc_sql() permits arbitrary SQL syntax — it only quotes string literals. An attacker can still inject operators, parentheses, and keywords. The engineer applied sanitize_key() before string interpolation to enforce a whitelist of safe characters (letters, digits, underscores); this is the only technique that prevents identifiers from being misused as injection points. The esc_sql() call is retained for defence-in-depth, but it is redundant after sanitize_key() strips all dangerous characters. The dual-layer approach ensures that even if sanitize_key() is accidentally removed in a future refactor, the code is not immediately broken — though still vulnerable.

Hardening Checklist

  • Use sanitize_key() for all dynamically-constructed identifier names (column names, table names, field names) before string interpolation, even when combined with esc_sql(). Make it a code review rule: identifiers are never parameterizable in SQL, so they must be whitelisted.

  • Audit all direct string interpolation in SQL queries across your plugin. Use a regex pattern to find "SELECT.*\$var" or $wpdb->prepare( "... $var ..." and replace each with the correct sanitizer: sanitize_key() for identifiers, %s-parameterization for values.

  • Replace wpdb->prepare() calls that mix parameterization and interpolation with helpers that parameterize identifiers too, or use a query builder library that handles both safely. If your framework does not support identifier parameterization, document the constraint and enforce sanitization manually.

  • Require WP_List_Table::get_sortable_columns() validation if your AJAX handlers accept order or orderby parameters: whitelist them against a fixed set of approved column names using a switch statement rather than trusting user input as a field name.

  • Add automated static analysis (PHPStan with WordPress rules, or SonarQube) to your CI pipeline to detect unparameterized variables in SQL query strings and flag them before merge.

References

Frequently asked questions about CVE-2025-12166

What is CVE-2025-12166?

CVE-2025-12166 is a security vulnerability identified in simply-schedule-appointments. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2025-12166?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2025-12166. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2025-12166 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology affecting simply-schedule-appointments. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2025-12166?

CVE-2025-12166 affects simply-schedule-appointments. Check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2025-12166?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls for simply-schedule-appointments.

What is the CVSS score for CVE-2025-12166?

The severity rating and CVSS scoring for CVE-2025-12166 affecting simply-schedule-appointments is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.