PolySwarmPolySwarmPolySwarmPolySwarm
Go to PolySwarm
Home

PolySwarm Customer API v3

A Python interface for interacting with version 3 of the PolySwarm Customer APIs, to view the Legacy Version 2 documentation, navigate here.

Supports Python 3.7 and greater.

Getting Started

Installation

From PyPI:

$ pip install polyswarm-api

If you get an error about a missing package named wheel, that means your version of pip is too old. You need pip version 19 or newer. To update pip, run pip install -U pip.

From source:

$ python setup.py install

If you get an error about a missing package named wheel, that means your version of setuptools is too old. You need setuptools version 40.8.0 or newer. To update setuptools, run pip install -U setuptools.

Creating an API Client

from polyswarm_api.api import PolyswarmAPI

api_key = "1234123412341234123412341234"
community_name = "default"
api = PolyswarmAPI(key=api_key, community=community_name)

Locate the api_key for the User/Team from here

If the Subscription plan has "Private Communities" then Define the Private Community Name provided to you by PolySwarm in the community_name value above and the Team API key in the api_key field.

Retrieve account information

Feature What is it for? Package
Account Details Retrieve account information including account number and what teams you are part of. api.account_whois()
Account features and quotas Retrieve what features your account/team has enabled and the quota details. api.features()

Scanning an Artifact

Feature What is it for? Package
Scan File Scan a File in the PolySwarm network to retrieve a verdict. api.submit()
Scan URL Scan a URL in the PolySwarm network to retrieve a verdict. api.submit(URL, artifact_type='url')
Scan a File
FILE = '/home/user/malicious.bin'

positives = 0
total = 0

instance = api.submit(FILE)
result = api.wait_for(instance)

if result.failed:
    print(f'Failed to get results')
    sys.exit()

print('Engine Assertions:')
for assertion in result.assertions:
    if assertion.verdict:
        positives += 1
    total += 1
    print('\tEngine {} asserts {}'.\
            format(assertion.author_name,
                   'Malicious' if assertion.verdict else 'Benign'))

print(f'Positives: {positives}')
print(f'Total: {total}')
print(f'PolyScore: {result.polyscore}\n')

print(f'sha256: {result.sha256}')
print(f'sha1: {result.sha1}')
print(f'md5: {result.md5}')
print(f'Extended type: {result.extended_type}')
print(f'First Seen: {result.first_seen}')
print(f'Last Seen: {result.last_seen}\n')

print(f'Permalink: {result.permalink}')

Here is another example of sending a sample inside a zip file that is protected with a password infected:

result = api.submit('./malicious-enc.zip',
                    preprocessing={'type': 'zip', 'password': 'infected'})
print(result.status)
Scan a URL

When scanning a URL, you should always include the protocol (http:// or https://).

URL = 'https://polyswarm.io'

positives = 0
total = 0

instance = api.submit(URL, artifact_type='url')
result = api.wait_for(instance)

if result.failed:
    print(f'Failed to get results')
    sys.exit()

print('Engine Assertions:')
for assertion in result.assertions:
    if assertion.verdict:
        positives += 1
    total += 1
    print('\tEngine {} asserts {}'.\
            format(assertion.author_name,
                   'Malicious' if assertion.verdict else 'Benign'))

print(f'Positives: {positives}')
print(f'Total: {total}\n')

print(f'Permalink: {result.permalink}')

Rescanning Artifacts

Feature What is it for? Package
Rescan Rescan an Artifact to provide up to date verdict and analysis api.rescan()
instance = api.rescan("275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f")
result = api.wait_for(instance)

if result.failed:
    print(f'Failed to get results')
    sys.exit()

positives = 0
total = 0

print('Engine Assertions:')
for assertion in result.assertions:
    if assertion.verdict:
        positives += 1
    total += 1
    print('\tEngine {} asserts {}'.\
            format(assertion.author_name,
                   'Malicious' if assertion.verdict else 'Benign'))

print(f'Positives: {positives}')
print(f'Total: {total}')
print(f'PolyScore: {result.polyscore}\n')

print(f'sha256: {result.sha256}')
print(f'sha1: {result.sha1}')
print(f'md5: {result.md5}')
print(f'Extended type: {result.extended_type}')
print(f'First Seen: {result.first_seen}')
print(f'Last Seen: {result.last_seen}\n')

print(f'Permalink: {result.permalink}')

Downloading Artifacts

Feature What is it for? Package
Download Artifact Download the file locally by searching with a hash value api.download()
OUTPUT_DIR = '/tmp/'
EICAR_HASH = '275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f'

artifact = api.download(OUTPUT_DIR, EICAR_HASH)

Reporting

Feature What is it for? Package
Create report Create a report in html or pdf for an artifact, this endpoint is also used to create a zip file of sandbox artifacts. api.report_create()
Get report status Retrieve the report creation status api.report_get()
Download report Download the finished report locally api.report_download()
List templates List the templates api.report_template_list()
Create a template Create a new template api.report_template_create()
Delete a template Delete a template api.report_template_delete()
Get template details View specific template api.report_template_get()
Update template Update a current template api.report_template_update()
Update template logo Upload a logo for template api.report_template_logo_update()
Delete template logo Delete a logo for a template api.report_template_logo_delete()
Download template logo Download a logo from the template api.report_template_logo_download()
Create report
report = api.report_create(type='scan', format='pdf', instance_id=instance_id)
print(f'Report ID: {report.id} (State: {report.state})')

# `report_wait_for()` is a method to fetch the report progress over
# and over until is not in PENDING state anymore
report = api.report_wait_for(report.id, timeout=timeout_seconds)
if report.state == 'SUCCEEDED':
    response = requests.get(report.url, stream=True)
    response.raise_for_status()
    with open(f'scan-{instance_id}.pdf', 'wb') as f:
        response.raw.decode_content = True
        shutil.copyfileobj(response.raw, f)
else:
    print(f'Report failed (State: {report.state})')
Create a ZIP file of Sandbox Artifacts

The template_metadata value can contain one or many separated by commas of: report , raw_report , screenshot , recording , dropped_file , memory_dump , pcap or jarm.

The below example highlights how to download a zip file that contains the following sandbox files: pdf report, report, raw_report, screenshot and jarm.

The sandbox_task_id is the ID for the sandbox session that you wish to download the files from.

report = api.report_create(type='sandbox_zip', format='zip', sandbox_task_id=123456, template_metadata={'zip_report_ids': [123], 'sandbox_artifact_types': ['report', 'raw_report', 'screenshot', 'jarm'] })
List Templates
results = api.report_template_list()
for template in results:
   print(f'ID: {template.id}')
   print(f'Created: {template.created}')
   print(f'Name: {template.template_name}')
   print(f'Color: {template.primary_color}\n')
Create a template
result = api.report_template_create(template_name='testreport1')
Delete a template
result = api.report_template_delete(98453877554394669)
Get template details
result = api.report_template_get(95389624286242180)
print(f'ID: {result.id}')
print(f'Created: {result.created}')
print(f'Name: {result.template_name}')
print(f'Color: {result.primary_color}\n')

Hash Searching

Feature What is it for? Package
Search Search the polyswarm dataset with a hash (sha256,md5 or sha1) value api.search()
# sha256, md5, and sha1 supported
EICAR_HASH = '275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f'

positives = 0
total = 0

try:
    results = api.search(EICAR_HASH)

    for result in results:
        if result.failed:
            print(f'Failed to get result.')
            break

        if not result.assertions:
            print('Artifact not scanned yet - Run rescan for Engine Assertions.')
        else:
            print('Engine Assertions:')

            for assertion in result.assertions:
                if assertion.verdict:
                    positives += 1
                total += 1
                print('\tEngine {} asserts {}'. \
                      format(assertion.author_name,
                             'Malicious' if assertion.verdict else 'Benign'))

        print(f'Positives: {positives}')
        print(f'Total: {total}')
        print(f'PolyScore: {result.polyscore}\n')

        print(f'sha256: {result.sha256}')
        print(f'sha1: {result.sha1}')
        print(f'md5: {result.md5}')
        print(f'Extended type: {result.extended_type}')
        print(f'First Seen: {result.first_seen}')
        print(f'Last Seen: {result.last_seen}\n')

        print(f'Permalink: {result.permalink}')
except exceptions.NoResultsException:
    print(f'No results for the provided hash.')

Metadata Searching

PolySwarm's Metadata Search is a powerful and flexible means to discover previously unknown malware. Metadata commands can be built and fed into the arguments for the below api endpoint. To understand how to build out this general argument, fields to use and logic, see the cli pages here.

Feature What is it for? Package
Search Search the polyswarm dataset for metadata api.search_by_metadata()

The following sections will list specific examples with the scopes of the searches and real world use case examples.

Artifact Scope

Attributes that provide contextual and surface-level information about Artifacts are available under the artifact Scope. To see the full scope of the supported Attributes, see here

query = 'artifact.sha256:"275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f"'

results = api.search_by_metadata(query)

# Our query is by cryptographic hash; we expect at most 1 result.
# Regardless, it's good practice to properly handle multiple results.
for result in results:
    print(f"Artifact Attributes: {result.artifact}")

Running this script will produce:

Artifact Attributes: {'created': '2019-05-03T23:43:42.298385+00:00', 'id': '90465088441197206', 'md5': '44d88612fea8a8f36de82e1278abb02f', 'sha1': '3395856ce81f2b7382dee72602f798b642f14140', 'sha256': '275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f'}

Scan Attributes

Attributes that describe Scans executed against Artifacts are available under the scan Scope. To see the full scope of the supported Attributes, see here

Deprecations:

The scan.filename Attribute is deprecated. Please use scan.[first_scan|latest_scan].filename.

The scan.url Attribute is deprecated. Please use scan.[first_scan|latest_scan].url.

The scan.first_seen and scan.last_seen Attributes are deprecated. Please use scan.[first_scan|latest_scan].created.

The scan.countries Attribute will soon be replaced by artifact.countries.

PolySwarm maintains a record of all Scan Result Sets, but only the first Scan (scan.first_scan) and latest Scan (scan.latest_scan) are generally available via Metadata Search.

Engine Result Objects

Attributes that describe a given Engine's assertion (malicious or benign), identified malware_family (as applicable) and additional optional data are available under the scan.<NAME OF SCAN>.<ENGINE NAME> (e.g. scan.latest_scan.ClamAV) Scope.

Remember: In PolySwarm, Engines are not required to provide responses to Scan requests. For this and other reasons (e.g. in the case of errors), any particular Engine may or may not appear within the scan.<NAME OF SCAN> scope. An Engine that appeared in scan.first_scan may or may not appear in scan.latest_scan and vice versa.

Things to keep in mind:

  1. Although unlikely, 0 Engines may choose to respond to any given Scan Request, resulting in 0 Engine Result Objects for the given Scan Result Set. Do not assume there is at least 1 Engine Result Object on any given Scan Result Set.
  2. Do not assume that any particular Engine produced an Engine Result Object for a given Scan Result Set.

ClamAV's Engine Result Object for the first_scan Scan Result Set for an EICAR file in context:

query = 'artifact.sha256:"275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f"'

results = api.search_by_metadata(query)

# Our query is by cryptographic hash; we expect at most 1 result.
# Regardless, it's good practice to properly handle multiple results.
for result in results:

    print(f"Artifact: {result.sha256}")

    # Artifacts that have not been scanned will lack the scan object in Metadata results.
    if not hasattr(result, 'scan'):
        print(f"Artifact has not been scanned.")
        continue

    # Engines choose whether to respond to each scan request.
    # We cannot assume any given Engine Result Object exists, so much check first.
    if not 'ClamAV' in result.scan['latest_scan']:
        print(f"ClamAV did not deliver an assertion for the latest scan of this Artifact.")
        continue

    # If the scan object exists, the scan.latest_scan object will also exist.
    print(f"ClamAV asserted: {result.scan['latest_scan']['ClamAV']['assertion']}")
    print(f"ClamAV identified the malware family as: {result.scan['latest_scan']['ClamAV']['metadata']['malware_family']}")

Running this script will produce:

Artifact: 275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f
ClamAV asserted: malicious
ClamAV identified the malware family as: Win.Test.EICAR_HDB-1

Tool Attributes

Metadata Extraction Tools are applied on a per-Artifact basis according to some relevancy criteria.

Attributes produced by each Tool are addressed at the top level by the Tool's name. To see the full scope of the supported Attributes, see here

query = 'artifact.sha256:"275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f"'

results = api.search_by_metadata(query)

# Our query is by cryptographic hash; we expect at most 1 result.
# Regardless, it's good practice to properly handle multiple results.
for result in results:

    print(f"Artifact: {result.sha256}")

    # The hash Tool applies to all Artifacts and is eventually run on all Artifacts.
    # There is a window, however, between submission and Tool execution, so we verify
    # the output from the Tool exists before attempting to access output members.
    if not hasattr(result, 'hash'):
        print(f"The hash Tool hasn't been run on this Artifact yet.")
        continue

    print(f"ssdeep fuzzy hash of Artifact: {result.hash['ssdeep']}")

Running this script will produce:

Artifact: 275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f
ssdeep fuzzy hash of Artifact: 3:a+JraNvsgzsVqSwHq9:tJuOgzsko

Metadata Examples

PolySwarm's Metadata Search is a powerful tool for finding unique variants and even completely new versions of malware.

In this section, we'll run through several real-world example hunts where new malware, including brand new versions of malware that hadn't be previously published, were found using PolySwarm's Metadata Search.

If you wish to use the cli for these examples see here.

Hunting Syrian Nation State Android Malware

Lookout published a blog post on COVID-19 related Android malware released by the Syrian Electronic Army.

The post discloses:

  1. Where the command and control (C2) addresses are stored within the malicious applications (within res/values/strings.xml)
  2. A list of SHA1 hashes of applications known to belong to this family of malware

First, we look up Lookout's first SHA1 hash on PolySwarm:

  • via the Web UI: link
  • via the Python library:
query = 'hash.sha1:"1aefc2ebaf1a78f23473ce6275b0b514bbcdfb08"'

results = api.search_by_metadata(query)

for result in results:
    print(f"Artifact Attributes: {result.artifact}")

We have a hit!

Next, we download the Artifact (using your choice of Web UI, CLI or Python) and use apktool with the d flag to extract res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    ...
    <string name="MT_Bin_dup_0x7f0c0020">Android Telegram</string>
    <string name="MT_Bin_dup_0x7f0c0021">10000</string>
    <string name="MT_Bin_dup_0x7f0c0022">82.137.218.185</string>
    ...
</resources>

It appears as though the C2 address is 82.137.218.185. This information was not published in Lookout's blog post.

We can use Metadata Search to "pivot" using this IP(v4) address:

  • via the Web UI: link (must be logged in to view)
  • via the Python library:
results = api.search_by_metadata("*", ip="82.137.218.185")

for result in results:
    print(f"Artifact Attributes: {result.artifact}")

At the time of writing, we see:

  • 50 results,
  • at least 23 of which were not identified by Lookout in their blog post, and
  • at least 5 of which cannot be found on platforms similar to PolySwarm.

Using PolySwarm, researchers can quickly identify additional variants of malware and produce something that immediately expands on the public knowledge of the threat.

Hunting Iranian Nation State Spyware

ZDNet published an article on Iran's official COVID-19 tracker application that sends the real time location of installees to the Iranian government.

The article provides only a single IOC - a SHA256 hash (0f73ac8839f153cf0e830554d9b34af2ea90fd6514ed3992b66a96bc9c12bb4b) we can find on PolySwarm:

  • via the Web UI: link
  • via the Python library:
query = 'hash.sha256:"0f73ac8839f153cf0e830554d9b34af2ea90fd6514ed3992b66a96bc9c12bb4b"'

results = api.search_by_metadata(query)

for result in results:
    print(f"Artifact Attributes: {result.artifact}")

Let's take a look some of the Metadata Attributes from this Artifact:

{
    "artifact": {
        "created": "2020-03-10T10:16:50.900548+00:00",
        "id": "71592690635387748",
        "md5": "766e5ecf6b1d86abf401ad9223de857d",
        "sha1": "f1271aa0ccf79d16b036bac5320ed4349af69b65",
        "sha256": "0f73ac8839f153cf0e830554d9b34af2ea90fd6514ed3992b66a96bc9c12bb4b"
    },
    ...
    "strings": {
        "domains": [
            "V.mr",
            "",
            "covid-19-e9057.appspot.com",
            "p.to",
            "II1046766097017-4va56jc12ajt308tpbuge0tc5iqla179.apps.googleusercontent.com",
            "b.mc",
            "YJ.cz",
            "6.om",
            "6.gm",
            "covid-19-e9057.firebaseio.com"
        ],
        ...
    }
}

This excerpt was taken from polyswarm --fmt pretty-json search metadata 'hash.sha256:"0f73ac8839f153cf0e830554d9b34af2ea90fd6514ed3992b66a96bc9c12bb4b"'. The data is, of course, available to the Web UI and Python library interfaces as well.

There are several interesting domains extracted by the strings Tool:

  1. covid-19-e9057.appspot.com
  2. covid-19-e9057.firebaseio.com

It appears as though some portion of the Iranian government's backend for this app is Google's Appspot and Firebase services. This is mildly interesting because Google removed the application from their Play Store.

Next, we conduct a Metadata Search for the unique portion of these domains (covid-19-e9057) + a wildcard (*) to find additional Artifacts that contain these strings:

$ polyswarm --fmt sha256 search metadata 'strings.domains:covid-19-e9057*'

This search nets 4 Artifacts, all of which have been identified as malicious by Engines on PolySwarm. 3 of these Artifacts were, of course, not mentioned in the ZDNet article. Perhaps they have new functionality worth investigating!

Switching gears from Android to Windows malware, TrendMico recently published a blog post on some malware exploiting the rise in Zoom popularity.

Among other tidbits mentioned in the article, this malware:

  1. is a Powershell script,
  2. that embeds a 7zip extractor, and
  3. 7zip-compressed Tor, coinminer (the actual malware) and (legitimate) Zoom installers

The malware will cause the victim machine to mine cryptocurrency if the infected computer is powerful enough (notably, has a discrete GPU) over Tor.

TrendMicro published a handful of IOCs, including a C2 URL: https://2no.co/1O5aW.

Again, we turn to Metadata search to find Artifacts that contain this URL:

  • via the Web UI: link (must be logged in to view)
  • via the Python library:
query = 'strings.urls:*2no.co*1O5aW'

results = api.search_by_metadata(query)

for result in results:
    print(f"Artifact Attributes: {result.artifact}")

We get 1 result, which was not part of the IOCs published by TrendMicro, but is clearly a PowerShell script exactly as described in their blog!

Hunt for Exploits

Often when identifying malware families, Engines on PolySwarm will name the malware family and the exploited vulnerability using its associated CVE number.

Users interested in vulnerability analysis and exploitation can use PolySwarm to find Artifacts that exploit known vulnerabilities!

  • via the Web UI: link (must be logged in to view)
  • via the Python library:
query = 'scan.latest_scan.\*.metadata.malware_family:*cve*'

results = api.search_by_metadata(query)

for result in results:
    print(f"Artifact Attributes: {result.artifact}")

At the time of writing, this query returns 12885 results!

Hunting EventBot

PolySwarm has been tracking the EventBot malware, a family of Android trojan applications that spread automonmously and seek to steal credentials.

For full details on the latest EventBot samples along with Metadata search tips for finding these samples on PolySwarm, please consult our Blog.

Malicious Artifacts ingested within a specified 5 minute time window

The following Python script will print assertion (malicious / benign) information on Artifacts ingested 5 minutes prior to the script being run:

import os
from polyswarm_api.api import PolyswarmAPI
from datetime import datetime, timedelta

end = datetime.utcnow().replace(microsecond=0)

start = end - timedelta(minutes=5)

for metadata_result_obj in api.search_by_metadata('artifact.created:[{} TO {}]'.format(start.isoformat(), end.isoformat())):
    if metadata_result_obj.malicious and metadata_result_obj.malicious > 2:
        print("{} marked malicious by {} engines and benign by {}".format(metadata_result_obj.sha256, metadata_result_obj.malicious, metadata_result_obj.benign))
        artifact_instance = next(api.search(metadata_result_obj.sha256))
        print("Polyscore: {}".format(artifact_instance.polyscore))
        break

IOC Searching

IOC Searching can be split into three groups of functions, these are:

Feature What is it for? Package
Associated IOCs Searching for Associated IOCs related to a Hash api.iocs_by_hash()
Associated Hashes Searching for Associated Hashes to a IP, URL, imphash or MITRE TTP api.search_by_ioc()
Known Good Domains Check for known good domains and IPs api.check_known_hosts()

Searching for Associated IOCs

results = api.iocs_by_hash('sha256', 'aac08c6f7474c979acf2a3aef1f2727820ece755001530cdebf346b5d1ae2ccb', hide_known_good=True)
for result in results:
    iocs = result.json
    print(f"ips: {iocs['ips']}")
    print(f"urls: {iocs['urls']}")
    print(f"ttps: {iocs['ttps']}")
    print(f"imphash: {iocs['imphash']}")

Searching for Associated Hashes

# IOC Search by ip
for result in api.search_by_ioc(ip="108.159.227.121"):
    print(f"sha256: {result.json}")


# IOC Search by domain
for result in api.search_by_ioc(domain="img-s-msn-com.akamaized.net"):
    print(f"sha256: {result.json}")

# IOC Search by MITRE ttp
for result in api.search_by_ioc(ttp="T1060"):
    print(f"sha256: {result.json}")

Searching for Known Good Domains and IPs

results = api.check_known_hosts(domains=["polyswarm.network"], ips=["0.0.0.0"]):
for result in results:
    ioc = result.json
    print(f"type:{ioc.type}, host:{ioc.host}, source:{ioc.source}, good:{ioc.good}")

A word of caution with Known Good checking!

Our list of known good domains and IPs is not all-inclusive! Our goal for this feature is to provide an easy way to find the top most commonly known good domains and IPs, so they can be excluded from analysis.

Sandboxing

Sandboxing in PolySwarm provides the ability to submit files directly to be sandboxed, submit Artifacts already in PolySwarm to be sandboxed, and review what has been submitted to be sandboxed. Sandbox Analysis will take around 2-5 minutes before the results can be accessed.

To view some commonly asked questions and answers about Sandboxing , see here

Feature What is it for? Package
Submit File Submit a file to be sandboxed, define the sandbox name along with the sandbox VM. api.sandbox_file()
Submit URL Submit a URL to be sandboxed, define the sandbox name along with the sandbox vm, and chosen browser. api.sandbox_url()
Submit Submit an already-scanned artifact for processing, provide the instance id of the artifact, the sandbox name along with the sandbox vm. api.sandbox()
List List the available Sandbox providers, to obtain the sandbox name and sandbox vm. api.sandbox_providers()
Lookup Get a sandbox task by id. api.sandbox_task_status()
Lookup Latest Lookup the latest sandbox task by sha256 and sandbox provider name, providing the metadata from the sandbox api.sandbox_task_latest()
List Tasks List sandbox tasks that were created by you or someone on your team. api.sandbox_my_tasks_list()
Search Search sandbox tasks by sha256 and sandbox, status, startdate, and/or enddate. api.sandbox_task_list()
Download Download Reports and other sandbox artifacts. api.download_id()
Sandboxing a File

Want to know what files types are supported? See here

network_enabled This boolean controls the network access for a sandbox execution. If this value is not passed or None, the default for a public community is True and a private community is False.

result = api.sandbox_file('./malicious.exe', 'triage', 'windows11-21h2-x64')
print(result.status)

Here is another example of sending a sample inside a zip file that is protected with a password infected:

result = api.sandbox_file('./malicious-enc.zip',
                          'triage',
                          'windows11-21h2-x64',
                          preprocessing={'type': 'zip', 'password': 'infected'},
                          network_enabled=True)
print(result.status)

Sandboxes have multiple returned statuses, these are listed below.

Status What is it for?
Success Finished processing correctly.
Started Sandbox session has started.
Collecting Data Sandbox session has been successful and data is being collected.
Failed Sandbox session has failed, this can be due to many reasons.
Pending Sandbox session is queued up and ready to start.
Delayed Sandbox session has been delayed and will start soon.
Failed with Quota Reimbursement Finished processing but failed, quota will be reimbursed.
Timed out with Quota Reimbursement Delayed in the queue for too long, got timed out and then reimbursement.
Sandboxing a URL
result = api.sandbox_url('www.polyswarm.io', 'triage', 'windows11-21h2-x64', browser='edge')
print(result.status)

If the URL is stored in a QR Code image, here is how to send it:

result = api.sandbox_url(None,
                         'cape',
                         'win-10-build-19041',
                         artifact='/path/to/qrcode.png',
                         preprocessing={'type': 'qrcode'},
                         browser='firefox')
print(result.status)
Sandboxing an Existing Artifact
result = api.sandbox(42445563653708569, 'triage', 'windows11-21h2-x64', True)
print(result.status)
Sandboxing in a Private Community

When sandboxing in a private community, if the network_enabled flag is not passed, it will by default be True for a public community and False for a private community.

result = api.sandbox_file('./tests/eicar.yara', 'triage', 'windows11-21h2-x64')
List Sandbox Providers
sandboxes = api.sandbox_providers()
print(sandboxes)
Lookup Sandbox Task
task = api.sandbox_task_status(53445563653708569)
print(task)
Download Sandbox Artifacts
task = api.download_id('./outdir', 53445563653708569)
print(task)
Lookup Latest Sandbox Task
latest = api.sandbox_task_latest('18e5b8fe65e8f73c3a4a637c258c02aeec8a6ab702b15b7ee73f5631a9879e40', 'triage')
print(latest)
List my Sandbox Tasks
tasks = api.sandbox_my_tasks_list(sandbox='triage')
print(tasks)
Search Sandbox Tasks
tasks = api.sandbox_task_list(sandbox='triage', start_date='2023-10-31', status="SUCCEEDED")
print(tasks)

Hunting with Yara

Hunting with Yara can be split into the below three sections:

Managing Yara Rulesets

Feature What is it for? Package
Create Create a Ruleset to be used in Hunting. api.ruleset_create()
List List the Rulesets that have been created. api.ruleset_list()
Update Update the ruleset with new values. api.ruleset_update()
Delete Delete a Ruleset permanently. api.ruleset_delete()
Create Ruleset
new_ruleset = api.ruleset_create(name='eicar',
                                 rules=open('eicar.yara').read(),
                                 description='eicar ruleset')
print(f'ID: {new_ruleset.id}')
List Rulesets
rulesets = api.ruleset_list()

for ruleset in rulesets:
    print(f'ID: {ruleset.id}')
Update Ruleset
# updating the ruleset yara rules (can also update name and description)
api.ruleset_update(new_ruleset.id, rules=open('another.yara').read())
Delete Ruleset
api.ruleset_delete(new_ruleset.id)

Live Hunts

Feature What is it for? Package
Get Ruleset ID Get the ruleset id required to start a Live Hunt. api.ruleset_get()
Start Start a Live Hunt based on a ruleset. api.live_start()
View Live Results of a Live Hunt View all the live results generated from the live hunts. api.live_feed()
View a Singular Result Inspect a particular result and get a download link. api.live_result()
Delete Delete a Live Hunt permanently. api.live_feed_delete()
Stop Stop a Live Hunt. api.live_stop()
Get Ruleset ID
ruleset = api.ruleset_get(57989886451857569)
Start Live Hunt
ruleset = api.live_start(ruleset.id)
print(f'ID: {ruleset.livescan_id}')
View Live Results of a Live Hunt
# reverse chronologically ordered iterator
results = api.live_feed(since=999999)
for result in results:
    print(f'ID: {result.id}')
View a Singular Result
# you can inspect more details about a single result
# based on its id, it also provides a download link
# to the file and the origial yara rule used
# these extra info does not come directly from the
# feed listing method for performance reasons
result = api.live_result(91163237970748480)
print(f'ID: {result.id}')
print(f'URL: {result.download_url}')
Delete a Result
api.live_feed_delete([91163237970748480])
Stop a Live Hunt
ruleset = api.ruleset_get(57989886451857569)
ruleset = api.live_stop(ruleset.id)

Historical Hunts

Feature What is it for? Package
Create Create a Historical Hunt by providing a Yara ruleset. api.historical_create
Update Update the Historical Hunt. api.historical_update()
List Hunts List the Historical Hunts. api.historical_list()
View Details View Historical Hunt Details. api.historical_get()
View Results View the results of a Historical Hunt. api.historical_results()
View Single Result View and Download a Single Result. api.historical_result()
Delete Result Delete an undesirable result. api.historical_results_delete()
Delete Hunt Delete an Historical Hunt. api.historical_delete()
Create a Historical Hunt
historical = api.historical_create(rule=open('eicar.yara').read())
print(f'ID: {historical.id}')
Update a Historical Hunt
# the only update you can perform on a historical hunt
# is to cancel the hunt before it finishes
api.historical_update(49988514210960880)
List Historical Hunts
# you can also list all historical hunts you have in your account
results = api.historical_list(since=9999999)
for result in results:
    print(f'ID: {result.id}')
View Historical Hunt Details
# you can retrieve extra information about the hunt
# this also includes a consolidated results csv
historical = api.historical_get(48011760326110718)
print(f'ID: {historical.id}')
print(f'Results CSV: {historical.results_csv_uri}')
View Historical Hunt Results
# you can check the results of a historical hunt
results = api.historical_results(48011760326110718)
for result in results:
    print(f'ID: {result.id}')
View a Singular Historical Hunt Result
# retrieve a single result with extra information
result = api.historical_result(89734617019442134)
print(f'ID: {result.id}')
print(f'URL: {result.download_url}')
Delete an Historical Hunt Result
# delete an undesirable result
api.historical_results_delete([89734617019442134])
Delete a Historical Hunt
# you can delete a historical hunt
# keep in mind that this is an async process and the
# hunt will be scheduled for deletio..
api.historical_delete(49988514210960880)

Get a Stream

Feature What is it for? Package
Stream Fetch a Stream of data from PolySwarm. api.stream()
SINCE = 60 # Fetch stream from the last 60 minutes
streams = api.stream(since=SINCE)

for stream in streams:
    print(f'ID: {stream.id}')
    print(f'URI: {stream.uri}')
    print(f'Created: {stream.created}')
    print(f'Community: {stream.community}')

Stream is a paid feature that is added to an account on a case-by-case basis. If you'd like to add this feature to your account, contact us at [email protected].

Changelog

Version 3.11.0

Release Date: 2024-12-09 Breaking Changes: N/A

Item Topic Description
1.0 New accounts field Added new field features[].backing_feature to the response of account_features().

Version 3.10.0

Release Date: 2024-09-24 Breaking Changes: N/A

Item Topic Description
1.0 New Artifact field Added new field failed_reason to the responses of submit(), sandbox_file() and sandbox_url() calls when there is a known error.
2.0 Bug fix Added check first whether a report can be downloaded to report_download().

Version 3.9.0

Release Date: 2024-08-07 Breaking Changes: Item 4.0

Item Topic Description
1.0 Scan and Sandboxing of QR Code images with URL as payload Implemented in the submit() and sandbox_url() APIs.
2.0 Get account's basic information New API method account_whois().
3.0 Get accounts' features and quota New API method account_features().
4.0 Change zip file submissions Replace is_zip and zip_password with new preprocessing argument in the submit() and sandbox_file() APIs.

Version 3.8.0

Release Date: 2024-06-27

Breaking Changes: N/A

Item Topic Description
1.0 Support zip file submissions New is_zip and zip_password argument in the submit() and sandbox_file() APIs.

Version 3.7.0

Release Date: 2024-05-20

Breaking Changes: Item 3.0

Item Topic Description
1.0 Reports Generation Introduction of reports generation API: report_create, report_get and report_download
2.0 Reports Templates Introduction of reports templates API: report_template_** methods.
3.0 Python versions supported Minimal Python version supported is 3.7.

Version 3.6.0

Release Date: 2024-04-30

Breaking Changes: N/A

Item Topic Description
1.0 Communities Support EU communities.
2.0 Permalinks Fix permalink parsing.
3.0 IOC beta New method add_known_bad_host.

Version 3.5.2

Release Date: 2024-02-22

Breaking Changes: N/A

Item Topic Description
1.0 URL Sandboxing Introduction of URL Sandboxing (sandbox_url) API.

Version 3.4.3

Release Date: 2023-09-20

Breaking Changes: N/A

Item Topic Description
1.0 Added Community Parameter to Live Results Added parameter community to /v3/hunt/live/list to allow you to see results from a private community.
2.0 Added Community Parameter to Historical Results Added parameter community to /v3/hunt/historical/results/list to allow you to see results from a private community.
3.0 New Permalink Structure New Permalink Structure.

Version 3.4.0

Release Date: 2023-07-12

Breaking Changes: N/A

Item Topic Description
1.0 Sandbox Task Config Added sandbox task config field on sandbox task model.
2.0 api.sandbox and api.sandbox_file Endpoint now accept provider and vm slugs.
3.0 api.sandbox_providers Now returns provider and vm config information.

Version 3.3.2

Release Date: 2023-06-20

Breaking Changes: Item 3.0

Item Topic Description
1.0 Dropping python 2.7 support. -
2.0 Added New Polyswarm Lookup and Search Features. Added api.sandbox_task_status, api.sandbox_task_latest, api.sandbox_my_tasks and api.sandbox_task_list.
3.0 Changed the Sandbox Submit Interface. -

2024 © PolySwarm Pte. Ltd.