Protecting our customers Our security research team has built and deployed a rule to protect…
In my last post, Getting Started With HoneyPy — Part 2, I covered honeypot services and HoneyPy’s service profiles. In this post, I’ll provide more detail on HoneyPy plugins and loggers.
HoneyPy Plugins
Plugins are the engine behind the services you run with HoneyPy. They are the code that emulates a service protocol and determines the level of interaction allowed. You’ll find the code to current plugins here, and a description of them here. The idea behind plugins is to make HoneyPy as extensible as possible. You can create plugins to emulate new services or modify the existing plugins to enhance their functionality.
Creating Plugins
If you are handy with Python and want to create a new plugin the first thing you should do is review the existing plugin files. You’ll notice they have a consistent structure. The most basic plugin is the Echo plugin, and it can be used as a template to create a new plugin. Open the Echo.py file and notice the structure and the comment blocks. The comment blocks are guidelines to show where you add or modify your code. The existing code outside of those comment blocks should not be modified unless you really know what you are doing. This existing code handles the network connections, logging, and exposes the self.tx(data) class methods for you transfer data back to the connected client.
As for the comment blocks, here is a description of each block:
- START CUSTOM IMPORTS, if you are leveraging any other libraries or modules, add your import statements here.
- START CUSTOM VARIABLES, if you require any class variables, add and initialize them here.
- START CUSTOM CODE within the connectionMade method, add any custom logic to be performed immediately after the connection is established here.
- START CUSTOM CODE within the dataReceived method, this will be where a bulk of your custom logic goes.
- START CUSTOM FUNCTIONS, this is where you can add any functions needed to support your custom logic.
To try out creating a new plugin follow the steps below:
1. Copy the Echo plugin directory and its contents to a new directory called MyEcho.
2. In your MyEcho directory edit the __init__.py file to be:
from Echo import pluginFactory
3. Rename Echo.py to MyEcho.py.
4. In MyEcho.py, edit line 13 to use MyEcho as the class name. Example:
class MyEcho(protocol.Protocol): ### Set custom protocol class name
5. In MyEcho.py, edit line 56 to use MyEcho as the protocol. Example:
protocol = MyEcho ### Set protocol to custom protocol class name
6. Update your services.cfg file to add a service entry for your new MyEcho plugin. You can edit the existing Echo service entry or just add a new entry. Example:
[MyEcho] plugin = MyEcho low_port = tcp:7 port = tcp:10007 description = Echo back data received via tcp. enabled = Yes
Now that you have a new plugin you can start building on it to implement any type of service or interaction you have in mind. As another very simple exercise, make the following changes to your MyEcho plugin.
1. Echo a message every time a connection is made. Add the following code to the connectionMade method within the START CUSTOM CODE block, it should be at or close to line 27:
self.tx(“Congratulations, you are connected to MyEcho!n”)
2. Modify the line of code (should now be line 35), again within the START CUSTOM CODE block, to be as follows:
self.tx(“MyEcho: %s” % str(data))
Below is an excerpt of what the code looks like with the changes.

With the changes above applied, you can now run HoneyPy and make connections the service port running your MyEcho plugin. Or see the example interaction in the vide clip below.
Again, this is a very simple example, but it’s a starting point to build off of. For more advanced examples, take a look at the code of the other plugins. Also, as you review the other plugins, take note that the UDP based plugins are slightly different. Since UDP behaves differently, the only class method available by default is datagramReceived.
HoneyPy Loggers
Rather than having to parse a log file to monitor honeypot activity, HoneyPy implements special loggers to do something useful with the data. The current set of loggers are Twitter, HoneyDB, Slack, Logstash, and Elasticsearch. To configure HoneyPy to implement these loggers see the corresponding configuration sections in the etc/honeypy.cfg file.
Twitter—Yes, your honeypot can have its own Twitter account :-). This is most appropriate for either research or hobbyist type honeypots. Using Twitter as a sort of public service announcement system, you can share threat information publicly. Below is an example of the configuration to enable the Twitter logger.
[twitter] enabled = Yes consumerkey = <consumer key> consumersecret = <consumer secret> oauthtoken = <oauth token> oauthsecret = <oauth secret>
HoneyDB—This logger is most appropriate for research and hobbyist type honeypots. I will be posting more on HoneyDB in the future, but at a high level, it collects data from HoneyPy honeypot sensors and publishes it in a consumable format on the site. Below is an example of the configuration to enable the HoneyDB logger.
[honeydb] enabled = Yes url = https://riskdiscovery.com/honeydb/api/logger api_id = <api id> api_key = <api key>
Example HoneyPy data in HoneyDB:

Slack—If you use Slack in your organization this is a great way to alert the security operations team of events. Below is an example of the configuration to enable the Slack logger.
[slack] enabled = Yes webhook_url = <url>
Example HoneyPy data in Slack:

Logstash—Send HoneyPy logs directly to logstash for processing and sending to your central analytics system(s). Below is an example of the configuration to enable the Logstash logger.
[logstash] enabled = Yes host = <host> port = <port>
Elasticsearch—Use this logger if you just want to send all HoneyPy data directly into Elasticsearch. Below is an example of the configuration to enable the Elasticsearch logger.
[elasticsearch] enabled = Yes es_url = <url>
These five loggers provide several great options for consuming HoneyPy data. However, they may not be the perfect fit for your scenario. No problem, HoneyPy loggers are easy to create so you can extend HoneyPy to output honeypot data in any format or forward the data to any other system.
Creating Loggers
So you want to create a new HoneyPy logger. No problem, the steps to create one are fairly easy. However, you do need to consider if you are going to fork the HoneyPy project to maintain the code associated with your logger implementation, or if you’d like to contribute your new logger back to the project. Contributions are very welcome so please feel free to submit a pull request.
At a high level the steps to create a new logger are:
- Create a new logger file in the HoneyPy lib directory. The naming schema for logger files is honeypy_<your logger name>.py
- Determine what configuration parameters will be required for your logger, and add a section for your logger to the etc/honeypy.cfg file.
- Edit the lib/honeypy_log_triage.py file to integrate your logger.
- Edit the main Honey.py file to complete the integration of your logger.
Now let’s jump into the details of creating a new logger.
Update 11/26/2016: loggers are now located in the logger directory and organized by folder.
1. Create a New Logger File
All the loggers live in HoneyPy’s lib directory. The easiest way to create a new file would be to copy an existing logger file and rename it. For example:
cp honeypy_elasticsearch.py honeypy_my_logger.py
Notice the prefix “honeypy_”, it’s not required but I recommended using it since that helps distinguish HoneyPy libraries from other third-party libraries that may end up in this directory.
When you look at the code for any of the existing loggers you’ll also notice there is one function defined (example). Your logger will need to have at least one primary function, which will eventually be called from honeypy_log_triage.py. You can, of course, define other functions to support your primary function. Also, be sure to include any necessary libraries at the top of your logger file. In the existing loggers the primary function follows the naming scheme of “post_<logger_name>”, but you can name your primary function however you like as long as it doesn’t conflict with any other logger function names.
Now take a look at the parameters being passed to the primary function. The values being passed are coming from honeypy_log_triage.py, and you can determine which parameters you want to your primary function to handle. The values provided by honeypy_log_triage.py are:
- Configuration values, anything from honeypy.cfg. Typically you’ll only be interested in the configuration sections [honeypy] and [your_logger_config_section].
- HoneyPy event data, see the full list of TCP & UDP event data here.
At this point, you should have a good picture of how this works. The code that goes into your primary function, and supporting functions if needed, is entirely up to you and what you want your logger to do. Whether you are posting the data to an HTTP endpoint, TCP socket, message queues, or just writing it to a file, you can really use your logger to integrate HoneyPy with any other system you want.
A few additional notes on your logger…
Your primary function will be called every time HoneyPy processes an event, which includes:
- CONNECT—connection events
- RX—receive data events
- TX—transfer data events
Also, make sure you implement appropriate error handling and logging, see the try block here as an example.
2. Determine Configuration Options
The next step is to define your logger’s configuration options. At the minimum, the enabled option is required. This tells HoneyPy whether or not to enable your logger at runtime, example:
[my_logger] enabled = Yes
Additional configuration options are purely up to you and what your logger requires. As an example, the HoneyDB logger requires four values, enabled, url, api_id, and api_key.
[honeydb] enabled = No url = https://riskdiscovery.com/honeydb/api/logger api_id = api_key =
3. Edit honeypy_log_triage.py
Now that you have a logger and some configuration, the next step is to start integrating your logger into HoneyPy code. At a high level, the honeypy_log_triage.py file will need to:
- Determine if your logger is enabled.
- If your logger is enabled then import your logger.
- Triage income events, whether they be TCP or UDP events, call your primary function and pass it the values you specified.
Adding this integration is straight forward, here is an example template:
# Add a new if block to determine if your logger is enabled if ‘Yes’ == honeypy_config.get(‘my_logger’, ‘enabled’):
# import your logger here from lib.honeypy_my_logger import post_my_logger
# if it is a TCP connection, call logger and pass parameters # do not change the next 3 lines if ‘TCP’ == parts[4]: if 11 == len(parts): parts.append(‘’) # no data for CONNECT events
# call your primary function with parameters post_my_logger(my_param1, my_param2, my_param3)
# if it ain't TCP it's UDP else: # UDP splits differently, don't change next 2 lines if 12 == len(parts): parts.append(‘’) # no data sent
# call your primary function with parameters post_my_logger(my_param1, my_param2, my_param3)
Review the Elasticsearch integration and the others as an example. Note, to retreive a value from the configuration file use the following function:
honeypy_config.get('<section_name>', '<config_option_name>')
4. Edit Honey.py
The final step to integrate your logger involves editing the main Hone.py file. HoneyPy will only tail and triage log messages if a logger is enabled. If your logger is the only one enabled, then this change will make sure everything is working. There is an if statement starting around line 79, and you’ll need to add another condition for your logger to the if statement. Below is an example of what the final if statement should look like if you were to add “my_logger” to it:
if ‘Yes’ == honeypy_config.get(‘twitter’, ‘enabled’) or ‘Yes’ == honeypy_config.get(‘honeydb’, ‘enabled’) or ‘Yes’ == honeypy_config.get(‘slack’, ‘enabled’) or ‘Yes’ == honeypy_config.get(‘logstash’, ‘enabled’) or ‘Yes’ == honeypy_config.get(‘elasticsearch’, ‘enabled’) or ‘Yes’ == honeypy_config.get(‘my_logger’, ‘enabled’):
Conclusion
HoneyPy tries to make itself simple to extend with new plugins or loggers. With a little Python elbow grease, you can easily extend HoneyPy’s capability and customize it to your needs. If you do create new plugins or loggers, I hope you will contribute them back to the HoneyPy project on Github. I look forward to seeing what you come up with! But at the very least, I hope you have enjoyed this three part series on getting started with HoneyPy! This is the last post on HoneyPy, but my next post will provide an overview of HoneyDB, which itself opens up another great opportunity to contribute to the community. Questions and feedback are welcome, thanks for reading!