CVE-2019-17059: شرح Preauth-RCE في Cyberoam لسوفوس

لقد عملنا بجد مع باحثي الأمن الداخلي والخارجي هنا في TheBestVPN لكشف الثغرات الخطيرة القابلة للاستغلال عن بُعد في شبكات SSL VPN والجدران النارية مثل Cyberoam و Fortigate و Cisco VPN. هذه المقالة عبارة عن تقني يتعلق بنقاط الضعف الحرجة المصححة التي تؤثر على Cyberoam SSL VPN والمعروفة أيضًا باسم CyberoamOS.


يعد استغلال Cyberoam هذا ، والذي يطلق عليه CVE-2019-17059 ، ثغرة أمنية حرجة تسمح للمهاجمين بالوصول إلى جهاز Cyberoam دون تقديم أي اسم مستخدم أو كلمة مرور. علاوة على ذلك ، فإن الوصول الممنوح هو أعلى مستوى (الجذر) ، والذي يمنح المهاجم بشكل أساسي حقوقًا غير محدودة على جهاز Cyberoam الخاص بك.

في معظم بيئات الشبكات ، تُستخدم أجهزة Cyberoam كجدران حماية وبوابات SSL VPN. هذا يعطي المهاجم المحتمل موطئ قدم قوي في الشبكة. يجعل من السهل مهاجمة المضيفين داخل الشبكة ، وبما أن أجهزة Cyberoam عادة ما تكون موثوقة في معظم البيئات ، فإن ذلك يعطي ميزة إضافية للمهاجمين المحتملين.

وفقًا لـ Shodan (محرك بحث للأجهزة المتصلة بالإنترنت) ، هناك أكثر من 96000 جهاز Cyberoam يواجه الإنترنت من جميع أنحاء العالم. يتم تثبيت معظم هذه الأجهزة في المؤسسات والجامعات وبعضها في البنوك ذات الشهرة العالمية. هذا يؤدي إلى الهجمات التي لها تأثيرات كبيرة على هذه البيئات.

لقد كان العمل مع فريق الأمن في Sophos فرحة كبيرة لأنهم تصرفوا بسرعة من خلال التعرف على التصحيحات ونشرها بعد أيام قليلة فقط من تقديم تقريرنا الأولي إليهم. مجد لهم! (يقصد التورية!)

الكشف عن Cyberoam

ونظرًا لأن معظم هذه الكيانات أهداف جذابة للمهاجمين ، فإنها تجعل الأخطاء أكثر أهمية.

CyberoamOS البعيد غير مصدق تنفيذ الأوامر الجذر

CyberoamOS هو نظام تشغيل معتمد على نظام Linux للأجهزة Cyberoam. يحتوي نظام التشغيل هذا على واجهة تهيئة تعتمد على الويب وبوابة SSLVPN.

تنقسم واجهة الويب إلى قسمين رئيسيين:

  • واجهة مكتوبة بلغة جافا
  • خلفية تستخدم مزيجًا من C و Perl

لن نتعمق في الأجزاء الداخلية من الشفرة الأمامية أو الخلفية ، وذلك أساسًا لتوفير الوقت والحد من كمية المعلومات التي تم الكشف عنها. ولكن سنناقش لفترة وجيزة كيف يتم تشغيل الخطأ.

تحتوي كل من واجهات التكوين و SSLVPN على servlet يعالج العمليات الرئيسية. يتم تعريف هذه العمليات باستخدام معلمة تسمى "الوضع".

معظم هذه مصادقة. ولكن هناك عدد قليل من العمليات التي يمكننا الوصول إليها دون مصادقة (مثل تسجيل الدخول).

الأخطاء التي وجدناها تكمن في وحدة مكافحة البريد الإلكتروني / مكافحة البريد العشوائي. وضع الطلب لنقطة النهاية هذه (الوحدة النمطية ، المرجع السابق) هو 458.

شيء واحد هو أن نلاحظ أن رموز الشفرة يتم تعيينها إلى أسمائهم في قاعدة بيانات Cyberoam (قاعدة بيانات Postgres الداخلية). بالبحث عن 458 ، يمكننا معرفة ما هو اسم شفرة التشغيل هذه.

فيما يلي سطر من البرنامج النصي SQL تهيئة قاعدة البيانات يظهر اسم شفرة التشغيل 458:

إدراج في tblcrevent (شفرة التشغيل والوصف والوضع ونوع الطلب)
القيم ('RELEASEQUARANTINEMAILFROMMAIL' ، 'RELEASE QUARANTINE MAIL from MAIL'، '458'، 2)؛

يتم تخزين وظائف شفرة التشغيل في الدليل / _conf / csc / cscconf /. لن نكشف عن الشفرة الكاملة للوظيفة المستضعفة ، لكننا سنقدم بعض المقتطفات التي توضح مكان وكيفية حدوث الخلل.

رمز من الواجهة الأمامية Java يعالج شفرة التشغيل 458:

if ((jsonObject.getString ("hdnSender") .equals ("") ||
validateEmail (jsonObject.getString ("hdnSender"))) &&
validateEmail (jsonObject.getString ("hdnRecipient")) &&
isSafeFilePath (jsonObject.getString ("hdnFilePath")) && ب) {
httpServletResponse.setContentType ("نص / HTML")؛
CyberoamLogger.debug ("مكافحة الفيروسات / مكافحة البريد التطفلي", "CSC قيمة ثابتة " +
CSCConstants.isCCC)؛

كما ترون أعلاه ، يتم فحص بعض المعلمات للتأكد من صحتها. إذا كانت قيمًا صالحة ، يحدث ما يلي:

eventBean eventByMode النهائي = EventBean.getEventByMode (363) ؛
... حجب.
final int sendWizardEvent = cscClient.sendWizardEvent (eventByMode، hashMap، sqlReader)؛

كما نرى أعلاه ، لدينا رمز حدث جديد (363) سيتم إرساله إلى الواجهة الخلفية. الخطأ الذي اكتشفناه موجود في الكود الذي يعالج هذا في الخلفية.

شفرة التشغيل تسمى sendmail ، ولتجنب استغلال هذا الخطأ ، سنقوم بتنقيح معظم الكود من الكود التالي.

معالج شفرة التشغيل لـ send_mail.

...حجب ...

$ param = طلب $->{إطلاق سراح}؛
param = DLOPEN (base64_decode ، param)
سجل applog " فك شفرة القيم :: $ param \ n"
٪ requestData = split (/ [&=] / ، $ param) ؛
$ mailServerHost = $ requestData {hdnDestDomain}؛
$ mailFrom = $ requestData {hdnSender}؛
$ mailTo = $ requestData {hdnRecipient}؛
ملف $ = QUARANTINE_PATH $."/".$ requestData {hdnFilePath}؛

$ mailfile = $ requestData {hdnFilePath}؛
$ validate_email ="خاطئة".
my $ email_regex = '^ ([\.]؟ [_ \ - \! \ # \ {\} \ $ \٪ \ ^ \&\ * \ + \ = \ |؟ \ \ '\\\\\\ / أ-ي0-9]) * @ ([ل-ي0-9] ([-] [A-zA- Z0-9] +) * \) + ([ل-ي0-9] {0،6}) $. '؛
if ($ requestData {hdnRecipient} = ~ / $ email_regex / && ((معرّفة $ requestData {hdnSender} && $ requestData {hdnSender} eq '') || $ requestData {hdnSender} = ~ / $ email_regex /) && الفهرس ($ requestData {hdnFilePath} ، '.. /') == -1) {
$ validate_email ="صحيح".
}
.... منقحة....

كما نرى أعلاه ، يوضح لنا الرمز الزائف - بيرل كيف تتلقى الواجهة الخلفية مدخلات من الواجهة الأمامية ($ requestData) وكيف تحاول التحقق من بعض المعلمات التي نرسلها.

بعد التحقق ، إذا كانت المعلمات لدينا صالحة ، يتم تنفيذ التعليمات البرمجية التالية:

٪ mailreq = ("mailaction"=>"$ MAIL_FORWARD","موضوع"=>"$ strSubject","toEmail"=>"$ ميلتو","attachmentfile"=>"$ ملف","smtpserverhost"=>"$ mailServerHost","من العنوان"=>"$ mailFrom")؛

خارج = OPCODE mail_sender json٪ mailreq

الكود أعلاه يعين معلمات طلبنا في متغير mailreq ويستدعي وظيفة mail_sender (OPCODE). سنرى كيف يتم تنفيذ شفرة التشغيل هذه وأين يحدث RCE بالضبط:

#mailaction 0 = mail_with_var ، 1 = mail_forward ، 2 = mail_attachment
$ = $ mailaction طلب->{mailaction}؛
$ موضوع = $ طلب->{موضوع}؛
$ mailbody = ''؛
$ = $ attachmentfile طلب->{attachmentfile}؛
$ toEmail = $ طلب->{toEmail}؛

# البريد الجسم
إذا("طلب محدد دولار->{mailbody} && طلب ني $->{mailbody}") {
$ = $ mailbody طلب->{mailbody}؛
}
مضيف خادم #SMTP
إذا("طلب محدد دولار->{smtpserverhost} && طلب ني $->{smtpserverhost}") {
$ = $ smtpserverhost طلب->{smtpserverhost}؛
}آخر{
النتيجة = QUERY "حدد servicevalue من tblclientservices حيث servicekey = 'MailServer'"
إذا("نتيجة محددة دولار->{انتاج}->{servicevalue} [0] && نتيجة ني $->{انتاج}->{servicevalue} [0]") {
$ = $ smtpserverhost نتيجة->{انتاج}->{servicevalue} [0]؛
}آخر{
$ smtpserverhost ="127.0.0.1".
}
}

#SMTP خادم الميناء
إذا("طلب محدد دولار->{smtpserverport} && طلب ني $->{smtpserverport}") {
$ = $ smtpserverport طلب->{smtpserverport}؛
}آخر{
النتيجة = QUERY "حدد servicevalue من tblclientservices حيث servicekey = 'MailServerPort'"
إذا("نتيجة محددة دولار->{انتاج}->{servicevalue} [0] && نتيجة ني $->{انتاج}->{servicevalue} [0]") {
$ = $ smtpserverport نتيجة->{انتاج}->{servicevalue} [0]؛
}آخر{
$ smtpserverport ="25".
}
}

علامة المصادقة #SMTP
$ smtpauthflag ="0".
إذا("طلب محدد دولار->{smtpauthflag} && طلب ني $->{smtpauthflag}") {
$ = $ smtpauthflag طلب->{smtpauthflag}؛
}آخر{
النتيجة = QUERY "حدد servicevalue من tblclientservices حيث servicekey = 'SMTPAuthenticationFlag'"
إذا("نتيجة محددة دولار->{انتاج}->{servicevalue} [0] && نتيجة ني $->{انتاج}->{servicevalue} [0]") {
$ = $ smtpauthflag نتيجة->{انتاج}->{servicevalue} [0]؛
}
}

إذا("smtpauthflag $ == 1") {
إذا("طلب محدد دولار->{mailusername} && طلب ني $->{mailusername}") {

$ = $ mailusername طلب->{mailusername}؛
$ = $ mailpassword طلب->{mailpassword}؛

}آخر{
النتيجة = QUERY "حدد servicevalue من tblclientservices حيث servicekey = 'MailServerUsername'"
اسم البريد $ = نتيجة $->{انتاج}->{servicevalue} [0]؛
النتيجة = QUERY "حدد servicevalue من tblclientservices حيث servicekey = 'MailServerPassword'"
$ mailpassword = نتيجة $->{انتاج}->{servicevalue} [0]؛
}
}آخر{

اسم البريد $ = "".
$ mailpassword = "".

}
إذا("طلب محدد دولار->{من العنوان} && طلب ني $->{من العنوان}") {
$ = $ FROMADDRESS طلب->{من العنوان}؛
}آخر{
النتيجة = QUERY "حدد servicevalue من tblclientservices حيث servicekey = 'FromAddress'"
fromaddress $ = نتيجة $->{انتاج}->{servicevalue} [0]؛
}

#وضع التأمين
إذا("طلب محدد دولار->{smtpsecurity} && طلب ني $->{smtpsecurity}") {
$ = $ smtpsecurity طلب->{smtpsecurity}؛
}آخر{
النتيجة = QUERY "حدد servicevalue من tblclientservices حيث servicekey = 'smtpsecurity'"
smtpsecurity $ = نتيجة $->{انتاج}->{servicevalue} [0]؛
}

$ smtpsecuritymode = 0؛
إذا("smtpsecurity $ مكافئ "STARTTLS"") {
$ smtpsecuritymode = 1؛
} عدا ذلك ("smtpsecurity $ مكافئ "SSL / TLS"") {
$ smtpsecuritymode = 2؛
}

#SMTP الشهادة

$ smtpcertificate = ''؛
$ certpassword = ''؛

إذا("$ smtpsecuritymode! = 0") {
إذا("طلب محدد دولار->{smtpcertificate} && طلب ني $->{smtpcertificate}") {
النتيجة = QUERY "حدد certname ، كلمة المرور من tblvpncertificate حيث certid = طلب $->{smtpcertificate}"
}آخر{
النتيجة = QUERY "حدد اسم الشهادة وكلمة المرور من tblvpncertificate حيث certid = (حدد servicevalue :: int من tblclientservices حيث servicekey = 'smtpcertificate')"
}

smtpcertificate $ = النتيجة $->{انتاج}->{certname} [0]؛
$ = $ certpassword نتيجة->{انتاج}->{كلمة} [0]؛

}

# من العنوان بالاسم
إذا("طلب محدد دولار->{fromaddresswithname} && طلب ني $->{fromaddresswithname}") {
$ = $ fromaddresswithname طلب->{fromaddresswithname}؛
}آخر{
fromaddresswithname $ = $ OEMNAME . " <" . $ FROMADDRESS . ">".
}

الكود أعلاه يفعل نفس الشيء الذي فعلته شفرة التشغيل الأخرى عندما يبدأ. يقوم بتهيئة المتغيرات (بعضها منا أو من الجهاز إن لم يكن محددًا).

بعد تعيين المتغيرات ، يتم تنفيذ كتلة التعليمات البرمجية التالية.

خارج = EXECSH "/ bin / cschelper mail_send '$ fromaddress' '$ fromaddresswithname' '$ toEmail' '$ toEmail' '$ topic' '$ mailbody' '$ smtpserverhost' '$ smtpserverport' '$ mailusername' '$ mailusword' '$ mailaction' ' smtpsecuritymode $ '' $ smtpcertificate '' $ certpassword '' 1 '' $ attachmentfile '"

وها هو ، تنفيذ الأوامر. الآن الدعوة هنا هي EXECSH التي تستدعي / bin / sh -c "حشوات". مع حدوث التنفيذ باستخدام القيم التي نتحكم فيها ، يمكننا بسهولة تحقيق تنفيذ الأوامر عن بُعد ، كل ذلك دون مصادقة.

سنقوم بنشر تقرير كامل وإثبات الفكرة مع الخطوط العريضة المناسبة في غضون بضعة أشهر.

تحديث: تمت تغطية هذا البحث أولاً على TechCrunch ، اقرأ المزيد هنا.

Brayan Jackson Administrator
Sorry! The Author has not filled his profile.
follow me