Cofense Triage Deprecated
Deprecated. Use the Cofense Triage v2 integration instead.
Data Enrichment & Threat Intelligence · Cofense Triage
Details
| ID | Cofense Triage |
|---|---|
| Provider | Cofense |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.8.3.8715 |
| Supported Modules | Agentix XSIAM |
README
Deprecated. Use the Cofense Triage v2 integration instead.
Configure Cofense Triage on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for Cofense Triage.
- Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance.
- Server URL (e.g., https://192.168.0.1)
- User
- API Token
- Fetch incidents
- Incident type
- First fetch time ( , e.g., 12 hours, 7 days, 3 months, 1 year)
- Category ID to fetch - corresponds to the ranking that determines the Cofense Triage prioritization (1-5)
- Match Priority - the highest match priority based on rule hits for the report
- Tags - CSV list of tags of processed reports by which to filter
- Maximum number of incidents to fetch each time
- Trust any certificate (not secure)
- Use system proxy
- Click Test to validate the URLs, token, 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.
- Search reports: cofense-search-reports
- Get an attachment: cofense-get-attachment
- Get the reporter email address: cofense-get-reporter
- Get a report: cofense-get-report-by-id
1. Search reports
Runs a query for reports.
Base Command
cofense-search-reports
Input
| Argument Name | Description | Required |
|---|---|---|
| file_hash | File hash, MD5 or SHA256. | Optional |
| url | The reported URLs. | Optional |
| subject | Report subject. | Optional |
| reported_at | Retrieve reports that were reported after this time, for example: "2 hours, 4 minutes, 6 month, 1 day". | Optional |
| created_at | Retrieve reports that were created after this time, for example: "2 hours, 4 minutes, 6 month, 1 day". | Optional |
| reporter | Name or ID of the reporter. | Optional |
| max_matches | Maximum number of matches to fetch. Default is 30. | Optional |
| verbose | Returns all fields of a report. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Report.ID | unknown | ID number of the report. |
| Report.EmailAttachments | unknown | Email attachments. |
| Report.EmailAttachments.id | unknown | Email attachment ID. |
| Report.Tags | string | Report tags. |
| Report.ClusterId | number | Cluster ID number. |
| Report.CategoryId | number | Report category. |
| Report.CreatedAt | date | Report creation date. |
| Report.ReportedAt | string | Reporting time. |
| Report.MatchPriority | number | The highest match priority based on rule hits for the report. |
| Report.ReporterId | number | Reporter ID. |
| Report.Location | string | Location of the report. |
| Report.Reporter | string | Reporter email address. |
| Report.SuspectFromAddress | string | Suspect from address. |
| Report.ReportSubject | string | Report subject. |
| Report.ReportBody | string | Report body. |
| Report.Md5 | number | MD5 hash of the file. |
| Report.Sha256 | unknown | SHA256 hash of the file. |
Command Example
cofense-search-reports max_matches=30 created_at="60 days" reported_at="60 days" reporter=5328
Context Example
{
"Cofense.Report": [
{
"ReportBody": "Good day\n\n\nPlease arrange to provide the best offer for below attached Purchase Order\nThe requirement for our green field project in Berghofen,Dortmund.\nKindly get back to us\n\n \n\n\n1) Proforma invoice with bank details\n\n2) Delivery date \n\n3) FOB/CIF Port\n\n \n\n \n \nRegards,\n\nkahn Gotze\nSales & Services Assistant\n",
"ReportedAt": "2019-05-17T11:37:52.000Z",
"ReporterId": 5328,
"Tags": [],
"ClusterId": null,
"ID": 13232,
"Location": "Processed",
"EmailAttachments": [
{
"content_type": "application/octet-stream; name=ORDER#t571BA80.rar",
"size_in_bytes": 219777,
"decoded_filename": "ORDER#t571BA80.rar",
"email_attachment_payload": {
"sha256": "1e2c4ac7be08888c72c953adaeb79254e7e9b821988bfdad5d75d75b2467def1",
"id": 7037,
"mime_type": "application/x-rar; charset=binary",
"md5": "e74c45a697651f3942f86fc5fce009df"
},
"id": 17831,
"report_id": 13232
}
],
"ReportSubject": "NEW ORDER",
"MatchPriority": 5,
"Sha256": "ca2579c53bd4ff0fa70fe38ae09a893c9332b8dfeab6ca7a13b89a709d54c0bb",
"CategoryId": 3,
"CreatedAt": "2019-05-17T16:57:16.343Z",
"Md5": "f5a1766371c063414d8b6a616b19bad0"
}
]
}
Human Readable Output
Reports:
| Category Id | Email Attachments | Sha256 | Created At | Id | Match Priority | Location | Report Body | Report Subject | Reported At | Reporter Id | Md5 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 3 | {'id': 17831, 'report_id': 13232, 'decoded_filename': 'ORDER#t571BA80.rar', 'content_type': 'application/octet-stream; name=ORDER#t571BA80.rar', 'size_in_bytes': 219777, 'email_attachment_payload': {'id': 7037, 'md5': 'e74c45a697651f3942f86fc5fce009df', 'sha256': '1e2c4ac7be08888c72c953adaeb79254e7e9b821988bfdad5d75d75b2467def1', 'mime_type': 'application/x-rar; charset=binary'}} | ca2579c53bd4ff0fa70fe38ae09a893c9332b8dfeab6ca7a13b89a709d54c0bb | 2019-05-17T16:57:16.343Z | 13232 | 5 | Processed | Good day Please arrange to provide the best offer for below attached Purchase Order The requirement for our green field project in Berghofen,Dortmund. Kindly get back to us 1) Proforma invoice with bank details 2) Delivery date 3) FOB/CIF Port Regards, kahn Gotze Sales & Services Assistant |
NEW ORDER | 2019-05-17T11:37:52.000Z | 5328 | f5a1766371c063414d8b6a616b19bad0 |
2. Get an attachment
Retrieves an attachment by the attachment ID number.
Base Command
cofense-get-attachment
Input
| Argument Name | Description | Required |
|---|---|---|
| attachment_id | ID of the attachment. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Attachment.ID | string | The ID number of the report that contains the attachment. |
| File.Size | string | The date and time (in UTC) when the threat was found on the device. |
| File.EntryID | string | The file path where the threat was found on the device. |
| File.Name | string | The name of the threat. |
| File.SHA1 | string | The SHA1 hash of the threat. |
| File.SHA256 | string | The SHA256 hash of the threat. |
| File.MD5 | string | The MD5 hash of the threat. |
Command Example
cofense-get-attachment attachment_id=8195
Context Example
{
"Cofense.Attachment": {
"ID": "8195"
}
}
3. Get the reporter email address
Retrieves the email address of the reporter, by reporter ID.
Base Command
cofense-get-reporter
Input
| Argument Name | Description | Required |
|---|---|---|
| reporter_id | ID of the reporter. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Report.ID | unknown | ID of the reporter. |
| Report.Email | unknown | Reporter email address. |
Command Example
cofense-get-reporter reporter_id=5328
Context Example
{
"Cofense.Reporter": {
"Email": "vishnuetp16@gmail.com",
"ID": "5328"
}
}
Human Readable Output
Reporter: vishnuetp16@gmail.com
4. Get a report
Retrieves a report by the report ID.
Base Command
cofense-get-report-by-id
Input
| Argument Name | Description | Required |
|---|---|---|
| report_id | ID of the report. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Report.ID | unknown | ID number of the report. |
| Report.EmailAttachments | unknown | Email attachments. |
| Report.EmailAttachments.id | unknown | Email attachment ID. |
| Report.Tags | string | Report tags. |
| Report.ClusterId | number | Cluster ID number. |
| Report.CategoryId | number | Report category. |
| Report.CreatedAt | date | Report creation date. |
| Report.ReportedAt | string | Reporting time. |
| Report.MatchPriority | number | The highest match priority based on rule hits for the report. |
| Report.ReporterId | number | Reporter ID. |
| Report.Location | string | Location of the report. |
| Report.Reporter | string | Reporter email address. |
| Report.SuspectFromAddress | string | Suspect from address. |
| Report.ReportSubject | string | Report subject. |
| Report.ReportBody | string | Report body. |
| Report.Md5 | number | MD5 hash of the file. |
| Report.Sha256 | unknown | SHA256 hash of the file. |
Command Example
cofense-get-report-by-id report_id=5760
Context Example
{
"Cofense.Report": [
{
"ReportedAt": "2019-04-17T16:54:57.000Z",
"ReporterId": 3280,
"Reporter": "no-reply@server.com",
"Tags": [],
"ClusterId": null,
"ID": 5760,
"Location": "Processed",
"EmailAttachments": [],
"ReportSubject": "example.gmail.com Reset password instruction",
"MatchPriority": 0,
"Sha256": "4f6bc0d9c1217a2a6f327423e16b7a6e9294c68cfb33864541bd805fe4ab2d72",
"CategoryId": 4,
"CreatedAt": "2019-04-17T20:53:02.090Z",
"Md5": "f13bbc172fe7d394828ccabb25c3c99e"
}
]
}
Human Readable Output
Cofense HTML Report:
HTML report download request has been completed
Report Summary:
| Category Id | Sha256 | Created At | Id | Match Priority | Location | Report Subject | Reported At | Reporter Id | Md5 | Reporter |
|---|---|---|---|---|---|---|---|---|---|---|
| 4 | 4f6bc0d9c1217a2a6f327423e16b7a6e9294c68cfb33864541bd805fe4ab2d72 | 2019-04-17T20:53:02.090Z | 5760 | 0 | Processed | example.gmail.com password i |
Configuration parameters
host— Server URL (e.g., https://192.168.0.1) (required)user— User (required)token— API Token (required)isFetch— Fetch incidentsincidentType— Incident typedate_range— First fetch time (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)category_id— Category ID to fetch - corresponds to the ranking that determines the Cofense Triage prioritization (1-5)match_priority— Match Priority - the highest match priority based on rule hits for the reporttags— Tags - CSV list of tags of processed reports by which to filtermax_fetch— Maximum number of incidents to fetch each timeinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (4)
-
cofense-get-attachmentRetrieves an attachment by the attachment ID number.
-
cofense-get-report-by-idRetrieves a report by the report ID number.
-
cofense-get-reporterRetrieves Email address of the reporter by ID
-
cofense-search-reportsRuns a query for reports.
from CommonServerPython import * '''IMPORTS''' import requests from typing import Any, List, Dict # disable insecure warnings requests.packages.urllib3.disable_warnings() ''' GLOBALS ''' BASE_URL = demisto.getParam('host').rstrip('/') + '/api/public/v1' # type: str TOKEN = demisto.getParam('token') # type: str USER = demisto.getParam('user') # type: str USE_SSL = not demisto.params().get('insecure', False) # type: bool HEADERS = { "Authorization": f"Token token={USER}:{TOKEN}", "Accept": "application/json" } # type: dict DEFAULT_TIME_RANGE = '7 days' # type: str TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ' # type: str CATEGORIES = { 1: 'Non-Malicious', 2: 'Spam', 3: 'Crimeware', 4: 'Advanced Threats', 5: 'Phishing Simulation' } # Severity levels are 4 - Critical, 3 - High, 2 - Medium, 1 - Low, 0 - Unknown CATEGORIES_SEVERITY = { 1: 1, # non malicious -> low 2: 0, # spam -> unknown 3: 2, # crimeware -> medium 4: 2, # advanced threats -> medium 5: 1 # phishing simulation -> low } TERSE_FIELDS = [ 'id', 'cluster_id', 'reporter_id', 'location', 'created_at', 'reported_at', 'report_subject', 'report_body', 'md5', 'sha256', 'category_id', 'match_priority', 'tags', 'email_attachments' ] # HELPER FUNCTIONS # def snake_to_camel_keys(snake_list: List[Dict]) -> List[Dict]: def snake_to_camel(snake_str) -> str: if snake_str == 'id': return 'ID' components = snake_str.split('_') return ''.join(x.title() for x in components) return [{snake_to_camel(k): v for k, v in snake_d.items()} for snake_d in snake_list] def split_snake(string: str) -> str: return string.replace("_", " ").title() # MAIN FUNCTIONS # def http_request(url_suffix: str, params=None, body=None, raw_response=False) -> Any: """ Generic request to Cofense Triage. Client applications can make 25 requests to Cofense Triage within a five-minute interval using the Cofense Triage API. """ response = requests.get( BASE_URL + url_suffix, headers=HEADERS, params=params, data=body, verify=USE_SSL, ) try: if not response.ok: return_error(f'Call to Cofense Triage failed [{response.status_code}] - [{response.text}]') elif response.status_code == 206: # 206 indicates Partial Content, reason will be in the warning header demisto.debug(str(response.headers)) if raw_response: return response data = response.json() if response.text and response.text != '[]' else {} # type: Any return data except TypeError as ex: demisto.debug(str(ex)) return_error(f'Error in API call to Cofense Triage, could not parse result [{response.status_code}]') return {} def get_fetch_response(): start_date, _ = parse_date_range(demisto.getParam('date_range'), date_format=TIME_FORMAT) max_fetch = int(demisto.getParam('max_fetch')) # type: int params = { 'category_id': demisto.getParam('category_id'), 'match_priority': demisto.getParam('match_priority'), 'tags': demisto.getParam('tags'), 'start_date': start_date, } # running the API command response = http_request( '/processed_reports', params=params, ) return response, max_fetch def test_function() -> None: try: response = requests.get( BASE_URL + '/processed_reports', headers=HEADERS, params="", verify=USE_SSL, ) if response.ok: # test fetching mechanism if demisto.params().get('isFetch'): get_fetch_response() demisto.results('ok') except Exception as ex: demisto.debug(str(ex)) return_error('API call to Cofense Triage failed, please check URL, or integration parameters.') def fetch_reports() -> None: # parameters importing reports, max_fetch = get_fetch_response() # loading last_run last_run = json.loads(demisto.getLastRun().get('value', '{}')) already_fetched = last_run.get('already_fetched', []) # parsing outputs incidents = [] for report in reports: if report.get('id') not in already_fetched: category_id, report_id = report.get('category_id'), report['id'] report_body = report.pop('report_body') incident = { 'name': f"cofense triage report {report_id}: {CATEGORIES.get(category_id, 'Unknown')}", 'occurred': report.get('created_at'), 'rawJSON': json.dumps(report), 'severity': CATEGORIES_SEVERITY.get(category_id, 0) } # load HTML attachment into the incident attachment = load_attachment(report_body, report_id) if attachment: incident['attachment'] = attachment else: # attachment is not HTML file, keep it as plain text report['report_body'] = report_body incident['rawJSON'] = json.dumps(report) incidents.append(incident) already_fetched.append(report_id) if len(incidents) >= max_fetch: break demisto.incidents(incidents) last_run = {'already_fetched': already_fetched} demisto.setLastRun({'value': json.dumps(last_run)}) def load_attachment(report_body: Any, report_id: int) -> list: if report_body and 'HTML' in report_body: html_attachment = fileResult(filename=f'{report_id}-report.html', data=report_body.encode()) attachment = { 'path': html_attachment.get('FileID'), 'name': html_attachment.get('FileName') } return [attachment] return [] def search_reports_command() -> None: # arguments importing subject = demisto.getArg('subject') # type: str url = demisto.getArg('url') # type: str file_hash = demisto.getArg('file_hash') # type: str reported_at, _ = parse_date_range(demisto.args().get('reported_at', DEFAULT_TIME_RANGE)) created_at, _ = parse_date_range(demisto.args().get('created_at', DEFAULT_TIME_RANGE)) reporter = demisto.getArg('reporter') # type: str max_matches = int(demisto.getArg('max_matches')) # type: int verbose = demisto.getArg('verbose') == "true" # running the API command results = search_reports(subject, url, file_hash, reported_at, created_at, reporter, verbose, max_matches) # parsing outputs if results: ec = {'Cofense.Report(val.ID && val.ID == obj.ID)': snake_to_camel_keys(results)} hr = tableToMarkdown("Reports:", results, headerTransform=split_snake, removeNull=True) demisto.results({ 'Type': entryTypes['note'], 'ContentsFormat': formats['markdown'], 'Contents': results if results else "no results were found", 'HumanReadable': hr, 'EntryContext': ec }) else: return_outputs("no results were found.", {}) def search_reports(subject=None, url=None, file_hash=None, reported_at=None, created_at=None, reporter=None, verbose=False, max_matches=30) -> list: params = {'start_date': datetime.strftime(reported_at, TIME_FORMAT)} reports = http_request(url_suffix='/processed_reports', params=params) if not isinstance(reports, list): reports = [reports] reporters = [] # type: list if reporter: reporters = get_all_reporters(time_frame=min(reported_at, created_at)) matches = [] for report in reports: if subject and subject != report.get('subject'): continue if url and url != report.get('url'): continue if created_at and 'created_at' in report and created_at >= datetime.strptime(report['created_at'], TIME_FORMAT): continue if file_hash and file_hash != report.get('md5') and file_hash != report.get('sha256'): continue if reporter and int(reporter) != report.get('reporter_id') and reporter not in reporters: continue if not verbose: # extract only relevant fields report = {key: report[key] for key in report.keys() & TERSE_FIELDS} matches.append(report) if len(matches) >= max_matches: break return matches def get_all_reporters(time_frame) -> list: res = http_request('/reporters', params={'start_date': time_frame}) if not isinstance(res, list): res = [res] reporters = [reporter.get('email') for reporter in res] return reporters def get_reporter_command() -> None: # arguments importing reporter_id = demisto.getArg('reporter_id') # type: str # running the API command res = get_reporter(reporter_id) # parsing outputs ec = {'Cofense.Reporter(val.ID && val.ID == obj.ID)': {'ID': reporter_id, 'Email': res}} hr = f'Reporter: {res}' if res else 'Could not find reporter with matching ID' return_outputs(readable_output=hr, outputs=ec) def get_reporter(reporter_id) -> str: res = http_request(url_suffix=f'/reporters/{reporter_id}') if not isinstance(res, list): res = [res] reporter = res[0].get('email') return reporter def get_attachment_command() -> None: # arguments importing attachment_id = demisto.getArg('attachment_id') # type: str file_name = demisto.getArg('file_name') or attachment_id # type: str # running the command res = get_attachment(attachment_id) # parsing outputs context_data = {'ID': attachment_id} demisto.results(fileResult(file_name, res.content)) demisto.results({ 'Type': entryTypes['note'], 'ContentsFormat': formats['markdown'], 'Contents': '', 'HumanReadable': '', 'EntryContext': {'Cofense.Attachment(val.ID == obj.ID)': context_data} }) def get_attachment(attachment_id): response = http_request(f'/attachment/{attachment_id}', params={'attachment_id': attachment_id}, raw_response=True) if not response.ok: return_error(f'Call to Cofense Triage failed [{response.status_code}]') else: return response def get_report_by_id_command() -> None: # arguments importing report_id = int(demisto.getArg('report_id')) # type: int verbose = demisto.getArg('verbose') == "true" # running the command res = get_report_by_id(report_id)[0] # parsing outputs if not verbose: # extract only relevant fields res = {k: res[k] for k in res.keys() & TERSE_FIELDS} # get the report body, and create html file if necessary if res: parse_report_body(res) res['reporter'] = get_reporter(res.get('reporter_id')) # enrich: id -> email hr = tableToMarkdown("Report Summary:", res, headerTransform=split_snake, removeNull=True) ec = {'Cofense.Report(val.ID && val.ID == obj.ID)': snake_to_camel_keys([res])} return_outputs(readable_output=hr, outputs=ec) else: return_error('Could not find report with matching ID') def parse_report_body(report) -> None: if 'report_body' in report and 'HTML' in report['report_body']: attachment = fileResult( filename=f'{report.get("id")}-report.html', data=report.get('report_body').encode(), ) attachment['HumanReadable'] = '### Cofense HTML Report:\nHTML report download request has been completed' demisto.results(attachment) del report['report_body'] def get_report_by_id(report_id): response = http_request(url_suffix=f'/reports/{report_id}', params={'report_id': report_id}) return response try: handle_proxy() # COMMANDS if demisto.command() == 'test-module': test_function() if demisto.command() == 'fetch-incidents': fetch_reports() elif demisto.command() == 'cofense-search-reports': search_reports_command() elif demisto.command() == 'cofense-get-attachment': get_attachment_command() elif demisto.command() == 'cofense-get-reporter': get_reporter_command() elif demisto.command() == 'cofense-get-report-by-id': get_report_by_id_command() except Exception as e: return_error(str(e)) raise