
Advisory: CVE-2026-38360 path traversal (CWE-22) no dash-uploader (Python/PyPI)
Path traversal não autenticado em fohrloop/dash-uploader (Python, PyPI) permitindo escrita arbitrária de arquivos, levando a (mas não se limitando a) Execução Remota de Código (RCE), sobrescrita de código fonte da aplicação, XSS armazenado e instalação persistente de backdoor.
O repositório foi arquivado em 2025-07-19 sem mantenedor ativo. Todas as versões publicadas (0.1.0 até 0.7.0a2) são afetadas e continuarão assim. O pacote ainda tem aproximadamente 28.000 downloads mensais.
Qualquer um que execute dash-uploader em produção deve aplicar uma mitigação por conta própria. A correção recomendada é migrar para o componente dcc.Upload embutido do Plotly Dash. Veja Mitigação para opções completas.
| ID CVE | CVE-2026-38360 (NVD) |
| Vulnerabilidade | Path Traversal (CWE-22) |
| CVSS 3.1 | 9.8 / Crítico (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Produto | dash-uploader |
| Versões afetadas | 0.1.0 até 0.7.0a2 (todas as 18 versões) |
| Versão corrigida | nenhuma (projeto arquivado em 2025-07-19) |
| Vetor de ataque | Remoto, não autenticado |
| Descobridor | Muhammad Fitri Bin Mohd Sultan |
| Atribuído por | MITRE, 2026-05-07 |
| Relacionado | CVE-2026-38361 (DoS na mesma biblioteca) |
Três parâmetros controlados pelo usuário de request.form.get() em dash_uploader/httprequesthandler.py são passados diretamente para os.path.join() e os.makedirs() sem qualquer saneamento ou validação:
upload_id (linha 57 → linha 161, BaseHttpRequestHandler.get_temp_root): controla o diretório de destino. Um atacante pode enviar upload_id=../../../../usr/local/lib/python3.10/site-packages e os arquivos são escritos no diretório de pacotes do Python.
resumableFilename (linha 51 → linha 108, BaseHttpRequestHandler._post): controla o nome final do arquivo. Um atacante pode atravessar para fora do diretório de upload através do nome do arquivo mesmo com um upload_id legítimo.
resumableIdentifier (linha 54 → linha 64, BaseHttpRequestHandler._post): usado com os.makedirs() para criar o diretório temporário. Um atacante pode criar diretórios arbitrários em qualquer lugar do sistema de arquivos.
O endpoint de upload (/API/dash-uploader por padrão) não requer autenticação. O hook http_request_handler adicionado na v0.5.0 permite verificações pré-requisição via post_before(), mas o método vulnerável _post() lê todos os parâmetros diretamente de request.form após o hook retornar. O hook não pode sanear parâmetros antes da biblioteca processá-los. Um desenvolvedor que adiciona autenticação via hook ainda está vulnerável a path traversal de um usuário autenticado.
# dash_uploader/httprequesthandler.py
def _post(self):
resumableFilename = request.form.get("resumableFilename", default="error", type=str)
resumableIdentifier = request.form.get("resumableIdentifier", default="error", type=str)
upload_id = request.form.get("upload_id", default="", type=str)
...
temp_root = self.get_temp_root(upload_id) # upload_id flows in here
temp_dir = os.path.join(temp_root, resumableIdentifier) # raw user input -> os.path.join
if not os.path.isdir(temp_dir):
os.makedirs(temp_dir) # raw user input -> os.makedirs
def get_temp_root(self, upload_id):
return os.path.join(self.upload_folder, upload_id) # no sanitization: ../../ escapes upload_folder
Três sinks de traversal independentes compartilham a mesma causa raiz: valores do formulário chegam a os.path.join e os.makedirs sem validação.
Um atacante remoto não autenticado envia uma requisição HTTP POST multipart para o endpoint de upload. Ao injetar sequências de path traversal (../) no parâmetro de formulário upload_id, o atacante controla o diretório de destino do arquivo enviado. Por exemplo, upload_id=../../../../usr/local/lib/python3.10/site-packages escreve arquivos no diretório de pacotes do Python, permitindo RCE na próxima inicialização do interpretador via auto-execução de .pth.
Nenhuma autenticação, token de sessão ou token CSRF é necessário. Dois parâmetros adicionais (resumableFilename e resumableIdentifier) fornecem vetores de traversal independentes através do mesmo endpoint. A configuração padrão da biblioteca conforme mostrado na documentação oficial de início rápido é explorável com um único comando curl.
Escrita arbitrária de arquivos em qualquer diretório gravável pelo processo do servidor. Isso se traduz em Execução Remota de Código (RCE) através de várias primitivas conhecidas:
Primitivas RCE
.pth colocado em site-packages. Executa código fornecido pelo atacante na próxima inicialização do interpretador.sitecustomize.py ou usercustomize.py. Executa em toda inicialização do Python.app.wsgi, wsgi.py). Executa na próxima recarga de worker./etc/cron.d/, /etc/cron.hourly/, spool de crontab do usuário) quando o processo tem os privilégios necessários. Execução agendada./etc/systemd/system/, ~/.config/systemd/user/). Executa na próxima inicialização do serviço ou reinicialização./etc/ld.so.preload quando o processo é executado como root. Pré-carrega código do atacante em toda execução binária subsequente.~/.bashrc, ~/.profile, ~/.bash_profile). Executa no próximo login interativo do usuário da aplicação.~/.ssh/authorized_keys. Concede acesso SSH persistente ao host como o usuário da aplicação.Impacto na camada web
site-packages), afetando todos os usuários em cada carregamento de página até que a aplicação seja reiniciadaImpacto no sistema de arquivos
os.makedirs() com o parâmetro resumableIdentifier não saneado (utilizável para exaustão de inodes ou para preparar escritas em árvores de diretórios inexistentes)dash_uploader/httprequesthandler.pyBaseHttpRequestHandler.get_temp_root()BaseHttpRequestHandler._post()Opções para usuários atualmente implantados, em ordem de preferência:
dcc.Upload, o componente de upload oficial enviado com Plotly Dash. Os arquivos chegam no callback como uma string base64; nenhum manipulador de escrita no sistema de arquivos é exposto e nenhum caminho de destino controlado pelo cliente existe, então a classe de bug aqui não se aplica. Mais adequado para arquivos pequenos e médios. Para uploads muito grandes, veja o item 2.werkzeug.utils.secure_filename() e um diretório de destino hardcoded no servidor. Nunca aceite valores de upload_id, nome de arquivo ou identificador fornecidos pelo cliente como componentes de caminho.upload_id, resumableFilename e resumableIdentifier contra uma lista de permissões estrita (por exemplo, apenas UUIDs) em uma camada que reescreva ou rejeite a requisição antes que o manipulador da biblioteca a veja. O hook http_request_handler da biblioteca NÃO impede o traversal porque os parâmetros são lidos de request.form após o hook retornar; o saneamento deve acontecer acima da biblioteca..., variantes codificadas (%2e%2e, ..%2f, %2e%2e%2f) ou caminhos absolutos.| Data | Evento |
|---|---|
| 2026-03-17 | Vulnerabilidade descoberta durante pesquisa de segurança em uma implantação de produção. |
| 2026-03-19 | Solicitação de CVE submetida ao MITRE. |
| 2026-05-07 | CVE-2026-38360 atribuído pelo MITRE. |
| 2026-05-07 | Aviso público publicado. |
| 2026-05-09 | Registro CVE publicado no banco de dados CVE do MITRE e no NVD. |
0.6.1 (linha estável). Pré-lançamentos estendem-se a 0.7.0a2.dash. Dependência opcional: pyyaml. Licença: MIT.Muhammad Fitri Bin Mohd Sultan