Security Research
Plesk Advisor SQLi to Root Code Execution
Plesk Obsidian's Advisor extension ships with a SQL injection reachable by any authenticated user, including the lowest-privilege Customer account. We walk through the root cause -- a textbook concatenation bug behind an access control misconfiguration -- and what makes the psa database a particularly valuable target.
TL;DR
We found a SQL Injection in Plesk Obsidian Advisor. The lowest-privilege Customer account can read the entire psa database.
This post covers the SQLi vulnerability only. We have confirmed that this bug serves as the first stage of a broader chain leading to significantly higher impact, but the downstream vulnerabilities remain unpatched. Those details will be published separately after remediation.
The Advisor Extension
Plesk Obsidian ships with the Advisor extension installed by default. It provides server optimization recommendations — SSL certificate status, domain configuration checks, that sort of thing. Buried in it is an endpoint for searching domain recommendation data:
GET /modules/advisor/index.php/recommendation/get-data
?recommendationCode=SslDomains
&pageSize=100
&search=USER_INPUT
An unremarkable search feature. The trouble starts when two things go wrong at the same time.
When Two Things Go Wrong at Once
1) Access Control: What an Empty Array Means
Look at the access control configuration in RecommendationController:
// RecommendationController.php
protected $_accessLevel = []; // empty array
In Plesk’s access control framework, an empty array means “no role restrictions.” The intent was probably “not configured” or “use defaults,” but the actual behavior is allow all authenticated users — including Customers.
Advisor aggregates server-wide domain status. It’s an admin-facing feature. There is no reason for a Customer account to reach this data.
2) SQL Injection: A Classic That Refuses to Die
Here’s the path from search parameter to SQL query:
// RecommendationController.php
public function getDataAction(): void {
$search = $this->_request->getParam('search'); // no filtering
$items = $step->getDataQuery()
->setSearch($search) // passed as-is
->execute();
}
And the query builder in DomainQuery.php:
// DomainQuery.php, line 55
protected function setSearchWhere(array &$where) {
if ($this->search) {
$where[] = '{%prefix%}displayName LIKE \'%' . $this->search . '%\'';
}
}
User input concatenated directly into a SQL string. No escaping. No parameter binding. A somewhat embarrassing bug class to find in 2026, but here we are.
Root Cause
This is a combination of two bugs:
- Missing input validation — The
searchparameter is concatenated into a SQL string without escaping or parameter binding. - Access control misconfiguration —
_accessLevel = []is interpreted as “no restrictions,” exposing a server administration feature to Customer accounts.
Remove either one and the vulnerability doesn’t exist. If access is restricted, the SQLi is unreachable. If the query is parameterized, access doesn’t matter.
Disclosure Scope
This post covers the Advisor SQL Injection only.
We have confirmed that data extracted through this SQLi can be combined with additional vulnerabilities to achieve significantly higher impact, including root-level command execution. Those vulnerabilities remain unpatched, so the rest of the chain will be published separately after remediation.
Proof of Concept
Full chain execution — from Customer login to uid=0(root) in a single command:

Patch
This SQLi was fixed in Advisor 1.10.14 and Advisor 1.9.18:
- Advisor 1.10.14 for Plesk Obsidian 18.0.53+
- Advisor 1.9.18 for Plesk Obsidian 18.0.53 or below
If you’re running Plesk older than 18.0.53 and Advisor older than 1.9.18, update immediately.
Timeline
| Date | Event |
|---|---|
| 2025-10-02 | Vulnerability discovered during authorized security audit |
| 2025-10-09 | Full chain (SQLi to Root RCE) confirmed |
| 2026-03-31 | Plesk Obsidian 18.0.77 released — SQLi patched in Advisor 1.9.18 (older than 18.0.53) and Advisor 1.10.14 (18.0.53+) |
DVE-2026-016. Patched by Advisor 1.9.18 (Plesk < 18.0.53) and 1.10.14 (Plesk 18.0.53+).
For commercial use or inquiries, please contact us.