#!/usr/bin/env python from httplib import HTTPConnection, HTTPSConnection import ssl from numbers import Number from msgpack import packb, unpackb __author__ = 'Nadeem Douba' __copyright__ = 'Copyright 2012, PyMetasploit Project' __credits__ = [] __license__ = 'GPL' __version__ = '0.4' __maintainer__ = 'Nadeem Douba' __email__ = 'ndouba@gmail.com' __status__ = 'Development' __all__ = [ 'MsfRpcError', 'MsfRpcMethod', 'MsfPlugins', 'MsfRpcClient', 'MsfTable', 'NotesTable', 'LootsTable', 'CredsTable', 'AuthInfoTable', 'HostsTable', 'ServicesTable', 'VulnsTable', 'EventsTable', 'ClientsTable', 'Workspace', 'MsfManager', 'WorkspaceManager', 'DbManager', 'AuthManager', 'PluginManager', 'JobManager', 'CoreManager', 'MsfModule', 'ExploitModule', 'PostModule', 'EncoderModule', 'AuxiliaryModule', 'PayloadModule', 'NopModule', 'ModuleManager', 'MsfSession', 'MeterpreterSession', 'ShellSession', 'SessionManager', 'MsfConsole', 'ConsoleManager', 'ReportFilter', 'ReportFilterQuery' ] class MsfRpcError(Exception): pass class MsfRpcMethod(object): AuthLogin = 'auth.login' AuthLogout = 'auth.logout' AuthTokenList = 'auth.token_list' AuthTokenAdd = 'auth.token_add' AuthTokenGenerate = 'auth.token_generate' AuthTokenRemove = 'auth.token_remove' ConsoleCreate = 'console.create' ConsoleList = 'console.list' ConsoleDestroy = 'console.destroy' ConsoleRead = 'console.read' ConsoleWrite = 'console.write' ConsoleTabs = 'console.tabs' ConsoleSessionKill = 'console.session_kill' ConsoleSessionDetach = 'console.session_detach' CoreVersion = 'core.version' CoreStop = 'core.stop' CoreSetG = 'core.setg' CoreUnsetG = 'core.unsetg' CoreSave = 'core.save' CoreReloadModules = 'core.reload_modules' CoreModuleStats = 'core.module_stats' CoreAddModulePath = 'core.add_module_path' CoreThreadList = 'core.thread_list' CoreThreadKill = 'core.thread_kill' DbHosts = 'db.hosts' DbServices = 'db.services' DbVulns = 'db.vulns' DbWorkspaces = 'db.workspaces' DbCurrentWorkspace = 'db.current_workspace' DbGetWorkspace = 'db.get_workspace' DbSetWorkspace = 'db.set_workspace' DbDelWorkspace = 'db.del_workspace' DbAddWorkspace = 'db.add_workspace' DbGetHost = 'db.get_host' DbReportHost = 'db.report_host' DbReportService = 'db.report_service' DbGetService = 'db.get_service' DbGetNote = 'db.get_note' DbGetClient = 'db.get_client' DbReportClient = 'db.report_client' DbReportNote = 'db.report_note' DbNotes = 'db.notes' DbReportAuthInfo = 'db.report_auth_info' DbGetAuthInfo = 'db.get_auth_info' DbGetRef = 'db.get_ref' DbDelVuln = 'db.del_vuln' DbDelNote = 'db.del_note' DbDelService = 'db.del_service' DbDelHost = 'db.del_host' DbReportVuln = 'db.report_vuln' DbEvents = 'db.events' DbReportEvent = 'db.report_event' DbReportLoot = 'db.report_loot' DbLoots = 'db.loots' DbReportCred = 'db.report_cred' DbCreds = 'db.creds' DbImportData = 'db.import_data' DbGetVuln = 'db.get_vuln' DbClients = 'db.clients' DbDelClient = 'db.del_client' DbDriver = 'db.driver' DbConnect = 'db.connect' DbStatus = 'db.status' DbDisconnect = 'db.disconnect' JobList = 'job.list' JobStop = 'job.stop' JobInfo = 'job.info' ModuleExploits = 'module.exploits' ModuleAuxiliary = 'module.auxiliary' ModulePayloads = 'module.payloads' ModuleEncoders = 'module.encoders' ModuleNops = 'module.nops' ModulePost = 'module.post' ModuleInfo = 'module.info' ModuleCompatiblePayloads = 'module.compatible_payloads' ModuleCompatibleSessions = 'module.compatible_sessions' ModuleTargetCompatiblePayloads = 'module.target_compatible_payloads' ModuleOptions = 'module.options' ModuleExecute = 'module.execute' ModuleEncodeFormats = 'module.encode_formats' ModuleEncode = 'module.encode' PluginLoad = 'plugin.load' PluginUnload = 'plugin.unload' PluginLoaded = 'plugin.loaded' SessionList = 'session.list' SessionStop = 'session.stop' SessionShellRead = 'session.shell_read' SessionShellWrite = 'session.shell_write' SessionShellUpgrade = 'session.shell_upgrade' SessionMeterpreterRead = 'session.meterpreter_read' SessionRingRead = 'session.ring_read' SessionRingPut = 'session.ring_put' SessionRingLast = 'session.ring_last' SessionRingClear = 'session.ring_clear' SessionMeterpreterWrite = 'session.meterpreter_write' SessionMeterpreterSessionDetach = 'session.meterpreter_session_detach' SessionMeterpreterSessionKill = 'session.meterpreter_session_kill' SessionMeterpreterTabs = 'session.meterpreter_tabs' SessionMeterpreterRunSingle = 'session.meterpreter_run_single' SessionMeterpreterScript = 'session.meterpreter_script' SessionMeterpreterDirectorySeparator = 'session.meterpreter_directory_separator' SessionCompatibleModules = 'session.compatible_modules' class MsfPlugins(object): IpsFilter = "ips_filter" SocketLogger = "socket_logger" DbTracker = "db_tracker" Sounds = "sounds" AutoAddRoute = "auto_add_route" DbCredCollect = "db_credcollect" class MsfRpcClient(object): _headers = { 'Content-Type' : 'binary/message-pack' } def __init__(self, password, **kwargs): """ Connects and authenticates to a Metasploit RPC daemon. Mandatory Arguments: - password : the password used to authenticate to msfrpcd Optional Keyword Arguments: - username : the username used to authenticate to msfrpcd (default: msf) - uri : the msfrpcd URI (default: /api/) - port : the remote msfrpcd port to connect to (default: 55553) - server : the remote server IP address hosting msfrpcd (default: localhost) - ssl : if true uses SSL else regular HTTP (default: SSL enabled) - verify : if true, verify SSL cert when using SSL (default: False) """ self.uri = kwargs.get('uri', '/api/') self.port = kwargs.get('port', 55553) self.server = kwargs.get('server', '127.0.0.1') self.ssl = kwargs.get('ssl', True) self.verify_ssl = kwargs.get('verify', False) self.sessionid = kwargs.get('token') if self.ssl: if self.verify_ssl: self.client = HTTPSConnection(self.server, self.port) else: self.client = HTTPSConnection(self.server, self.port, context=ssl._create_unverified_context()) else: self.client = HTTPConnection(self.server, self.port) self.login(kwargs.get('username', 'msf'), password) def call(self, method, *args): """ Builds an RPC request and retrieves the result. Mandatory Arguments: - method : the RPC call method name (e.g. db.clients) Optional Arguments: - *args : the RPC method's parameters if necessary Returns : RPC call result """ l = [ method ] l.extend(args) if method == MsfRpcMethod.AuthLogin: self.client.request('POST', self.uri, packb(l), self._headers) r = self.client.getresponse() if r.status == 200: return unpackb(r.read()) raise MsfRpcError('An unknown error has occurred while logging in.') elif self.authenticated: l.insert(1, self.sessionid) self.client.request('POST', self.uri, packb(l), self._headers) r = self.client.getresponse() if r.status == 200: result = unpackb(r.read()) if 'error' in result: raise MsfRpcError(result['error_message']) return result raise MsfRpcError('An unknown error has occurred while performing the RPC call.') raise MsfRpcError('You cannot perform this call because you are not authenticated.') @property def core(self): """ The msf RPC core manager. """ return CoreManager(self) @property def modules(self): """ The msf RPC modules RPC manager. """ return ModuleManager(self) @property def sessions(self): """ The msf RPC sessions (meterpreter & shell) manager. """ return SessionManager(self) @property def jobs(self): """ The msf RPC jobs manager. """ return JobManager(self) @property def consoles(self): """ The msf RPC consoles manager """ return ConsoleManager(self) @property def authenticated(self): """ Whether or not this client is authenticated. """ return self.sessionid is not None @property def plugins(self): """ The msf RPC plugins manager. """ return PluginManager(self) @property def db(self): """ The msf RPC database manager. """ return DbManager(self) @property def auth(self): """ The msf authentication manager. """ return AuthManager(self) def login(self, username, password): """ Authenticates and reauthenticates the user to msfrpcd. """ if self.sessionid is None: r = self.call(MsfRpcMethod.AuthLogin, username, password) try: if r['result'] == 'success': self.sessionid = r['token'] except KeyError: raise MsfRpcError('Login failed.') else: try: r = self.call(MsfRpcMethod.DbStatus) except MsfRpcError: raise MsfRpcError('Login failed.') def logout(self): """ Logs the current user out. Note: do not call directly. """ self.call(MsfRpcMethod.AuthLogout, self.sessionid) class MsfTable(object): def __init__(self, rpc, wname): self.rpc = rpc self.name = wname def dbreport(self, atype, attrs): attrs.update({ 'workspace' : self.name }) return self.rpc.call('db.report_%s' % atype, attrs) def dbdel(self, atype, attrs): attrs.update({ 'workspace' : self.name }) return self.rpc.call('db.del_%s' % atype, attrs) def dbget(self, atype, attrs): attrs.update({ 'workspace' : self.name }) return self.rpc.call('db.get_%s' % atype, attrs)[atype] def records(self, atypes, **kwargs): kwargs.update({'workspace' : self.name}) return self.rpc.call('db.%s' % atypes, kwargs)[atypes] @property def list(self): raise NotImplementedError def report(self, *args, **kwargs): raise NotImplementedError def delete(self, *args, **kwargs): raise NotImplementedError def find(self, **kwargs): raise NotImplementedError update = report class NotesTable(MsfTable): @property def list(self): return super(NotesTable, self).records('notes') def find(self, **kwargs): """ Find notes based on search criteria. Optional Keyword Arguments: - limit : the maximum number of results. - offset : skip n results. - addresses : a list of addresses to search for. - names : comma separated string of service names. - ntype : the note type. - ports : the port associated with the note. - proto : the protocol associated with the note. """ if 'ports' in kwargs: kwargs['port'] = True return super(NotesTable, self).records('notes', **kwargs) def report(self, type, data, **kwargs): """ Report a Note to the database. Notes can be tied to a Workspace, Host, or Service. Mandatory Arguments: - type : The type of note, e.g. 'smb_peer_os'. - data : whatever it is you're making a note of. Optional Keyword Arguments: - host : an IP address or a Host object to associate with this Note. - service : a dict containing 'host', 'port', 'proto' and optionally 'name' keys. - port : along with 'host' and 'proto', a service to associate with this Note. - proto : along with 'host' and 'port', a service to associate with this Note. - update : what to do in case a similar Note exists, see below. The 'update' option can have the following values: - unique : allow only a single Note per host/type pair. - unique_data : like 'unique', but also compare 'data'. - insert : always insert a new Note even if one with identical values exists. If the provided 'host' is an IP address and does not exist in the database, it will be created. If 'host' and 'service' are all omitted, the new Note will be associated with the current 'workspace'. """ kwargs.update({ 'data' : data, 'type' : type }) kwargs.update(kwargs.pop('service', {})) self.dbreport('note', kwargs) def delete(self, **kwargs): """ Delete one or more notes based on a search criteria. Optional Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addresses' is specified - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. - port : the port associated with a Note. - proto : the protocol associated with a Note. - ntype : the note type, e.g. 'smb_peer_os'. """ self.dbdel('note', kwargs) def get(self, **kwargs): """ Get a Note from the database based on the specifications of one or more keyword arguments. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addr' is specified. - address : the address associated with a Note, not required if 'host' or 'addr' is specified. - addr : same as 'address', not required if 'host' or 'address' is specified. Optional Keyword Arguments: - proto : the protocol associated with the Note. - port : the port associated with the Note. - ntype : the type of Note. """ if not any([i in kwargs for i in ('host', 'address', 'addr')]): raise TypeError('Expected a host, address, or addr.') return self.dbget('note', kwargs) update = report class LootsTable(MsfTable): @property def list(self): return super(LootsTable, self).records('loots') def find(self, **kwargs): """ Find loot based on search criteria. Optional Keyword Arguments: - limit : the maximum number of results. - offset : skip n results. """ return super(LootsTable, self).records('loots', **kwargs) def report(self, path, type, **kwargs): """ Report Loot to the database Mandatory Arguments: - path : the filesystem path to the Loot - type : the type of Loot - ltype : the same as 'type', not required if 'type' is specified. Optional Keyword Arguments: - host : an IP address or a Host object to associate with this Note - ctype : the content type of the loot, e.g. 'text/plain' - content_type : same as 'ctype'. - service : a service to associate Loot with. - name : a name to associate with this Loot. - info : additional information about this Loot. - data : the data within the Loot. """ kwargs.update({ 'path' : path, 'type' : type }) self.dbreport('loot', kwargs) update = report class CredsTable(MsfTable): @property def list(self): return super(CredsTable, self).records('creds') def find(self, **kwargs): """ Find creds based on search criteria. Optional Keyword Arguments: - limit : the maximum number of results. - offset : skip n results. """ return super(CredsTable, self).records('creds', **kwargs) def report(self, host, port, **kwargs): """ Store a set of credentials in the database. Mandatory Arguments: - host : an IP address or Host object reference - port : a port number Optional Keyword Arguments: - user : the username. - password : the password, or path to ssh_key. - ptype : the type of password (password(ish), hash, or ssh_key). - proto : a transport name for the port. - sname : service name. - active : by default, a cred is active, unless explicitly false. - proof : data used to prove the account is actually active. Sources: Credentials can be sourced from another credential, or from a vulnerability. For example, if an exploit was used to dump the smb_hashes, and this credential comes from there, the source_id would be the Vuln id (as reported by report_vuln) and the type would be "Vuln". - source_id : The Vuln or Cred id of the source of this cred. - source_type : Either Vuln or Cred. """ kwargs.update({'host' : host, 'port' : port}) kwargs['pass'] = kwargs.get('password') self.dbreport('cred', kwargs) update = report class AuthInfoTable(MsfTable): def report(self, host, port, **kwargs): """ Store a set of credentials in the database. Mandatory Arguments: - host : an IP address or Host object reference - port : a port number Optional Keyword Arguments: - user : the username. - pass : the password, or path to ssh_key. - ptype : the type of password (password(ish), hash, or ssh_key). - proto : a transport name for the port. - sname : service name. - active : by default, a cred is active, unless explicitly false. - proof : data used to prove the account is actually active. Sources: Credentials can be sourced from another credential, or from a vulnerability. For example, if an exploit was used to dump the smb_hashes, and this credential comes from there, the source_id would be the Vuln id (as reported by report_vuln) and the type would be "Vuln". - source_id : The Vuln or Cred id of the source of this cred. - source_type : Either Vuln or Cred. """ kwargs.update({'host' : host, 'port' : port}) self.dbreport('auth_info', kwargs) update = report class HostsTable(MsfTable): @property def list(self): return super(HostsTable, self).records('hosts') def find(self, **kwargs): """ Find hosts based on search criteria. Optional Keyword Arguments: - limit : the maximum number of results. - offset : skip n results. - only_up : find only hosts that are alive. - addresses : find hosts based on a list of addresses. """ return super(HostsTable, self).records('hosts', **kwargs) def report(self, host, **kwargs): """ Store a host in the database. Mandatory Keyword Arguments: - host : an IP address or Host object reference. Optional Keyword Arguments: - state : a host state. - os_name : an operating system. - os_flavor : something like 'XP or 'Gentoo'. - os_sp : something like 'SP2'. - os_lang : something like 'English', 'French', or 'en-US'. - arch : an architecture. - mac : the host's MAC address. - scope : interface identifier for link-local IPv6. - virtual_host : the name of the VM host software, e.g. 'VMWare', 'QEMU', 'Xen', etc. """ kwargs.update({'host' : host}) self.dbreport('host', kwargs) def delete(self, **kwargs): """ Deletes a host and associated data matching this address/comm. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addresses' is specified - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. """ if not any([ i in kwargs for i in ('host', 'address', 'addresses')]): raise TypeError('Expected host, address, or addresses.') self.dbdel('host', kwargs) def get(self, **kwargs): """ Get a host in the database. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addr' is specified. - address : the address associated with a Note, not required if 'host' or 'addr' is specified. - addr : same as 'address', not required if 'host' or 'address' is specified. """ if not any([ i in kwargs for i in ('addr', 'address', 'host')]): raise TypeError('Expected addr, address, or host.') return self.dbget('host', kwargs) update = report class ServicesTable(MsfTable): @property def list(self): return super(ServicesTable, self).records('services') def find(self, **kwargs): """ Find hosts based on search criteria. Optional Keyword Arguments: - limit : the maximum number of results. - offset : skip n results. - only_up : find only hosts that are alive. - addresses : find hosts based on a list of addresses. - proto : the protocol of the service. - ports : a comma separated string of ports. - names : a comma separated string of service names. """ return super(ServicesTable, self).records('services', **kwargs) def report(self, host, port, proto, **kwargs): """ Record a service in the database. Mandatory Arguments: - host : the host where this service is running. - port : the port where this service listens. - proto : the transport layer protocol (e.g. tcp, udp). Optional Keyword Arguments: - name : the application layer protocol (e.g. ssh, mssql, smb) - sname : an alias for the above """ kwargs.update({'host' : host, 'port' : port, 'proto' : proto}) self.dbreport('service', kwargs) def delete(self, **kwargs): """ Deletes a port and associated vulns matching this port. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addresses' is specified - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. or - port : used along with 'proto', specifies a service. - proto : used along with 'port', specifies a service. """ if not any([i in kwargs for i in ('host', 'address', 'addresses')]) and \ not all([i in kwargs for i in ('proto', 'port')]): raise TypeError('Expected host or port/proto pair.') self.dbdel('service', kwargs) def get(self, **kwargs): """ Get a service record from the database. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addresses' is specified - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. or - port : used along with 'proto', specifies a service. - proto : used along with 'port', specifies a service. Optional Keyword Arguments: - up : specifies whether or not the service is alive. - names : a comma separated string of service names. """ if not any([i in kwargs for i in ('host', 'addr', 'address')]) and \ not all([i in kwargs for i in ('proto', 'port')]): raise TypeError('Expected host or port/proto pair.') return self.dbget('service', kwargs) update = report class VulnsTable(MsfTable): @property def list(self): return super(VulnsTable, self).records('vulns') def find(self, **kwargs): """ Find vulns based on search criteria. Optional Keyword Arguments: - limit : the maximum number of results. - offset : skip n results. - addresses : find hosts based on a list of addresses. - proto : the protocol of the service. - ports : a comma separated string of ports. - names : a comma separated string of service names. """ return super(VulnsTable, self).records('vulns', **kwargs) def report(self, host, name, **kwargs): """ Record a Vuln in the database. Mandatory Arguments: - host : the host where this vulnerability resides. - name : the scanner-specific id of the vuln (e.g. NEXPOSE-cifs-acct-password-never-expires). Optional Keyword Arguments: - info : a human readable description of the vuln, free-form text. - refs : an array of Ref objects or string names of references. """ kwargs.update({'host' : host, 'name' : name}) self.dbreport('vuln', kwargs) def delete(self, **kwargs): """ Deletes a vuln and associated data matching this address/comm. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addresses' is specified - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. """ if not any([ i in kwargs for i in ('host', 'address', 'addresses')]): raise TypeError('Expected host, address, or addresses.') self.dbdel('vuln', kwargs) def get(self, **kwargs): """ Get a vuln in the database. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addr' is specified. - address : the address associated with a Note, not required if 'host' or 'addr' is specified. - addr : same as 'address', not required if 'host' or 'address' is specified. """ if not any([ i in kwargs for i in ('addr', 'address', 'host')]): raise TypeError('Expected addr, address, or host.') return self.dbreport('vuln', kwargs) update = report class EventsTable(MsfTable): @property def list(self): return super(EventsTable, self).records('events') def find(self, **kwargs): """ Find events based on search criteria. Optional Keyword Arguments: - limit : the maximum number of results. - offset : skip n results. """ return super(EventsTable, self).records('events', **kwargs) def report(self, **kwargs): """ Record a Vuln in the database. Mandatory Arguments: - username : user that invoked the event. - host : host that invoked the event. """ if not any([i in kwargs for i in ('username', 'host')]): raise TypeError('Expected either username or host') self.dbreport('vuln', kwargs) update = report class ClientsTable(MsfTable): @property def list(self): return super(ClientsTable, self).records('clients') def find(self, **kwargs): """ Find clients based on search criteria. Optional Keyword Arguments: - limit : the maximum number of results. - offset : skip n results. - ua_name : a user-agent string. - ua_ver : the user-agent version. - addresses : a list of IP addresses. """ return super(ClientsTable, self).records('clients', **kwargs) def report(self, ua_string, host, **kwargs): """ Report a client running on a host. Mandatory Arguments: - ua_string : the value of the User-Agent header - host : the host where this client connected from, can be an ip address or a Host object Optional Keyword Arguments - ua_name : one of the user agent name constants - ua_ver : detected version of the given client - campaign : an id or Campaign object Returns a Client. """ kwargs.update({'host' : host, 'ua_string' : ua_string}) self.dbreport('client', kwargs) def delete(self, **kwargs): """ Deletes a client and associated data matching this address/comm. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addresses' is specified - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. """ self.dbdel('client', kwargs) def get(self, **kwargs): """ Get a client in the database. Mandatory Keyword Arguments: - host : the host associated with a Note, not required if 'address' or 'addr' is specified. - ua_string : the value of the User-Agent header """ if not any([ i in kwargs for i in ('host', 'ua_string')]): raise TypeError('Expected host or ua_string.') return self.dbreport('client', kwargs) update = report class Workspace(object): def __init__(self, rpc, name): """ Initializes a workspace object. Mandatory Arguments: - rpc : the msfrpc client object - name : the name of the workspace """ self.rpc = rpc self.name = name @property def current(self): """ The name of the current workspace. """ return self.name @current.setter def current(self, name): self.name = name @property def notes(self): """ Returns the notes table for the current workspace. """ return NotesTable(self.rpc, self.name) @property def hosts(self): """ Returns the hosts table for the current workspace. """ return HostsTable(self.rpc, self.name) @property def services(self): """ Returns the services table for the current workspace. """ return ServicesTable(self.rpc, self.name) @property def vulns(self): """ Returns the vulns table for the current workspace. """ return VulnsTable(self.rpc, self.name) @property def events(self): """ Returns the events table for the current workspace. """ return EventsTable(self.rpc, self.name) @property def loots(self): """ Returns the loots table for the current workspace. """ return LootsTable(self.rpc, self.name) @property def creds(self): """ Returns the creds table for the current workspace. """ return CredsTable(self.rpc, self.name) @property def clients(self): """ Returns the clients table for the current workspace. """ return ClientsTable(self.rpc, self.name) def delete(self): """ Delete the current workspace. """ self.rpc.call(MsfRpcMethod.DbDelWorkspace, {'workspace' : self.name}) def importdata(self, data): self.rpc.call(MsfRpcMethod.DbImportData, {'workspace' : self.name, 'data' : data}) def importfile(self, fname): r = file(fname, mode='rb') self.rpc.call(MsfRpcMethod.DbImportData, {'workspace' : self.name, 'data' : r.read()}) r.close() class MsfManager(object): def __init__(self, rpc): """ Initialize a msf component manager. Mandatory Arguments: - rpc : the msfrpc client object. """ self.rpc = rpc class WorkspaceManager(MsfManager): @property def list(self): """ The list of all workspaces in the current msf database. """ return self.rpc.call(MsfRpcMethod.DbWorkspaces)['workspaces'] def workspace(self, name='default'): """ Returns a Workspace object for the given workspace name. Optional Arguments: - name : the name of the workspace """ w = self.list if name not in w: self.add(name) return Workspace(self.rpc, name) def add(self, name): """ Adds a workspace with the given name. Mandatory Arguments: - name : the name of the workspace """ self.rpc.call(MsfRpcMethod.DbAddWorkspace, name) def get(self, name): """ Get a workspace with the given name. Mandatory Arguments: - name : the name of the workspace """ return self.rpc.call(MsfRpcMethod.DbGetWorkspace, name)['workspace'] def remove(self, name): """ Adds a workspace with the given name. Mandatory Arguments: - name : the name of the workspace """ self.rpc.call(MsfRpcMethod.DbDelWorkspace, name) def set(self, name): """ Sets the current workspace. Mandatory Arguments: - name : the name of the workspace """ self.rpc.call(MsfRpcMethod.DbSetWorkspace, name) @property def current(self): """ The current workspace. """ return self.workspace(self.rpc.call(MsfRpcMethod.DbCurrentWorkspace)['workspace']) class DbManager(MsfManager): def connect(self, username, database='msf', **kwargs): """ Connects to a database and creates the msf schema if necessary. Mandatory Arguments: - username : the username for the database connection Optional Keyword Arguments: - host : the IP or hostname of the database server (default: 'localhost') - driver : the driver to use for the database connection (default: 'postgresql') - password : the password for the database connection - database : the database name (default: 'msf') - port : the port that the server is running on (default: 5432) """ runopts = { 'username': username, 'database' : database } runopts.update(kwargs) return self.rpc.call(MsfRpcMethod.DbConnect, runopts)['result'] == 'success' @property def driver(self): """ The current database driver in use. """ return self.rpc.call(MsfRpcMethod.DbDriver, {})['driver'] @driver.setter def driver(self, d): self.rpc.call(MsfRpcMethod.DbDriver, {'driver' : d}) @property def status(self): """ The status of the database connection. """ return self.rpc.call(MsfRpcMethod.DbStatus) def disconnect(self): """ Disconnect from the database. """ self.rpc.call(MsfRpcMethod.DbDisconnect) @property def workspaces(self): """ A WorkspaceManager object. """ return WorkspaceManager(self.rpc) @property def workspace(self): """ The name of the current workspace. """ return self.rpc.call(MsfRpcMethod.DbCurrentWorkspace)['workspace'] @workspace.setter def workspace(self, w): self.rpc.call(MsfRpcMethod.DbSetWorkspace, w) class AuthManager(MsfManager): def login(self, password, **kwargs): """ Login to the msfrpc daemon. Mandatory Arguments: - password : the password used to login to msfrpc Optional Keyword Arguments: - username : the username used to authenticate to msfrpcd (default: msf) - uri : the msfrpcd URI (default: /api/) - port : the remote msfrpcd port to connect to (default: 55553) - server : the remote server IP address hosting msfrpcd (default: localhost) - ssl : if true uses SSL else regular HTTP (default: SSL enabled) """ return MsfRpcClient(password, **kwargs) def logout(self, sid): """ Logs out a user for a given session ID. Mandatory Arguments: - sid : a session ID that is active. """ return self.rpc.call(MsfRpcMethod.AuthLogout, sid) @property def tokens(self): """ The current list of active session IDs. """ return self.rpc.call(MsfRpcMethod.AuthTokenList)['tokens'] def add(self, token): """ Add a session ID or token. Mandatory Argument: - token : a random string used as a session identifier. """ self.rpc.call(MsfRpcMethod.AuthTokenAdd, token) def remove(self, token): """ Remove a session ID or token. Mandatory Argument: - token : a session ID or token that is active. """ self.rpc.call(MsfRpcMethod.AuthTokenRemove, token) def generate(self): """ Generate a session ID or token. """ return self.rpc.call(MsfRpcMethod.AuthTokenGenerate)['token'] class PluginManager(MsfManager): @property def list(self): """ A list of loaded plugins. """ return self.rpc.call(MsfRpcMethod.PluginLoaded)['plugins'] def load(self, plugin): """ Load a plugin of a given name. Mandatory Arguments: - plugin : a name of a plugin to load. """ self.rpc.call(MsfRpcMethod, MsfRpcMethod.PluginLoad, plugin) def unload(self, plugin): """ Unload a plugin of a given name. Mandatory Arguments: - plugin : a name of a loaded plugin to unload. """ self.rpc.call(MsfRpcMethod, MsfRpcMethod.PluginUnload, plugin) class JobManager(MsfManager): @property def list(self): """ A list of currently running jobs. """ return self.rpc.call(MsfRpcMethod.JobList) def stop(self, jobid): """ Stop a job. Mandatory Argument: - jobid : the ID of the job. """ self.rpc.call(MsfRpcMethod.JobStop, jobid) def info(self, jobid): """ Get job information for a particular job. Mandatory Argument: - jobid : the ID of the job. """ return self.rpc.call(MsfRpcMethod.JobInfo, jobid) class CoreManager(MsfManager): @property def version(self): """ The version of msf core. """ return self.rpc.call(MsfRpcMethod.CoreVersion) def stop(self): """ Stop the core. """ self.rpc.call(MsfRpcMethod.CoreStop) def setg(self, var, val): """ Set a global variable Mandatory Arguments: - var : the variable name - val : the variable value """ self.rpc.call(MsfRpcMethod.CoreSetG, var, val) def unsetg(self, var): """ Unset a global variable Mandatory Arguments: - var : the variable name """ self.rpc.call(MsfRpcMethod.CoreUnsetG, var) def save(self): """ Save the core state. """ self.rpc.call(MsfRpcMethod.CoreSave) def reload(self): """ Reload all modules in the core. """ self.rpc.call(MsfRpcMethod.CoreReloadModules) @property def stats(self): """ Get module statistics from the core. """ return self.rpc.call(MsfRpcMethod.CoreModuleStats) def addmodulepath(self, path): """ Add a search path for additional modules. Mandatory Arguments: - path : the path to search for modules. """ return self.rpc.call(MsfRpcMethod.CoreAddModulePath, path) @property def threads(self): """ The current threads running in the core. """ return self.rpc.call(MsfRpcMethod.CoreThreadList) def kill(self, threadid): """ Kill a thread running in the core. Mandatory Arguments: - threadid : the thread ID. """ self.rpc.call(MsfRpcMethod.CoreThreadKill, threadid) class MsfModule(object): def __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'] @property def options(self): """ All the module options. """ return self._moptions.keys() @property def required(self): """ The required module options. """ return self._roptions @property def evasion(self): """ Module options that are used for evasion. """ return self._eoptions @property def advanced(self): """ Advanced module options. """ return self._aoptions @property def runoptions(self): """ The running (currently set) options for a module. This will raise an error if some of the required options are missing. """ outstanding = set(self.required).difference(self._runopts.keys()) if outstanding: raise TypeError('Module missing required parameter: %s' % ', '.join(outstanding)) return self._runopts def optioninfo(self, option): """ Get information about the module option Mandatory Argument: - option : the option name. """ return self._moptions[option] def __getitem__(self, item): """ Get the current option value. Mandatory Arguments: - item : the option name. """ if item not in self._moptions: raise KeyError("Invalid option '%s'." % item) return self._runopts.get(item) def __setitem__(self, key, value): """ Set the current option value. Mandatory Arguments: - key : the option name. - value : the option value. """ if key not in self.options: raise KeyError("Invalid option '%s'." % key) elif 'enums' in self._moptions[key] and value not in self._moptions[key]['enums']: raise ValueError("Value ('%s') is not one of %s" % (value, repr(self._moptions[key]['enums']))) elif self._moptions[key]['type'] == 'bool' and not isinstance(value, bool): raise TypeError("Value must be a boolean not '%s'" % type(value).__name__) elif self._moptions[key]['type'] in ['integer', 'float'] and not isinstance(value, Number): raise TypeError("Value must be an integer not '%s'" % type(value).__name__) self._runopts[key] = value def __delitem__(self, key): del self._runopts[key] def __contains__(self, item): return item in self._runopts def update(self, d): """ Update a set of options. Mandatory Arguments: - d : a dictionary of options """ for k in d: self[k] = d[k] def execute(self, **kwargs): """ Executes the module with its run options as parameters. Optional Keyword Arguments: - payload : the payload of an exploit module (this is mandatory if the module is an exploit). - **kwargs : can contain any module options. """ runopts = self.runoptions.copy() if isinstance(self, ExploitModule): payload = kwargs.get('payload') runopts['TARGET'] = self.target if 'DisablePayloadHandler' in runopts and runopts['DisablePayloadHandler']: pass elif payload is None: runopts['DisablePayloadHandler'] = True else: if isinstance(payload, PayloadModule): if payload.modulename not in self.payloads: raise ValueError( 'Invalid payload (%s) for given target (%d).' % (payload.modulename, self.target) ) runopts['PAYLOAD'] = payload.modulename for k, v in payload.runoptions.iteritems(): if v is None or (isinstance(v, basestring) and not v): continue if k not in runopts or runopts[k] is None or \ (isinstance(runopts[k], basestring) and not runopts[k]): runopts[k] = v # runopts.update(payload.runoptions) elif isinstance(payload, basestring): if payload not in self.payloads: raise ValueError('Invalid payload (%s) for given target (%d).' % (payload, self.target)) runopts['PAYLOAD'] = payload else: raise TypeError("Expected type str or PayloadModule not '%s'" % type(kwargs['payload']).__name__) return self.rpc.call(MsfRpcMethod.ModuleExecute, self.moduletype, self.modulename, runopts) class ExploitModule(MsfModule): def __init__(self, rpc, exploit): """ Initializes the use of an exploit module. Mandatory Arguments: - rpc : the rpc client used to communicate with msfrpcd - exploit : the name of the exploit module. """ super(ExploitModule, self).__init__(rpc, 'exploit', exploit) self._target = self._info.get('default_target', 0) @property def payloads(self): """ A list of compatible payloads. """ # return self.rpc.call(MsfRpcMethod.ModuleCompatiblePayloads, self.modulename)['payloads'] return self.targetpayloads(self.target) @property def target(self): return self._target @target.setter def target(self, target): if target not in self.targets: raise ValueError('Target must be one of %s' % repr(self.targets.keys())) self._target = target def targetpayloads(self, t=0): """ Returns a list of compatible payloads for a given target ID. Optional Keyword Arguments: - t : the target ID (default: 0, e.g. 'Automatic') """ return self.rpc.call(MsfRpcMethod.ModuleTargetCompatiblePayloads, self.modulename, t)['payloads'] class PostModule(MsfModule): def __init__(self, rpc, post): """ Initializes the use of a post exploitation module. Mandatory Arguments: - rpc : the rpc client used to communicate with msfrpcd - post : the name of the post exploitation module. """ super(PostModule, self).__init__(rpc, 'post', post) @property def sessions(self): """ A list of compatible shell/meterpreter sessions. """ return self.rpc.compatiblesessions(self.modulename) class EncoderModule(MsfModule): def __init__(self, rpc, encoder): """ Initializes the use of an encoder module. Mandatory Arguments: - rpc : the rpc client used to communicate with msfrpcd - encoder : the name of the encoder module. """ super(EncoderModule, self).__init__(rpc, 'encoder', encoder) class AuxiliaryModule(MsfModule): def __init__(self, rpc, auxiliary): """ Initializes the use of an auxiliary module. Mandatory Arguments: - rpc : the rpc client used to communicate with msfrpcd - auxiliary : the name of the auxiliary module. """ super(AuxiliaryModule, self).__init__(rpc, 'auxiliary', auxiliary) class PayloadModule(MsfModule): def __init__(self, rpc, payload): """ Initializes the use of a payload module. Mandatory Arguments: - rpc : the rpc client used to communicate with msfrpcd - payload : the name of the payload module. """ super(PayloadModule, self).__init__(rpc, 'payload', payload) class NopModule(MsfModule): def __init__(self, rpc, nop): """ Initializes the use of a nop module. Mandatory Arguments: - rpc : the rpc client used to communicate with msfrpcd - nop : the name of the nop module. """ super(NopModule, self).__init__(rpc, 'nop', nop) class ModuleManager(MsfManager): def execute(self, modtype, modname, **kwargs): """ Execute the module. Mandatory Arguments: - modtype : the module type (e.g. 'exploit') - modname : the module name (e.g. 'exploits/windows/http/icecast_header') Optional Keyword Arguments: - **kwargs : the module's run options """ return self.rpc.call(MsfRpcMethod.ModuleExecute, modtype, modname, kwargs) @property def exploits(self): """ A list of exploit modules. """ return self.rpc.call(MsfRpcMethod.ModuleExploits)['modules'] @property def payloads(self): """ A list of payload modules. """ return self.rpc.call(MsfRpcMethod.ModulePayloads)['modules'] @property def auxiliary(self): """ A list of auxiliary modules. """ return self.rpc.call(MsfRpcMethod.ModuleAuxiliary)['modules'] @property def post(self): """ A list of post modules. """ return self.rpc.call(MsfRpcMethod.ModulePost)['modules'] @property def encodeformats(self): """ A list of encoding formats. """ return self.rpc.call(MsfRpcMethod.ModuleEncodeFormats) @property def encoders(self): """ A list of encoder modules. """ return self.rpc.call(MsfRpcMethod.ModuleEncoders)['modules'] @property def nops(self): """ A list of nop modules. """ return self.rpc.call(MsfRpcMethod.ModuleNops)['modules'] def use(self, mtype, mname): """ Returns a module object. Mandatory Arguments: - mname : the module name (e.g. 'exploits/windows/http/icecast_header') """ if mtype == 'exploit': return ExploitModule(self.rpc, mname) elif mtype == 'post': return PostModule(self.rpc, mname) elif mtype == 'encoder': return EncoderModule(self.rpc, mname) elif mtype == 'auxiliary': return AuxiliaryModule(self.rpc, mname) elif mtype == 'nop': return NopModule(self.rpc, mname) elif mtype == 'payload': return PayloadModule(self.rpc, mname) raise MsfRpcError('Unknown module type %s not: exploit, post, encoder, auxiliary, nop, or payload' % mname) class MsfSession(object): def __init__(self, id, rpc, sd): """ Initialize a meterpreter or shell session. Mandatory Arguments: - id : the session identifier. - rpc : the msfrpc client object. - sd : the session description """ self.id = id self.rpc = rpc self.__dict__.update(sd) def stop(self): """ Stop a meterpreter or shell session. """ return self.rpc.call(MsfRpcMethod.SessionStop, self.id) @property def modules(self): """ A list of compatible session modules. """ return self.rpc.call(MsfRpcMethod.SessionCompatibleModules, self.id)['modules'] @property def ring(self): return SessionRing(self.rpc, self.id) class SessionRing(object): def __init__(self, rpc, sessionid): self.rpc = rpc self.id = sessionid def read(self, seq=None): """ Reads the session ring. Optional Keyword Arguments: - seq : the sequence ID of the ring (default: 0) """ if seq is not None: return self.rpc.call(MsfRpcMethod.SessionRingRead, self.id, seq) return self.rpc.call(MsfRpcMethod.SessionRingRead, self.id) def put(self, line): """ Add a command to the session history. Mandatory Arguments: - line : arbitrary data. """ self.rpc.call(MsfRpcMethod.SessionRingPut, self.id, line) @property def last(self): """ Returns the last sequence ID in the session ring. """ return int(self.rpc.call(MsfRpcMethod.SessionRingLast, self.id)['seq']) def clear(self): """ Clear the session ring. """ return self.rpc.call(MsfRpcMethod.SessionRingClear, self.id) class MeterpreterSession(MsfSession): def read(self): """ Read data from the meterpreter session. """ return self.rpc.call(MsfRpcMethod.SessionMeterpreterRead, self.id)['data'] def write(self, data): """ Write data to the meterpreter session. Mandatory Arguments: - data : arbitrary data or commands """ self.rpc.call(MsfRpcMethod.SessionMeterpreterWrite, self.id, data) def runsingle(self, data): """ Run a single meterpreter command Mandatory Arguments: - data : arbitrary data or command """ self.rpc.call(MsfRpcMethod.SessionMeterpreterRunSingle, self.id, data) return self.read() def runscript(self, path): """ Run a meterpreter script Mandatory Arguments: - path : path to a meterpreter script on the msfrpcd host. """ self.rpc.call(MsfRpcMethod.SessionMeterpreterScript, self.id, path) return self.read() @property def sep(self): """ The operating system path separator. """ return self.rpc.call(MsfRpcMethod.SessionMeterpreterDirectorySeparator, self.id)['separator'] def detach(self): """ Detach the meterpreter session. """ return self.rpc.call(MsfRpcMethod.SessionMeterpreterSessionDetach, self.id) def kill(self): """ Kill the meterpreter session. """ self.rpc.call(MsfRpcMethod.SessionMeterpreterSessionKill, self.id) def tabs(self, line): """ Return a list of commands for a partial command line (tab completion). Mandatory Arguments: - line : a partial command line for completion. """ return self.rpc.call(MsfRpcMethod.SessionMeterpreterTabs, self.id, line)['tabs'] class ShellSession(MsfSession): def read(self): """ Read data from the shell session. """ return self.rpc.call(MsfRpcMethod.SessionShellRead, self.id)['data'] def write(self, data): """ Write data to the shell session. Mandatory Arguments: - data : arbitrary data or commands """ self.rpc.call(MsfRpcMethod.SessionShellWrite, self.id, data) def upgrade(self, lhost, lport): """ Upgrade the current shell session. """ self.rpc.call(MsfRpcMethod.SessionShellUpgrade, self.id, lhost, lport) return self.read() class SessionManager(MsfManager): @property def list(self): """ A list of active sessions. """ return self.rpc.call(MsfRpcMethod.SessionList) def session(self, id): """ Returns a session object for meterpreter or shell sessions. Mandatory Arguments: - id : the session identifier. """ s = self.list if id not in s: for k in s: if s[k]['uuid'] == id: if s[id]['type'] == 'meterpreter': return MeterpreterSession(id, self.rpc, s) elif s[id]['type'] == 'shell': return ShellSession(id, self.rpc, s) raise KeyError('Session ID (%s) does not exist' % id) if s[id]['type'] == 'meterpreter': return MeterpreterSession(id, self.rpc, s) elif s[id]['type'] == 'shell': return ShellSession(id, self.rpc, s) raise NotImplementedError('Could not determine session type: %s' % s[id]['type']) class MsfConsole(object): def __init__(self, rpc, cid=None): """ Initializes an msf console. Mandatory Arguments: - rpc : the msfrpc client object. Optional Keyword Arguments: - cid : the console identifier if it exists already otherwise a new one will be created. """ self.rpc = rpc if cid is None: r = self.rpc.call(MsfRpcMethod.ConsoleCreate) if 'id' in r: self.cid = r['id'] else: raise MsfRpcError('Unable to create a new console.') else: self.cid = cid def read(self): """ Read data from the console. """ return self.rpc.call(MsfRpcMethod.ConsoleRead, self.cid) def write(self, command): """ Write data to the console. """ if not command.endswith('\n'): command += '\n' self.rpc.call(MsfRpcMethod.ConsoleWrite, self.cid, command) def sessionkill(self): """ Kill all active meterpreter or shell sessions. """ self.rpc.call(MsfRpcMethod.ConsoleSessionKill, self.cid) def sessiondetach(self): """ Detach the current meterpreter or shell session. """ self.rpc.call(MsfRpcMethod.ConsoleSessionDetach, self.cid) def tabs(self, line): """ Tab completion for console commands. Mandatory Arguments: - line : a partial command to be completed. """ return self.rpc.call(MsfRpcMethod.ConsoleTabs, self.cid, line)['tabs'] def destroy(self): """ Destroy the console. """ self.rpc.call(MsfRpcMethod.ConsoleDestroy, self.cid) class ConsoleManager(MsfManager): @property def list(self): """ A list of active consoles. """ return self.rpc.call(MsfRpcMethod.ConsoleList) def console(self, cid=None): """ Connect to an active console otherwise create a new console. Optional Keyword Arguments: - cid : the console identifier. """ s = self.list if cid is None: return MsfConsole(self.rpc) if cid not in s: raise KeyError('Console ID (%s) does not exist' % cid) else: return MsfConsole(self.rpc, cid=cid) def destroy(self, cid): """ Destory an active console. Mandatory Arguments: - cid : the console identifier. """ self.rpc.call(MsfRpcMethod.ConsoleDestroy, cid)