
Documenta o CVE-2026-65320, uma travessia de caminho tar-slip em untar_dir() do fastcore, com uma prova de conceito inofensiva que demonstra escrita arbitrária de arquivos e execução de código.
untar_dir()Severidade: Crítica, CVSS 3.1 9.8
Vetor (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Afetado: fastcore <= 2.2.30
Corrigido em: Não corrigido no momento da redação
CWE: CWE-22 (Limitação Imprópria de um Nome de Caminho a um Diretório Restrito, 'Path Traversal')
Componente: fastcore.xtras.untar_dir() → fastcore.xtras._unpack() → shutil.unpack_archive()
Runtime de alto risco: Python < 3.14 (Python 3.14 torna o filtro tar data seguro o padrão)
Reportado por: Rahul Karne
CNA: VulnCheck
fastcore.xtras.untar_dir(fname, dest) é um helper documentado que extrai um arquivo tar "para dentro de dest".
Internamente, ele entrega o arquivo à função shutil.unpack_archive() da biblioteca padrão sem um filtro de extração, sem validar os caminhos dos membros do arquivo e sem qualquer verificação de contenção pós-extração.
Como resultado, um arquivo TAR cujos membros contenham sequências de travessia ../ ou caminhos absolutos pode gravar arquivos fora do diretório no qual o chamador pediu ao fastcore para extrair.
Um chamador que razoavelmente espera que untar_dir(fname, dest) confine tudo a dest em vez disso obtém gravação arbitrária de arquivos em qualquer lugar onde o processo possa gravar.
Gravação arbitrária de arquivos sob a autoridade do processo que chama untar_dir().
Dependendo do que for sobrescrito, isso pode resultar em:
A prova de conceito demonstra a cadeia completa gravação → importação → execução de código de ponta a ponta, inteiramente dentro de um diretório de demonstração inofensivo.
fastcore é uma dependência fundamental em todo o ecossistema fast.ai, incluindo:
fastainbdevghapiEle recebe dezenas de milhões de downloads por mês.
untar_dir() é a primitiva de extração por trás do fluxo de trabalho comum "baixar um arquivo, depois descompactá-lo", que segue o mesmo padrão geral do untar_data do fastai.
Qualquer caminho de código que forneça a untar_dir() um arquivo cujo conteúdo não seja totalmente confiável está exposto.
O código vulnerável existe em fastcore/xtras.py:
def _unpack(fname, out):
import shutil
shutil.unpack_archive(str(fname), str(out)) # <-- no filter, no validation
ls = out.ls()
return ls[0] if len(ls) == 1 else out
def untar_dir(fname, dest, rename=False, overwrite=False, uid=-1, gid=-1):
"untar `file` into `dest` ..."
import tempfile, shutil
dest = Path(dest)
with tempfile.TemporaryDirectory() as d:
out = Path(d) / remove_suffix(Path(fname).stem, '.tar')
out.mkdir()
...
src = _unpack(fname, out) # extraction escapes `out` here
...
shutil.move(str(src), dest)
...
return dest
untar_dir() cria um diretório temporário interno de extração:
<tempdir>/<random>/<archive-stem>/
Em seguida, chama _unpack(), que encaminha o arquivo diretamente para:
shutil.unpack_archive()
Para arquivos TAR, isso acaba chamando tarfile.extractall().
No Python < 3.14, o comportamento padrão de extração é fully_trusted, o que significa que membros com travessia podem ser aceitos.
Por exemplo, um membro de arquivo como:
../../../some/other/dir/file
é gravado em relação ao diretório de extração e, portanto, pode escapar dele.
Nada no fastcore:
filter="data" onde há suporte....untar_dir() trata shutil.unpack_archive() como se fosse um extrator de arquivos seguro e isolado.
Ele não é.
A documentação da biblioteca padrão do Python alerta que extrair arquivos não confiáveis pode criar arquivos fora do destino solicitado e recomenda filtragem segura de extração para arquivos TAR.
O fastcore nem passa um filtro de extração apropriado nem implementa uma proteção de contenção equivalente para as versões do Python que suporta.
Ele também não alerta os chamadores de untar_dir() sobre esse comportamento sensível à segurança.
Membros TAR podem conter strings de caminho arbitrárias.
Em versões afetadas do Python, tarfile.extractall() não neutraliza automaticamente sequências de travessia .. por padrão.
Gravar um membro com travessia em relação ao diretório temporário de extração do fastcore é, portanto, suficiente para escapar desse diretório e gravar em qualquer lugar onde o processo tenha permissão de gravação.
Uma primitiva confiável é a travessia relativa como:
../../../target/file
Esta é a técnica usada pela prova de conceito.
A entrada perigosa é o conteúdo do arquivo, não o caminho de destino escolhido pelo desenvolvedor.
Arquivos são frequentemente obtidos de locais externos ou semiconfiáveis, incluindo:
Qualquer serviço ou ferramenta que baixe um arquivo de uma fonte não confiável ou semiconfiável e subsequentemente o processe usando untar_dir() pode, portanto, estar acessível.
A PoC também inclui uma variante que serve o arquivo manipulado a partir de um endpoint HTTP controlado pelo atacante para demonstrar esse caminho de entrega.
A exploração bem-sucedida requer:
A aplicação chama fastcore.xtras.untar_dir() em um arquivo cujo conteúdo é influenciado pelo atacante, como um arquivo enviado, baixado ou de outra forma não confiável.
O runtime usa Python < 3.14, ou outra configuração onde um filtro seguro de extração TAR não é aplicado.
Para escalar de gravação arbitrária de arquivos para execução de código, o processo posteriormente importa ou carrega um arquivo de um local alcançável pela travessia, como:
Gravação arbitrária de arquivos e modificação de configuração/dados requerem apenas as condições 1 e 2.
poc_fastcore_cna_demo.py invoca a API real fastcore.xtras.untar_dir().
A demonstração é intencionalmente inofensiva.
Todo arquivo criado pela PoC permanece dentro de um único diretório de demonstração especificado usando:
--demo-root
A PoC não modifica arquivos reais do sistema e não acessa serviços de nuvem ou credenciais.
Imprime os detalhes do ambiente e o código-fonte vulnerável de _unpack() e untar_dir().
Cria um layout de demonstração contendo:
service_app/plugins/
Cria:
attacker_supplied_upload.tar
contendo membros de arquivo projetados para apontar fora da raiz de extração pretendida, incluindo membros com travessia e:
service_app/plugins/startup_hook.py