Cymon API v2.0

The focus of the new release is to create an easy-to-use "threat exchange" for Indicators of Compromise (IoC).

New features include:

The API is not generally available yet. We posted the documentation early so we can get feedback from the community. Get in touch if you have comments: info@cymon.io

Feeds API

Create Feed

Register a new user-feed.

Path /feeds/
Method POST
Authentication required Yes

Request parameters

Parameter Type Required Location Description
feed JSON Yes body Feed object

Feed object

Attribute Type Required Description
name string Yes Unique feed name (case sensitive)
link string Yes Website for more information
privacy string Yes Value can be public or private
tags list of strings Yes tags to help identify the feed
admins list of strings No Users that can update feed
members list of strings No Users that can submit and read IoCs
guests list of strings No Users with read-only access

Setting a feed as private will only allow admins, members or guests to access it.

Sample feed object

{
  "name": "Feed Name",
  "link": "https://feed-website.example.com",
  "privacy": "public",
  "tags": ["malware", "botnet"],
  "admins": ["username1"],
  "members": ["username2", "username3"],
  "guests": ["username4", "username5"]
}

Request example

Using curl:

curl -X POST https://api.cymon.io/feeds \
-d '{"feed": {"name": "Feed Name", "privacy": "public", "tags": ["test"]}}' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer xxxxxxxxxx'

Response example

{"status": 200, "message": "feed created"}

Update Feed

Update feed details.

Note that the name field is immutable

Path /feeds/
Method PUT
Authentication required Yes
Parameter Type Required Location Description
feed JSON Yes body Feed object

Sample feed object

{
  "name": "Feed Name",
  "link": "https://feed-website.example.com",
  "privacy": "private",
  "tags": ["malware", "botnet"],
  "admins": ["username1"],
  "members": ["username2", "username3"],
  "guests": []
}

Request example

Using curl:

curl -X PUT https://api.cymon.io/feeds \
-d '{"feed": {"name": "Feed Name", "privacy": "private", "tags": ["test"]}}' \
-H 'Content-Type: application/json'\
-H 'Authorization: Bearer xxxxxxxxxx'

Response example

{"status": 200, "message": "feed updated"}

List Feeds

List of all feeds on Cymon.

Path /feeds/
Method GET
Authentication required Yes

Request parameters

Parameter Type Required Location Description
filter string No querystring Show feeds user has access to. Acceptable values: user

Request example

No filters:

GET /feeds

With filter parameter:

GET /feeds?filter=user

Response example

{
  "feeds": [
    {
        "name": "AlienVault",
        "link": "https://www.alienvault.com",
        "privacy": "public",
        "tags": [
          "malware",
          "spam",
          "scan",
          "botnet"
        ],
        "is_owner": false,
        "is_admin": false,
        "is_member": false,
        "is_guest": false
      },
      {
        "name": "Google SafeBrowsing",
        "link": "https://developers.google.com/safe-browsing/",
        "privacy": "public",
        "tags": [
          "phishing",
          "malware"
        ],
        "is_owner": false,
        "is_admin": false,
        "is_member": false,
        "is_guest": false
      }
  ],
  "total": 2,
  "count": 2
}

Feed Details

Get details for specified feed.

Path /feeds/{feed_name}
Method GET
Authentication required Yes

Request parameters

Parameter Type Required Location Description
feed_name String Yes path Feed name

Request example

GET /feeds/My%20Feed

Special characters must be url-encoded

Response example

{
  "name": "test",
  "link": "http://example.com",
  "tags": [
    "roy",
    "test"
  ],
  "privacy": "public"
}

IoC API

Submit IoC

Report IoC

Path /submit/
Method POST
Authentication required Yes

Request parameters

Parameter Type Required Location Description
report JSON Yes body Report object

Report object

Attribute Type Required Description
timestamp integer Yes Epoch timestamp
feed string Yes Existing feed name
title string Yes Short description
description string No Full-text description
link string No Link for more details on this IoC
tags list of strings Yes IoC tags
iocs object Yes IoC patterns. Accepted keys: ipv4, ipv6, domain, hostname, url, md5, sha1, sha256, ssdeep

Each report object must have at least one of the following tags:

You are encouraged to include additional tags, such as malware name (eg. cryptowall) for easy searching.

Multiple IoC fields can be used to represent related information, such as MD5+URL+IPv4 (when malware was served from URL that resolved to IPv4 address), should all be entered as one IoC. Multiple reports should be submitted if the same MD5 hash was served from other URLs and/or IPs.

Sample report object

{
    "report": {
        "feed": "test",
        "title": "Short IoC title text",
        "description": "Detailed description text",
        "timestamp": 1464153681,
        "tags": ["test"],
        "iocs": {
            "ipv4": "127.0.0.1",
            "url": "http://localhost",
            "hostname": "localhost.localdomain"
        }
    }
}

Response example

{"status": 200, "message": "report accepted"}

Query API

Search by Tags

Search IoC reports by one or more tags.

Path /query/tags/{tags}
Method GET
Authentication required Yes

Request parameters

Parameter Type Required Location Description
tags string Yes path Comma separated tags
from string No querystring Date filter: YYYY-MM-DD (default is -14 days)

Request example

No filters:

GET /query/tags/dnsbl,spam,malicious%20activity

With from date filter:

GET /query/tags/dnsbl,spam,malicious%20activity?from=2016-06-20

Special characters must be url-encoded

Response example

{
  "status": 200,
  "hits": [
    {
      "feed": "cbl.abuseat.org",
      "description": "",
      "tags": [
        "malicious activity",
        "dnsbl",
        "spam"
      ],
      "timestamp": "2016-06-25T16:02:47.000Z",
      "iocs": {
        "ipv4": "91.216.107.90"
      },
      "title": "Blacklisted by cbl.abuseat.org"
    },
    ...
  ],
  "total": 4,
  "count": 4
}

Search by IoC

Search reports by IoC.

Path /query/ioc/{ioc_type}/{ioc_value}
Method GET
Authentication required No

Request parameters

Parameter Type Required Location Description
ioc_type string Yes path Accepted keys: ipv4, ipv6, domain, hostname, url, md5, sha1, sha256, ssdeep
ioc_value string Yes path IoC value
from string No querystring Date filter: YYYY-MM-DD (default is -14 days)

Request examples

No filters:

GET /query/ioc/ipv4/91.216.107.90

With from date filter:

GET /query/ioc/ipv4/91.216.107.90?from=2016-06-20

Response example

{
  "status": 200,
  "hits": [
    {
      "feed": "cbl.abuseat.org",
      "description": "",
      "tags": [
        "malicious activity",
        "dnsbl",
        "spam"
      ],
      "timestamp": "2016-06-25T16:02:47.000Z",
      "iocs": {
        "ipv4": "91.216.107.90"
      },
      "title": "Blacklisted by cbl.abuseat.org"
    },
    ...
  ],
  "total": 4,
  "count": 4
}

Search by Term

Search for IoCs that match a given string.

Path /query/term/{term}
Method GET
Authentication required No

Request parameters

Parameter Type Required Location Description
term string Yes path Text to search for
from string No querystring Date filter: YYYY-MM-DD (default is -14 days)

Request examples

No filters:

GET /query/term/ransomware

With from date filter:

GET /query/term/ransomware?from=2016-07-15

Response example

{
  "status": 200,
  "hits": [
    {
      "feed": "ransomware.tracker.exmaple.com",
      "title": "Ransomware reported by Ransomware Tracker",
      "description": "",
      "tags": [
        "botnet",
        "malware",
        "ransomware"
      ],
      "timestamp": "2016-06-25T16:02:47.000Z",
      "iocs": {
        "ipv4": "91.216.107.90"
      }
    },
    ...
  ],
  "total": 4,
  "count": 4
}

Search by Feed

Get IoC reports for a specific feed (if user has 'read' access to it).

Path /query/feed/{feed_name}
Method GET
Authentication required Yes

Request parameters

Parameter Type Required Location Description
feed_name String Yes path Feed name
from string No querystring Date filter: YYYY-MM-DD (default is -14 days)

Request example

No filters:

GET /query/feed/My%20Feed

With from date filter:

GET /query/feed/My%20Feed?from=2016-06-25

Special characters must be url-encoded

Response example

{
  "status": 200,
  "hits": [
    {
      "feed": "My Feed",
      "tags": [
        "spam"
      ],
      "timestamp": "2016-06-25T16:02:47.000Z",
      "iocs": {
        "ipv4": "91.216.107.90"
      },
      "title": "Blacklisted by My Feed"
    },
    ...
  ],
  "total": 4,
  "count": 4
}

Authentication API

API access requires authentication using JSON Web Token (JWT).

Process overview:

  1. POST credentials to the Login API to receive a token.
  2. Send token in header of each request.
  3. Repeat steps when token expires.

The following diagram shows this process:

jwt process

Visit jwt.io to learn more about JSON Web Tokens

Login

Authenticate using your Cymon.io credentials.

Path /auth/login
Method POST
Authentication required No

Request parameters

Parameter Type Required Location Description
username string Yes JSON Your username
password string Yes JSON Your password

Request example

Using curl:

curl -X POST https://api.cymon.io/auth/login \
-d '{"username": "example", "password": "monkey"}' \
-H 'Content-Type: application/json'

Response body example

{
  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzODY4OTkxMzEsImlzcyI6ImppcmE6MTU0ODk1OTUiLCJxc2giOiI4MDYzZmY0Y2ExZTQxZGY3YmM5MGM4YWI2ZDBmNjIwN2Q0OTFjZjZkYWQ3YzY2ZWE3OTdiNDYxNGI3MTkyMmU5IiwiaWF0IjoxMzg2ODk4OTUxfQ.uKqU9dTB6gKwG6jQCuXYAiMNdfNRw98Hw_IWuA5MaMo",
  "expires": 1466972778,
  "created": 1466969178,
  "status": 200
}

Request HTTP header example

Authenticated requests made to the Cymon API need to include your token in the Authorization HTTP header. The key should be prefixed by the string literal "Bearer", with whitespace separating the two strings. For example:

GET /query/tags/ransomware HTTP/1.1
Host: api.cymon.io
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzODY4OTkxMzEsImlzcyI6ImppcmE6MTU0ODk1OTUiLCJxc2giOiI4MDYzZmY0Y2ExZTQxZGY3YmM5MGM4YWI2ZDBmNjIwN2Q0OTFjZjZkYWQ3YzY2ZWE3OTdiNDYxNGI3MTkyMmU5IiwiaWF0IjoxMzg2ODk4OTUxfQ.uKqU9dTB6gKwG6jQCuXYAiMNdfNRw98Hw_IWuA5MaMo

Authenticated request example

curl -X GET https://api.cymon.io/query/tags/ransomware \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzODY4OTkxMzEsImlzcyI6ImppcmE6MTU0ODk1OTUiLCJxc2giOiI4MDYzZmY0Y2ExZTQxZGY3YmM5MGM4YWI2ZDBmNjIwN2Q0OTFjZjZkYWQ3YzY2ZWE3OTdiNDYxNGI3MTkyMmU5IiwiaWF0IjoxMzg2ODk4OTUxfQ.uKqU9dTB6gKwG6jQCuXYAiMNdfNRw98Hw_IWuA5MaMo'

Unauthenticated responses that are denied permission will result in an HTTP 401 Unauthorized response with an appropriate message in the body. For example:

{
  "message": "Unauthorized"
}

See full example here.

If you don't have a Cymon account, you can create one here

Pagination

Cymon API uses "Limit-Offset" pagination. The client includes both a "limit" and an "offset" query parameter. The limit indicates the maximum number of items to return. The max limit size is 10. The offset indicates the starting position of the query in relation to the complete set of unpaginated items.

The free API will not return results beyond ?offset=1000 to help maintain the service's health (bulk API access is available to partners).

Request example:

GET https://api.cymon.io/query/tags/malware?limit=5&offset=5

Response:

{
    "status": 200,
    "total": 10,
    "count": 5,
    "hits": [
        ...
    ]
}

Examples

Node.js example

var request = require('request');

module.exports.login = function (user, pass, callback) {
  var params = {
    method: "POST",
    body: {username: user, password: pass},
    json: true,
    url: "https://api.cymon.io/auth/login"
  };
  request(params, callback);
};

module.exports.getEventsByTag = function (token, callback) {
  var params = {
    method: "GET",
    url: "https://api.cymon.io/query/tags/malware",
    headers: {
      Authorization: "Bearer " + token
    }
  };
  request(params, callback);
};

module.exports.handler = function (user, pass) {
  // authenticate
  exports.login(user, pass, function (error, auth_res) {
    var token = auth_res.body.jwt;

    // send API request with authorization token
    exports.getEventsByTag(token, function (error, res) {
      console.log(res.body);
    });
  });
};

Python example

import requests
import time
from datetime import datetime

class Cymon(object):
    def __init__(self, username, password, endpoint='https://api.cymon.io'):
        self.endpoint = endpoint
        self.creds = {'username': username, 'password': password}
        self.token = None
        self.token_expiry = None

    def login(self):
        h = {'Content-Type': 'application/json'}
        r = requests.post(self.endpoint + '/auth/login', json=self.creds, headers=h)
        r.raise_for_status()
        self.token = r.json().get('jwt')
        self.token_expiry = datetime.fromtimestamp(r.json().get('expires'))
        return self.token

    def get(self, path):
        self.check_token()
        h = {'Authorization': 'Bearer {}'.format(self.token)}
        r = requests.get(self.endpoint + path, headers=h)
        r.raise_for_status()
        return r

    def post(self, path, params):
        self.check_token()
        h = {
            'Authorization': 'Bearer {}'.format(self.token),
            'Content-Type': 'application/json'
        }
        r = requests.post(self.endpoint + path, json=params, headers=h)
        r.raise_for_status()
        return r

    def check_token(self):
        if self.token is None or self.token_expiry is None:
            self.login()
        if datetime.utcnow() > self.token_expiry:
            self.login()

# initialize connection object
cymon = Cymon("user", "mypassword")

# create new feed
cymon.post("/feeds", {
    "feed": {
        "name": "Test Feed",
        "link": "https://feed-website.example.com",
        "privacy": "private",
        "tags": ["malware", "botnet"],
        "members": ["username1"],
        "guests": ["username2", "username3"]
    }
})

# submit new IoC report
cymon.post("/submit", {
    "report": {
        "feed": "Test Feed",
        "title": "IoC description text",
        "timestamp": int(time.time()),
        "tags": ["test"],
        "iocs": {
            "ipv4": "127.0.0.1",
            "url": "http://localhost",
            "hostname": "localhost.localdomain"
        }
    }
})

Curl example

Login:

curl -X POST https://api.cymon.io/auth/login \
-d '{"username": "example", "password": "monkey"}' \
-H 'Content-Type: application/json'

Search for IoC:

curl -X GET https://api.cymon.io/query/ioc/ipv4/x.x.x.x \
-H 'Authorization: Bearer xxxxxxxxxx'

Submit IoC report:

JSON_DATA=$(cat << EOF
{
  "report": {
    "feed": "My Feed",
    "timestamp": $(date +%s),
    "title": "Botnet activity reported by My Feed",
    "description": "Sandbox analysis revealed a botnet C&C channel",
    "tags": ["botnet", "c2", "cryptowall"],
    "iocs": {
      "ipv4": "x.x.x.x",
      "hostname": "some.host.example.com"
    }
  }
}
EOF
)

curl -X POST https://api.cymon.io/submit \
-d "$JSON_DATA" \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer xxxxxxxxxx'