Skip to content
Thomas Chopitea edited this page Apr 20, 2015 · 8 revisions

Malcom has a web API that can be queried using any web client (curl, wget, urllib, or the python requests library).

Getting your API key

Malcom generates an API key for every user created in the database. The API key can be found in your account settings. Just point your browser to /account/settings. This API key has to be included as a header in your requests. The curl command for this is:

curl <your Malcom host>:8080/api/query/?value=malcom.io -H "X-Malcom-API-Key: <copy and paste your API key here>"

If you have redirections, you might want to pass the The -L flag to curl to follow them

Querying the database

Malcom opens some functions that allow for free database query.

Standard query: /api/query/

Malcom's database can be queried freely launching GET requests to its web API located at /api/query/. Since values may vary from element type to element type, to get an idea of what parameters you can pass, have a look at the /dataset view.

Common parameters include:

  • value: curl <your Malcom host>:8080/api/query/?value=malcom.io
  • type: curl <your Malcom host>:8080/api/query/?type=url
  • tags: curl <your Malcom host>:8080/api/query/?tags=zeus

The response is a JSON blob, similar to this:

{
  "elements": [
    {
      "last_analysis": {
        "$date": 1429550682281
      },
      "next_analysis": {
        "$date": 1429572282281
      },
      "tags": [
        "search"
      ],
      "date_updated": {
        "$date": 1429550682292
      },
      "link_value": "/nodes/value/malcom.io",
      "evil": {},
      "value": "malcom.io",
      "date_created": {
        "$date": 1429550681802
      },
      "refresh_period": 21600,
      "_id": {
        "$oid": "553536591d41c8b9dd75aa3c"
      },
      "type": "hostname",
      "link_type": "/nodes/type/hostname"
    }
  ],
  "total_results": 1,
  "fields": [
    [
      "value",
      "Value"
    ],
    [
      "type",
      "Type"
    ],
    [
      "tags",
      "Tags"
    ],
    [
      "date_updated",
      "Updated"
    ],
    [
      "date_created",
      "Created"
    ],
    [
      "last_analysis",
      "Analyzed"
    ]
  ],
  "chrono_query": "0:00:00.001154",
  "chrono_count": "0:00:00.000538",
  "per_page": 50,
  "page": 0
}
  • total_results: The number of results returned by the query. This is currently limited to 500.
  • fields: The fields (and matching field label) corresponding to the type of the first element returned by the query
  • elements: An array of elements returned by the query. These can be of different types, and usually have different fields.
  • chrono_query and chrono_count: The time it took to run the query and to count results.

If nothing is found, the following is returned:

{
  "elements": [],
  "total_results": 0,
  "fields": [
    ["value", "Value"],
    ["type", "Type"],
    ["tags", "Tags"]
  ],
  "chrono_query": "0:00:00.450309",
  "chrono_count": "0:00:00.001948",
  "per_page": 50,
  "page": 0
}

Requesting a node's neighbors: /api/neighbors/

This takes all nodes matching an input query and returns all immediate neighbors for such nodes, and corresponding links.

For example, a GET request to malcom:8080/api/neighbors/?value=malcom.io will yield:

{
  "nodes": [
    {
      "last_analysis": {
        "$date": 1405338044878
      },
      "next_analysis": {
        "$date": 1405359644878
      },
      "tags": [],
      "fields": [
        [ "value", "Value"],
        [ "type", "Type"],
        [ "tags", "Tags"],
        [ "date_updated", "Updated"],
        [ "date_created", "Created"],
        [ "last_analysis", "Analyzed"]
      ],
      "value": "bevrifuli.geohats.com",
      "date_created": {
        "$date": 1405316440420
      },
      "refresh_period": 21600,
      "_id": {
        "$oid": "53c36d584374d35c84e57e4e"
      },
      "type": "hostname",
      "whois": "Whois resolution failed"
    },
    {...},
  ],
  "edges": [
    {
      "attribs": "host",
      "src": {
        "$oid": "53c36d5a4374d320aae58118"
      },
      "dst": {
        "$oid": "53c36d584374d35c84e57e4e"
      },
      "first_seen": {
        "$date": 1405316442882
      },
      "_id": {
        "$oid": "53c36d5a4374d35c9ee57e4e"
      },
      "last_seen": {
        "$date": 1405316442882
      }
    },
    {...},
  ]
}

The node array and its elements have the same format as before. edges is an array of elements which contain the source (src) element's _id (src), the destination (dst) element's _id, and dates where this specific link was first seen and last seen. All src and dst values in the edges array match an _id in the nodes array.

If nothing is found, an empty response is returned:

{
  "nodes": [],
  "edges": []
}

Recursively finding evil: /api/evil/

This takes all nodes matching an input query (in the same format than the /api/query/ API) and will hop from neighbor to neighbor checking if it finds elements that have been tagged evil. When successful, it will return the whole chain. For example, this might come in handy if a sample is contacting a domain that was previously seen hosting malware.

The /api/evil/ API takes a depth parameter (defaults and max to 2), which is the recursion level that the function will use to hop from node to node until it finds an element tagged 'evil'. If the recursion limit is reached and Malcom didn't find anything, it will return an empty result for this element.

For example, a GET request to malcom:8080/api/evil/?value=bevrifuli.geohats.com will yield the following JSON blob:

{
  "nodes": [
    {...},
  ],
  "edges": [
    {
      "attribs": "host",
      "src": {
        "$oid": "53c36d5a4374d320aae58118"
      },
      "dst": {
        "$oid": "53c36d584374d35c84e57e4e"
      },
      "first_seen": {
        "$date": 1405316442882
      },
      "_id": {
        "$oid": "53c36d5a4374d35c9ee57e4e"
      },
      "last_seen": {
        "$date": 1405316442882
      }
    },
    {...},
  ]
}

The node array and its elements have the same format as before. edges is an array of elements which contain the source (src) element's _id (src), the destination (dst) element's _id, and dates where this specific link was first seen and last seen. All src and dst values in the edges array match an _id in the nodes array.

If nothing of interest is found for the given node, empty arrays are returned:

{
  "nodes": [],
  "edges": []
}

Sniffer sessions API

Malcom opens some function that allow for querying sniffer sessions.

Get a session list: /api/sniffer/list/

This will return a list of sniffer sessions. By default, all public sniffer sessions are returned. You can use the following parameters to tune your request.

  • user: Setting this parameter will have your own (private or not) sniffer sessions included in the response.
  • private: Setting this parameter will limit results to only session opened by you (public or not)
  • page: Responses are limited to 50 results. Increase this parameter to get the following 50 results.

The response to curl -H "X-Malcom-API-Key: xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx" malcom:8080/api/sniffer/sessionlist/ looks like this:

{
  "session_list": [
    {
      "status": "Running",
      "name": "dridex",
      "packets": 1405,
      "public": true,
      "edges": 7,
      "date_created": {
        "$date": 1429551276776
      },
      "nodes": 5,
      "id": "553538ac1d41c8b9dd75aa3e"
    }
  ]
}

Extract data from sniffer sessions

You can specify three parameters:

All data: /api/sniffer/data/<string:session_id>/?all=1

This will yield all data related to a given session ID:

{
  "status": false,
  "packet_count": 1405,
  "name": "dridex",
  "node_list": [
    "553538b61d41c8b9dd75aa49",
    "553538ad1d41c8b9dd75aa41",
    "553538cf1d41c8b9dd75aa52",
    [more node IDs],
    "553538ad1d41c8b9dd75aa42",
    "553538ec1d41c8b9dd75aa7f",
    "553538d01d41c8b9dd75aa55"
  ],
  "id": "553538ac1d41c8b9dd75aa3e",
  "filter": "ip and not host 127.0.0.1 and not host 172.16.254.1  and not host 172.16.254.137 ",
  "pcap_filename": "553538ac1d41c8b9dd75aa3e-dridex.pcap",
  "pcap": true,
  "public": true
}

Notable fields:

  • status: The session status (running or not)
  • node_list: The list of node IDs. Should you need extra details on these, you can issue a new call to /api/query/ or /api/sniffer/data/<string:session_id>/?elements=1 (see below)
  • filter: BPF used for this session
  • pcap_filename: The filename where Malcom stored the raw .pcap
  • pcap: Is a .pcap file available for this session?

Full element details: /api/sniffer/data/<string:session_id>/?elements=1

This should be used when you want the details of elements contained in a sniffing session. To get all elements contained in a sniffing session, use the parameter ?elements=1. This will return a node_list element containing all nodes in the sniffing session.

Example result:

{
  "node_list": [
    {
      "domain": "-",
      "last_analysis": {
        "$date": 1429551278113
      },
      "next_analysis": {
        "$date": 1429810478113
      },
      "tags": [
        "dridex",
        "sniffer"
      ],
      "country": "",
      "ISP": "Private IP Address LAN",
      "evil": {},
      "bgp": "",
      "value": "192.168.221.134",
      "date_created": {
        "$date": 1429551276817
      },
      "refresh_period": 259200,
      "_id": {
        "$oid": "553538ac1d41c8b9dd75aa3f"
      },
      "type": "ip",
      "asn": "",
      "name": ""
    },
    {...}
  ]
}

Evil element details: /api/sniffer/data/<session_id>/?evil=1

This will filter elements from /api/sniffer/data/<session_id>/?elements=1 only leaving out elements that have the evil tag set.

api.add_resource(SnifferSessionDelete, '/api/sniffer/delete/<session_id>') api.add_resource(SnifferSessionStart, '/api/sniffer/new/', endpoint='malcom_api.session_start')

Delete sniffer session: /api/sniffer/delete/<session_id>

Create a new sniffer session: /api/sniffer/new/

This function takes the following POST parameters as a request:

  • pcapfile: The pcap file you want to parse. Not specifying this will result in Malcom sniffing from the wire.
  • session_name: Session name (required)
  • intercept_tls: Intercept TLS traffic? (boolean, default is False)
  • public: Share this session with other users (boolean, default is True)
  • start: Start sniffing right away (boolean, default is False)
  • filter: BPF filter to use for this session

A successful call will return a session ID.

Control a session: /api/sniffer/control/<string:session_id>/<string:action>

Set the action parameter to start or stop so start or stop a sniffing session.

Obtain PCAP for session: /api/sniffer/pcap/<session_id>

This will return the full, raw PCAP associated with a session. Use the id parameter from the sessionlist call.

curl -H "X-Malcom-API-Key: xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx" malcom:8080/api/sniffer/53c2cfd31d41c857b73536bb/pcap > test_session.pcap