Write-Your-Own-Plugins
It is a fact that writing extension for Burp
is a pain, and other tools that do,
only provide plugins in the language they're written in.
So the basic core idea behind proxenet
, is to allow pentesters to easily
interact with HTTP requests/responses in their favorite high-level language.
HOWTO write a plugin
Proxenet
was purposely made to be extremely extensible, and as such, it is easy to
write plugins for it in your favorite language. You just have to implement two functions
called (by default) proxenet_response_hook
and proxenet_request_hook
.
These two functions have the following properties:
- take 3 arguments
request_id
(or resp.response_id
) - type Integer - the request/response identifier. This parameter is unique for each request and allows linking a request to its response(s) from the server (as a response can be delivered in different chunks).request
(or resp.response
) - type String - the request/response itself. The format (depending of the interpreter), is either a regular string or an array of bytes.uri
- type String - the full URI- return a String (or array of bytes)
To use, simply drop the new plugin into the default plugins directory (as defined by
CFG_DEFAULT_PLUGINS_PATH
(by default ./proxenet-plugins
), or by specifying the
command line option -x
.
.
You can then load the plugin via the client.
>>> plugin load 1MyNewPlugin.rb
Plugin '1MyNewPlugin.rb' successfully added!
Step-by-Step : A basic plugin example
For the sake of simplicity, here's a very simple example of a proxenet plugin written in Python. Note that the exact same methodology applies for plugins written in Ruby, Lua, etc.
The demo plugin is purposely dumb. It's simply to make you familiar with the way
proxenet
proceeds with plugins. It will append an HTTP header in every request
and response.
Pre-requisite
Let's assume that an instance is already up and running.
A very basic Python template for creating plugins would be like this:
def proxenet_request_hook(request_id, request, uri):
return request
def proxenet_response_hook(response_id, response, uri):
return response
if __name__ == "__main__":
pass
The "hard" work
Create a new Python script in the plugins directory, and copy/paste this template.
Appending an HTTP header simply means that we want to substitute the double CRLF - marking the end of the HTTP headers blob, with our header, followed by this double CRLF.
The implementation comes then right out of the box:
def proxenet_request_hook(request_id, request, uri):
crlf = "\r\n"
hdr = "X-Proxenet-Rules: Python-Style"
return request.replace(crlf * 2, crlf + hdr + crlf * 2)
def proxenet_response_hook(response_id, response, uri):
return response
if __name__ == "__main__":
pass
Plug it in
Once the plugin is ready, simply use the client to load it. Valid loaded scripts will immediately become active, and can also be disabled through the client.
>>> plugin load 1AddHeader.py
Plugin '1AddHeader.py' successfully added!
Now use any site and check the headers sent. Yup, it was that simple.
Too easy for you ? Want a more real-life use of proxenet
. No worries, just
keep reading
A more advanced plugin example
Now let's do some useful stuff.
The following plugin will log every HTTP request/response in a
SQLite database, given us the possibility to
(transparently) save the whole HTTP sessions during a pentest. For the record,
this 'feature' is not available in the free version of BurpSuite. So by hooking
proxenet
behind your free Burp and using this plugin, you will never lose any
session.
Re-use working stuff
We will use the exact same template from the previous example, along with 2 Python
modules sqlite3
and time
. Both of which are built-in.
The "hard" work
We want our plugin to have a low priority, meaning that other plugins (which
potentially modify requests/responses) will be executed first.
Create a file in the plugins directory, for example 9LogReqRes.py
, and
copy/paste the template example provided above.
Unfortunately, SQLite (not unlike many tools and libraries) does not support
multi-threading. Luckily, proxenet
was designed to multi-thread only the
hooking functions and nothing else.
Subsequently, we only need to define the SQLite database object as a Python
global
in the main thread. Like this:
import time, sqlite3
class SqliteDb:
def __init__(self, dbname="/tmp/proxenet.db"):
self.data_file = dbname
self.execute("CREATE TABLE requests (id INTEGER, request BLOB, uri TEXT, timestamp INTEGER)")
self.execute("CREATE TABLE responses (id INTEGER, response BLOB, uri TEXT, timestamp INTEGER)")
return
def connect(self):
self.conn = sqlite3.connect(self.data_file)
self.conn.text_factory = str
return self.conn.cursor()
def disconnect(self):
self.conn.close()
return
def execute(self, query, values=None):
cursor = self.connect()
cursor.execute(query, values)
self.conn.commit()
return cursor
db = SqliteDb()
By defining our database globally, the Python main thread acquires the lock for it, but it is still reachable by other threads.
Now we simply have to fill the functions:
def proxenet_request_hook(request_id, request, uri):
global db
db.execute("INSERT INTO requests VALUES (?, ?, ?, ?)", (request_id, request, uri, int( time.time() )))
return request
def proxenet_response_hook(response_id, response, uri):
global db
db.execute("INSERT INTO responses VALUES (?, ?, ?, ?)", (response_id, response, uri, int( time.time() )))
return response
That's it! Now simply load it in proxenet
and never lose any request/response
again! The full version of this plugin is available
here.
Want to add your 50c ?
The GitHub repository proxenet-plugins contains a few plugins already (more will come). But if you want to share your cool plugin, feel free to send a "Pull Request".
Thanks for using proxenet
.