Skip to content

Developer Guidelines

Mrigank Anand edited this page Sep 10, 2022 · 7 revisions

Configuration File

Framework Configurationconfig.py

it contains framework default configs.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import socket

from core.time_helper import hours
from core.compatible import generate_token

# Protocol number to protocol string table
protocol_table = {
    num: name[8:]
    for name, num in vars(socket).items()
    if name.startswith("IPPROTO")
}
real_machine_ip_address = socket.gethostbyname(socket.gethostname())


def api_configuration():
    """
    API Config (could be modify by user)

    Returns:
        a JSON with API configuration
    """
    # DOCKER_ENV variable is set in the docker-compose file.
    if os.environ.get('ELASTICSEARCH_DOCKER_ENV') == "true":
        db_url = "elasticsearch:9200"
    else:
        db_url = "127.0.0.1:9200"

    return {  # OWASP Honeypot API Default Configuration
        "api_host": "0.0.0.0",
        "api_port": 5000,
        "api_debug_mode": False,
        "api_access_without_key": True,
        "api_access_key": generate_token(),  # or any string, or None
        "api_client_white_list": {
            "enabled": False,
            "ips": [
                "127.0.0.1",
                "10.0.0.1",
                "192.168.1.1"
            ]
        },
        "api_access_log": {
            "enabled": False,
            "filename": "ohp_api_access.log"
        },
        "api_database": db_url,
        "api_database_http_auth": ('elastic', 'changeme')
    }


def network_configuration():
    """
    network configuration

    Returns:
        JSON/Dict network configuration
    """
    return {
        "store_network_captured_files": False,
        "real_machine_ip_address": real_machine_ip_address,
        "ignore_real_machine_ip_address": True,  # or if you want to simulate from local network, save as False
        "ignore_virtual_machine_ip_addresses": True,  # or if you want simulate from local network, save as False
        "real_machine_identifier_name": "stockholm_server_1",  # can be anything e.g. real_machine_ip_address, name, etc
        "ignore_real_machine_ip_addresses": list(
            {
                real_machine_ip_address,
                "127.0.0.1"
            }
        ),
        # e.g. ["10.0.0.1", "192.168.1.1"]
        "ignore_real_machine_ports": [],  # e.g. [22, 80, 5000]
        "split_pcap_file_timeout": 3600  # Default value
    }


def docker_configuration():
    """
    docker configuration

    Returns:
        JSON/Dict docker configuration
    """
    return {
        "virtual_machine_storage_limit": 0.5,  # Gigabyte
        "virtual_machine_container_reset_factory_time_seconds": hours(-1),  # -1 is equals to never reset!

    }


def user_configuration():
    """
        user configuration
    Returns:
        JSON/Dict user configuration
    """
    return {
        "language": "en_US",
        "events_log_file": "tmp/ohp.log",
        "default_selected_modules": "all",  # or select one or multiple (e.g. ftp/strong_password,ssh/strong_password)
        "default_excluded_modules": None  # or any module name separated with comma
    }


def sentry_configuration() -> dict:
    """
        sentry configuration
    Returns:
        JSON/Dict sentry configuration
    """
    return {
        "sentry_monitoring": False,
        # Enter your Sentry Project URL here.
        "sentry_dsn_url": "",
        # Enter Trace Rate Here.
        "sentry_trace_rate": 1
        # Set sentry_trace_rate to 1.0 to capture 100%
        # of transactions for performance monitoring.
        # sentry_trace_rate is ratio of errors being
        # reported to the number of issues which arise.

    }

Modules Configuration

Category Configuration modules\ftp\__init__.py

every category (e.g. ftp, http, ssh) have their own configuration in __init__.py. feel free to customize them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-


def category_configuration():
    """
    category configuration

    Returns:
        JSON/Dict category configuration
    """
    return {
        "virtual_machine_name": "ohp_ftpserver",
        "virtual_machine_port_number": 21,
        "virtual_machine_internet_access": True,
        "real_machine_port_number": 21
    }

Module Configuration modules\ftp\weak_password\__init__.py

every module (e.g. ftp/weak_password) have their own configuration in __init__.py. feel free to customize them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

def module_configuration():
    """
    module configuration

    Returns:
        JSON/Dict module configuration
    """
    return {
        "username": "admin",
        "password": "admin",
        "extra_docker_options": ["--volume {0}/tmp/ohp_ftp_weak_container/:/root:z".format(os.getcwd())],
        "module_processor": ModuleProcessor()
    }

Use Multiple Module in the Same Category

To use multiple modules in the same category and avoid ports confliction, you must replace the real_machine_port_number by adding them to modules. you can replace ANY category configuration by adding the keys to the module configuration.

#!/usr/bin/env python
# -*- coding: utf-8 -*-


def module_configuration():
    """
    module configuration

    Returns:
        JSON/Dict module configuration
    """
    return {
        "username": "admin",
        "password": "admin",
        "virtual_machine_port_number": 21
        # you can add anything in here to avoid the default category configuration
        # "virtual_machine_internet_access": False
    }

Copy a File To Docker Image

Any module may require to copy a file/service/configuration in the image, the easiest way to do this in the framework, you can use docker COPY or ADD command in Dockerfile, but you must locate all your files in modules/protocol/moduleOrType/files.

# copy vsftpd.conf to /etc/vsftpd.conf
COPY files/vsftpd.conf /etc/vsftpd.conf

Use Docker Options in Module Configuration

To use extra docker options for each container of each module it is possible now using extra_docker_options in the module configuration for each module. For example, as shown --volume can be used to share the volume between docker container and docker host. The other options can also be specified in the extra_docker_options and the container would be built according to the options mentioned for each module.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

def module_configuration():
    """
    module configuration

    Returns:
        JSON/Dict module configuration
    """
    return {
        "username": "root",
        "password": generate_token(16),
        "extra_docker_options": ["--volume " + os.getcwd() + "/tmp/:/root/logs/"],
        "module_processor": ModuleProcessor()
    }

ModuleProcessor class for some modules if required

In the module configuration for each module, there is module processor class added which is used to grab the log the files obtained from the docker container or do some other needed process. Now it is being used to store the logs obtained from the docker container regarding the honeypot activities into the database.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import os
import json
from core.compatible import generate_token
from database.connector import insert_to_credential_events_collection
from database.datatypes import CredentialEvent

LOGFILE = 'tmp/ohp_ssh_strong_password_creds_logs.txt'
LOGFILE_DUMP = 'tmp/ohp_ssh_strong_password_creds_logs.json'


class ModuleProcessor:
    """
    this is the processor to run after docker-machine is up to grab the
    log files or do other needed process...
    """

    def __init__(self):
        self.kill_flag = False

    def processor(self):
        """
        processor function will be called as a new thread and will be
        die when kill_flag is True
        """
        if os.path.exists(LOGFILE):
            os.remove(LOGFILE)  # remove if exist from past
        while not self.kill_flag:
            if os.path.exists(LOGFILE):
                os.rename(LOGFILE, LOGFILE_DUMP)
                data_dump = open(LOGFILE_DUMP).readlines()
                for data in data_dump:
                    data = json.loads(data)
                    insert_to_credential_events_collection(
                        CredentialEvent(
                            ip=data['ip'],
                            username=data['username'],
                            password=data['password'],
                            module_name=data['module_name'],
                            date=data['date']
                        )
                    )
                os.remove(LOGFILE_DUMP)
            time.sleep(0.1)

In this example it is copying the honeypot logs into another file, then inserts into the database and sleeps for 0.1s.

Adding a new Module

OWASP Python Honeypot currently supports multiple types of protocols with different types and modules for various purposes like getting credentials, network events, files, honeypot events, and custom data coming from each module.

To add a new protocol you should create a new folder inside the /OWASP-Honeypot/modules directory of the project.

Set up protocol files

Each protocol has an __init__.py file which has the category configuration, below shown is the template for the same.

def category_configuration():
    """
    category configuration
    Returns:
        JSON/Dict category configuration
    """
    return {
        "virtual_machine_name": OHP_Module_name,
        "virtual_machine_port_number": PORT_NUMBER,
        "virtual_machine_internet_access": Bool,
        "real_machine_port_number": DOCKER_HOST_PORT_NUMBER
    }

Then if the protocol has modules like weak and strong password then two separate folders should be created. Inside the module folder there should be:

  • files folder if the modules require some extra scripts/config files which need to be moved to the module containers.
  • __init__.py

contains module processor and module configuration

class ModuleProcessor:
    """
    this is the processor to run after docker-machine is up to grab the
    log files or do other needed process...
    """

    def __init__(self):
        self.kill_flag = False

    def processor(self):
        """
        processor function will be called as a new thread and will
        be die when kill_flag is True
        """
        while not self.kill_flag:
            LOGGING
            INSTRUCTIONS
            go
            here
            time.sleep(0.1)


def module_configuration():
    """
    module configuration
    Returns:
        JSON/Dict module configuration
    """
    return {
        "virtual_machine_port_number": PORT_NUMBER,
        "real_machine_port_number": PORT_NUMBER_FOR_DOCKER_HOST,
        "extra_docker_options": [""],
        "module_processor": ModuleProcessor()
    }
  • readme.md: Describing the module
  • Dockerfile: For setting up all the packages, libraries, scripts to run by the module.

Testing the module

For testing the module run the command

python3 ohp.py -m protocol/moduleOrType

Also one must make sure that the test for the module is passing.

python3 ohp.py -m protocol/moduleOrType --test