CVE Tools
Back to blog

Cacti's rfilter SQL injection is back: a pre-auth 9.8 in graph_view.php (CVE-2026-39893)

A graph-filter variable slips SQL past a regex check into a MySQL RLIKE clause — reachable with no login when guest viewing is on. The same sink was already an unauth SQLi in 2023. No public exploit yet, so patch before one lands.

Cacti is the open-source performance and fault-monitoring framework that ISPs and enterprises use to graph network health — SNMP polling, RRDtool graphs, the works. CVE-2026-39893 lives in its graph browser: the rfilter request variable, which is the graph-title filter, gets concatenated straight into a MySQL RLIKE clause in graph_view.php without sanitization (CWE-89). Because graph viewing supports guest access, on installs where the guest user is enabled the injection is reachable pre-authentication — a remote attacker with no credentials can talk to Cacti's database.

What CVE-2026-39893 actually is

Cacti lets a viewer filter graphs by title using a regular expression, applied through MySQL's RLIKE operator. Per the GitHub Security Advisory (GHSA-69gg-mjfm-jjpc), the rfilter value is concatenated into that RLIKE clause on the gtg.title_cache column — the advisory pins it to graph_view.php lines 470 and 753. The value is validated only as a valid regular expression, not as SQL-safe. That distinction is the whole bug.

Because the filter lands inside a quoted RLIKE "..." string, an attacker crafts a payload that is simultaneously a valid regex and an unbalanced-quote SQL breakout — a single quote (') is a legal regex character, so it sails through the regex validator and reaches the database. It's the same lesson the codebase already learned once: an input validator that only checks "is this a well-formed regex" is not a sanitizer. In fact, this exact rfilter → RLIKE sink was an unauthenticated SQL injection back in 2023 (CVE-2023-39361), so CVE-2026-39893 is effectively a re-introduction of a three-year-old bug on the same parameter.

The attack chain

Everything hinges on one gate: is guest graph viewing enabled on an internet-reachable Cacti? If not, the bug isn't pre-auth (patch anyway — config drifts, and the fix is in code). If yes, the chain runs unauthenticated. The injection is blind — the filter result isn't echoed back — so extraction is inferential: boolean conditions through the RLIKE regex, or classic time-based SLEEP(). The prize is Cacti's user_auth table, which holds local password hashes; crack an admin hash and you have an authenticated session, from which older Cacti CVEs have a documented SQLi-to-RCE path. The cheapest place to break the chain is the guest gate; the most durable is the patch.

CVE-2026-39893 attack chain — pre-auth rfilter SQLi to credential theft

  1. Internet-facing Cacti <= 1.2.30 — A Cacti console reachable from the internet running a vulnerable version. Cacti is often internal, but a meaningful subset is exposed (~1,600+ during past campaigns).
  2. Guest graph viewing enabled? — Request `graph_view.php?action=tree` with no session cookie. HTTP 200 + graph markup = guest ON = **pre-auth exploitable**. A 302 redirect to login = guest OFF = authenticated-only.
  3. Not pre-auth — patch anyway — Guest disabled means the bug needs an authenticated graph-view user. Lower urgency, but still upgrade — config drifts and the fix is in code.
  4. Craft regex-valid quote breakout in rfilter — Payload must be BOTH a valid regex AND an unbalanced-quote SQL breakout (e.g. `.*') OR SLEEP(6)-- -`). A single quote is a legal regex char, so it passes validation and reaches the RLIKE clause.
  5. Blind SQLi via RLIKE (boolean / time-based) — No echoed output — infer data via RLIKE boolean conditions or MySQL SLEEP()/BENCHMARK() timing. Slow, but fully scriptable.
  6. Read user_auth password hashes — Blind/UNION read of Cacti's `user_auth` table extracts local password hashes — the pivot from 'database read' to 'account takeover'.
  7. Crack hash -> authenticated -> RCE (precedent only) — By precedent (Rapid7's cacti_filter_sqli_rce for the CVE-2023-39361 family), an authenticated attacker can abuse poller/settings for code execution. NOT demonstrated for this CVE — theoretical-by-precedent.
  8. PREVENT: patch 1.2.31 / disable guest / WAF — The db_qstr_rlike() helper closes the sink; disabling guest removes pre-auth reach; a WAF rule rejecting a quote in rfilter blocks the breakout. Any one of these breaks the chain at entry.
  9. DETECT: quote+SQL token; slow queries on user_auth — Alert on `rfilter` carrying a quote (%27/') plus a SQL token, and on slow_query_log SELECTs > 5s or any UNION SELECT touching user_auth. Bare regex metacharacters are NOT indicators.

Am I affected?

You're on the vulnerable side if you run Cacti 1.2.30 or earlier. You're in the pre-auth blast radius only if, on top of that, the console is network-reachable and guest graph viewing is enabled. Two quick checks settle it: read the version, then probe guest access.

# 1) Version oracle — include/cacti_version is plaintext on most installs:
curl -s -m 10 'https://TARGET/cacti/include/cacti_version'      # -> e.g. 1.2.30 (compare to 1.2.31)

# 2) Is guest graph-viewing enabled? (unauth heuristic — send NO cookie)
#    200 + graph markup => guest ON (pre-auth exploitable); 302 -> login => guest OFF.
curl -s -m 10 -o /dev/null -w '%{http_code} %{redirect_url}\n' \
     'https://TARGET/cacti/graph_view.php?action=tree'

# 3) Authoritative check with DB access (0 = No User / disabled):
#    mysql cacti -N -e "SELECT value FROM settings WHERE name='guest_user';"
ItemDetail
AffectedCacti 1.2.30 and earlier
FixedCacti 1.2.31 (routes rfilter through a new db_qstr_rlike() helper)
Preconditions for pre-authGuest graph viewing enabled (disabled by default) + network reachable
Access requiredNone if guest viewing is on; otherwise an authenticated graph-view user
ImpactSQL injection against Cacti's MySQL — read local password hashes (user_auth); blind (boolean/time-based)
CVSS / CWE9.8 CRITICAL (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), CWE-89 — 9.8 assumes guest enabled
EPSS~0.36% (28th percentile), as of 2026-07-03 — low, reflecting no known exploitation
StatusNot on CISA KEV; no public PoC, Nuclei template, or in-the-wild activity as of 2026-07-03

Exploitation status: no PoC yet — which is the window

As of July 3, 2026 this is a disclosure-and-patch event, not an active incident. A multi-source sweep found no public proof-of-concept, no Nuclei template, no Metasploit module, no ExploitDB entry, and no in-the-wild or scanning reports. The advisory itself is description-only, with no payload. So exploit maturity is genuinely none today.

One honesty note on escalation: whether this SQLi can reach remote code execution is plausible by precedent, not demonstrated. Cacti's graph-filter SQLi lineage has a documented SQLi-to-RCE chain — Rapid7's cacti_filter_sqli_rce Metasploit module (CVE-2023-39361 family) chains a graph_view.php injection to code execution via poller/settings abuse. That establishes a realistic path (steal an admin hash from user_auth, log in, abuse authenticated features), but no public writeup demonstrates it for CVE-2026-39893. Treat RCE as theoretical-by-precedent, and the credential theft as the concrete near-term risk.

Detection & hunting

No public detection content exists for this CVE yet, so the rules below are authored from the RLIKE-clause mechanics — validate them in a lab first. The load-bearing signal is simple: a single quote (%27 / ') inside rfilter. Legitimate rfilter values are graph-title regexes and never need a quote breakout, so bare regex metacharacters like ( ) [ ] . ** are not* indicators — it's the quote plus a SQL token that matters. Because the attack is blind, the strongest evidence is server-side, in the database logs.

  • Web / proxy logs: requests to graph_view.php where the rfilter parameter contains a quote (%27 or ') and a SQL token — UNION, SELECT, SLEEP(, BENCHMARK(, information_schema, updatexml, extractvalue, @@version, or comment markers (-- -, /*). A Sigma rule keyed on the endpoint + parameter + these tokens keeps false positives low.
  • MySQL general/slow query log (highest signal for blind SQLi): RLIKE clauses carrying SQL keywords should never occur from normal graph filtering — grep -iE "RLIKE '[^'](union|select . from |sleep\(|benchmark\(|information_schema)". Enable slow_query_log and hunt SELECTs over ~5s originating from graph_view (time-based blind leaves slow entries).
  • Credential-theft tell: any UNION SELECT ... FROM user_auth in the MySQL general log is a high-severity indicator that hashes are being read.
  • SQL errors from probing: error in your SQL syntax, Unknown column, Subquery returns more than 1 row in the MySQL error log, correlated with guest-user request spikes from a single IP in Cacti's own log (Console -> Utilities -> View Cacti Log).
  • Nuclei: no official template exists — a safe, time-based check (baseline vs rfilter=.')%20OR%20SLEEP(6)--%20-, comparing response duration) can be authored locally, but the parenthesis depth of the surrounding query isn't published, so try the .', .'), .')) breakout variants. Run only against systems you own.

What to do now

  1. Patch to Cacti 1.2.31 or later — the durable fix. It routes rfilter through a new db_qstr_rlike() helper that caps length, strips regex metacharacters, and properly quotes the value. Note 1.2.31 also raises the minimum PHP to 8.1 and bundles ~33 security fixes, so test in staging.
  2. Can't patch this hour? Disable guest access. Set Console -> Configuration -> Settings -> Authentication -> Guest User = 'No User' (or UPDATE settings SET value='0' WHERE name='guest_user'), and strip the guest user's Graph View permission. This instantly downgrades the bug from pre-auth to authenticated — the single highest-value stopgap.
  3. Reduce reachability. Put the Cacti console behind VPN / IP allowlist / SSO. graph_view.php should never face the open internet.
  4. Add a WAF virtual patch. Block requests to graph_view.php where rfilter contains a single quote (%27/') or a SQL token. Because legitimate rfilter regexes never contain a quote, rejecting a quote there is a high-precision virtual patch that mirrors exactly what the code fix does.
  5. Verify the fix landed: confirm include/cacti_version reads 1.2.31+, and grep -R db_qstr_rlike graph_view.php lib/database.php finds the helper wired in. If a guest-enabled box was exposed for a while, run the DB/log hunts above and rotate local Cacti credentials — the hashes in user_auth are the target.

FAQ

Is CVE-2026-39893 being exploited?
Not as of July 3, 2026. There's no public proof-of-concept, no Nuclei template, no CISA KEV listing, and no reported in-the-wild activity. It's a disclosure-and-patch event — but Cacti's history of rapid botnet weaponisation is the reason to patch before an exploit appears.
Is it really a no-login (pre-auth) bug?
Only when guest graph viewing is enabled, which is off by default in Cacti. On a default/hardened install the injection requires an authenticated user with graph-view rights. That's why the CVSS 9.8 (which assumes no privileges) is a worst-case rating.
Which versions are affected and what's the fix?
Cacti 1.2.30 and earlier are vulnerable. Upgrade to 1.2.31, which routes the rfilter value through a new db_qstr_rlike() helper. If you can't patch immediately, disabling guest access removes the pre-auth exposure.
What can an attacker actually get?
The injection is blind SQL injection against Cacti's MySQL database — most directly, reading local password hashes from the user_auth table. Remote code execution is plausible by analogy to older Cacti SQLi-to-RCE chains but has not been demonstrated for this CVE.
How do I detect exploitation attempts?
Watch for a single quote (%27 or ') plus a SQL keyword in the rfilter parameter on graph_view.php, and hunt your MySQL general/slow query logs for RLIKE clauses carrying SQL keywords or any UNION SELECT touching user_auth. Bare regex characters are not indicators.

Sources

  • GitHub Security Advisory GHSA-69gg-mjfm-jjpc (root cause, sink lines, fix): https://github.com/Cacti/cacti/security/advisories/GHSA-69gg-mjfm-jjpc
  • NVD — CVE-2026-39893: https://nvd.nist.gov/vuln/detail/CVE-2026-39893
  • Cacti 1.2.31 release notes: https://github.com/Cacti/cacti/releases/tag/release/1.2.31
  • GHSA-6r43-q2fw-5wrg — CVE-2023-39361, the near-identical 2023 rfilter SQLi: https://github.com/Cacti/cacti/security/advisories/GHSA-6r43-q2fw-5wrg
  • Rapid7 — cacti_filter_sqli_rce Metasploit module (SQLi-to-RCE precedent): https://www.rapid7.com/db/modules/exploit/unix/http/cacti_filter_sqli_rce/
  • Sonar — Cacti unauthenticated RCE research (product exposure context): https://www.sonarsource.com/blog/cacti-unauthenticated-remote-code-execution/
  • CyberPress — Critical Cacti Vulnerabilities: https://cyberpress.org/critical-cacti-vulnerabilities/