[python]Automated process to look for hosts that are vulnerable to eternalblue

Disclaimer
This code is meant for educational purpose only, you should be well aware that you must not in any case use the code to scan for vulnerable host in production, to test the code, please load in your VM and use host-only network interface to test and learn in a safe environment.
All credits go to worawit.

The python codes

  1. analysis.py – This code uses Worawit’s checker.py code and Worawit’s mysmb library
  2. reconn.py – Uses nmap to check for live host that has tcp 445 opened
  3. network_discovery.py – Automatically checks which network the interface is connected to, the subnet is later used by reconn.py to check for hosts that have tcp 445 opened.

Requirements
Copy and paste these and save as requirements.txt.

ipaddress==1.0.22
netifaces==0.10.9
python-nmap==0.6.1
impacket==0.9.19

For the above python codes to work you need to install modules in the requirements.txt, pip install -r requirements.txt

impacket is required to use Worawit’s mysmb library.

Execution
Run the analysis.py to get the result printout.

Why not run the Worawit’s checker.py
anaylsys.py is mostly Worawit’s checker.py code and Worwit’s mysmb module, checker.py takes in sys.argv[1], which I do not know how to pass argument like this within a python script. I have also tried subprocess.run, however running checker.py had failed.

network_disovery.py codes

from netifaces import interfaces, ifaddresses
from ipaddress import IPv4Interface


# Refer to doc https://pypi.org/project/netifaces/
# interfaces method returns a list of interface.
# ifaddresses method returns a dictionary on specific interface.
# 18 returns layer 2 information i.e. mac address.
# 2 returns layer 3 information i.e. IP address.
# Hence I am only interested if any interfaces in my computer has a valid IP address.
# Then I use another if loop to filter away localhost address.
# The information is structured in a dictionary for easy manipulation.
def check_local_valid_address():
    available_interfaces = interfaces()
    for interface in available_interfaces:
        if ifaddresses(interface).get(2):
            if ifaddresses(interface).get(2)[0].get('addr') != '127.0.0.1':
                intf = interface
                intf_ip = ifaddresses(interface).get(2)[0].get('addr')
                intf_netmask = ifaddresses(interface).get(2)[0].get('netmask')
                intf_info =  {'interface': intf,
                        'ip_address': intf_ip,
                        'netmask': intf_netmask}
                cidr = str(convert_subnet_to_cidr(intf_info['netmask']))
                return address_belongs_to_which_subnet(intf_info['ip_address'] + '/' + cidr)


# See demonstration in https://docs.python.org/3/library/ipaddress.html#ipaddress.IPv4Interface
# Under network section.
# The result will return an IPv4Address object hence I use str() to convert to string object.
def address_belongs_to_which_subnet(host_address):
    return str(IPv4Interface(host_address).network)


# count the number of 1 in the binary representation of 255, 254, 252, 248, 240, 224, 192, 128
# refer to this https://stackoverflow.com/questions/38085571/how-use-netaddr-to-convert-subnet-mask-to-cidr-in-python
# credit goes to Eugene Yarmash who replied in the post.
def convert_subnet_to_cidr(subnet_mask):
    return (sum(bin(int(x)).count('1') for x in subnet_mask.split('.')))

reconn.py codes

from nmap import PortScanner
from network_discovery import check_local_valid_address

def find_eternal_blue_vuln_hosts():
    vuln_hosts = []
    nm = PortScanner()
    # Get the subnet from network_discovery.py and use it to scan
    nm.scan(check_local_valid_address(),'445')
    for host in nm.all_hosts():
        # Check if tcp/445 is open or not, this ensures tcp/445 is accessible before
        # doing further analysis using analysis.py
        # PortScanner returns a dictionary of the result, you need to check
        # the dictionary keys from nm[host].
        if nm[host]['tcp'][445]['state'] == 'open':
            # collect only hosts that have 445 opened.
            vuln_hosts.append(nm[host]['addresses']['ipv4'])
    return vuln_hosts

analysis.py code

from reconn import find_eternal_blue_vuln_hosts
from struct import pack
import sys
# https://github.com/worawit/MS17-010/
sys.path.append('/opt/eternalblue/MS17-010')
from mysmb import MYSMB # From Worawit

# credits go to Worawit, the below uses codes from worawit
def which_host_is_vuln():
    collect_vuln_hosts = []
    for host in find_eternal_blue_vuln_hosts():
        conn = MYSMB(host)
        conn.login('', '')
        tid = conn.tree_connect_andx('\\\\' + host + '\\' + 'IPC$')
        conn.set_default_tid(tid)
        TRANS_PEEK_NMPIPE = 0x23
        recvPkt = conn.send_trans(pack('<H', TRANS_PEEK_NMPIPE), maxParameterCount=0xffff, maxDataCount=0x800)
        status = recvPkt.getNTStatus()
        if status == 0xC0000205:  # STATUS_INSUFF_SERVER_RESOURCES
            collect_vuln_hosts.append('The target {} is not patched'.format(host))
    return collect_vuln_hosts

if __name__ == '__main__':
    hosts = which_host_is_vuln()
    if hosts:
        for host in hosts:
            print(host)
    else:
        print("Though there are hosts that are opened to 445 but they are not vulnerable to eternalblue.")

Results
vuln_hostsTwo Win7 hosts that have their host firewalls disabled.

kali1Results from running the code from kali linux.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s