Skip to content

REST API chat update accepts arbitrary chat fields across department boundaries

High
remdex published GHSA-hjqq-qmvj-9whm May 2, 2026

Package

No package listed

Affected versions

4.84v

Patched versions

None

Description

Summary

The Live Helper Chat REST API chat update endpoint allows a REST user with lhchat/use to update a chat in a department they cannot read. The endpoint accepts arbitrary chat object fields, so the user can change the chat hash and status and then access or tamper with the chat through visitor/widget paths. The same write primitive can set operation_admin, which is later emitted as operator-side JavaScript.

Details

PUT /restapi/chat/{chat_id} is documented as requiring lhchat/use and an assigned department in write mode. In lhc_web/modules/lhrestapi/chat.php, the PUT branch validates the REST request, checks only lhchat/use, fetches the target chat by path ID, calls erLhcoreClassRestAPIHandler::hasAccessToWrite($chat), and then assigns every request-body attribute except id before calling $chat->saveThis().

The authorization check used by this branch is weaker than the read check. hasAccessToRead() verifies the API user's all_departments flag, assigned departments, ownership, and chat status rules. hasAccessToWrite() only checks whether the chat department is in erLhcoreClassUserDep::getUserReadDepartments(). That helper returns departments explicitly marked read-only for the user, not every department outside the user's scope. A user who is not assigned to the target department can therefore fail hasAccessToRead($chat) but still pass hasAccessToWrite($chat) if the target department is not explicitly present as read-only.

The chat model persists security-sensitive fields that are accepted by this mass assignment path. erLhcoreClassModelChat::getState() and the persistent mapping include hash, status, dep_id, user_id, and operation_admin. Setting hash and status is enough to make visitor/widget endpoints that trust chat_id + hash accept the attacker-chosen hash. Setting operation_admin stages JavaScript for operator polling: syncadmin.php returns oad=1, lh.js appends a script tag for chat/loadoperatorjs/(type)/chat/(id)/<chat_id>, and loadoperatorjs.php echoes the stored operation_admin value as text/javascript.

I did not identify a server-side RCE path from this primitive. The confirmed code path is cross-department chat modification/read access and operator-side same-origin JavaScript when an operator/admin browser processes the affected chat.

PoC

  1. Set up Live Helper Chat 4.84v with two departments. Create a REST API user with lhchat/use, all_departments = 0, and assignment only to department A. Create or choose a target chat in department B.
  2. Confirm the intended boundary: hasAccessToRead($targetChat) should return false for this API user because department B is not in the user's assigned departments.
  3. Send a PUT request with the normal REST API authentication for that user:
PUT /<siteaccess>/restapi/chat/<target_chat_id> HTTP/1.1
Content-Type: application/json

{
  "hash": "attacker-controlled-hash",
  "status": 1,
  "operation_admin": "alert(document.domain)"
}
  1. Expected state transition from the audited code path: the lh_chat row for <target_chat_id> now has the attacker-supplied hash, status, and operation_admin values even though the API user cannot read department B chats.
  2. Use the attacker-chosen hash against a visitor/widget chat endpoint that checks chat_id + hash, such as the chat widget or message sync path, and observe that the target chat is accepted under the new hash.
  3. When an operator/admin browser polls or opens the affected chat, syncadmin.php marks oad=1; the frontend loads chat/loadoperatorjs/(type)/chat/(id)/<target_chat_id>, and loadoperatorjs.php returns the stored operation_admin value as JavaScript.

Impact

A low-privileged REST API user can modify and access chats outside their assigned departments. This breaks the department isolation model and exposes chat contents and chat state to users who should not be able to read them. The operation_admin field also turns the same primitive into stored operator-side JavaScript execution in the Live Helper Chat origin. Follow-on actions through that JavaScript depend on the privileges of the operator/admin session that processes the affected chat.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N

CVE ID

CVE-2026-44633

Weaknesses

Incorrect Authorization

The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. Learn more on MITRE.

Credits