Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomas Gonzalez Vivo committed Jul 29, 2017
0 parents commit 1e8932c
Show file tree
Hide file tree
Showing 9 changed files with 599 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM kalilinux/kali-linux-docker

ENV DEBIAN_FRONTEND noninteractive

RUN echo "deb http://http.kali.org/kali kali-rolling main contrib non-free" > /etc/apt/sources.list && \
echo "deb-src http://http.kali.org/kali kali-rolling main contrib non-free" >> /etc/apt/sources.list

RUN apt-get -y update && apt-get -y dist-upgrade && apt-get clean

RUN apt-get install -y curl apt-utils nmap python ruby ruby-dev postgresql
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall && \
chmod 755 msfinstall && \
./msfinstall

RUN service postgresql start && msfconsole

EXPOSE 5432
EXPOSE 55553

ENV username msfUser
ENV password 123456

CMD msfrpcd -U msfUser -P $password -n -f -a 0.0.0.0
144 changes: 144 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
![MsfRpc logo](./assets/logo.png)
> The Metasploit RPC api for Node.js
## Getting started

### Installing

This will install the cli in your system.
```shell
npm install -g msfrpc
```

This will add the msfrpc module to your Node.js project.
```shell
npm install --save msfrpc
```

### Using the cli

```shell
msfrpc-cli <URI>
```

URI example: ```https://msfUser:123456@localhost:55553```

### Using the api

All msfrpc methods are grouped in the following "method groups":
* Auth
* Base
* Console
* Core
* Db
* Job
* Module
* Plugin
* Session

To call a msfrpc, use the following pattern:
```
msfrpc.<method group>.<method name (camel case)>([arguments]);
```

All methods returns Promises.

Please note that we don't pass tokens to the methods.
Tokens are added automatically by MsfRpc.

Here is an example:
```js
const MsfRpc = require('msfrpc');

const msfrpcUri = 'https://msfUser:123456@localhost:55553';
const msfrpc = new MsfRpc(msfrpcUri);

console.log(`Connecting to ${msfrpcUri}`);
msfrpc.connect().then(() => {
return msfrpc.core.version().then((res) => {
console.log(`Metasploit Framework version ${res.version}`);
}).then(() => {
const keyword = 'windows';
console.log(`Search modules containing "${keyword}". This may take a few seconds...`);
return msfrpc.module.search(keyword).then((modules) => {
console.log(`Found the ${modules.length} modules for "${keyword}":`);
modules.forEach((module) => {
console.log('=========', module.fullname);
console.log(' Name', module.name);
console.log(' Type', module.type);
console.log(' Rank', module.rank);
if(module.disclosuredate) {
console.log(' Date', module.disclosuredate);
}
});
});
});
});
```

In the example, we:
* Connect to the msfrpc server
* Obtain and print the Metasploit Version
* Find and print all modules containing a given keyword

### Api Reference

For a list and documentation of all available methods, visit the following links.
* http://www.nothink.org/metasploit/documentation/RemoteAPI_4.1.pdf
* https://help.rapid7.com/metasploit/Content/api/rpc/overview.html
* https://help.rapid7.com/metasploit/Content/api/rpc/standard-reference.html
* https://rapid7.github.io/metasploit-framework/api/Msf/RPC.html

Here are some examples of method calls:

Get version information:
```js
msfrpc.core.version()
```

Get module stats:
```js
msfrpc.core.moduleStats()
```

Search for a module:
```js
msfrpc.module.search('keyword')
```

List payloads:
```js
msfrpc.module.payloads()
```

## Developing

### Optional Prerequisites
This project includes a ```Dockerfile``` (and ```docker-compose.yml```) so you dont have to build a testing environment yourself.
To use docker, you need it installed in your system.
For installation, follow the steps [here](https://docs.docker.com/compose/install/).

### Setting up Dev

Clone the repository and install dependencies.
```shell
git clone https://github.com/tomasgvivo/node-msfrpc.git
cd node-msfrpc/
npm install
```

## Licensing

Copyright 2017 Tomas Gonzalez Vivo

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Binary file added assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: "3.3"

services:
msfrpc:
build: .
network_mode: host
203 changes: 203 additions & 0 deletions lib/msfrpc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/**
* Allow self-signed ssl sertificates.
*/
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

/**
* Load dependencies.
*/
const URL = require('url');
const decamelize = require('decamelize');
const request = require('request');
const MsgPack5 = require('msgpack5');
const Promise = require('bluebird');

/**
* Instance MsgPack5.
*/
const msgpack = MsgPack5();

/**
* MsfRpc Class.
* @constructor constructor(uri, options)
* @constructor constructor(options)
*/
module.exports = class MsfRpc {

/**
* MsfRpc constructor.
* @param {string|object} uri - Url string with auth credentials, hostname and port.
* @example 'https://msfUser:myPassword@msfrpchost:55553'
* @param {object} options - MsfRpc options.
* @property {string} options.user - MsfRpc username.
* @property {string} options.pass - MsfRpc password.
* @property {string} options.host - MsfRpc hostname.
* @property {number} options.port - MsfRpc port.
* @property {boolean} options.ssl - MsfRpc ssl - true: use https protocol.
* @property {string} options.token - MsfRpc token.
* @return {MsfRpc}
*/
constructor(uri, options) {
options = options || {};

// If uri is an object, it must be options.
if(typeof uri === 'object') {
options = uri;
} else {
// Parse uri.
const url = URL.parse(uri);
const { hostname, port, protocol } = url;
const [ user, pass ] = (url.auth || '').split(':');

// Put uri parameters in options.
options.user = user;
options.pass = pass;
options.host = hostname;
options.port = port;
options.ssl = protocol === 'https:';
}

// Save options to this.
this.user = options.user;
this.pass = options.pass;
this.host = options.host;
this.port = options.port;
this.ssl = options.ssl;
this.token = options.token;

// Load api methodGroups proxys.
const methodGroups = [ "auth", "base", "console", "core", "db", "job", "module", "plugin", "session" ];
methodGroups.forEach((methodGroup) => {
this[methodGroup] = new Proxy({}, {
get: (target, methodName) => {
methodName = decamelize(methodName, '_');
return (...args) => {
return this._exec(methodGroup + '.' + methodName, ...args);
}
}
});
});
}

/**
* Connects to the remote MsfRpc server.
* This is not required if a token was specified in the constructor options.
* @return {Promise}
*/
connect() {
return this._call("auth.login", this.user, this.pass).then((res) => {
// Save token to this.
this.token = res.token;
});
}

/**
* Call an MsfRpc method.
* Take a look at the following links for more information about abailable methods:
* http://www.nothink.org/metasploit/documentation/RemoteAPI_4.1.pdf
* https://help.rapid7.com/metasploit/Content/api/rpc/overview.html
* https://help.rapid7.com/metasploit/Content/api/rpc/standard-reference.html
* https://rapid7.github.io/metasploit-framework/api/Msf/RPC.html
* @param {string} method - MsfRpc method.
* @param {...[any]} args - MsfRpc method's arguments.
* @return {Promise<object>} - MsfRpc call response.
*/
_call(method, ...args) {
// Encode method and arguments as msgpack buffer.
const buffer = msgpack.encode([method, ...args]);

return new Promise((resolve) => {
// Send post request to the MsfRpc server.
request({
method: 'POST',
uri: `${this.ssl ? 'https' : 'http'}://${this.host}:${this.port}/api/1.0`,
body: buffer,
headers: {
'content-type': 'binary/message-pack'
},
// This translates to encode body as binary data.
encoding: null
}, (error, response, body) => {
if(error) {
throw error;
} else {
// Parse body.
body = this._parseBody(body)

// If server responded with an exception, build and throw error.
if(body.error) {
const errorLines = [];
errorLines.push(`${body.error_message}`);
errorLines.push(` Backtrace:`);
body.error_backtrace.forEach((trace) => {
errorLines.push(` ${trace}`);
});
errorLines.push('');
throw new Error(errorLines.join('\n'));
} else {
// Else, resolve with parsed body.
resolve(body);
}
}
});
});
}

/**
* Call a MsfRpc method and include the access token.
* @param {string} method - MsfRpc method.
* @param {...[any]} args - MsfRpc method's arguments.
* @return {Promise<object>} - MsfRpc call response.
*/
_exec(method, ...args) {
return this._call(method, this.token, ...args);
}

/**
* Decodes and debufferizes response body.
* @param {object} body - Response body.
* @return {object}
*/
_parseBody(body) {
const decoded = msgpack.decode(body);
const debufferized = this._debufferize(decoded);
return debufferized;
}

/**
* Debufferizes an object.
* @description
* When msgpack5 decodes a buffer, it returns an object.
* All object values are buffers.
* So this method recursively converts all buffes to strings.
* @param {any} value - Value to debufferize.
* @return {any}
*/
_debufferize(value) {
if(value instanceof Buffer) {
return value.toString();
} else if(value instanceof Array) {
const arr = [];
value.forEach((val) => {
arr.push(this._debufferize(val));
});
return arr;
} else if(value instanceof Array) {
const arr = [];
value.forEach((val) => {
arr.push(this._debufferize(val));
});
return arr;
} else if(value instanceof Object) {
const obj = {};
Object.keys(value).forEach((key) => {
if(value.hasOwnProperty(key)) {
obj[key] = this._debufferize(value[key]);
}
});
return obj;
} else {
return value;
}
}
}
Loading

0 comments on commit 1e8932c

Please sign in to comment.