This is a guest badge that quietly opens the server room
CVE-2026-28797 is an authenticated server-side template injection in RAGFlow Agent workflow components, specifically Text Processing (StringTransform) and Message. In RAGFlow 0.24.0 and earlier, those components render user-controlled Jinja2 templates without sandboxing, so a low-privileged user can turn a harmless-looking template field into OS command execution on the RAGFlow server.
The vendor's HIGH call is directionally right on impact but a little hot if you care about enterprise patch queues. This is not pre-auth internet spray-and-pray RCE: the attacker needs a valid account and access to Agent workflow features, which sharply narrows exposed population; but the default self-registration path and the fact that *any* authenticated user can jump straight to server command execution keep it solidly in HIGH rather than MEDIUM.
4 steps from start to impact.
Get a basic RAGFlow account
REGISTER_ENABLED=1, so on casually exposed deployments this can be as simple as creating a normal user instead of stealing one.- RAGFlow is reachable from the attacker's network position
- The attacker can self-register or already has valid credentials
- Agent workflow features are available to the authenticated user
- Many enterprise deployments put RAGFlow behind SSO, VPN, or reverse proxies
- Some shops disable self-registration or do not expose the app publicly
- This prerequisite already implies a post-initial-access or valid-user scenario when registration is closed
Plant a Jinja2 SSTI payload in an Agent component
Text Processing or Message node and inserts a malicious Jinja2 expression such as the advisory's cycler.__init__.__globals__.os.popen(...) payload. The vulnerable code path uses unsandboxed jinja2.Template, so template syntax becomes code-reachable rather than data-only.- Authenticated user can create or edit an Agent flow
- Vulnerable code is present in
agent/component/string_transform.pyoragent/component/message.py
- Requires knowledge of the Agent workflow UI and which nodes render templates
- If the deployment already moved to sandboxed rendering in
v0.25.0+, this breaks
Trigger render and execute commands
os.popen() executes with the privileges of the RAGFlow server process, turning a normal user workflow action into host-level command execution in the application context.- The crafted workflow is executed
- The server process can spawn commands through Python runtime access
- EDR can sometimes catch suspicious child processes like
bash,sh, orpythonspawned by the app - Container hardening, seccomp, or restricted runtime privileges can limit follow-on damage even if code execution lands
Harvest secrets and pivot inside the stack
conf/service_conf.yaml, environment variables, and service credentials for MySQL, Redis, MinIO, and Elasticsearch. That means the practical blast radius is not just the web app process; it often becomes database access, document theft, API key exposure, and lateral movement into adjacent internal services.- RAGFlow stores usable credentials locally or in environment variables
- Backend services are reachable from the compromised app host or container
- Segmentation and secret externalization reduce the value of app compromise
- Read-only service accounts and isolated data-plane services can blunt the pivot
conf/service_conf.yaml, bulk DB access from the app tier, unusual Redis/Elasticsearch/MinIO connections, and egress after Agent workflow executions.The supporting signals.
| In-the-wild status | No public active-exploitation evidence found in the reviewed authoritative sources, and CISA KEV does not list this CVE as of 2026-05-30. |
|---|---|
| PoC availability | Yes, effectively public. The GitHub advisory includes full reproduction steps, working payloads, and a demo video, which is enough for immediate copy-paste weaponization. |
| EPSS | User-supplied EPSS is 0.00102 (~0.10%), which is low. Primary FIRST reference confirms EPSS is a daily probability signal, but the exact percentile for this CVE was not directly verifiable from reviewed primary sources. |
| KEV status | Not KEV-listed. That materially lowers urgency versus edge-device or mass-exploitation bugs. |
| CVSS vector readout | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H means network reachable, easy to exploit, but requires valid low privileges. The whole reassessment turns on that PR:L prerequisite. |
| Affected versions | Authoritative sources say RAGFlow 0.24.0 and earlier are affected, in Agent workflow StringTransform and Message components. |
| Fixed version | The advisory said no public patch at publication. Upstream code and release timing indicate the sandboxed fix is present by v0.25.0; no distro backport guidance was found. |
| Exposure reality | This is not a firewall-class product with huge edge prevalence. Public web search of Shodan facet pages shows only low-count indexed banners titled RAGFlow, suggesting limited direct internet exposure; exact counts fluctuate and this is an inference from public scan pages. |
| Disclosure timeline | GitHub advisory published 2026-04-01; NVD published 2026-04-03; NVD last modified 2026-04-22. |
| Reporter / source | The authoritative disclosure path is GitHub Security Advisory GHSA-vvwj-fvwh-4whx, published by yingfeng for infiniflow/ragflow. |
noisgate verdict.
The decisive factor is the authenticated-user requirement: this is not a perimeter bug, it is a valid-user-to-server-compromise bug. That said, the blast radius after successful exploitation is large because RAGFlow commonly holds API keys, database credentials, and access to internal data services.
Why this verdict
- Downgrade from vendor 8.8 for
PR:L: requiring authenticated remote access means the attacker is not starting from the open internet unless self-registration is enabled or creds are already compromised. - Exposure population is narrower: RAGFlow is popular in open-source circles, but it is not an internet-edge staple, and public scan artifacts suggest only limited direct exposure compared with mass-targeted platforms.
- Upgrade pressure remains because blast radius is ugly: once inside, the attacker can pivot from app-level template injection to command execution, secret theft, data access, and likely control of backing services.
Why not higher?
This is not pre-auth RCE and not a KEV-listed or publicly observed mass-exploitation event. The chain assumes either valid credentials or a registration path, and many enterprise deployments should already put this app behind SSO, VPN, or restricted admin access.
Why not lower?
The permission bar is low: GitHub's advisory says *any authenticated user* can reach the vulnerable workflow components, and self-registration is enabled by default. Once triggered, the vulnerability is not a narrow tenant-only issue; it can expose host commands, service credentials, and all tenant data reachable by the server.
What to do — in priority order.
- Disable self-registration — If you cannot patch immediately, remove the easiest attacker on-ramp by setting
REGISTER_ENABLED=0or otherwise closing public signup. For a HIGH verdict, deploy this compensating control within 30 days; do it first on any internet-reachable instance. - Put RAGFlow behind identity and network gates — Force access through SSO, VPN, IP allowlists, or a private reverse proxy so this stays an internal-user problem instead of an internet problem. For a HIGH verdict, apply this within 30 days, prioritizing exposed or partner-accessible deployments.
- Restrict Agent workflow access — Remove Agent creation/edit rights from ordinary users and confine those functions to trusted admins or service users. This directly cuts the exploit path because the vulnerable components sit inside the Agent workflow feature set; enforce within 30 days.
- Harden the runtime — Run RAGFlow with least privilege, tight container profiles, minimal mounted secrets, and segmentation to MySQL/Redis/MinIO/Elasticsearch. This will not prevent code execution, but it can cap post-exploitation blast radius; implement within 30 days where patching is delayed.
- Alert on app-to-shell behavior — Tune EDR or container runtime telemetry to flag shells,
os.popen, reverse shells, or unusual outbound connections from the RAGFlow process/container. This is a detection-oriented compensating control and should also be in place within 30 days for lingering vulnerable nodes.
- A generic WAF is not enough; the exploit sits in authenticated business logic and uses legitimate workflow fields rather than a clean unauthenticated HTTP exploit string.
- Relying on network-only vuln scanning will miss too much because the vulnerable path requires login and specific Agent workflow interactions.
- Simply rotating LLM provider API keys does not solve the core issue; the attacker can still execute commands and read whatever new secrets the app can access.
Crowdsourced verification payload.
Run this on the target RAGFlow host or inside the ragflow-server container. Example: docker exec -i ragflow-server bash -s -- /ragflow < ./check_cve_2026_28797.sh or, from a source checkout, bash ./check_cve_2026_28797.sh /path/to/ragflow; no root is required unless you need docker exec access.
#!/usr/bin/env bash
# check_cve_2026_28797.sh
# Detects likely vulnerable vs patched RAGFlow code for CVE-2026-28797
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
TARGET="${1:-/ragflow}"
if [ ! -d "$TARGET" ]; then
if [ -d ".git" ] && [ -f "agent/component/string_transform.py" ]; then
TARGET="."
fi
fi
STR_FILE="$TARGET/agent/component/string_transform.py"
MSG_FILE="$TARGET/agent/component/message.py"
if [ ! -f "$STR_FILE" ] || [ ! -f "$MSG_FILE" ]; then
echo "UNKNOWN - could not find RAGFlow source files under: $TARGET"
exit 2
fi
has_vuln_pattern() {
local f="$1"
grep -Eq 'from jinja2 import Template as Jinja2Template|Jinja2Template\(' "$f"
}
has_patch_pattern() {
local f="$1"
grep -Eq 'from jinja2\.sandbox import SandboxedEnvironment|SandboxedEnvironment\(|_jinja2_sandbox|from_string\(' "$f"
}
str_vuln=0
msg_vuln=0
str_patch=0
msg_patch=0
has_vuln_pattern "$STR_FILE" && str_vuln=1
has_vuln_pattern "$MSG_FILE" && msg_vuln=1
has_patch_pattern "$STR_FILE" && str_patch=1
has_patch_pattern "$MSG_FILE" && msg_patch=1
version_hint="unknown"
if [ -f "$TARGET/README.md" ]; then
version_hint=$(grep -Eo 'v0\.[0-9]+\.[0-9]+' "$TARGET/README.md" | head -n1 || true)
[ -z "$version_hint" ] && version_hint="unknown"
fi
if [ "$str_vuln" -eq 1 ] || [ "$msg_vuln" -eq 1 ]; then
echo "VULNERABLE - unsandboxed Jinja2 usage detected (version hint: $version_hint)"
exit 1
fi
if [ "$str_patch" -eq 1 ] && [ "$msg_patch" -eq 1 ]; then
echo "PATCHED - sandboxed Jinja2 patterns detected in both vulnerable components (version hint: $version_hint)"
exit 0
fi
echo "UNKNOWN - source present but could not confidently classify patched state (version hint: $version_hint)"
exit 2
If you remember one thing.
0.24.0 or earlier, then immediately separate the internet-reachable ones from the internal-only ones because the presence of self-registration is the practical risk multiplier here. For this HIGH verdict, the noisgate mitigation SLA is within 30 days: disable public registration, gate the app behind SSO/VPN/IP allowlists, and restrict Agent workflow access; the noisgate remediation SLA is within 180 days: move all instances to v0.25.0 or later and verify the vulnerable components use SandboxedEnvironment rather than raw jinja2.Template.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.