Project

General

Profile

Building a Sensor

The example listed on this page make use of new feature provided since libprelude 0.9.19. If you use an earlier version, please look at the DevelAgentBuildingOld page.

Using C

Building a sensor in C to communicate with the prelude manager is done in three building steps:
  • Creating the Prelude client
  • Using the low level IDMEF API
  • Using the high level IDMEF API

We will describe each step and the functions you can use from the API accordingly.

Initializing the Prelude library

Before any operation are done using the prelude library, the user first need to initialize it using
the prelude_init() function.

#include "prelude.h" 

int ret;

ret = prelude_init(&argc, argv);
if ( ret < 0 ) {
        prelude_perror(ret, "unable to initialize the prelude library");
        return -1;
}

Creating the Prelude client

The first thing to do before using the Prelude library function is to create a prelude_client_t
object. This object will be necessary for most of the work you are going to do with prelude.

The creation of a prelude_client_t object involve several steps, including, but not limited to:
  • Parsing specific Prelude command line / configuration file options
  • Checking that the profile has been registered (the "my-analyzer" argument ahead).

The prelude_client_new() function should be used for this task.

int ret;
prelude_client_t *client;

ret = prelude_client_new(&client, "my-analyzer");
if ( ! client ) {
        prelude_perror(ret, "Unable to create a prelude client object");
        return -1;
}

Once the client is created, an you have everything setup. You will need to start your client.
The prelude_client_start() function is responsible for this, and will trigger the connection to
the configured manager, and send the initial client heartbeat.

ret = prelude_client_start(client);
if ( ret < 0 ) {
       prelude_perror(ret, "Unable to start prelude client");
       return -1;
}

The prelude library will also register an internal timer in order to send heartbeat message at the defined interval. Timer registered by the library itself or by the program will either be called automatically if the PRELUDE_CLIENT_FLAGS_ASYNC_TIMER is set, otherwise, the program is responssible for calling the prelude_timer_wakeup() function every second from it's main loop, in order to check the registered timer.

  • PRELUDE_CLIENT_FLAGS_HEARTBEAT - Used for client to send heartbeat (this is the default).
  • PRELUDE_CLIENT_FLAGS_ASYNC_SEND - Used if you want message to be sent asynchronously.
  • PRELUDE_CLIENT_FLAGS_ASYNC_TIMER - Used if you want timer to be automatically called from the asynchronous thread.
ret = prelude_client_set_flags(client, PRELUDE_CLIENT_FLAGS_ASYNC_SEND|PRELUDE_CLIENT_FLAGS_ASYNC_TIMER);
if ( ret < 0 ) {
       fprintf(stderr, "Unable to set asynchronous send and timer.\n");
       return -1;
}

Which IDMEF API to use, low or high level?

It all depend on your sensor requirement. Using the low level API is more error prone, and the resulting code will be harder to maintain, but the result will be faster and more optimized. On the other end, the higher level API is slower, but maintaining the code will be much easier.

Using the low level IDMEF API

Here is an example of creating different IDMEF object. The top level IDMEF object
is always idmef_message_t. You should refer to the IDMEF draft, or to the Prelude API documentation in order to get a complete listing of theses objet, or a description of what information an object carry.

idmef_message_t *idmef;

ret = idmef_message_new(&idmef);
if ( ret < 0 ) {
        prelude_perror(ret, "unable to create IDMEF message");
        return;
}

ret = idmef_message_new_alert(idmef, &alert);
if ( ret < 0 ) {
        prelude_perror(ret, "unable to create IDMEF alert");
        idmef_message_destroy(idmef);
        return;
}

ret = idmef_alert_new_classification(alert, &class);
if ( ret < 0 ) {
        prelude_perror(ret, "unable to create IDMEF classification");
        idmef_message_destroy(idmef);
        return;
}

ret = idmef_classification_new_text(class, &str);
if ( ret < 0 ) {
        prelude_perror(ret, "unable to create classification text");
        idmef_message_destroy(idmef);
        return;
}

prelude_string_set_constant(str, "My classification");

Using the high level IDMEF API

Here is an example of using the high level IDMEF API:

idmef_message_t *idmef;

ret = idmef_message_new(&idmef);
if ( ret < 0 )
        return -1;

idmef_message_set_string(idmef, "alert.classification.text", "My classification text");
idmef_message_set_string(idmef, "alert.classification.reference(0).name", "OSVDB-XXXX");
idmef_message_set_string(idmef, "alert.classification.reference(0).origin", "osvdb");
idmef_message_set_string(idmef, "alert.classification.reference(0).url", "http://my.url/");

Sending the IDMEF message

This function will trigger the sending of the idmef_message_t you created above. Once this function is called, you might safely destroy the message (even if you are using the asynchronous sending mode).

prelude_client_send_idmef(client, idmef);
idmef_message_destroy(idmef);

Destroying the client

In case the analyzer you are developing is not a persistant analyzer (meaning an analyzer that is not supposed to exit), it is important that you call the prelude_client_destroy() function prior to exiting. This function have the side effect of sending an heartbeat to the remote manager, as well as an information regarding the analyzer state.

This state information is important since an analyzer not reporting a successful exit status, or an analyzer which stop sending heartbeat at all will be reported as having a problem.

  • PRELUDE_CLIENT_EXIT_STATUS_SUCCESS - Exiting the sensor is the expected behavior.
  • PRELUDE_CLIENT_EXIT_STATUS_FAILURE - There is something wrong going on, notice the security analyst.
prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);

As a side note, please remember that a persistant sensor should never use this function (except maybe if it is working in batch mode), unless it want to report the PRELUDE_CLIENT_EXIT_STATUS_FAILURE exit status. This is also the case if your persistant sensor is interrupted by a signal.

Integration

To make integration into your probe, you must surely use the m4 tools with libprelude you can download : here

You may also need the m4 for libpreludedb : here

Using C++

#include <libprelude/prelude.hxx>

using namespace Prelude;

int main(int argc, char **argv)
{
        prelude_init(&argc, argv);

        ClientEasy client = ClientEasy("prelude-correlator", Client::IDMEF_WRITE);
        client.start();

        IDMEF idmef;

        // Classification
        idmef.set("alert.classification.text", "C++ Example");

        // Source
        idmef.set("alert.source(0).node.address(0).address", "10.0.0.1");

        // Target
        idmef.set("alert.target(0).node.address(0).address", "10.0.0.2");
        idmef.set("alert.target(1).node.address(0).address", "10.0.0.3");

        // Assessment
        idmef.set("alert.assessment.impact.severity", "low");
        idmef.set("alert.assessment.impact.completion", "failed");
        idmef.set("alert.assessment.impact.type", "recon");

        // Additional Data
        idmef.set("alert.additional_data(0).data", "something");

        client.sendIDMEF(idmef);
        prelude_deinit();
}

Integration

To make integration into your probe, you must surely use the m4 tools with libpreludecpp you can download : here

Using Perl

use strict;
use Prelude;

# Create a new Prelude client.
my $client = new Prelude::ClientEasy("MySensor");
$client->start();

# Create an IDMEF message
my $idmef = new Prelude::IDMEF();

# Classification
$idmef->set("alert.classification.text", "Perl Example");
$idmef->set("alert.source(0).node.address(0).address", "10.0.0.1");
$idmef->set("alert.target(0).node.address(0).address", "10.0.0.2");
$idmef->set("alert.target(1).node.address(0).address", "10.0.0.3");

# Assessment
$idmef->set("alert.assessment.impact.severity", "low");
$idmef->set("alert.assessment.impact.completion", "failed");
$idmef->set("alert.assessment.impact.type", "recon");

# Additional Data
$idmef->set("alert.additional_data(0).data", "something");

# Send the generated message
$client->sendIDMEF($idmef);

Using Python

import prelude

# Create a new Prelude client.
client = prelude.ClientEasy("MySensor")
client.start()

# Create the IDMEF message
idmef = prelude.IDMEF()

# Classification
idmef.set( "alert.classification.text", "Python Example")

# Source
idmef.set("alert.source(0).node.address(0).address", "10.0.0.1")

# Target
idmef.set("alert.target(0).node.address(0).address", "10.0.0.2")
idmef.set("alert.target(1).node.address(0).address", "10.0.0.3")

# Assessment
idmef.set("alert.assessment.impact.severity", "low")
idmef.set("alert.assessment.impact.completion", "failed")
idmef.set("alert.assessment.impact.type", "recon")

# Additional Data
idmef.set("alert.additional_data(0).data", "something")

client.sendIDMEF(idmef)

Using Ruby

require("Prelude")

# Create a new Prelude client.
client = Prelude::ClientEasy.new("MySensor")
client.start()

# Create the IDMEF message
idmef = Prelude::IDMEF.new()

# Classification
idmef.set( "alert.classification.text", "Ruby Example")

# Source
idmef.set("alert.source(0).node.address(0).address", "10.0.0.1")

# Target
idmef.set("alert.target(0).node.address(0).address", "10.0.0.2")
idmef.set("alert.target(1).node.address(0).address", "10.0.0.3")

# Assessment
idmef.set("alert.assessment.impact.severity", "low")
idmef.set("alert.assessment.impact.completion", "failed")
idmef.set("alert.assessment.impact.type", "recon")

# Additional Data
idmef.set("alert.additional_data(0).data", "something")

client.sendIDMEF(idmef)

Using Lua

require("prelude")

-- Create a new Prelude client.
client = prelude.ClientEasy("MySensor")
client:start()

-- Create the IDMEF message
idmef = prelude.IDMEF()

-- Classification
idmef:set( "alert.classification.text", "Lua Example")

-- Source
idmef:set("alert.source(0).node.address(0).address", "10.0.0.1")

-- Target
idmef:set("alert.target(0).node.address(0).address", "10.0.0.2")
idmef:set("alert.target(1).node.address(0).address", "10.0.0.3")

-- Assessment
idmef:set("alert.assessment.impact.severity", "low")
idmef:set("alert.assessment.impact.completion", "failed")
idmef:set("alert.assessment.impact.type", "recon")

-- Additional Data
idmef:set("alert.additional_data(0).data", "something")

client:sendIDMEF(idmef)

Documentation

libprelude.m4 (7.95 KB) Thomas ANDREJAK, 09/10/2016 03:52 PM

libpreludedb.m4 (7.63 KB) Thomas ANDREJAK, 09/10/2016 03:52 PM

libpreludecpp.m4 (7.93 KB) Thomas ANDREJAK, 09/10/2016 03:52 PM