ReversingLabs A1000 Deprecated

Deprecated. Use the ReversingLabs A1000 v2 integration instead.

Forensics & Malware Analysis · ReversingLabs A1000

Details

IDReversingLabs A1000
ProviderReversingLabs
CategoryForensics & Malware Analysis
From Version5.0.0
Docker Imagedemisto/python:2.7.18.20958
Supported ModulesAgentix XSIAM

README

Overview

Use the A1000 Malware Analysis Platform to support advanced hunting and investigations through high-speed automated static analysis. A1000 Malware Analysis Platform is integrated with file reputation services to provide in-depth rich context and threat classification on over 6B files and across all file types.

This integration was integrated and tested with ReversingLabs A1000 Malware Analysis Platform™, 3.7.1.4.


Use Cases

  • Supports automated analysis processes (Static Analysis) that includes unpacking and identifying the families of archives, installers, packers and compressors.
  • Extracts over 3,000 PTIs from extracted files for PE/Windows, ELF/Linux, Mac OS, iOS, Android, firmware, and documents.
  • Calculates file threat level using extracted information.

Helps in regulating the investigation process by uploading and downloading samples to A1000 cloud platform at any point in time. (File reputation data can also be retrieved from an A1000 appliance.)


Prerequisites

You need to obtain the following ReversingLabs A1000 platform information.

  • Base URL of the A1000 box (https://a1000.reversinglabs.com)
  • Authentication Token

Get Your ReversingLabs A1000 Authentication Token

  1. Log in to the ReversingLabs A1000 platform.
  2. Navigate to Administration and click the Tokens icon.
  3. On the Authentication Tokens page, click the red plus button to create a new token key.
  4. Select the necessary user, and click the Save button.
    The new key is listed on the token table.
  5. Copy the key (authentication token), which you will paste when you configure the integration on Cortex XSOAR.

Configure ReversingLabs A1000 Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for ReversingLabs A1000.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • Base URL of the A1000 box : https://a1000.reversinglabs.com
    • Authentication Token: paste the token that you copied.
  4. Click Test to validate the URLs and connection.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details.


Upload a sample to A1000 for analysis : reversinglabs-upload

Upload a sample to A1000 for analysis. 

Command Example

!reversinglabs-upload entryId="126@331388d4-d045-4e89-843f-28569f05556e" cloud_analyze="true"

Input

Parameter

Description

entryId

Entry ID of the file to be uploaded. (Once you upload a file on Cortex XSOAR’s platform, click on the link(chain) icon to get the entry ID of that file)

comment

A comment for the file to be uploaded.

tags

A comma separated list of tags for the file.

cloud_analyze

Specifies if the sample is sent to TiCloud for scanning. By default the value is true.

  • True = sent to TiCloud
  • False = not sent to TiCloud

 

Human Readable Output

 

Raw Output

{
"code": 201,
 "message": "Done.",
 "detail": {
     "id": 604883,
     "sha1": "2fb56606463867310bbe853501d662cc21a7f6d9",
     "user": 585,
     "created": "2018-05-27T22:32:06.830474Z",
     "filename": "2018-05-24-Trickbot-malspam-1140-UTC.eml",
     "href": "/?q=2fb56606463867310bbe853501d662cc21a7f6d9"
 }
}

Reanalyze sample set: reversinglabs-analyze

Schedule a set of samples that were previously updated to the A1000 appliance to be reanalyzed.

Command Example

!reversinglabs-analyze hash="2fb56606463867310bbe853501d662cc21a7f6d9"

 Input

Parameter

Description

hash

The hash of a previously uploaded sample. Hexadecimal representation of SHA-1, SHA-256, SHA-512, or MD5 digest.

 

Human Readable Output

 

Raw Output

{
     "code": 200,
     "message": "Sample is already queued for analysis.",
     "detail": {
     "sha1": "f3be1fe7a01b73f0a6eaef9b62945433fcf53d3c",
     "Sha256": "f474543f4dace1e830df2dcbed83aecd89a24069c99952f5d24cf0379c116195",
     "md5": "fdd22060f2680b46f2783b3b579e82d5"
     }
}

Download a sample to the investigation: reversinglabs-download

Download a sample that resides on A1000 to the investigation.

Command Example

!reversinglabs-download hash="fdd22060f2680b46f2783b3b579e82d5"

Input

Parameter

Description

hash

The hash of a sample that resides on A1000. Hexadecimal representation of SHA-1, SHA-256, SHA-512, or MD5 digest.

  

Human Readable Output

 

Raw Output

There is no raw output for this command.

When the command runs successfully, you get a downloadable file.

When the command fails, you receive a notification with one of the following error codes.

  • 404 - Sample not found.
  • 401 - Unable to retrieve sample content.

Download samples obtained through the unpacking process : reversinglabs-download-unpacked

Download samples obtained through the unpacking process. 

Command Example

!reversinglabs-download-unpacked hash="6bb767e20c25e4d37e73fc0733c7ef2fce4ac8c6"

Input

Parameter

Description

hash

The hash of a sample that resides on A1000. Hexadecimal representation of SHA-1, SHA-256, SHA-512, or MD5 digest

 

Human Readable Output

 

Raw Output

There is no raw output for this command.

When the command runs successfully, you get a downloadable file.

When the command fails, you are receive a notification with one of the following error codes.

  • 404 - Sample not found.
  • 401 - Unable to retrieve sample content.

List extracted files from a sample : reversinglabs-extracted-files

List extracted files from a sample

Command Example

!reversinglabs-extracted-files hash="f3be1fe7a01b73f0a6eaef9b62945433fcf53d3c"

Input

Parameter

Description

hash

The hash of an already uploaded sample. Hexadecimal representation of SHA-1, SHA-256, SHA-512, or MD5 digest

 

Human Readable Output

 

Raw Output

{
 "count": 5,
 "next": null,
 "previous": null,
 "results": [
   {
     "id": 197,
     "parent_relationship": null,
     "sample": {
     "id": 192,
     "sha1": "e906fa3d51e86a61741b3499145a114e9bfb7c56",
     "type_display": "PE/Exe",
     "category": "application",
     "file_type": "PE",
     "file_subtype": "Exe",
     "identification_name": "",
     "identification_version": "",
     "file_size": 267278,
     "extracted_file_count": 2,
     "local_first_seen": "2016-05-05T09:57:50.910412Z",
     "local_last_seen": "2016-05-05T13:43:21.282072Z",
     "threat_status": "malicious",
     "trust_factor": 5,
     "threat_level": 5,
     "threat_name": "Win32.Trojan.Bitman"
   },
 "filename": "DeVuongHoi.exe",
 "path": "DeVuongHoi.exe"
},
{
 "id": 198,
 "parent_relationship": null,
 "sample": {
   "id": 198,
   "sha1": "e654d39cd13414b5151e8cf0d8f5b166dddd45cb",
   "type_display": "PE/Exe",
   "category": "application",
   "file_type": "PE",
   "file_subtype": "Exe",
   "identification_name": "",
   "identification_version": "",
   "file_size": 290816,
ReversingLabs
123
   "extracted_file_count": 1,
   "local_first_seen": "2016-05-05T09:58:27.096525Z",
   "local_last_seen": "2016-05-05T09:58:27.096525Z",
   "threat_status": "malicious",
   "trust_factor": 5,
   "threat_level": 5,
   "threat_name": "Win32.Malware.YARA"
 },
 "filename": "DieGroupv8.exe",
 "path": "DieGroupv8.exe"
},
...

Delete an uploaded sample : reversinglabs-delete

Delete an uploaded sample for the specified hash value that resides A1000. All related data, including extracted samples and metadata, will be deleted.

Command Example

!reversinglabs-delete hash="942d85abb2e94a4e5205eae7efdc5677ee6a0881" 

Input

Parameter

Description

hash

The hash of a sample that resides on A1000. Hexadecimal representation of SHA-1, SHA-256, SHA-512, or MD5 digest

  

Human Readable Output

 

Raw Output

{
     "code": 200,
     "message": "Sample deleted successfully.",
     "detail": {
     "sha1": "f3be1fe7a01b73f0a6eaef9b62945433fcf53d3c",
     "Sha256": "f474543f4dace1e830df2dcbed83aecd89a24069c99952f5d24cf0379c116195",
     "Sha512": "68d32e15df33735173ac457c948ed411e2f5aac1dc0d48feaafe5e38f1cd86fe5d67053dca7de36206beed4c9c6274d605a9dfcb19a6c48c8ceb2ffb0bc530d5"
     "md5": "fdd22060f2680b46f2783b3b579e82d5"
     }
}

Retrieve file reputation data from an A1000 appliance : file

Retrieve file reputation data from an A1000 appliance.

Command Example

!file file="8437682e44a764d0c0f610f6cb262a98"

 

Input

Parameter

Description

file

The hash that you want to retrieve reputation data for. Hexadecimal representation of SHA-1, SHA-256, SHA-512, or MD5 digest.

 

Human Readable Output

 

Context Output

Parameter

Description

File.MD5

Bad hash detected.

File.SHA1

Bad hash SHA-1.

File.Malicious.Vendor

For malicious files, the vendor that made the decision.

File.Malicious.Detections

For malicious files, the total number of detections.

File.Malicious.TotalEngines

For malicious files, the total number of engines.

DBotScore.Indicator

The indicator that is being tested.

DBotScore.Type

Indicator type.

DBotScore.Vendor

Vendor used to calculate the score.

DBotScore.Score

The actual score

 

Raw Output

"results": [
{
          "category": "archive",
          "aliases": {
           "0": "2017-12-04-artifacts-from-Dridex-malspam-infection.zip"
          },
          "classification_origin":{
                    "imphash": "33259202a22c25d002be697749eb957e",
                    "md5": "dbf96ab40b728c12951d317642fbd9da",
                    "sha1": "38687e06f4f66a6a661b94aaf4e73d0012dfb8e3",
                    "sha256": "daab430bb5771eaa7af0fbd3417604e8af5f4693099a6393a4dc3b440863bced",
                    "sha512": "a49cc96651d01da5d6cbb833df36b7987eafb4f09cc9c516c10d0d812002d06ae8edee4e7256c84e300dc2eadad90f7bb37c797bccdee4bad16fcaf88277b381"
          }
          "classification_reason": "cloud",
          "extracted_file_count": 79,
          "file_size": 1525222,
          "file_subtype": "Archive",
          "file_type": "Binary",
          "identification_name": "ZIP",
          "identification_version": "Generic",
          "local_first_seen": "2018-05-27T22:50:27.414449Z",
          "local_last_seen": "2018-05-27T22:50:27.414449Z",
          "md5": "fdd22060f2680b46f2783b3b579e82d5",
          "sha1": "f3be1fe7a01b73f0a6eaef9b62945433fcf53d3c",
          "sha256": "f474543f4dace1e830df2dcbed83aecd89a24069c99952f5d24cf0379c116195",
          "sha512": "68d32e15df33735173ac457c948ed411e2f5aac1dc0d48feaafe5e38f1cd86fe5d67053dca7de36206beed4c9c6274d605a9dcb19a6c48c8ceb2ffb0bc530d5",
          "summary":{
                    "id": 8306112,
                    "indicators": []
          }
          "sha1": "f3be1fe7a01b73f0a6eaef9b62945433fcf53d3c",
          "threat_level": 5,
          "threat_name": Win32.Trojan.Dridex,
          "threat_status": malicious,
          "ticloud": {
                    "first_seen": "2018-05-27T22:50:32Z",
                    "last_seen": "2018-05-27T22:55:00Z",
                    "threat_level": 0,
                    "threat_name": null,
                    "threat_status": "known",
                    "trust_factor": 5,
                    "Trust_factor" 5
          }
} 

Configuration parameters

  • base — Base URL of the A1000 box (required)
  • token — Token to use for authentication (required)
  • extended — Provide extended information when available
  • proxy — Use system proxy settings
  • verify — Verify host certificates

Commands (7)

  • file Deprecated

    Retrieve file reputation data from an A1000 appliance

  • reversinglabs-analyze Deprecated

    Re-analyze sample

  • reversinglabs-delete Deprecated

    Delete an uploaded sample

  • reversinglabs-download Deprecated

    Download a sample to the investigation

  • reversinglabs-download-unpacked Deprecated

    Download samples obtained through the unpacking process

  • reversinglabs-extracted-files Deprecated

    List extracted files from a sample

  • reversinglabs-upload Deprecated

    Upload a sample to A1000 for analysis

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401
import os
import re
import shutil

import requests
from urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

VERSION = "v1.0.1"
USER_AGENT = "ReversingLabs XSOAR A1000 {version}".format(version=VERSION)

BASE_URL = demisto.getParam('base')
if BASE_URL[-1] == '/':
    BASE_URL = BASE_URL[0:-1]

HEADERS = {
    'Authorization': 'Token ' + demisto.getParam('token'),
    "User-Agent": USER_AGENT
}
EXTENDED = demisto.getParam('extended')
VERIFY_CERT = demisto.getParam('verify')
A1000_FIELDS = ('sha1', 'sha256', 'sha512', 'md5', 'category', 'file_type', 'file_subtype', 'identification_name',
                'identification_version', 'file_size', 'extracted_file_count', 'local_first_seen', 'local_last_seen',
                'classification_origin', 'classification_reason', 'threat_status', 'trust_factor', 'threat_level',
                'threat_name', 'summary', 'ticloud', 'aliases')

if not demisto.getParam('proxy'):
    del os.environ['HTTP_PROXY']
    del os.environ['HTTPS_PROXY']
    del os.environ['http_proxy']
    del os.environ['https_proxy']


class NoReferenceFoundError(Exception):
    def __init__(self, message="No reference was found for this input"):
        super(NoReferenceFoundError, self).__init__(message)


no_rerence_object = NoReferenceFoundError()
no_reference_message = "{original_message} - {added_message}".format(
    original_message=str(no_rerence_object),
    added_message="Reply does not contain results"
)


# pylint: disable=function-redefined
def return_error(data):  # type: ignore
    """
    Return error as result and exit - filter 404 as non-errors
    """
    if '404' in data or 'Reply does not contain results' in data:
        demisto.results(
            {
                'Type': entryTypes['note'],
                'ContentsFormat': formats['text'],
                'Contents': data
            }
        )
    else:
        demisto.results(
            {
                'Type': entryTypes['error'],
                'ContentsFormat': formats['text'],
                'Contents': data
            }
        )
    sys.exit(0)


def validate_hash(hash_value):
    """
    Validate that the given hash is valid and return the type
    """
    type_dict = {
        32: {
            'type': 'md5',
            'regex': r'([a-fA-F\d]{32})'
        },
        40: {
            'type': 'sha1',
            'regex': r'([a-fA-F\d]{40})'
        },
        64: {
            'type': 'sha256',
            'regex': r'([a-fA-F\d]{64})'
        }
    }
    if len(hash_value) not in type_dict.keys():
        return_error('Provided input string length does not match any hash type')
    if not re.match(type_dict[len(hash_value)]['regex'], hash_value):
        return_error('Provided input string is not as hash due to containing invalid characters')
    return type_dict[len(hash_value)]['type']


def validate_http(r):
    """
    Make sure that the HTTP response is valid and return relevant data if yes
    """
    if 299 >= r.status_code >= 200:
        try:
            return True, r.json()
        except Exception as e:
            return False, 'HTTP response is not JSON [{error}] - {body}'.format(error=e, body=r.text)
    elif r.status_code in (401, 403):
        return False, 'Credential error - The provided A1000 credentials/token are either incorrect or lack ' \
                      'API roles [{code}] - {body}'.format(code=r.status_code, body=r.text)
    elif r.status_code == 404:
        return False, 'No reference found - There were no results found for the provided input ' \
                      '[{code}] - {body}'.format(code=r.status_code, body=r.text)
    else:
        return False, 'An error has occurred [{code}] - {body}'.format(
            code=r.status_code,
            body=r.text
        )


def file(hash_type, hash_value):
    """
    Get the summary data from A1000 with most of the fields except TICORE
    """
    ok, r = validate_http(requests.post(
        url=BASE_URL + '/api/samples/list/',
        headers=HEADERS,
        data={
            'hash_values': hash_value,
            'fields': A1000_FIELDS
        },
        verify=VERIFY_CERT
    ))
    if not ok:
        return_error(r)

    results = r.get('results')
    if not results:
        return_error(no_reference_message)

    res = results[0]
    status = res['threat_status']
    score = {'unknown': 0, 'known': 1, 'suspicious': 2, 'malicious': 3}[status]
    prop = status.title()
    file_data = {
        prop: {
            'Vendor': 'ReversingLabs A1000',
            'Status': status
        },
        'properties_to_append': prop
    }
    md = '## ReversingLabs A1000 reputation for: {}\n'.format(hash_value)
    ec = {'DBotScore': []}  # type: ignore

    md5 = res.get('md5')
    sha1 = res.get('sha1')
    sha256 = res.get('sha256')
    sha512 = res.get('sha512')
    file_type = res.get('file_type')
    file_info = res.get('file_subtype')
    file_size = res.get('file_size')
    if md5:
        file_data['MD5'] = md5
        md += 'MD5: **' + md5 + '**\n'
        ec['DBotScore'].append({'Indicator': md5, 'Type': 'hash', 'Vendor': 'ReversingLabs A1000', 'Score': score})
    if sha1:
        file_data['SHA1'] = sha1
        md += 'SHA1: **' + sha1 + '**\n'
        ec['DBotScore'].append({'Indicator': sha1, 'Type': 'hash', 'Vendor': 'ReversingLabs A1000', 'Score': score})
    if sha256:
        file_data['SHA256'] = sha256
        md += 'SHA256: **' + sha256 + '**\n'
        ec['DBotScore'].append({'Indicator': sha256, 'Type': 'hash', 'Vendor': 'ReversingLabs A1000', 'Score': score})
    if sha512:
        file_data['SHA512'] = sha512
        md += 'SHA512: **' + sha512 + '**\n'
    if file_type:
        file_data['Type'] = file_type
    if file_info:
        file_data['Info'] = file_info
    if file_size:
        file_data['Size'] = file_size

    ec[outputPaths['file']] = file_data  # type: ignore

    md += 'ID: **{}**\n'.format(demisto.get(res, 'summary.id'))
    md += 'Malware status: **{}**\n'.format(status)
    md += 'Local first seen: **{}**\n'.format(res.get('local_first_seen'))
    md += 'Local last seen: **{}**\n'.format(res.get('local_last_seen'))
    md += 'First seen: **{}**\n'.format(demisto.gets(res, 'ticloud.first_seen'))
    md += 'Last seen: **{}**\n'.format(demisto.gets(res, 'ticloud.last_seen'))
    md += 'Trust factor: **{}**\n'.format(res.get('trust_factor'))
    if status == 'malicious':
        md += 'Threat name: **{}**\n'.format(res.get('threat_name'))
        md += 'Threat level: **{}**\n'.format(res.get('threat_level'))
    md += 'Category: **{}**\n'.format(res.get('category'))
    md += 'Classification origin: **{}**\n'.format(res.get('classification_origin'))
    md += 'Classification reason: **{}**\n'.format(res.get('classification_reason'))
    md += 'Aliases: **{}**\n'.format(','.join(res.get('aliases')))
    md += 'Extracted file count: **{}**\n'.format(res.get('extracted_file_count'))
    md += 'File type: **{}/{}**\n'.format(file_type, file_info)
    md += 'File size: **{}**\n'.format(file_size)
    md += 'Identification name: **{}**\n'.format(res.get('identification_name'))
    md += 'Identification version: **{}**\n'.format(res.get('identification_version'))
    indicators = demisto.get(res, 'summary.indicators')
    if indicators:
        md += tableToMarkdown('Indicators', indicators)

    demisto.results({'Type': entryTypes['note'], 'ContentsFormat': formats['json'],
                     'Contents': r, 'EntryContext': ec, 'HumanReadable': md})


def extracted_files():
    """
    Get the list of extracted files for a given sample
    """
    parent = demisto.getArg('hash')
    endpoint = '/api/samples/{}/extracted-files/'.format(parent)
    ok, r = validate_http(
        requests.get(
            url=BASE_URL + endpoint,
            headers=HEADERS,
            verify=VERIFY_CERT
        ))
    if not ok:
        return_error(r)

    results = r.get('results')
    if not results:
        return_error(no_reference_message)

    ec = {'DBotScore': []}  # type: ignore
    file_list = []
    file_context_list = []
    for res in results:
        sha1 = demisto.get(res, 'sample.sha1')
        status = demisto.get(res, 'sample.threat_status')
        score = {'unknown': 0, 'known': 1, 'suspicious': 2, 'malicious': 3}[status]
        prop = status.title()
        file_data = {
            'SHA1': sha1,
            'Name': res.get('filename'),
            'Info': demisto.get(res, 'sample.type_display'),
            'Size': demisto.get(res, 'sample.file_size'),
            'Path': res.get('path'),
            'Local First': demisto.get(res, 'sample.local_first_seen'),
            'Local Last': demisto.get(res, 'sample.local_last_seen'),
            'Malware Status': status,
            'Trust': demisto.get(res, 'sample.trust_factor'),
            'Threat Name': demisto.get(res, 'sample.threat_name'),
            'Threat Level': demisto.get(res, 'sample.threat_level')
        }
        file_context = {
            'SHA1': sha1,
            'Type': demisto.get(res, 'sample.file_type'),
            'Name': res.get('filename'),
            'Info': demisto.get(res, 'sample.type_display'),
            'Size': demisto.get(res, 'sample.file_size'),
            prop: {
                'Vendor': 'ReversingLabs A1000',
                'Status': status
            },
            'properties_to_append': prop
        }
        file_list.append(file_data)
        file_context_list.append(file_context)
        ec['DBotScore'].append({'Indicator': sha1, 'Type': 'hash', 'Vendor': 'ReversingLabs A1000', 'Score': score})

    md = tableToMarkdown('ReversingLabs A1000 extracted files for: {}\n'.format(parent), file_data,
                         ['SHA1', 'Name', 'Path', 'Info', 'Size', 'Local First', 'Local Last', 'Malware Status',
                          'Trust', 'Threat Name', 'Threat Level'])
    ec[outputPaths['file']] = file_context_list
    demisto.results({'Type': entryTypes['note'], 'ContentsFormat': formats['json'],
                     'Contents': r, 'EntryContext': ec, 'HumanReadable': md})


def upload():
    """
    Upload a file to A1000 for analysis
    """
    data = {}
    if demisto.getArg('comment'):
        data['comment'] = demisto.getArg('comment')
    if demisto.getArg('tags'):
        data['tags'] = demisto.getArg('tags')
    cloud = demisto.getArg('cloud_analyze')
    if cloud and cloud.lower() == 'true':
        data['analysis'] = 'cloud'
    try:
        file_entry = demisto.getFilePath(demisto.getArg('entryId'))
        data['filename'] = file_entry['name']
        with open(file_entry['path'], 'rb') as f:
            ok, r = validate_http(requests.post(
                url=BASE_URL + '/api/uploads/',
                data=data,
                files={'file': f},
                headers=HEADERS,
                verify=VERIFY_CERT
            ))
            if not ok:
                return_error(r)
            md = '## ReversingLabs A1000 file upload\n'
            md += 'Message: **{}**\n'.format(r.get('message'))
            md += 'ID: **{}**\n'.format(demisto.get(r, 'detail.id'))
            md += 'SHA1: **{}**\n'.format(demisto.get(r, 'detail.sha1'))
            md += 'Created: **{}**\n'.format(demisto.get(r, 'detail.created'))
            demisto.results(
                {'Type': entryTypes['note'], 'ContentsFormat': formats['json'], 'Contents': r, 'HumanReadable': md})
    except Exception:
        return_error('Entry ID {} is not a file'.format(demisto.getArg('entryId')))


def delete_sample():
    """
    Delete a file from A1000
    """
    ok, r = validate_http(requests.delete(
        url=BASE_URL + '/api/samples/{}/'.format(demisto.getArg('hash')),
        headers=HEADERS,
        verify=VERIFY_CERT
    ))
    if not ok:
        return_error(r)
    res = r.get('results')
    if not res:
        return_error('Deleted successfully but got wrong JSON reply')
    md = '## ReversingLabs A1000 file delete\n'
    md += 'Message: **{}**\n'.format(res.get('message'))
    md += 'MD5: **{}**\n'.format(demisto.get(res, 'detail.md5'))
    md += 'SHA1: **{}**\n'.format(demisto.get(res, 'detail.sha1'))
    md += 'SHA256: **{}**\n'.format(demisto.get(res, 'detail.sha256'))
    demisto.results({'Type': entryTypes['note'], 'ContentsFormat': formats['json'], 'Contents': r, 'HumanReadable': md})


def download():
    """
    Download a sample from A1000
    """
    hash_value = demisto.getArg('hash')
    r = requests.get(
        url=BASE_URL + '/api/samples/{}/download/'.format(hash_value),
        headers=HEADERS,
        stream=True,
        verify=VERIFY_CERT
    )
    if r.status_code < 200 or r.status_code > 299:
        return_error('Bad HTTP response [{code}] - {body}'.format(code=r.status_code, body=r.text))
    filename = hash_value + '.bin'
    with open(filename, 'wb') as f:
        r.raw.decode_content = True
        shutil.copyfileobj(r.raw, f)
    demisto.results(file_result_existing_file(filename))


def reanalyze():
    """
    Re-Analyze a sample already existing on A1000
    """
    ok, r = validate_http(requests.post(
        url=BASE_URL + '/api/samples/{}/analyze/'.format(demisto.getArg('hash')),
        headers=HEADERS,
        data={'analysis': 'cloud'},
        verify=VERIFY_CERT
    ))
    if not ok:
        return_error(r)
    md = '## ReversingLabs A1000 file re-analyze\n'
    md += 'Message: **{}**\n'.format(r.get('message'))
    md += 'MD5: **{}**\n'.format(demisto.get(r, 'detail.md5'))
    md += 'SHA1: **{}**\n'.format(demisto.get(r, 'detail.sha1'))
    md += 'SHA256: **{}**\n'.format(demisto.get(r, 'detail.sha256'))
    demisto.results({'Type': entryTypes['note'], 'ContentsFormat': formats['json'], 'Contents': r, 'HumanReadable': md})


def unpacked():
    """
    Download samples obtained through the unpacking process
    """
    hash_value = demisto.getArg('hash')
    r = requests.get(
        url=BASE_URL + '/api/samples/{}/unpacked/'.format(hash_value),
        headers=HEADERS,
        stream=True,
        verify=VERIFY_CERT
    )
    if r.status_code < 200 or r.status_code > 299:
        return_error('Bad HTTP response [{code}] - {body}'.format(code=r.status_code, body=r.text))
    filename = hash_value + '.zip'
    with open(filename, 'wb') as f:
        r.raw.decode_content = True
        shutil.copyfileobj(r.raw, f)
    demisto.results(file_result_existing_file(filename))


if __name__ in ('__main__', '__builtin__', 'builtins'):
    if demisto.command() == 'test-module':
        ok, r = validate_http(requests.get(
            url=BASE_URL + '/api/samples/21841b32c6165b27dddbd4d6eb3a672defe54271/ticloud/',
            headers=HEADERS,
            verify=VERIFY_CERT
        ))
        if ok:
            demisto.results('ok')
        else:
            return_error(r)
    elif demisto.command() == 'file':
        hash_value = demisto.args()['file']
        hash_type = validate_hash(hash_value)
        file(hash_type, hash_value)
    elif demisto.command() == 'reversinglabs-upload':
        upload()
    elif demisto.command() == 'reversinglabs-delete':
        delete_sample()
    elif demisto.command() == 'reversinglabs-extracted-files':
        extracted_files()
    elif demisto.command() == 'reversinglabs-download':
        download()
    elif demisto.command() == 'reversinglabs-analyze':
        reanalyze()
    elif demisto.command() == 'reversinglabs-download-unpacked':
        unpacked()
    else:
        return_error('Command [{}] not implemented'.format(demisto.command()))