Creating custom requests for Lampyre desktop app

Lampyre.io
6 min readFeb 14, 2023

--

Even though we offer a huge list of over 230 types of requests, you may still need to enrich your investigation with more information: get data from local database or registry, look up profiles on certain websites, add images from outdoor web cameras and so on. We got that covered! Lampyre allows you to add custom requests as scripts written in Python.

How to create a custom request?

So, let’s start from scratch. First of all, download and install Python for Windows (version 3 and newer). Once you have Python installed, you can write and implement your own requests. But first, take a look at Lighthouse API components: go to Lampyre installation folder, then open Lighthouse folder. You will find the following two files:

  • lighthouse.py — it contains information about classes, which are the basis for writing your own scripts.
  • lighthouse_ontology.py — it contains a set of standard objects, attributes and links. This set should be sufficient for creating custom requests, but you can still add your own objects, attributes and links to the file if necessary.

We will try not to get into much detail on the coding in Python itself. Instead, we will focus on basic principles of the script, as well as some important points to keep in mind when writing your own requests.
Below is a simple script which writes the input value into the table:

from lighthouse import Task, Header, Field, ValueType, EnterParamCollection, EnterParamField

class MyHeader(metaclass=Header): # A class that describes table header
Value = Field('Value', ValueType.String) # Header field

class SimplestRequest(Task):
def get_id(self):
return '0c05eb51-8673-416f-8682-fdad96d4578a'

def get_display_name(self):
return 'My first request'

def get_category(self):
return 'Learn'

def get_headers(self):
return MyHeader

def get_enter_params(self):
return EnterParamCollection(
EnterParamField('parameter', 'Parameter', ValueType.String)
)
# Executable part

def execute(self, enter_params, result_writer, log_writer, temp_directory, **kwargs):
result_writer.write_line({MyHeader.Value: enter_params.parameter},
header_class=MyHeader)

It works as follows: When the method is started, an instance of the class inheriting the Task is created and then executed. As a result, the table is filled with data. To write a row to a table, the write_line is used from the result_writer object, which receives a dictionary with header fields and data to be put into cells.

To define key parameters of your custom request, refer to Task class from lighthouse.py file:

  • Each custom request should have a unique id in uuid4 format (string) set by def get_id(self)
  • To define task category, use def get_category(self)
  • To define task name, use def get_display_name(self)
  • To define task description, use def get_description(self)

For example, let’s use the following definitions in our script:

 def get_display_name(self):
return 'Custom search name'
def get_category(self):
return 'My custom searches'
def get_description(self):
return 'A description to my imported custom search'

As a result, our custom request will be displayed as follows in List of requests window:

The request input parameters can be set using items described in EnterParamField. The following three are mandatory:

  • system_name is a unique name used to access the value in the execute method.
  • display_name is the name of the parameter for the interface.
  • type — data type, one of the ValueType list (String, Integer, Float, Boolean, Datetime)

The result of running custom requests should return at least one table. The tables are defined with headers. Table headers are mandatory, and they include columns (Fields) with basic (int, float, boolean, datetime string) or complex (color, image, resource) data types.

Table cells may include a color, an image or a file. If required, you can write None as cell values — just make sure you do not use zeros, empty rows and False.

You can create your own execution log, which you will see in the lower part of Requests window. To do it, use log_writer argument inside execute part.

Creating custom objects, attributes and links

As we already mentioned above, you can add your own objects, attributes and links to lighthouse_ontology.py file.

Attributes:

  • Attributes define properties of an object or a link. Each entity must have at least one attribute
  • Identifying attribute (IdentAttrs) represents uniqueness of an object. Objects with same identifying attributes will be merged
  • New attributes are described under Attribute class. Make sure that you use unique names (i.e., which is not present in the standard set) when creating a new attribute

Objects:

  • Objects can be placed on a graph or on a map
  • Objects may have a caption (CaptionAttrs) and/or an icon (Image)
  • If GeoPoint, GeoLineString, GeoPolygon attributes are used, you will be able to place an object on the map
  • If both GeoPoint and DateTime attributes are used, you will be able to draw a route on the map

Below is an example of an object with one standard attribute, two custom attributes and an image:

class Employee(metaclass=Object):
Name = Attributes.System.Name # standard attribute from ontology
SSN = Attribute('SSN', ValueType.String) # custom attributes
Age = Attribute('Age', ValueType.Integer)

IdentAttrs = [SSN] # identifying attribute
CaptionAttrs = [Name, Age] # name and age are shown in object's caption
Image = Utils.base64string('employee.png')# relative path to object icon

Link:

  • A link description should include endpoints (Begin and End)
  • Links may have captions
  • A link may have no attributes
  • If a link name is not set, it will be generated automatically based on the names of linked objects

Below is an example of a simple link with no attributes:

class IpToEmployee(metaclass=Link):
Begin = IP
End = Employee

This is an example of a link with an attribute, a name and a caption:

class IpToEmployee(metaclass=Link):
name = 'Workstation IP'
Begin = IP
End = Employee

MacAddress = Attributes.System.MacAddress
CaptionAttrs = [MacAddress]

Importing a script for custom request

To import a script, open an investigation in Lampyre, then go to Windows → Scripts. Click the “Upload from files” button, and select the script to be uploaded (make sure the file format is .py).

If import is successful, you will see your request in Tasks section of Scripts window:

Your request will also be available in List of requests window, together with other requests in Lampyre.

While importing the request, you may get an error — we will show you how it can be fixed.

If you get:

An error occurred trying to start process “python.exe” with working directory C:\Users\Admin\AppData\Local\Programs\Lampyre 1.8. The system cannot find the file specified.

it means you need to specify the path to “python.exe” file. To do this, go to the folder in which Lampyre is installed, locate “config.json” file and open it. Find the row with “pythonPath” and change it to match the correct path to Python executable file — like this:

"pythonPath": "C://Users//Admin//AppData//Local//Programs//Python//Python311//python.exe"

If you get:

ModuleNotFoundError: No module named ‘module name’

it means that the library or module indicated in your script is missing and you need to install them.

In conclusion, let’s summarize why custom requests are useful, and what advantages they have. Firstly, creating custom requests allows you to get more data from different sources. Secondly, you can define and use your own library of objects and links, making your investigation more tailored to your area of interest. Last but not least, custom requests are a perfect way to enrich data which already exists in your investigation.

--

--

Lampyre.io
Lampyre.io

Written by Lampyre.io

Data analysis & OSINT tool for everyone. Obtain, visualize and analyze data in one place to see what others can’t.

No responses yet