CVE-2022-23808
Description
An issue was discovered in phpMyAdmin 5.1 before 5.1.2. An attacker can inject malicious code into aspects of the setup script, which can allow XSS or HTML injection.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
phpMyAdmin 5.1 before 5.1.2 allows XSS and HTML injection via insufficient input sanitization in the setup script.
Vulnerability
The vulnerability resides in the setup script of phpMyAdmin versions 5.1.0 and 5.1.1 (prior to 5.1.2). Insufficient filtering of query parameters and inadequate escaping of the action attribute in the configuration form allow an attacker to inject arbitrary HTML or JavaScript code. The setup script is accessible only if no config.inc.php file exists (i.e., the installation is not yet configured) [4].
Exploitation
An attacker can craft a malicious URL containing injected script or HTML in parameters such as formset or eol. When a victim with access to the setup script visits the crafted URL, the injected code executes in the context of the phpMyAdmin setup page. No authentication is required because the setup script is unauthenticated, but the attacker must convince the victim to visit the malicious link [1][4].
Impact
Successful exploitation leads to Cross-Site Scripting (XSS) or HTML injection, allowing the attacker to execute arbitrary JavaScript in the victim's browser. This could result in session hijacking, defacement, or other client-side attacks within the phpMyAdmin domain [4].
Mitigation
The issue is fixed in phpMyAdmin 5.1.2, released on 2022-01-10. Users should upgrade immediately. If upgrade is not possible, creating a config.inc.php file prevents access to the setup script, mitigating the attack. The fix includes commits 44eb12f and 5118acc in the official repository [1][3][4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
phpmyadmin/phpmyadminPackagist | >= 5.1.0, < 5.1.2 | 5.1.2 |
Affected products
5- phpMyAdmin/phpMyAdmindescription
- osv-coords4 versionspkg:bitnami/phpmyadminpkg:composer/phpmyadmin/phpmyadminpkg:rpm/opensuse/phpMyAdmin&distro=openSUSE%20Leap%2015.4pkg:rpm/suse/phpMyAdmin&distro=SUSE%20Package%20Hub%2015%20SP4
>= 5.1.0, < 5.1.2+ 3 more
- (no CPE)range: >= 5.1.0, < 5.1.2
- (no CPE)range: >= 5.1.0, < 5.1.2
- (no CPE)range: < 5.2.1-bp154.2.3.1
- (no CPE)range: < 5.2.1-bp154.2.3.1
Patches
244eb12f15a56Improve query params filtering in Setup pages
9 files changed · +48 −82
libraries/classes/Controllers/Setup/ConfigController.php+6 −3 modified@@ -5,8 +5,8 @@ namespace PhpMyAdmin\Controllers\Setup; use PhpMyAdmin\Config\FormDisplayTemplate; -use PhpMyAdmin\Core; use PhpMyAdmin\Setup\ConfigGenerator; +use function is_string; class ConfigController extends AbstractController { @@ -17,6 +17,9 @@ class ConfigController extends AbstractController */ public function index(array $params): string { + $formset = isset($params['formset']) && is_string($params['formset']) ? $params['formset'] : ''; + $eol = isset($params['eol']) && $params['eol'] === 'win' ? 'win' : 'unix'; + $pages = $this->getPages(); $formDisplayTemplate = new FormDisplayTemplate($GLOBALS['PMA_Config']); @@ -34,13 +37,13 @@ public function index(array $params): string $config = ConfigGenerator::getConfigFile($this->config); return $this->template->render('setup/config/index', [ - 'formset' => $params['formset'] ?? '', + 'formset' => $formset, 'pages' => $pages, 'form_top_html' => $formTop, 'fieldset_top_html' => $fieldsetTop, 'form_bottom_html' => $formBottom, 'fieldset_bottom_html' => $fieldsetBottom, - 'eol' => Core::ifSetOr($params['eol'], 'unix'), + 'eol' => $eol, 'config' => $config, ]); }
libraries/classes/Controllers/Setup/FormController.php+3 −2 modified@@ -10,6 +10,7 @@ use PhpMyAdmin\Setup\FormProcessing; use function ob_get_clean; use function ob_start; +use function is_string; class FormController extends AbstractController { @@ -22,7 +23,7 @@ public function index(array $params): string { $pages = $this->getPages(); - $formset = Core::isValid($params['formset'], 'scalar') ? $params['formset'] : null; + $formset = isset($params['formset']) && is_string($params['formset']) ? $params['formset'] : ''; $formClass = SetupFormList::get($formset); if ($formClass === null) { @@ -36,7 +37,7 @@ public function index(array $params): string $page = ob_get_clean(); return $this->template->render('setup/form/index', [ - 'formset' => $params['formset'] ?? '', + 'formset' => $formset, 'pages' => $pages, 'name' => $form::getName(), 'page' => $page,
libraries/classes/Controllers/Setup/HomeController.php+4 −45 modified@@ -9,10 +9,8 @@ use PhpMyAdmin\Config\ServerConfigChecks; use PhpMyAdmin\Core; use PhpMyAdmin\LanguageManager; -use PhpMyAdmin\Sanitize; use PhpMyAdmin\Setup\Index; -use function preg_replace; -use function uniqid; +use function is_string; class HomeController extends AbstractController { @@ -23,11 +21,9 @@ class HomeController extends AbstractController */ public function index(array $params): string { - $pages = $this->getPages(); + $formset = isset($params['formset']) && is_string($params['formset']) ? $params['formset'] : ''; - // Handle done action info - $actionDone = Core::isValid($params['action_done'], 'scalar') ? $params['action_done'] : ''; - $actionDone = preg_replace('/[^a-z_]/', '', $actionDone); + $pages = $this->getPages(); // message handling Index::messagesBegin(); @@ -53,43 +49,6 @@ public function index(array $params): string $text .= '</a>'; Index::messagesSet('notice', 'no_https', __('Insecure connection'), $text); - // Check for done action info and set notice message if present - switch ($actionDone) { - case 'config_saved': - /* Use uniqid to display this message every time configuration is saved */ - Index::messagesSet( - 'notice', - uniqid('config_saved'), - __('Configuration saved.'), - Sanitize::sanitizeMessage( - __( - 'Configuration saved to file config/config.inc.php in phpMyAdmin ' - . 'top level directory, copy it to top level one and delete ' - . 'directory config to use it.' - ) - ) - ); - break; - case 'config_not_saved': - /* Use uniqid to display this message every time configuration is saved */ - Index::messagesSet( - 'notice', - uniqid('config_not_saved'), - __('Configuration not saved!'), - Sanitize::sanitizeMessage( - __( - 'Please create web server writable folder [em]config[/em] in ' - . 'phpMyAdmin top level directory as described in ' - . '[doc@setup_script]documentation[/doc]. Otherwise you will be ' - . 'only able to download or display it.' - ) - ) - ); - break; - default: - break; - } - Index::messagesEnd(); $messages = Index::messagesShowHtml(); @@ -205,7 +164,7 @@ public function index(array $params): string ); return $this->template->render('setup/home/index', [ - 'formset' => $params['formset'] ?? '', + 'formset' => $formset, 'languages' => $languages, 'messages' => $messages, 'servers_form_top_html' => $serversFormTopHtml,
libraries/classes/Controllers/Setup/ServersController.php+16 −8 modified@@ -5,10 +5,12 @@ namespace PhpMyAdmin\Controllers\Setup; use PhpMyAdmin\Config\Forms\Setup\ServersForm; -use PhpMyAdmin\Core; use PhpMyAdmin\Setup\FormProcessing; use function ob_get_clean; use function ob_start; +use function is_string; +use function is_numeric; +use function in_array; class ServersController extends AbstractController { @@ -19,12 +21,18 @@ class ServersController extends AbstractController */ public function index(array $params): string { + $formset = isset($params['formset']) && is_string($params['formset']) ? $params['formset'] : ''; + $id = isset($params['id']) && is_numeric($params['id']) && (int) $params['id'] >= 1 ? (int) $params['id'] : 0; + $mode = ''; + if (isset($params['mode']) && in_array($params['mode'], ['add', 'edit', 'revert'], true)) { + $mode = $params['mode']; + } + $pages = $this->getPages(); - $id = Core::isValid($params['id'], 'numeric') ? (int) $params['id'] : null; - $hasServer = ! empty($id) && $this->config->get('Servers/' . $id) !== null; + $hasServer = $id >= 1 && $this->config->get('Servers/' . $id) !== null; - if (! $hasServer && ($params['mode'] !== 'revert' && $params['mode'] !== 'edit')) { + if (! $hasServer && $mode !== 'revert' && $mode !== 'edit') { $id = 0; } @@ -33,10 +41,10 @@ public function index(array $params): string $page = ob_get_clean(); return $this->template->render('setup/servers/index', [ - 'formset' => $params['formset'] ?? '', + 'formset' => $formset, 'pages' => $pages, 'has_server' => $hasServer, - 'mode' => $params['mode'], + 'mode' => $mode, 'server_id' => $id, 'server_dsn' => $this->config->getServerDSN($id), 'page' => $page, @@ -48,9 +56,9 @@ public function index(array $params): string */ public function destroy(array $params): void { - $id = Core::isValid($params['id'], 'numeric') ? (int) $params['id'] : null; + $id = isset($params['id']) && is_numeric($params['id']) && (int) $params['id'] >= 1 ? (int) $params['id'] : 0; - $hasServer = ! empty($id) && $this->config->get('Servers/' . $id) !== null; + $hasServer = $id >= 1 && $this->config->get('Servers/' . $id) !== null; if (! $hasServer) { return;
libraries/classes/Setup/FormProcessing.php+11 −5 modified@@ -8,10 +8,12 @@ namespace PhpMyAdmin\Setup; use PhpMyAdmin\Config\FormDisplay; -use PhpMyAdmin\Core; use PhpMyAdmin\Response; use PhpMyAdmin\Template; use PhpMyAdmin\Url; +use function in_array; +use function is_string; +use function is_numeric; /** * PhpMyAdmin\Setup\FormProcessing class @@ -52,10 +54,14 @@ public static function process(FormDisplay $form_display) } // form has errors, show warning - $page = $_GET['page'] ?? ''; - $formset = $_GET['formset'] ?? ''; - $formId = Core::isValid($_GET['id'], 'numeric') ? $_GET['id'] : ''; - if ($formId === null && $page === 'servers') { + $page = 'index'; + if (isset($_GET['page']) && in_array($_GET['page'], ['form', 'config', 'servers'], true)) { + $page = $_GET['page']; + } + + $formset = isset($_GET['formset']) && is_string($_GET['formset']) ? $_GET['formset'] : ''; + $formId = isset($_GET['id']) && is_numeric($_GET['id']) && (int) $_GET['id'] >= 1 ? (int) $_GET['id'] : 0; + if ($formId === 0 && $page === 'servers') { // we've just added a new server, get its id $formId = $form_display->getConfigFile()->getServerCount(); }
phpstan-baseline.neon+0 −5 modified@@ -270,11 +270,6 @@ parameters: count: 1 path: libraries/classes/Controllers/Setup/HomeController.php - - - message: "#^Parameter \\#1 \\$server of method PhpMyAdmin\\\\Config\\\\ConfigFile\\:\\:getServerDSN\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: libraries/classes/Controllers/Setup/ServersController.php - - message: "#^Comparison operation \"\\>\" between 0 and 0 is always false\\.$#" count: 1
psalm-baseline.xml+0 −8 modified@@ -446,9 +446,6 @@ </PossiblyNullArrayAccess> </file> <file src="libraries/classes/Controllers/Setup/FormController.php"> - <PossiblyNullArgument occurrences="1"> - <code>$formset</code> - </PossiblyNullArgument> <UndefinedClass occurrences="1"> <code>new $formClass($this->config)</code> </UndefinedClass> @@ -461,11 +458,6 @@ <code>PMA_IS_WINDOWS</code> </TypeDoesNotContainType> </file> - <file src="libraries/classes/Controllers/Setup/ServersController.php"> - <PossiblyNullArgument occurrences="1"> - <code>$id</code> - </PossiblyNullArgument> - </file> <file src="libraries/classes/Controllers/Table/ChangeController.php"> <PossiblyNullArgument occurrences="1"> <code>$where_clause ?? null</code>
setup/index.php+3 −5 modified@@ -27,10 +27,9 @@ Core::fatalError(__('Configuration already exists, setup is disabled!')); } -$page = Core::isValid($_GET['page'], 'scalar') ? (string) $_GET['page'] : ''; -$page = preg_replace('/[^a-z]/', '', $page); -if ($page === '') { - $page = 'index'; +$page = 'index'; +if (isset($_GET['page']) && in_array($_GET['page'], ['form', 'config', 'servers'], true)) { + $page = $_GET['page']; } Core::noCacheHeader(); @@ -77,6 +76,5 @@ $controller = new HomeController($GLOBALS['ConfigFile'], new Template()); echo $controller->index([ 'formset' => $_GET['formset'] ?? null, - 'action_done' => $_GET['action_done'] ?? null, 'version_check' => $_GET['version_check'] ?? null, ]);
templates/setup/servers/index.twig+5 −1 modified@@ -11,6 +11,10 @@ <h2>{% trans 'Add a new server' %}</h2> {% endif %} -{{ page|raw }} +{% if mode == 'add' or mode == 'edit' or mode == 'revert' %} + {{ page|raw }} +{% else %} + <p>{% trans 'Something went wrong.' %}</p> +{% endif %} {% endblock %}
5118acce1dfcEscape config-form's action attribute
3 files changed · +5 −5
templates/config/form_display/form_top.twig+1 −1 modified@@ -1,4 +1,4 @@ -<form method="{{ method }}" action="{{ action|raw }}" class="config-form disableAjax"> +<form method="{{ method }}" action="{{ action|e('html_attr') }}" class="config-form disableAjax"> <input type="hidden" name="tab_hash" value=""> {% if has_check_page_refresh %} <input type="hidden" name="check_page_refresh" id="check_page_refresh" value="">
test/classes/Config/FormDisplayTemplateTest.php+3 −3 modified@@ -38,13 +38,13 @@ protected function setUp(): void */ public function testDisplayFormTop(): void { - $_SERVER['REQUEST_URI'] = 'https://www.phpmyadmin.net'; + $_SERVER['REQUEST_URI'] = 'https://www.phpmyadmin.net/index.php?key=value&key2=">value2'; $GLOBALS['cfg']['ServerDefault'] = ''; $result = $this->formDisplayTemplate->displayFormTop(null, 'posted', [1]); $this->assertStringContainsString( - '<form method="get" action="https://www.phpmyadmin.net" ' . - 'class="config-form disableAjax">', + '<form method="get" action="https://www.phpmyadmin.net/' + . 'index.php?key=value&key2=">value2" class="config-form disableAjax">', $result );
test/classes/Config/PageSettingsTest.php+1 −1 modified@@ -54,7 +54,7 @@ public function testShowGroupBrowse(): void '<div id="page_settings_modal">' . '<div class="page_settings">' . '<form method="post" ' - . 'action="index.php?db=db&server=1&lang=en" ' + . 'action="index.php?db=db&server=1&lang=en" ' . 'class="config-form disableAjax">', $html );
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-vcwc-6mr9-8m7cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-23808ghsaADVISORY
- security.gentoo.org/glsa/202311-17ghsavendor-advisoryWEB
- github.com/phpmyadmin/phpmyadmin/commit/44eb12f15a562718bbe54c9a16af91ceea335d59ghsaWEB
- github.com/phpmyadmin/phpmyadmin/commit/5118acce1dfcdb09cbc0f73927bf51c46feeaf38ghsaWEB
- infosecwriteups.com/exploit-cve-2022-23808-85041c6e5b97ghsaWEB
- www.phpmyadmin.net/security/PMASA-2022-2ghsaWEB
- www.phpmyadmin.net/security/PMASA-2022-2/mitre
News mentions
0No linked articles in our index yet.