Writing Sarlacc Plugins
Since releasing Sarlacc it has received a lot more attention that I expected. It is still in it’s infancy and even though I work on it as much as I can I haven’t written any real documentation for it (besides python docstrings). Because of this I figured I’d write a little tutorial on writing a Sarlacc plugin.
Overview
Sarlacc will load any python files found in the smtpd/src/plugins
directory as a python module that extends the SarlaccPlugin
class, defined here: https://github.com/scrapbird/sarlacc/blob/master/smtpd/src/plugins/plugin.py.
You can also write your plugins as a python module in it’s own directory, as we will do today. This means that all we need to do is drop our plugin into the correct directory and restart Sarlacc and it will be running.
We will be writing a plugin to upload any previously unseen email attachments to Malshare.
Getting Started
Okay so first thing is first, let’s create a directory for our plugin and create the __init__.py
file:
cd smtpd/src/plugins
mkdir sarlacc-malshare
cd sarlacc-malshare
touch __init__.py
Next lets extend the SarlaccPlugin
class inside the __init__.py
file:
from plugins.plugin import SarlaccPlugin
class Plugin(SarlaccPlugin):
def run(self):
self.logger.info("Loading malshare plugin")
async def new_attachment(self, _id, sha256, content, filename, tags):
self.logger.info("Uploading new sample to malshare. SHA256: %s", sha256)
We are overloading the new_attachment
method from the base SarlaccPlugin
class here so that our plugin will get notified whenever a previously unseen attachment is detected. This method will pass in any data you will need about the attachment, including the raw file data, filename etc. To see which other methods are available to notify our plugins of events see the plugin.py doc strings.
The Malshare API requires an API key, so we will create a config file and load the key from there. Create a file in your plugin directory named malshare.cfg
with the following contents (replacing API_KEY with your API key):
[malshare]
key = API_KEY
Now we need to read this config in our plugin. Let’s add some code to do this, modify your plugin code to look like the following:
from plugins.plugin import SarlaccPlugin
from configparser import ConfigParser
import os
class Plugin(SarlaccPlugin):
def run(self):
self.logger.info("Loading malshare plugin")
# Read config
self.config = ConfigParser()
self.config.readfp(open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "malshare.cfg")))
self.config.read(["smtpd.cfg",])
async def new_attachment(self, _id, sha256, content, filename, tags):
self.logger.info("Uploading new sample to malshare. SHA256: %s", sha256)
The last thing left to do is to actually upload the sample:
from plugins.plugin import SarlaccPlugin
from configparser import ConfigParser
import os
import requests
class Plugin(SarlaccPlugin):
def run(self):
self.logger.info("Loading malshare plugin")
# Read config
self.config = ConfigParser()
self.config.readfp(open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "malshare.cfg")))
self.config.read(["smtpd.cfg",])
async def new_attachment(self, _id, sha256, content, filename, tags):
self.logger.info("Uploading new sample to malshare. SHA256: %s", sha256)
requests.post(url="https://malshare.com/api.php?api_key={}&action=upload".format(self.config["malshare"]["key"]),
files={filename: content})
And there you have it, our plugin will upload any new attachments to Malshare.
Keep in mind that as python is single threaded this will pause the execution of Sarlacc while the upload is happening, so for large files this is not a good idea to do while under heavy load as for each new attachment execution will pause for a few seconds. To combat this we could use an HTTP client that makes proper use of asyncio
, this is mostly to be used as an example.
Full code for this plugin is available on github: https://github.com/scrapbird/sarlacc-malshare.