
इस रिपोजिटरी में CVE-2018-19987 के मेरे विश्लेषण के बारे में एक ब्लॉग पोस्ट है, जो कई D-Link राउटरों को प्रभावित करने वाला एक प्रमाणित OS कमांड इंजेक्शन है।
नमस्ते!
इस छोटी ब्लॉग पोस्ट में मैं CVE-2018-19987 के पीछे के विवरणों को बेहतर ढंग से समझने के लिए किए गए अपने एक त्वरित विश्लेषण को विस्तार से बताऊँगा। शुरुआत में मैं इस CVE के पीछे गया क्योंकि मुझे लगा कि इसके बारे में कोई सार्वजनिक जानकारी या एक्सप्लॉइट मौजूद नहीं था। पहला विश्लेषण करने के तुरंत बाद, मुझे GitHub पर दो सार्वजनिक PoC मिले जिन्हें आप यहाँ और यहाँ पा सकते हैं।
चूँकि मैंने अधिकांश काम पहले ही कर लिया था और मुझे कोई पूर्ण विश्लेषण नहीं मिला था, मैंने फिर भी ब्लॉग पोस्ट लिखने का निर्णय लिया।
आशा है आपको यह पसंद आएगा!
अद्यतन: खैर.. इस ब्लॉग पोस्ट के साथ अटका हुआ था, तभी गूगल करते समय मुझे निम्नलिखित लेख मिला जो D-Link DIR-818 में एक बहुत ही समान भेद्यता (CVE-2018-19986) का वर्णन करता है। राउटर भेद्यता विश्लेषण श्रृंखला (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, and DIR-890L Rev.A 1.21B02_BETA devices mishandle IsAccessPoint in /HNAP1/SetAccessPointMode. In the SetAccessPointMode.php source code, the IsAccessPoint parameter is saved in the ShellPath script file without any regex checking. After the script file is executed, the command injection occurs. A vulnerable /HNAP1/SetAccessPointMode XML message could have shell metacharacters in the IsAccessPoint element such as the `telnetd` string.
विश्लेषण शुरू करने के लिए मुझे कुछ ऐसा चाहिए था जिसे देखा जा सके, इसलिए मैंने राउटर के FS को निकालने की प्रक्रिया शुरू की।
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 एक Linux फ़ाइल सिस्टम का पता लगाता है। यदि हम अपने पास मौजूद जानकारी पर गहराई से नज़र डालें, तो हम देख सकते हैं कि एडवाइज़री /HNAP1 पथ का उल्लेख करती है। इसलिए, हम देखना शुरू कर सकते हैं कि राउटर इन URL को कैसे संभालता है।
जैसा कि उत्कृष्ट /dev/tty0 ब्लॉग पोस्ट में समझाया गया है, अंततः इन URL को cgibin नामक एक बाइनरी द्वारा संभाला जाता है, जो htdocs/cgibin पर स्थित है। लेकिन मैं यह बेहतर ढंग से समझना चाहता था कि यह कैसे कॉन्फ़िगर किया गया था और अंततः इसने मुझे यह समझने के लिए प्रेरित किया कि HTTP सर्वर को अनुरोधों को संसाधित करने के लिए कैसे कॉन्फ़िगर किया गया था। मैंने httpd.conf फ़ाइल नाम की खोज की, यह मानते हुए कि इसका उपयोग HTTP सर्वर को कॉन्फ़िगर करने के लिए किया गया था, और /etc/services/ फ़ोल्डर के अंतर्गत HTTP.php नामक एक फ़ाइल मिली, जिसमें अन्य चीज़ों के अलावा निम्नलिखित रोचक पंक्तियाँ शामिल थीं:
$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";
}
अब हमारे पास सभी टुकड़े हैं! हम मान सकते हैं कि वेब सर्वर /usr/sbin/hnap का उपयोग करके /HNAP1 के HTTP अनुरोधों को संभालने के लिए कॉन्फ़िगर किया गया है, और /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 फ़ाइल द्वारा लिखे गए वेरिएबल में OS कमांड इंजेक्शन कैसे निष्पादित होता है। यहाँ आप pr0v3rbs द्वारा इस समस्या का शोषण करने के लिए विकसित एक पूर्ण poc पा सकते हैं।
हम प्रभावित और सुधारित संस्करणों के बारे में थोड़ा और बाद में चर्चा करेंगे। जैसा कि आप नीचे दी गई तालिका में देख सकते हैं, ऐसा लगता है कि पैच के साथ किसी प्रकार का रिग्रेशन हुआ था, जिसने इस भेद्यता को उन संस्करणों में फिर से शामिल कर दिया जिन्हें पहले ही पैच किया जाना चाहिए था।
(*) मैंने इन फर्मवेयर संस्करणों की जाँच नहीं की क्योंकि उनके अनएन्क्रिप्टेड समकक्ष भेद्य थे।
जैसा कि हम ऊपर दी गई तालिका में देख सकते हैं, प्रभावित PHP फ़ाइल SetAccessPointMode.php को हटाकर भेद्यता को कुछ फर्मवेयर संस्करणों में ठीक किया गया था। एक बार फ़ाइल मौजूद नहीं होने पर, फ़ंक्शन में किए गए चेक विफल हो जाएँगे और कुछ भी निष्पादित नहीं होगा। मैंने फर्मवेयर संस्करणों FW v3.131 और 3.15B02 WW में hnap_function का विश्लेषण करके इसकी पुष्टि की; इस मुद्दे के संबंध में hnap_main का कोड नहीं बदला था, लेकिन PHP फ़ाइल अब मौजूद नहीं थी।
इससे भी अधिक दिलचस्प बात यह है कि संस्करण 3.11B01_icjg_WW में पैच किए जाने के बाद, किसी तरह यह बग फर्मवेयर संस्करण FW v3.11 से FW v3.12B04 तक में फिर से शामिल कर दिया गया था।
पहले निष्कर्ष के रूप में, मैं कहूँगा कि इन डिवाइसों पर वास्तविक सुरक्षित डिवाइस के रूप में कभी भरोसा न करें —अपडेट के साथ जो हुआ वह इस बात का स्पष्ट उदाहरण है कि आपको ऐसा क्यों नहीं करना चाहिए। साथ ही, किए गए विश्लेषण के बाद मैं यह निष्कर्ष निकाल सकता हूँ कि इस ब्लॉग पोस्ट में सूचीबद्ध पहला 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 | हाँ |