GHSA-G82G-M9VX-VHJG

Vulnerability from github – Published: 2026-04-15 19:46 – Updated: 2026-04-15 19:46
VLAI?
Summary
Kimai has Stored XSS via Incomplete HTML Attribute Escaping in Team Member Widget
Details

Summary

The client-side escapeForHtml() function in KimaiEscape.js, introduced in commit 89bfa82c (#2959) to fix a JavaScript XSS vulnerability, only escapes <, >, and & but does not escape " (double quote) or ' (single quote). When user-controlled data (profile alias) is placed in an HTML attribute context (title="__DISPLAY__") via the team member form prototype and rendered through innerHTML, the missing quote escaping allows HTML attribute injection, resulting in Stored XSS.

Details

Incomplete security patch. The escapeForHtml() function was meant to prevent XSS but missed quote characters, which are critical for HTML attribute context escaping.

Vulnerable codeassets/js/plugins/KimaiEscape.js:29-33:

const tagsToReplace = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    // MISSING: '"': '&quot;'
    // MISSING: "'": '&#039;'
};

Affected code files: - assets/js/plugins/KimaiEscape.js:24-38 — incomplete escape function - assets/js/forms/KimaiTeamForm.js:77,86 — replacement + innerHTML - templates/macros/widgets.html.twig:126title="{{ tooltip }}" in avatar macro - templates/form/blocks.html.twig:104{{ widgets.avatar('__INITIALS__', '__COLOR__', '__DISPLAY__') }}

PoC

poc.zip

Please extract the uploaded compressed file before proceeding

  1. ./setup.sh
  2. ./poc_xss.sh

스크린샷 2026-04-07 오후 9 06 27

Impact

  • Stored XSS: payload persists in the database (user alias field)
  • Privilege escalation: ROLE_USER injects XSS that executes in ROLE_ADMIN/ROLE_SUPER_ADMIN browser session
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "kimai/kimai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.53.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-40479"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-15T19:46:35Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\nThe client-side `escapeForHtml()` function in `KimaiEscape.js`, introduced in commit `89bfa82c` (#2959) to fix a JavaScript XSS vulnerability, only escapes `\u003c`, `\u003e`, and `\u0026` but does not escape `\"` (double quote) or `\u0027` (single quote). When user-controlled data (profile alias) is placed in an HTML attribute context (`title=\"__DISPLAY__\"`) via the team member form prototype and rendered through `innerHTML`, the missing quote escaping allows HTML attribute injection, resulting in Stored XSS.\n\n### Details\nIncomplete security patch. The `escapeForHtml()` function was meant to prevent XSS but missed quote characters, which are critical for HTML attribute context escaping.\n\n**Vulnerable code** \u2014 `assets/js/plugins/KimaiEscape.js:29-33`:\n```javascript\nconst tagsToReplace = {\n    \u0027\u0026\u0027: \u0027\u0026amp;\u0027,\n    \u0027\u003c\u0027: \u0027\u0026lt;\u0027,\n    \u0027\u003e\u0027: \u0027\u0026gt;\u0027,\n    // MISSING: \u0027\"\u0027: \u0027\u0026quot;\u0027\n    // MISSING: \"\u0027\": \u0027\u0026#039;\u0027\n};\n```\n\n**Affected code files**:\n- `assets/js/plugins/KimaiEscape.js:24-38` \u2014 incomplete escape function\n- `assets/js/forms/KimaiTeamForm.js:77,86` \u2014 replacement + innerHTML\n- `templates/macros/widgets.html.twig:126` \u2014 `title=\"{{ tooltip }}\"` in avatar macro\n- `templates/form/blocks.html.twig:104` \u2014 `{{ widgets.avatar(\u0027__INITIALS__\u0027, \u0027__COLOR__\u0027, \u0027__DISPLAY__\u0027) }}`\n\n### PoC\n[poc.zip](https://github.com/user-attachments/files/26537515/poc.zip)\n\nPlease extract the uploaded compressed file before proceeding\n\n1. ./setup.sh\n2. ./poc_xss.sh\n\n\u003cimg width=\"751\" height=\"155\" alt=\"\u1109\u1173\u110f\u1173\u1105\u1175\u11ab\u1109\u1163\u11ba 2026-04-07 \u110b\u1169\u1112\u116e 9 06 27\" src=\"https://github.com/user-attachments/assets/c09a23fb-f60b-49dd-9018-8c723e35b4c4\" /\u003e\n\n### Impact\n- Stored XSS: payload persists in the database (user alias field)\n- Privilege escalation: ROLE_USER injects XSS that executes in ROLE_ADMIN/ROLE_SUPER_ADMIN browser session",
  "id": "GHSA-g82g-m9vx-vhjg",
  "modified": "2026-04-15T19:46:35Z",
  "published": "2026-04-15T19:46:35Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/security/advisories/GHSA-g82g-m9vx-vhjg"
    },
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/pull/2959"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/kimai/kimai"
    },
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/releases/tag/2.53.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Kimai has Stored XSS via Incomplete HTML Attribute Escaping in Team Member Widget"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…