Langroid SQLChatAgent Prompt-to-SQL Injection Leading to Remote Code Execution
LLM-generated SQL executed without validation lets prompt injection reach COPY ... FROM PROGRAM and equivalent dialect RCE primitives on the database host
Langroid, an open-source framework for building LLM-powered agents, ships a SQLChatAgent that lets a language model write and execute SQL against a connected database. Versions before 0.63.0 pass the LLM's SQL output straight to the database with no statement-type validation, so a prompt injection — even indirect, via data the agent later reads — can steer the model into emitting administrative primitives instead of SELECTs. Where the database role has code-execution privileges (Postgres pg_execute_server_program, MySQL FILE, MSSQL xp_cmdshell), this becomes full remote code execution on the database host. Fixed in 0.63.0.
Technical Breakdown
Langroid SQLChatAgent Prompt-to-SQL Injection Leading to Remote Code Execution
SQLChatAgent hands the LLM a RunQueryTool capable of executing arbitrary SQL strings the LLM produces, with no statement-type validation or allowlisting before execution. Since the LLM's output is steerable via prompt injection — including indirect injection through ingested data — an attacker can get the LLM to emit and the agent to run dialect-specific administrative primitives (COPY ... FROM PROGRAM on Postgres, xp_cmdshell on MSSQL, FILE-based reads/writes on MySQL) instead of benign SELECTs, turning a "SQL chat agent" into an RCE primitive when the DB role has the privileges to support it.
No config flag pre-patch — strip pg_execute_server_program/FILE/xp_cmdshell from the agent's DB role
Root Cause
SQLChatAgent hands the LLM a RunQueryTool capable of executing arbitrary SQL strings the LLM produces, with no statement-type validation or allowlisting before execution. Because the LLM's output is steerable via prompt injection — including indirect injection through ingested data — an attacker can get the LLM to emit and the agent to run dialect-specific administrative primitives instead of benign SELECTs, turning a "SQL chat agent" into an RCE primitive whenever the DB role has the privileges to support it.
Vulnerable Pattern
SQLChatAgent.run_query() accepts the LLM's raw SQL string and hands it straight to the database driver using the agent's configured credentials — no parse-and-allowlist step, no distinction between SELECT and administrative DDL/DML, no gate between "the model decided to run this" and "this executed against production."
Why AI Services Are High-Value
SQL chat agents sit directly in front of production databases with real credentials, and the values that reach the LLM's context — chat messages, ingested documents, scraped pages, prior tool outputs — are frequently influenceable by outside parties. That makes SQLChatAgent a rare case where indirect prompt injection has a direct, mechanical path to database-host code execution rather than just misinformation or a data leak.
The Fix
0.63.0 introduces a SELECT-only, sqlglot-parsed statement gate with a dangerous-pattern blocklist as the default behavior for SQLChatAgent, rejecting DDL/DML/administrative primitives before they reach the database driver. An allow_dangerous_operations=True escape hatch exists for fully trusted, isolated deployments only.
Attack Chain
Recon: fingerprint the agent stack
Attacker identifies that a target application is built on Langroid's SQLChatAgent — error messages, docs, response formatting typical of Langroid's tool-call framing, or the app simply being a "chat with your database" feature.
Don't leak framework identity in error responses; monitor for automated probing that tests for chatbot/agent SQL behavior.
Deliver the injection payload
Attacker submits a crafted prompt directly, or plants it in content the agent will later ingest (support ticket, document, fetched web page), instructing the LLM to decode a Base64-encoded SQL payload and pass it as the query argument to RunQueryTool, telling it to skip showing the decode step.
Log/alert on Base64/encoded blobs in inbound prompts and any content ingested by agent tools; treat all text reaching the LLM's context as untrusted, including "internal" sources.
LLM complies and emits malicious SQL
The LLM decodes the payload and calls RunQueryTool with a multi-statement SQL string like DROP TABLE IF EXISTS log; CREATE TABLE log(content text); COPY log(content) FROM PROGRAM 'id'; SELECT * FROM log; — abusing Postgres's COPY...FROM PROGRAM to run an OS command and capture output.
This is the core failure point pre-0.63.0 — nothing validates statement type between LLM output and DB execution. Reinstate an equivalent SELECT-only gate if running a pre-patch/forked version.
Agent executes the SQL against the live database
SQLChatAgent.run_query() hands the string straight to the DB driver with the agent's configured credentials; if that role has pg_execute_server_program/FILE/xp_cmdshell privileges, the OS command executes on the DB host under the DB process's privileges.
Highest-leverage mitigation regardless of patch status — the DB role backing the agent should never hold superuser or filesystem/program-execution privileges. Enforce least privilege at the database layer.
Exfiltration / lateral movement / impact
Command output is captured into a table the LLM reads back and can relay to the attacker, or the attacker pivots further (reading arbitrary files, writing a webshell, using the DB host as a foothold into internal network segments).
Monitor for anomalous COPY...FROM PROGRAM/xp_cmdshell/LOAD_FILE calls at the DB audit-log level — rare in normal traffic, a strong detection signal; segment DB hosts from sensitive internal networks.
Impact
A SQL chat agent that trusts LLM output collapses the distance between "influence a prompt" and "execute code on the database host" — turning what looks like a chat feature into an unauthenticated-adjacent RCE primitive with no human checkpoint in between.
Remote Code Execution / Host Compromise
- Arbitrary OS command execution on the DB host via COPY...FROM PROGRAM/xp_cmdshell/FILE tricks
- Runs with the DB server process's privileges, often broader than expected
- Can drop persistence (cron jobs, webshells, reverse shells) on infrastructure that's often less monitored than app servers
- PoC confirmed running
idand reading output back through the chat interface
Data Confidentiality / Exfiltration
- Direct read access to every table the DB role can see, independent of what the agent was "supposed" to expose
- File-read primitives can pull arbitrary files off the DB host filesystem
- Output rides the existing legitimate chat/LLM response channel, evading typical network egress monitoring
- No separate exfiltration channel required — the chat UI is the exfil channel
Data Integrity / Availability
- Attacker can DROP/CREATE/modify arbitrary tables since there's no statement-type restriction
- Multi-statement SQL means one injected prompt can chain destructive DDL with the RCE primitive
- No human-in-the-loop checkpoint exists between LLM decision and execution
- Recovery requires DB-level audit review, not just an application rollback
Defensive Tutorial
Upgrade langroid to >= 0.63.0
Immediatepip install --upgrade "langroid>=0.63.0". Flips SQLChatAgent to a SELECT-only, sqlglot-parsed allowlist with a dangerous-pattern blocklist by default.
Audit for allow_dangerous_operations=True
Immediategrep -rn "allow_dangerous_operations" . across codebase/agent configs; if found and not genuinely needed in a fully trusted/isolated deployment, remove it.
Revoke dangerous DB privileges from the agent's role
ImmediateDo this regardless of patch status: REVOKE pg_execute_server_program FROM <agent_role>; (Postgres), remove FILE grant (MySQL), disable xp_cmdshell (MSSQL: EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE;). Fastest kill-switch, works before the code upgrade lands.
Grep audit logs for exploitation indicators
ImmediateSearch DB query/audit logs for COPY.*FROM PROGRAM, xp_cmdshell, LOAD_FILE, INTO OUTFILE, and unusual multi-statement blocks from the application's service account; also check app logs for Base64 blobs combined with "decode"+"query"/"RunQueryTool" instructions.
Enforce least-privilege DB roles as standing policy
ImportantSELECT only (and narrowly scoped INSERT/UPDATE if genuinely required) for every LLM-facing DB connection; never reuse an admin/app-owner credential for an agent connection string.
Add an input-sanitization / prompt-injection filter
ImportantIn front of any agent ingesting untrusted/semi-trusted content — encoding-detection heuristics (Base64/hex/URL-encoded blobs) and instruction-pattern filtering before content reaches the LLM context.
Network-isolate the database host from the agent's egress path
ImportantSegment with no outbound internet, restrict which service accounts/hosts can connect, so a successful RCE has a harder time exfiltrating or pivoting.
Adopt a human-in-the-loop or hard statement-allowlist gate
ImportantFor any agent that can execute state-changing/admin-capable operations, independent of the framework's own defaults — don't rely solely on internal validation; add an application-layer policy engine permitting only pre-approved query shapes, with explicit approval required for anything outside SELECT.
Track LLM-framework dependencies in SBOM / dependency scanning
ImportantAlert on GitHub Security Advisories for langroid (and similar frameworks) specifically for CWE-89/CWE-94 findings — this "framework hands LLM output to a privileged executor" bug class is recurring (note a closely related follow-on Neo4jChatAgent prompt-to-Cypher injection, CVE-2026-55615, disclosed after this one in the same codebase).
References
- NVD Entry CVE-2026-25879 — National Vulnerability Database entry with CVSS 9.8 vector and affected version range.
- GitHub Security Advisory GHSA-mxfr-6hcw-j9rq — Vendor advisory from Langroid with technical details and fix confirmation.
- PyPI Advisory Database PYSEC-2026-382 — Python Packaging Authority advisory record for the langroid PyPI package.
- CWE Reference CWE-89 — SQL Injection; CWE-94 — Improper Control of Generation of Code ('Code Injection')