High severityNVD Advisory· Published Apr 1, 2025· Updated Apr 1, 2025
Path Traversal allowing arbitrary read of files in Yeswiki
CVE-2025-31131
Description
YesWiki is a wiki system written in PHP. The squelette parameter is vulnerable to path traversal attacks, enabling read access to arbitrary files on the server. This vulnerability is fixed in 4.5.2.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
yeswiki/yeswikiPackagist | < 4.5.2 | 4.5.2 |
Affected products
1Patches
1f78c915369a6fix(thememanager): path traversal vulnerability
1 file changed · +55 −12
includes/services/ThemeManager.php+55 −12 modified@@ -119,31 +119,74 @@ public function loadTemplates($metadata = []): ?array $this->setFavorite('preset', $this->getConfigAsStringOrDefault('favorite_preset', '')); } else { // Sinon, on récupère premièrement les valeurs passées en REQUEST, ou deuxièmement les métasdonnées présentes pour la page, ou troisièmement les valeurs du fichier de configuration - if (isset($_REQUEST['theme']) && (is_dir('custom/themes/' . $_REQUEST['theme']) || is_dir('themes/' . $_REQUEST['theme'])) - && isset($_REQUEST['style']) && (is_file('custom/themes/' . $_REQUEST['theme'] . '/styles/' . $_REQUEST['style']) || is_file('themes/' . $_REQUEST['theme'] . '/styles/' . $_REQUEST['style'])) - && isset($_REQUEST['squelette']) && (is_file('custom/themes/' . $_REQUEST['theme'] . '/squelettes/' . $_REQUEST['squelette']) || is_file('themes/' . $_REQUEST['theme'] . '/squelettes/' . $_REQUEST['squelette'])) + $requested = []; + $keysToVerify = ['theme', 'squelette', 'style', 'preset']; + foreach ($keysToVerify as $val) { + $requested[$val] = null; + if (!empty($_REQUEST[$val])) { + if (preg_match('/\//', $_REQUEST[$val], $matches)) { + exit('ERROR: Suspicious path traversal attempt.'); + } + switch ($val) { + case 'theme': + $customThemePath = basename(realpath(getcwd() . '/custom/themes/' . $_REQUEST[$val])); + $classicThemePath = basename(realpath(getcwd() . '/themes/' . $_REQUEST[$val])); + $requested[$val] = !empty($customThemePath) ? $customThemePath : $classicThemePath; + break; + + case 'squelette': + $customPath = basename(realpath(getcwd() . '/custom/themes/' . $requested['theme'] . '/squelettes/' . $_REQUEST[$val])); + $classicPath = basename(realpath(getcwd() . '/themes/' . $requested['theme'] . 'squelettes/' . $_REQUEST[$val])); + $requested[$val] = null; + if (!empty($customPath) && file_exists(getcwd() . '/custom/themes/' . $requested['theme'] . '/squelettes/' . $customPath)) { + $requested[$val] = $customPath; + } elseif (file_exists(getcwd() . '/themes/' . $requested['theme'] . '/squelettes/' . $classicPath)) { + $requested[$val] = $classicPath; + } + if (!preg_match('/\.tpl\.html$/i', $requested[$val] ?? '', $matches)) { + $requested[$val] = null; + } + + break; + + default: + // ugly append of "s" to get the path of styleS, presetS and squeletteS + $customPath = basename(realpath(getcwd() . '/custom/themes/' . $requested['theme'] . '/' . $val . 's/' . $_REQUEST[$val])); + $classicPath = basename(realpath(getcwd() . '/themes/' . $requested['theme'] . '/' . $val . 's/' . $_REQUEST[$val])); + $requested[$val] = null; + if (!empty($customPath) && file_exists(getcwd() . '/custom/themes/' . $requested['theme'] . '/' . $val . 's/' . $customPath)) { + $requested[$val] = $customPath; + } elseif (file_exists(getcwd() . '/themes/' . $requested['theme'] . '/' . $val . 's/' . $classicPath)) { + $requested[$val] = $classicPath; + } + + break; + } + } + } + if (!empty($requested['theme']) && !empty($requested['style']) && !empty($requested['squelette']) && preg_match('/\.tpl\.html$/i', $requested['squelette'], $matches) ) { - $this->setFavorite('theme', $_REQUEST['theme']); - $this->setFavorite('style', $_REQUEST['style']); - $this->setFavorite('squelette', $_REQUEST['squelette']); + $this->setFavorite('theme', $requested['theme']); + $this->setFavorite('style', $requested['style']); + $this->setFavorite('squelette', $requested['squelette']); // presets - if (isset($_REQUEST['preset']) + if (!empty($requested['preset']) && ( ( - ($isCustom = (substr($_REQUEST['preset'], 0, strlen(self::CUSTOM_CSS_PRESETS_PREFIX)) == self::CUSTOM_CSS_PRESETS_PREFIX)) - && is_file(self::CUSTOM_CSS_PRESETS_PATH . '/' . substr($_REQUEST['preset'], strlen(self::CUSTOM_CSS_PRESETS_PREFIX))) + ($isCustom = (substr($requested['preset'], 0, strlen(self::CUSTOM_CSS_PRESETS_PREFIX)) == self::CUSTOM_CSS_PRESETS_PREFIX)) + && is_file(self::CUSTOM_CSS_PRESETS_PATH . '/' . substr($requested['preset'], strlen(self::CUSTOM_CSS_PRESETS_PREFIX))) ) || ( !$isCustom && ( - is_file('custom/themes/' . $_REQUEST['theme'] . '/presets/' . $_REQUEST['preset']) - || is_file('themes/' . $_REQUEST['theme'] . '/presets/' . $_REQUEST['preset']) + is_file('custom/themes/' . $requested['theme'] . '/presets/' . $requested['preset']) + || is_file('themes/' . $requested['theme'] . '/presets/' . $requested['preset']) ) ) ) ) { - $this->setFavorite('preset', $_REQUEST['preset']); + $this->setFavorite('preset', $requested['preset']); } if (isset($_REQUEST['bgimg']) && is_file('files/backgrounds/' . $_REQUEST['bgimg'])) {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-w34w-fvp3-68xmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-31131ghsaADVISORY
- github.com/YesWiki/yeswiki/commit/f78c915369a60c74ab8f38561ae93a4aaca9b989ghsax_refsource_MISCWEB
- github.com/YesWiki/yeswiki/security/advisories/GHSA-w34w-fvp3-68xmghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.