# -*- coding: utf-8 -*-
"""
Author: Eric.zhong
Contact: ericzhong2010@qq.com
Create Time: 2020/03/13
File: CVE-2020-0976.py
Info: Microsoft SMBv3 contains a vulnerability in the handling of compression, which may allow a remote,
  unauthenticated attacker to execute arbitrary code on a vulnerable system.
  Microsoft Server Message Block 3.1.1 (SMBv3) contains a vulnerability in the way that it handles connections that use compression.
  This vulnerability may allow a remote, unauthenticated attacker to execute arbitrary code on a vulnerable system.
  It has been reported that this vulnerability is "wormable."
  By connecting to a vulnerable Windows machine using SMBv3, or by causing a vulnerable Windows system to initiate a client connection to a SMBv3 server,
  a remote, unauthenticated attacker to execute arbitrary code with SYSTEM privileges on a vulnerable system.

Workaround:
	1. CVE-2020-0796 Patch
	https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796
	2. Disable SMBv3 compression
	Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" DisableCompression -Type DWORD -Value 1 -Force
	PS: No need to reboot.
"""
import queue
import socket
import struct
import tkinter
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import threading
import IPy
from netaddr import IPNetwork



class guiUI():
    def __init__(self, init_window_name):
        self.init_window_name = init_window_name
        self.width = init_window_name.winfo_screenwidth()  # 获取屏幕宽度
        self.height = init_window_name.winfo_screenheight()  # 获取屏幕高度
        w = 350
        h = 420
        x = (self.width-w)/2
        y = (self.height-h)/2
        self.init_window_name.geometry("%dx%d+%d+%d" %(w,h,0,0))
        self.init_window_name.resizable(False, False)

        # CVE-2020-0976 魔包变量
        self.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'

        # Create queue
        self.notify_queue = queue.Queue()

        # Init Entry Text
        self.pre_ipAddr = StringVar()
        self.pre_ipAddr.set("192.168.2.0/24")

    # CVE-2020-0976 核心扫描
    def scanner(self, tkMessageBox=None):
        FLAG = self.check_ip(self.txt_ipAddr.get())

        def scan(_queue,ip):
            sock = socket.socket(socket.AF_INET)
            sock.settimeout(3)
            FLAG2 = True
            try:
                sock.connect((str(ip), 445))
            except:
                self.status_tree.insert("", 'end', values=(ip, "不在线或未开放445端口"))
                FLAG2 = False
                sock.close()

            if FLAG2:
                sock.send(self.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":
                    self.status_tree.insert("", 'end', values=(ip, "Not vulnerable"))
                else:
                    self.status_tree.insert("", 'end', values=(ip, "Vulnerable"))

            _queue.put((1,))

        if FLAG:
            for ip in IPNetwork(self.txt_ipAddr.get()):
                filter_ip = (str(ip).split('.')[3])
                if filter_ip == "0" or filter_ip == "255":
                    continue
                th = threading.Thread(target=scan, args=(self.notify_queue,ip))
                th.setDaemon(True)
                th.start()
        else:
            tkinter.messagebox.askokcancel('提示','网络地址输入有误,格式参考:\n主机=>192.168.0.1\n网段=>192.168.0.0/24')

    # 检查是否为合规的网络地址格式
    def check_ip(self,ipAddr):
        try:
            IPy.IP(ipAddr)
            return True
        except Exception as e:
            return False

    # 程序窗体设计
    def set_init_window(self):
        # Add a title
        self.init_window_name.title("CVE-2020-0976 Scanner")

        '''
        TEXT 与 Button 相关
        '''
        self.frame1 = LabelFrame(width=400, height=80, text="")
        self.frame1.grid(column=0, row=1, padx=5, pady=5)

        label1 = Label(self.frame1, text="扫描地址段：")
        label1.grid(row=0, column=0)
        self.txt_ipAddr = Entry(self.frame1, width=28, textvariable=self.pre_ipAddr)
        self.txt_ipAddr.grid(row=0, column=1)

        btn_scanner = Button(self.frame1, text="开始扫描", command=self.scanner)
        btn_scanner.grid(row=0, column=3)

        self.statusframe = LabelFrame(width=380, height=100, text="扫描结果")
        self.statusframe.grid(column=0, row=2, padx=0, pady=0)

        # 定义树形结构与滚动条
        self.status_tree = ttk.Treeview(self.statusframe, height = 15, show="headings", columns=("a", "b"))
        self.vbar = ttk.Scrollbar(self.statusframe, orient=VERTICAL, command=self.status_tree.yview)
        self.status_tree.configure(yscrollcommand=self.vbar.set)

        # 表格的标题
        self.status_tree.column("a", width=140, anchor="center")
        self.status_tree.column("b", width=180, anchor="center")

        self.status_tree.heading("a", text="Ip Address")
        self.status_tree.heading("b", text="Status")

        self.status_tree.grid(row=5, column=0, sticky=NSEW)
        #self.sqltrace_tree.bind("<Double-1>", self.onDBClick)
        self.vbar.grid(row=5, column=1, sticky=NS)

        label2 = Label(text="Author：Eric.zhong(ericzhong2010@qq.com)")
        label2.grid(row=6, column=0)



if __name__ == '__main__':

    init_window = Tk()
    win = guiUI(init_window)

    win.set_init_window()
    init_window.mainloop()