How to use LibPrelude

This tutorial is meant to explain how the library LibPrelude works, what differs in LibPrelude from the original IDMEF and to give a few examples on how to implement it. If you want to build a sensor, we suggest you follow this tutorial and see the How to build a sensor section.

What is LibPrelude ?

LibPrelude is the first and only implementation of IDMEF. It is used as a mean of communication between Prelude modules. Prelude is a SIEM developed by C-S, which takes part in the SECEF project. Since part of Prelude is open-source, LibPrelude is also open-source and can be used freely.
As Prelude uses LibPrelude as a mean of communication between modules, LibPrelude is designed to send IDMEF alerts to a Prelude Manager and can’t be used, as for now, without one.
LibPrelude is written in C, but you canfind bindings for Python, Perl, C++, Ruby and Lua. We will give example on how to use it below, but you can also find it on Prelude’s site.

Download and install

You can install libprelude from the repositories on most distributions using :

 

Debian :

apt-get install libprelude-dev

CentOS :

rpm -i https://www.prelude-ids.org/attachments/download/297/prelude-ids-rhel-2-1.noarch.rpm
yum install libprelude

You can also find the packages on Prelude’s site.

How does it work ?

LibPrelude is a library that allows you to send IDMEF alerts to a Prelude manager. It is, however, impossible to just create IDMEF alerts and export it in a readable format. To send IDMEF alerts to a Prelude Manager, you first have to create a client. This client will be the one sending alerts to the manager. And to do so, it will need to registrate to the Prelude Manager. The client will send heartbeats to the manager, stating that he is still alive and well, and, whenn needed, send alerts the exact same way. Note that heartbeats are also IDMEF messages.

Using LibPrelude to send alerts to prelude manager can be done in several steps :

 

  • Initializing the Prelude Library
  • Creating the Prelude Client
  • Starting the Prelude Client : This will make it send heartbeats at a regular pace
  • Creating alerts
  • Sending Alerts

Format

LibPrelude is based on IDMEF. The module in charge of creating IDMEF messages is called IDMEFpath.

There’s a few easy rules determinating how to write the path to the field you want :

 

  • Prelude IDMEF Path are lower case
  • Subsequent member of a path should be separated using “*.*”
  • Where IDMEF XML class use upper case for word separation, Prelude use a dash “*_*”
  • In case a path member is a list, you can use a specific index to access the path, for example: alert.source(0).node.name

LibPrelude’s IDMEF paths globaly follow what’s described in the RFC 4735, but it differs on a few points.

First, LibPrelude does not use XML encoding format, as it is strongly suggested in the RFC. This means that some attributes, such as alertident, are slightly modified from the RFC. But we can also find differences on how to interprete some of the IDMEF fields.

Analyzer is a list in LibPrelude, whereas analyzers can only be chained in IDMEF. This difference come from the fact that the RFC and LibPrelude don’t have the same definition of an analyzer. LibPrelude sees as an analyzer every tool that participated in creating the alert.

A field that doesn’t exist in the RFC was added to IDMEFpath : alert.assessment.impact.description. It is meant to describe what the alert is thought to be about. Example, from vigor’s prelude-lml rule : assessment.impact.description=Vigor dropped an ICMP packet $3 – $4 ($7/$8). There are a few more differences between the RFC and LibPrelude’implementation of IDMEF but more important than that is the treewrap of IDMEF fields used in LibPrelude. Using LibPrelude to fill an alert is indeed rather easy when refering to this treewrap, which indicates how every field is handled by the library. This treewrap can be found in the documentation that comes along with the library when installing it.

First steps with LibPrelude in several languages

This section is just meant to give a brief overview of how to use LibPrelude. If you really want to build a new sensor, please refer to the Building a new Sensor section.

Using C

LibPrelude being originally written in C, you will have more choice using C than other languages. First, you will have to choose between using the low level API or the high level API. Using the low level API, as you would imagine, gives more performance, but needs more lines and is less intuitive. It also needs less memory. The high level API, indeed, creates a whole alerts, with every field already existing, everytime you create a new alert, whereas the low level API needs field to be created one by one. Let’s take a brief look at these API.

Low level API

  • Initializing the Prelude Library
#include <libprelude/prelude.h> 

int ret;

ret = prelude_init(&argc, argv);
if ( ret < 0 ) {
        prelude_perror(ret, "unable to initialize the prelude library");
        return -1;
}
  • Creating a new prelude client
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");

        /*This suppresses the client in case it was created but still sending errors*/
        prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
        return -1;
}
  • Starting a new client
ret = prelude_client_start(client);
if ( ret < 0 ) {
        prelude_perror(ret, "Unable to start prelude client");

           /*This suppresses the client in case something went wrong*/
        prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
        return -1;
}
  • Setting client options
ret = prelude_client_set_flags(prelude_client, PRELUDE_CLIENT_FLAGS_ASYNC_SEND|PRELUDE_CLIENT_FLAGS_ASYNC_TIMER);
if ( ret < 0) {
    fprintf(stderr, "Unable to set asynchronous send and timer.\n");

    /* This suppresses the client and avoid having a not configured client sending things */
    prelude_client_destroy(prelude_client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS); 
    return -1;
}
  • Creating the alert
idmef_message_t *idmef;

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

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

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

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

prelude_string_set_constant(str, "My classification");
  • Sending the alert
prelude_client_send_idmef(client, idmef);
idmef_message_destroy(idmef);
  • Destroying the client
prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);

 

High level API

  • Initializing the Prelude Library

#include <libprelude/prelude.h>
int ret;

ret = prelude_init(&argc, argv);
if ( ret < 0 ) {
        prelude_perror(ret, "unable to initialize the prelude library");
        return -1;
}
  • Creating a new prelude client
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");

        /*This suppresses the client in case it was created but still sending errors*/
        prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
        return -1;
}
  • Starting a new client
ret = prelude_client_start(client);
if ( ret < 0 ) {
        prelude_perror(ret, "Unable to start prelude client");

           /*This suppresses the client in case something went wrong*/
        prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
        return -1;
}
  • Setting client options
ret = prelude_client_set_flags(prelude_client, PRELUDE_CLIENT_FLAGS_ASYNC_SEND|PRELUDE_CLIENT_FLAGS_ASYNC_TIMER);
if ( ret < 0) {
    fprintf(stderr, "Unable to set asynchronous send and timer.\n");

    /* This suppresses the client and avoid having a not configured client sending things */
    prelude_client_destroy(prelude_client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS); 
    return -1;
}
  • Creating the alert
    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 alert
    prelude_client_send_idmef(client, idmef);
    idmef_message_destroy(idmef);
    
  • Destroying the client
prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);

 

Using C++

  • Initializing the Prelude Library
#include <libpreludecpp/prelude.hxx>
using namespace Prelude;

int main(int argc, char **argv)
{
        prelude_init(&argc, argv);
}
  • Creating the Prelude Client
ClientEasy client = ClientEasy("prelude-correlator", Client::IDMEF_WRITE);
  • Starting the Prelude Client : This will make it send heartbeats at a regular pace
 client.Start();
  • Creating alerts

As in C you’ll have to first create an IDMEF message. It then works like the high API : you can access every field without creating them first.

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");
  • Sending Alerts

This example contains also the extinction of the client.

 client.SendIDMEF(idmef);
 prelude_deinit();

 

Using Perl

  • Initializing the Prelude Library

Pay attention to the initialization for it has changed recently and won’t work if you follow the old How To.

use strict;
use Prelude;
  • Creating the Prelude Client
my $client = new Prelude::ClientEasy("analyzer_name", numeral_indicating_the_option, "analyzer_model", "analyzer_class", "manufacturer");
  • Starting the Prelude Client : This will make it send heartbeats at a regular pace
$client->start();
  • Creating alerts
# 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");
  • Sending Alerts
$client->sendIDMEF($idmef);

 

Using Python

As for Perl, this API has also changed recently so pay attention to the syntax.

 

  • Initializing the Prelude Library
import Prelude
  • Creating the Prelude Client
# Create a new Prelude client.
client = Prelude.ClientEasy("analyzer_name", numeral_indicating_the_option, "analyzer_model", "analyzer_class", "manufacturer")
  • Starting the Prelude Client : This will make it send heartbeats at a regular pace
client.start()
  • Creating alerts
# 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")
  • Sending Alerts

client.sendIDMEF(idmef)

 

Using Ruby

  • Initializing the Prelude Library
require("Prelude")
  • Creating the Prelude Client
client = Prelude::ClientEasy.new("analyzer_name", numeral_indicating_the_option, "analyzer_model", "analyzer_class", "manufacturer")
  • Starting the Prelude Client : This will make it send heartbeats at a regular pace
client.start()
  • Creating alerts
# 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")
  • Using Lua

    • Initializing the Prelude Library
    require("Prelude")
    
    • Creating the Prelude Client
    client = Prelude.ClientEasy("analyzer_name", numeral_indicating_the_option, "analyzer_model", "analyzer_class", "manufacturer")
    
    • Starting the Prelude Client : This will make it send heartbeats at a regular pace
    client:start()
    
    • Creating alerts
    # 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")
    
    • Sending Alerts
    client:SendIDMEF(idmef)