
يحتوي هذا المستودع على منشور مدونة حول تحليلي لـ CVE-2018-19987، وهو هجوم حقن أوامر نظام تشغيل مصادق عليه يؤثر على العديد من أجهزة توجيه D-Link
مرحبًا!
في هذا المقال القصير، سأشرح تحليلاً سريعًا قمت به لفهم التفاصيل وراء CVE-2018-19987 بشكل أفضل. في البداية، بحثت عن هذه الثغرة لأنني اعتقدت أنه لا توجد معلومات عامة أو استغلال. بعد وقت قصير من إجراء التحليل الأول، وجدت اثنين من PoCs العامة على GitHub يمكنك العثور عليها هنا و هنا.
نظرًا لأنني كنت قد أنجزت معظم العمل ولم أجد تحليلاً كاملاً، قررت كتابة المقال على أي حال.
أتمنى أن تستمتع به!
تحديث: حسنًا .. بعد البحث أثناء توقفي في هذا المقال، وجدت المقال التالي الذي يصف ثغرة مشابهة جدًا (CVE-2018-19986) في جهاز D-Link DIR-818. سلسلة تحليل ثغرات أجهزة التوجيه (5): تحليل وإعادة إنتاج ثغرة حقن الأوامر CVE-2018-19986 في DIR-818LW&828 (مترجم بواسطة Google) بقلم Ogur1.
بدأت تحليلي بجمع كل المعلومات التي استطعت عن الجهاز:
ملاحظة: أجريت كل التحليل مع إصدار البرنامج الثابت v3.01B02.
المعلومات العامة لهذه الثغرة، المتاحة على صفحة Mitre، وفرت معلومات كافية للبدء: أجهزة D-Link DIR-822 Rev.B 202KRb06, DIR-822 Rev.C 3.10B06, DIR-860L Rev.B 2.03.B03, DIR-868L Rev.B 2.05B02, DIR-880L Rev.A 1.20B01_01_i3se_BETA, وDIR-890L Rev.A 1.21B02_BETA تعالج IsAccessPoint بشكل خاطئ في /HNAP1/SetAccessPointMode. في كود مصدر SetAccessPointMode.php، يتم حفظ معامل IsAccessPoint في ملف البرنامج النصي ShellPath دون أي فحص regex. بعد تنفيذ ملف البرنامج النصي، يحدث حقن الأوامر. يمكن أن تحتوي رسالة XML الضعيفة /HNAP1/SetAccessPointMode على أحرف خاصة في عنصر IsAccessPoint مثل سلسلة telnetd.
لبدء التحليل، كنت بحاجة إلى شيء لأنظر إليه، لذلك شرعت في استخراج نظام الملفات الخاص بجهاز التوجيه.
binwalk -eM DIR822C1_FW301WWb02.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/1"
10380 0x288C LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 4213444 bytes
1376372 0x150074 PackImg section delimiter tag, little endian size: 10505216 bytes; big endian size: 5021696 bytes
1376404 0x150094 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 5019773 bytes, 2282 inodes, blocksize: 131072 bytes, created: 2016-03-18 09:35:31
كما نرى، يكتشف Binwalk نظام ملفات لينكس. إذا ألقينا نظرة أعمق على المعلومات المتاحة، نرى أن الإشعار يذكر مسار /HNAP1. لذا، يمكننا البدء في النظر في كيفية معالجة جهاز التوجيه لهذه الروابط.
كما هو موضح في منشور المدونة الممتاز /dev/tty0، تتم معالجة هذه الروابط في النهاية بواسطة ثنائي يسمى cgibin، الموجود في htdocs/cgibin. لكنني أردت فهمًا أفضل لكيفية تكوين ذلك، وفي النهاية قادني ذلك إلى فهم كيفية تكوين خادم HTTP لمعالجة الطلبات. شرعت في البحث عن اسم ملف httpd.conf، بافتراض أنه يُستخدم لتكوين خادم HTTP، ووجدت ملفًا يسمى HTTP.php تحت مجلد /etc/services/، يحتوي من بين أشياء أخرى على الأسطر التالية المثيرة للاهتمام:
$httpd_conf = "/var/run/httpd.conf";
fwrite("a",$START, "xmldbc -P /etc/services/HTTP/httpcfg.php > ".$httpd_conf."\n");
fwrite("a",$START, "event PREFWUPDATE add /etc/scripts/prefwupdate.sh\n");
fwrite("a",$START, "httpd -f ".$httpd_conf."\n");
fwrite("a",$START, "event HTTP.UP\n");
fwrite("a",$START, "exit 0\n");
افترضت أنه في مرحلة ما يتم تنفيذ هذا الملف ويكتب ملف /var/run/httpd.conf. لمعرفة كيفية تكوين خادم الويب، شرعت في تحليل ملف /etc/services/HTTP/httpcfg.php (أدرجت فقط الأجزاء ذات الصلة):
if ($hnap > 0)
{
echo
" Control". "\n".
" {". "\n".
" Alias /HNAP1". "\n".
" Location /htdocs/HNAP1". "\n".
" External". "\n".
" {". "\n".
" /usr/sbin/hnap { hnap }". "\n".
" }". "\n".
" IndexNames { index.hnap }". "\n".
" }". "\n";
}
الآن لدينا كل القطع! يمكننا افتراض أن خادم الويب مهيأ لمعالجة طلبات HTTP إلى /HNAP1 باستخدام /usr/sbin/hnap، وبناءً على منشور مدونة /dev/tty0 نعلم أنه سيكون رابطًا للثنائي htdocs/cgibin.
الخطوة التالية كانت إلقاء نظرة على الثنائي cgibin لفهم كيفية معالجته لطلبات HNAP. في الصورة التالية يمكننا رؤية جزء من دالة main المفككة حيث تتم مقارنة مسار URL الذي تم تمريره مع مسارات مختلفة (مثل session.cgi, authentication.cgi, captcha.cgi، على سبيل المثال لا الحصر) حتى يتم العثور على hnap ويتم استدعاء دالتنا hnap_main.
ملاحظة: أثناء تحليل الدالة main، واجهت بعض المشكلات في تحديد سلسلة hnap لأن Ghidra لم يكتشفها كسلسلة أثناء التحليل الأولي.
الخطوة التالية كانت فهم ما تفعله هذه الدالة. سأقدم فقط مقتطفات من الكود للأجزاء ذات الصلة التي ستساعد في فهم ما تفعله الدالة بشكل أفضل، وسأكمل أجزاء أخرى ذات صلة ببعض الكود الوهمي —أعدت تسمية بعض المتغيرات لتوضيح الغرض منها.
...
HTTP_SOAPACTION = getenv("HTTP_SOAPACTION");
REQUEST_METHOD = getenv("REQUEST_METHOD");
HNAP_AUTH = getenv("HTTP_HNAP_AUTH");
__haystack = getenv("HTTP_COOKIE");
pcVar1 = getenv("HTTP_REFERER");
...
if (HTTP_SOAPACTION != "") {
if (HTTP_SOAPACTION == GetDeviceSettings) {
...
} else {
// These actions will occur during auth. Process
if ("GetCAPTCHAsetting" in HTTP_SOAPACTION) {
sess_generate_captcha();
} else {
if ("Login" in HTTP_SOAPACTION) {
perform_login();
}
// We'll land here once auth.
if (HNAP_AUTH != "") {
if ("uid=" in HTTP_COOKIE){
is_valid_auth = perform_auth_process()
if (is_valid_auth) {
if("logout" in HTTP_SOAPACTION){
perform_logout();
// If we are auth. and NOT trying to logout
// the code will try to perform the action
// we requested
} else {
// If we arrive here we win
// interesting code below
goto LAB_004141d4;
}
}
}
}
// If we are not authenticated we can't do anything
Return "You need proper authorization to use this resource"
}
}
} else {
...
}
LAB_004141d4:
hnap_action = get_hnap_operation(HTTP_SOAPACTION);
if (HTTP_SOAPACTION != "") {
hnap_action_len = strlen(hnap_action);
}
snprintf(path_to_hnap_php_file,0x100,"%s/%s.php","/etc/templates/hnap/",hnap_action);
if (!check_file_access(path_to_hnap_php_file)){
return "HNAP ACTION DOES NOT EXIST (FAIL)"
}
if (REQUEST_METHOD == "POST") {
// Here arguments for the PHP are extracted
parse_request_and_extract_xml()
if (hnap_action == "GetFirmwareStatus") {
system("sh /etc/events/checkfw.sh > /dev/console");
}
// Here the final arguments for the xmldbc_ephp are crafted
snprintf(ARGS_FOR_XMLDBC_PHP,0x100,"%s%s.php\nShellPath=%s%s.sh\nPrivateKey=%s\n",
"/etc/templates/hnap/", hnap_action, &ShellPath, hnap_action, &PRIVATE_KEY);
// PHP is executed (in our case SetAccessPointMode.php) and the shell
// script is written to ShellPath
xmldbc_ephp(0,0,ARGS_FOR_XMLDBC_PHP,stdout);
snprintf(hnap_action, 0x100, "%s", hnap_action);
// Shell command is built to run the previously written shell file
// (File written by the PHP script)
shell_command = "sh %s%s.sh > /dev/console &";
}
snprintf(cmd_to_execute, 0x100, shell_command, &PATH, hnap_action);
// File is executed containing the command injection
system(cmd_to_execute);
}
...
بمجرد اكتمال تحليل هذه الدالة، قررت إلقاء نظرة على ملف PHP SetAccessPointMode.php — الذي كان في النهاية هو الذي يحتوي على الخلل — وحاولت تجميع كل القطع معًا:
...
$IsAccessPoint = query("/runtime/hnap/SetAccessPointMode/IsAccessPoint");
...
fwrite("w",$ShellPath, "#!/bin/sh\n");
fwrite("a",$ShellPath, "echo [$0] $1 ... > /dev/console\n");
fwrite("a",$ShellPath, "echo IsAccessPoint = ".$IsAccessPoint." > /dev/console\n");
fwrite("a",$ShellPath, "echo Result = ".$Result."\n");
...
كما نرى، لدينا متغير $ShellPath الذي تم ملؤه بواسطة الدالة hnap_main والمتغير $IsAccessPoint الذي يتحكم فيه المستخدم ويتم تمريره في طلب XML المرسل. بهذا يمكننا تأكيد كيفية تنفيذ حقن أوامر نظام التشغيل في المتغير المكتوب بواسطة ملف PHP. هنا يمكنك العثور على PoC كامل طوره pr0v3rbs لاستغلال هذه المشكلة.
سنناقش قليلاً حول الإصدارات المتأثرة والمصلحة لاحقًا. كما ترى في الجدول أدناه، يبدو أن هناك نوعًا من الانحدار في التصحيح، مما أعاد إدخال هذه الثغرة في إصدارات كان ينبغي أن تكون قد تم إصلاحها بالفعل.
(*) لم أتحقق من إصدارات البرامج الثابتة هذه لأن نظيراتها غير المشفرة كانت ضعيفة.
كما نرى في الجدول أعلاه، تم إصلاح الثغرة في بعض إصدارات البرامج الثابتة عن طريق إزالة ملف PHP المتأثر SetAccessPointMode.php. بمجرد عدم وجود الملف، ستفشل الفحوصات التي تتم في الدالة ولن يتم تنفيذ شيء. أكدت ذلك من خلال تحليل دالة hnap_function في إصدارات البرامج الثابتة FW v3.131 و 3.15B02 WW؛ كود hnap_main لم يتغير فيما يتعلق بهذه المشكلة، لكن ملف PHP لم يعد موجودًا.
الأكثر إثارة للاهتمام هو أن هذا الخلل أعيد إدخاله بطريقة ما في إصدار البرنامج الثابت FW v3.11 حتى FW v3.12B04، بعد أن تم إصلاحه في الإصدار 3.11B01_icjg_WW.
كاستنتاج أول، أقول: لا تثق أبدًا بهذه الأجهزة كجهاز آمن حقيقي —ما حدث مع التحديثات هو مثال واضح على سبب عدم القيام بذلك. أيضًا، بعد التحليل الذي تم إجراؤه، يمكنني استنتاج أن أول PoC المذكور في هذا المقال لن يعمل مع هذا الإصدار المحدد، كما رأينا أن هناك بعض الشروط التي يجب استيفاؤها لتنفيذ ملف PHP الضعيف.
والاستنتاج الأكثر أهمية: كن حذرًا جدًا عند تحديد مدى خطورة ثغرة بناءً على درجة CVSS الخاصة بها. قررت تحليل هذا CVE لأنه تم تقييمه بـ 9.8 (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) ومن الواضح أنه، على الأقل لهذا الإصدار، سيحتاج المهاجم إلى بيانات اعتماد ليكون قادرًا على استغلالها.
كخطوات تالية، سأعمل على القدرة على محاكاة هذا الثنائي لاستغلال هذه الثغرة دون الوصول إلى جهاز التوجيه نفسه، لكن ذلك سيكون مادة لمنشور آخر!
شكرًا للقراءة.
| البرنامج الثابت | الإصدار | تجزئة MD5 | تاريخ الإصدار | معرضة للخطر |
|---|
| DIR822C1_FW315WWb02.bin | 3.15B02 WW | 7121771c3e1706ba76fbf244023efad3 | 06/11/2019 | لا |
| DIR822C1_FW313WWb01.bin | FW v3.13 | aa16c7016f67be384e0784e439ce26d2 | 10/07/2019 | لا |
| DIR822C1_FW303WWb04_i4sa_middle.bin | FW v3.12B04 | c3b9a3f115c02e739690616aba2f2d99 | 26/04/2019 | نعم |
| DIR822C1_FW312WWb04.bin | FW v3.12B04 | eb11afbd136a5b29cea18141f727bfa8 | 26/04/2019 | نعم (*) |
| DIR822C1_FW311WWb01.bin | FW v3.11 | 6d7c90eaaae835667faea65c862b3c82 | 01/01/2019 | نعم |
| DIR822C1_FW311bWWb01_icjg.bin | 3.11B01_icjg_WW | 75e361e1465604aeda5d5dbcaecca977 | 21/12/2018 | لا |
| DIR822C1_FW303WWb04_i4sa_middle.bin | FW v3.10B06 | c3b9a3f115c02e739690616aba2f2d99 | 17/08/2018 | نعم |
| DIR822C1_FW310WWb06.bin | FW v3.10B06 | e33db75d0801fddb1c90308982e69fe5 | 17/08/2018 | نعم (*) |
| DIR-822_C1_FW302WWb05.bin | FW v3.02 | 0dbf840c0ff5d3a5b593d33690e15d82 | 14/09/2017 | نعم |
| DIR822C1_FW301WWb02.bin | FW v3.01B02 | 1bff7ec8b4da0643f65b4d44c630e92b | 27/04/2016 | نعم |