WebAPI

CAMAL's WebAPI is a simple JSON based WebAPI that allows easy interaction with all CAMAL services. In general, communication with the WebAPI is done through HTTP POST, response to the POST requests will be in the form of JSON objects.

Most use of the WebAPI requires an API key that can be obtained by registering an account. This API key is used for authentication in the POST requests.

This page lists all the API functions provided to users and also demonstrates how they can be used through some examples. Note that the examples given here will be written in Python but you are free to use your favorite language to interact with the WebAPI.

General

This is the only part of the WebAPI that does not require an API key and uses a GET request. It is used to check the status of the WebAPI.

Example:

>>> import urllib2
>>> r = urllib2.Request('https://camalapi.coseinc.com/camal/ping')
>>> response = urllib2.urlopen(r)
>>> response.read()
'CAMAL Web API is working.'

'/ping'
Parameters: none
Description: Used to check if webapi is up and running.
'/stats'
Parameters: none
Description: Obtains queue and sample statistics.

Customers

This section consists of the API used to check the status of your account.

'/customer/info'
Parameters:key
Description: Return the current limits and usage statistics for the current user.
'/customer/quota'
Parameters:key, feature(=upload,download,feed,cluster)
Description:Check if current user has quota for a given feature.

Samples

This section of the API allows you to manage your samples. These APIs allow you upload samples for scanning, download samples from the feed, set the permissions of the samples you own, or get a list of the latest public samples.

Here is an example of a script to download a sample:

import urllib
import urllib2
downloadurl = 'https://camalapi.coseinc.com/camal/files/get'

#required parameters
values = {'key': 'yourapikey', 'file_hash':'0a0a8416446ad0fd4b44df32ac7fe8ca'}
data = urllib.urlencode(values)

#send POST request
r = urllib2.Request(downloadurl, data)
response = urllib2.urlopen(r)
sample = response.read()

#save sample
f = open('yourfilename', 'wb')
f.write(sample)
f.close()


Here is another example of a script for downloading samples. This script gets the latest 255 samples from the feed and downloads them.


import json, urllib, urllib2
from datetime import date

base_url = 'https://camalapi.coseinc.com/camal'
api_key = 'yourapikey'

#---------------gets feed------------------------------
feed_url = base_url + '/files/feed'
feed_value = {'key': api_key}
feed_data = urllib.urlencode(feed_value)
feed_request = urllib2.Request(feed_url, feed_data)
feed_response = urllib2.urlopen(feed_request)
data = json.loads(feed_response.read())

# ---------get strings to use in download many---------
count = 0
for sample in data:
  # maximum of 20 samples per zip file from webapi
  # so each string will contain a maximum of 20 hashes
  if (count == 20):
    files[filecount] = ",".join(files[filecount])
    filecount += 1
    count = 0
    files.append([])
  files[filecount].append(sample['sha1'])
  count += 1
files[filecount] = ",".join(files[filecount])

#---------------start download---------------------------------------------------
download_url = base_url + '/files/get_many'

#sample filename in format YYYY-mm-dd-xx.zip
today = date.today().strftime('%Y-%m-%d')

for file_id, file in enumerate(files):
  download_value = {'key': api_key, 'file_hashes': file}
  download_data = urllib.urlencode(download_value)
  download_request = urllib2.Request(download_url, download_data)
  download_response = urllib2.urlopen(download_request)
  zipped_data = download_response.read()
  #download to current directory
  f = open('%s-%s.zip' % (today, file_id), 'wb')
  f.write(zipped_data)
  f.close()


Since the Python standard library does not support HTTP multipart/form-data POST requests, the next example makes use of a package called requests. This example shows how a file can be uploaded to CAMAL for scanning.


import requests
uploadurl = 'https://camalapi.coseinc.com/camal/files/upload'

#required parameters, 0 for public, 1 for private
values = {'key': 'yourapikey', 'is_public':'0'}
#assuming there is a file "samplefile" in the same path as this script
files = {'upload_sample': open(samplefile, 'rb')}
r = requests.post(uploadurl, data=values, files=files)
print r.text
>>> {sample_md5: 51f59f6919d48e6e46c3000d917739e5, sample_sha1: c5ceccc84230ed26060617658360b35367acae3d, sample_sha256: dde6017d016146437502464e3dae484977b70ddce4609bf8f3b4a8eb63771b29, queued:1}


'/files/exists'
Parameters: key, file_hash(md5, sha1, or sha256)
Description:Checks if a given sample hash (MD5, SHA1 or SHA256) exists in our database.
'/files/info'
Parameters: key, file_hash(md5, sha1, or sha256)
Description:Returns information about a sample.
'/files/upload'
Parameters: key, upload_sample, is_public(0-public, 1-private)
Description:Upload a single sample.
'/files/upload_many'
Parameters: key, zip_sample, is_public(0-public, 1-private)
Description:Upload many samples (in a zip file).
'/files/get'
Parameters: key, file_hash
Description:Download a single sample.
'/files/get_pcap'
Parameters: key, file_hash
Description:Download pcap file for sample.
'/files/get_many'
Parameters: key, file_hashes (seperated by comma ',')
Description:Download many samples.
'/files/antivirus_report'
Parameters: key, file_hash (sha256)
Description:Gets results from antivirus scanning.
'/files/feed'
Parameters: key, offset
Description:Get a list of latest 255 files. Offset is optional and can be used to get older feeds.
The offset here is just the number of files from the newest sample.
For example if there are samples A,B,C,D with A being the newest and D being the oldest.
Querying feed normally returns sample A, B and C.
If you query feed with offset 1, it will return sample B,C and D. (in the real case it will return 255 samples not just 3)
'/files/last'
Parameters: key
Description:Gets latest sample.
'/files/set_permissions'
Parameters: key, file_hash, permissions
Description:Set the permissions of the sample if the customer is the owner of it. Only the 1st uploaded of a sample is considered the owner.
The permissions value can be either 0 (public) or 1 (private).
'/files/set_all_private'
Parameters: key
Description:Set permissions of all samples owned by the user to private.
'/files/check_permissions'
Parameters: key, file_hash
Description:Check if the user has permission to download a sample.

Reports

CAMAL provides in-depth reports of all files that have been scanned. In addition to that, there is also a cluster report that provides a visual representation of similar files that have been scanned. The API here allows retrieval of these reports.

#This example shows how to download the report for a file you are interested in.
#It is similar to the example given for downloading of samples
import urllib
import urllib2
downloadurl = 'https://camalapi.coseinc.com/camal/reports/get'

#required parameters
values = {'key': 'yourapikey', 'file_hash':'0a0a8416446ad0fd4b44df32ac7fe8ca'}
data = urllib.urlencode(values)

#send POST request
r = urllib2.Request(downloadurl, data)
response = urllib2.urlopen(r)
report = response.read()

#save report
f = open('yourfilename.html', 'wb')
f.write(report)
f.close()

'/reports/get'
Parameters: key, file_hash
Description:Gets report for a single sample.
'/reports/cluster'
Parameters: key, file_hash
Description:Gets the cluster report for a samples. This is a visual representation of similar files that have been scanned.
*An internet connection is required to view this report properly even when it has been downloaded to your drive.

Clusters

Various APIs used to obtain cluster information of a sample. Includes finding similar samples through fuzzy hashing or graph hashing, and feeds of latest clusters found

#Assuming you have uploaded a sample with MD5 hash '03e0d33e4d7255c3351383ee0ab080e9'
#This script shows how to find similar samples and download their reports
import urllib
import urllib2
import json
similarurl = 'https://camalapi.coseinc.com/camal/clusters/similar_graph'
yourapikey = 'insertapikeyhere'
#required parameters
values = {'key': yourapikey, 'file_hash':'03e0d33e4d7255c3351383ee0ab080e9'}
data = urllib.urlencode(values)

#send POST request
r = urllib2.Request(similarurl, data)
response = urllib2.urlopen(r)
similar_samples = json.loads(response.read())

#From response, get SHA1 hash of similar samples and get reports
reporturl = 'https://camalapi.coseinc.com/camal/reports/get'
for sample in similar_samples:
  values = {'key': yourapikey, 'file_hash':sample['sample_sha256']}
  data = urllib.urlencode(values)
  r = urllib2.Request(downloadurl, data)
  response = urllib2.urlopen(r)
  report = response.read()
  f = open('%s.html' % sample['sample_sha256'], 'wb')
  f.write(report)
  f.close()

'/clusters/similar_binary'
Parameters: key, file_hash or hash
Description:Find similar samples via exact fuzzy hash match.
'/clusters/similar_graph'
Parameters: key, file_hash or hash
Description:Find similar samples via exact fuzzy graph hash match.
'/clusters/similar'
Parameters: key, file_hash or hash, search_type(fuzzy or graph)
Description:Find similar samples by either fuzzy or graph hash exact match.
'/clusters/raw_feed'
Parameters: key, offset
Description:Get the list of raw, probably unprocessed yet, new groups found.
'/clusters/raw_last'
Parameters: key
Description:Get the last raw and probably unprocessed group found.
'/clusters/non_clustered'
Parameters: key, offset
Description:Get the list of non clustered files.
'/clusters/non_clustered_last'
Parameters: key
Description:Get the last non clustered file.
'/clusters/feed'
Parameters: key, offset
Description:Get the feed of new (already processed) clusters found.
'/clusters/last'
Parameters: key
Description:Get the last (already processed) cluster found.
'/clusters/get_samples'
Parameters: key, cluster
Description:Gets a list of samples for the given cluster identifier(cluster id).
'/clusters/get_hashes'
Parameters: key, cluster
Description:Return the list of hashes for the given cluster identifier(cluster id). It also returns the total number of clustered samples for each graph hash.
'/clusters/get_hierarchy'
Parameters: key, cluster
Description: