cyberint
Cyberint provides intelligence-driven digital risk protection. This integration will help your enterprise effectively consume actionable cyber alerts to increase your security posture.
Data Enrichment & Threat Intelligence · Cyberint
Details
| ID | cyberint |
|---|---|
| Provider | Check Point Software Technologies |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Cyberint provides intelligence-driven digital risk protection. This integration will help your enterprise effectively consume actionable cyber alerts to increase your security posture.
Configure Cyberint on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for Cyberint.
- Click Add instance to create and configure a new integration instance.
| Parameter | Description | Required |
|---|---|---|
| Company Name | Company (client) name associated with Cyberint instance. | True |
| Cyberint Access Token | Cyberint API access token. | True |
| Cyberint API URL | Cyberint API URL on which the services run (i.e https://your-company.cyberint.io) | True |
| Create an incident per CSV record | An incident will be created with the originated Alert details per CSV file record. | False |
| Fetch incidents | False | |
| Fetch Severity | Severities to fetch. If none is chosen, all severity levels will be returned. | False |
| Fetch Status | Statuses to fetch. If none is chosen, all statuses will be returned. | False |
| Fetch Environment | Environments to fetch (comma separated). If empty, all available environments will be returned. | False |
| Incident Mirroring Direction | Choose the direction to mirror the incident: Incoming (from Cyberint to Cortex XSOAR), Outgoing (from Cortex XSOAR to Cyberint), or Incoming and Outgoing (from/to Cortex XSOAR and Cyberint). Cortex XSOAR only parameter. | False |
| Incident type | False | |
| Close Mirrored XSOAR Incident | When selected, closing the Cyberint alert is mirrored in Cortex XSOAR. Cortex XSOAR only parameter. | False |
| Close Mirrored Cyberint Alert | When selected, closing the Cortex XSOAR incident is mirrored in Cyberint. | False |
| Fetch Types | Types to fetch. If none is chosen, all types will be returned. | False |
| Fetch Limit | Max number of alerts per fetch. Defaults to the minimum 10, max is 100. | False |
| First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days) | False | |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False |
- Click Test to validate the URLs, token, and connection.
Commands
You can execute these commands from the 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.
cyberint-alerts-fetch
List alerts according to parameters
Base Command
cyberint-alerts-fetch
Input
| Argument Name | Description | Required |
|---|---|---|
| page | Page number to return. Default is 1. | Optional |
| page_size | Number of results in a page. Must be between 10 and 100. Default is 10. | Optional |
| created_date_from | ISO-Formatted creation date. Get alerts created since this date (YYYY-MM-DDTHH:MM:SSZ). | Optional |
| created_date_to | ISO-Formatted creation date. Get alerts created before this date (YYYY-MM-DDTHH:MM:SSZ). | Optional |
| created_date_range | You can specify a date range to search for from the current time. (<number> <time unit>, e.g., 12 hours, 7 days) instead of a start/end time. created_date_range will overwrite created_date. | Optional |
| updated_date_from | ISO-Formatted creation date. Get alerts updated since this date (YYYY-MM-DDTHH:MM:SSZ). | Optional |
| updated_date_to | ISO-Formatted creation date. Get alerts updated before this date (YYYY-MM-DDTHH:MM:SSZ). | Optional |
| updated_date_range | You can specify a date range to search for from the current time. (<number> <time unit>, e.g., 12 hours, 7 days) instead of a start/end time. updated_date_range will overwrite updated_date. | Optional |
| modification_date_from | ISO-Formatted modification date. Get alerts modified since this date (YYYY-MM-DDTHH:MM:SSZ). | Optional |
| modification_date_to | ISO-Formatted modification date. Get alerts modified before this date (YYYY-MM-DDTHH:MM:SSZ). | Optional |
| modified_date_range | You can specify a date range to search for from the current time. (<number> <time unit>, e.g., 12 hours, 7 days) instead of a start/end time. modified_date_range will overwrite modified_date. | Optional |
| environments | Environment in which the alerts were created. Can be more than one. | Optional |
| statuses | Status of the alert. Can be more than one. Possible values are: open, acknowledged, closed. | Optional |
| severities | Severity of the alert. Can be more than one. Possible values are: low, medium, high, very_high. | Optional |
| types | Type of the alert, can be more than one. Possible values are: refund_fraud, carding, coupon_fraud, money_laundering, victim_report, malicious_insider, extortion, phishing_email, phishing_kit, phishing_website, lookalike_domain, phishing_target_list, malicious_file, reconnaissance, automated_attack_tools, business_logic_bypass, target_list, official_social_media_profile, impersonation, intellectual_property_infringement, unauthorized_trading, negative_sentiment, fake_job_posting, defacement, compromised_pii, internal_information_disclosure, compromised_payment_cards, compromised_employee_credentials, compromised_customer_credentials, compromised_access_token, ransomware, exposed_web_interfaces, hijackable_subdomains, website_vulnerabilities, vulnerabilities, exposed_cloud_storage, exploitable_ports, mail_servers_in_blacklist, server_connected_to_botnet, email_security_issues, certificate_authority_issues, user_defined_saved_query, other, ssl_tls, web_app_security. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cyberint.Alert.id | String | Unique ID of the alert. |
| Cyberint.Alert.ref_id | String | Reference ID of the alert. |
| Cyberint.Alert.confidence | Number | Confidence score of the alert. |
| Cyberint.Alert.status | String | Status of the alert. |
| Cyberint.Alert.severity | String | Severity of the alert |
| Cyberint.Alert.created_by.email | String | User which has created the alert. |
| Cyberint.Alert.created_date | Date | Date in which the alert was created. |
| Cyberint.Alert.category | String | Category of the alert. |
| Cyberint.Alert.type | String | Type of the alert. |
| Cyberint.Alert.source_category | String | Source category of the alert. |
| Cyberint.Alert.source | String | Source of the alert. |
| Cyberint.Alert.targeted_vectors | String | Vectors targeted by the threat. |
| Cyberint.Alert.targeted_brands | String | Brands targeted by the threat. |
| Cyberint.Alert.related_entities | String | Entities related to the alert. |
| Cyberint.Alert.impacts | String | Impacts made by the threat. |
| Cyberint.Alert.acknowledged_date | Date | Date in which the alert was acknowledged. |
| Cyberint.Alert.acknowledged_by.email | String | User which has acknowledged the alert. |
| Cyberint.Alert.publish_date | Date | Date in which the alert was published. |
| Cyberint.Alert.title | String | Title of the alert. |
| Cyberint.Alert.alert_data.url | String | URL impacted by the event. |
| Cyberint.Alert.alert_data.detection_reasons | String | Reasons why a phishing event has been detected. |
| Cyberint.Alert.alert_data.tool_name | String | Name of a tool used for an exploit if available. |
| Cyberint.Alert.alert_data.application | String | Application affected by an event. |
| Cyberint.Alert.alert_data.source | String | Source of an event if available. |
| Cyberint.Alert.alert_data.domain | String | Domain related to an event if available. |
| Cyberint.Alert.alert_data.subdomian | String | Subdomain related to an event if available. |
| Cyberint.Alert.alert_data.misconfiguration_type | String | Type of misconfiguration for a misconfigured domain. |
| Cyberint.Alert.alert_data.ip | String | IP related to an event. |
| Cyberint.Alert.alert_data.port | String | Port related to an event. |
| Cyberint.Alert.alert_data.service | String | Service related to an event. |
| Cyberint.Alert.alert_data.access_token | String | Access token exposed in an event. |
| Cyberint.Alert.alert_data.access_token_type | String | Access token exposed in an event. |
| Cyberint.Alert.alert_data.username | String | Username of an account related to an event. |
| Cyberint.Alert.alert_data.csv.username | String | Username of an account found in a report CSV. |
| Cyberint.Alert.alert_data.csv.password | String | Password of an account found in a report CSV. |
| Cyberint.Alert.alert_data.email | String | Email of an account related to an event. |
| Cyberint.Alert.alert_data.author_email_address | String | Email of an author related to an event. |
| Cyberint.Alert.alert_data.repository_name | String | Repository name related to an event. |
| Cyberint.Alert.alert_data.mail_server | String | Mail server related to an event. |
| Cyberint.Alert.alert_data.blacklist_repository | String | Blacklist repository name related to an event. |
| Cyberint.Alert.alert_data.screenshot | String | Screenshot related to an event. |
| Cyberint.Alert.alert_data.spf_records | String | SPF records if applicable to the event. |
| Cyberint.Alert.alert_data.dmarc_record | String | DMARC records if applicable to the event. |
| Cyberint.Alert.alert_data.storage_link | String | Storage link if applicable to the event. |
| Cyberint.Alert.alert_data.interface_type | String | Interface type if applicable to the event. |
| Cyberint.Alert.alert_data.vulnerable_cname_record | String | Vulnerable CName record if applicable to the event. |
| Cyberint.Alert.ioc.type | String | Type of IOC related to the alert. |
| Cyberint.Alert.ioc.value | String | Value of the IOC related to the alert. |
| Cyberint.Alert.ticket_id | String | Ticket ID of the alert. |
| Cyberint.Alert.threat_actor | String | Actor to the threat related to the alert. |
| Cyberint.Alert.modification_date | Date | Date the alert was last modified. |
| Cyberint.Alert.closure_date | String | Date the alert was closed. |
| Cyberint.Alert.closed_by.email | String | User which has closed the alert. |
| Cyberint.Alert.closure_reason | String | Reason for closing the alert. |
| Cyberint.Alert.description | String | Description of the alert. |
| Cyberint.Alert.recommendation | String | Recommendation for the alert |
| Cyberint.Alert.tags | String | Tags related to the alert |
| Cyberint.Alert.attachments | String | Attachments related to the alert |
Command Example
!cyberint-alerts-fetch page="1" page_size="100" created_date_range="7 days"
Context Example
{
"Cyberint": {
"Alert": [
{
"acknowledged_by": null,
"acknowledged_date": null,
"alert_data": {
"csv": [
{
"password": "Carroza1947",
"username": "cabuneta47"
}
],
"hashed_attachment_content_csv": "e0857ec24c39125644e1db0c3d04de110b75ff3e34824a9a890fff47f2d9f461",
"total_credentials": 1
},
"analysis_report": null,
"attachments": [
{
"id": 18,
"mimetype": "image/png",
"name": "Compromised Account As Appears On Argos.png"
}
],
"category": "data",
"closed_by": null,
"closure_date": null,
"closure_reason": null,
"confidence": 100,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-12T00:01:12",
"csv_data": {
"csv_id": 1981,
"name": "Company Customer Credentials Exposed.csv"
},
"description": "Cyberint detected breached credentials of several Chase customers, which were uploaded to an anti-virus repository. The credentials seem to have been obtained through malware, sending user inputs to the operator, and the various credentials were logged in the uploaded .txt files. As such, the file contains users\u2019 credentials not only for chase.com but for other websites as well. \nBreached customers credentials may be used by Threat Actors to carry out fraudulent transactions on their behalf, exposing Chase to both financial impact and legal claims.\n\n\n\n",
"environment": "Argos Demo",
"impacts": [
"data_compromise",
"unauthorized_access",
"account_takeover",
"revenue_loss",
"brand_degradation",
"customer_churn",
"financial_penalties"
],
"iocs": [],
"modification_date": "2021-04-12T00:01:12",
"publish_date": "2020-11-23T17:44:42",
"recommendation": "1. Cyberint recommends enforcing password reset on the compromised accounts. \n2. In addition, CyberInt advises Chase to investigate internally whether any of the accounts have been involved in fraudulent transactions, at least up to the time of detection. In case the accounts were involved in any fraudulent activity, it is recommended to identify and extract relevant IOC\u2019s where possible and monitor them within the bank's systems.\n3. To reduce the chance of customer account takeovers by TAs, Cyberint recommends Chase implement MFA and CAPTCHA mechanisms. The former will help set another obstacle for a TA trying to abuse the account, and the latter can help blocking credentials-stuffing tools.",
"ref_id": "ARG-3",
"related_entities": [],
"severity": "high",
"source": "argos.1",
"source_category": "antivirus_repository",
"status": "open",
"tags": [],
"targeted_brands": [
"Chase"
],
"targeted_vectors": [
"customer"
],
"threat_actor": "",
"ticket_id": null,
"title": "Company Customer Credentials Exposed",
"type": "compromised_customer_credentials"
},
{
"acknowledged_by": null,
"acknowledged_date": null,
"alert_data": {
"a_record": "129.146.184.83",
"detection_reasons": [
"similar_logo_detected",
"url_mentioned_assets_or_twists"
],
"has_ssl_certificate": false,
"ip_reputation": "malicious",
"mx_records": null,
"nameservers": null,
"registrant_email": null,
"registrant_name": null,
"registrar": "NameSilo, LLC",
"requests_user_details": true,
"screenshot": {
"id": 166,
"mimetype": "image/png",
"name": "Argos Screenshot of the Phishing Website.png"
},
"site_status": "not_active",
"url": "http://supportcenter-ee.com/banks/bank.barclays.co.uk",
"url_reputation": "malicious",
"whois_created_date": null,
"whois_record": null
},
"analysis_report": {
"id": 104,
"mimetype": "application/pdf",
"name": "Expert Analysis - Active Phishing Website Targeting Company.pdf"
},
"attachments": [
{
"id": 21,
"mimetype": "image/png",
"name": "Forensic Canvas Investigation of supportcenter-ee.com.png"
}
],
"category": "phishing",
"closed_by": null,
"closure_date": null,
"closure_reason": null,
"confidence": 100,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-12T00:01:12",
"description": "Cyberint detected an active phishing website impersonating Barclays login page while abusing the brand\u2019s name, logo and photos.\nThe website contains login, registration and checkout forms, where unsuspecting victims could be lured to fill in their PII, credentials and payment details.\nPhishing websites such as the above are often used by attackers to obtain users' credentials and PII. This information can be utilized to take over customers' accounts, causing customer churn and damage to the brand's reputation.",
"environment": "Argos Demo",
"impacts": [
"brand_degradation",
"account_takeover",
"user_data_compromise",
"data_compromise",
"unauthorized_access"
],
"iocs": [
{
"type": "domain",
"value": "supportcenter-ee.com"
},
{
"type": "ip",
"value": "129.146.184.83"
},
{
"type": "url",
"value": "http://supportcenter-ee.com/banks/bank.barclays.co.uk"
}
],
"modification_date": "2021-04-12T00:01:12",
"publish_date": "2020-09-02T00:06:49",
"recommendation": "CyberInt recommends reporting the website to Google Safe Browsing, so that upon attempting to browse to the phishing website, a warning would be triggered on the victim's browser, informing them of the danger and suggesting they don't enter.\nBarclays is also advised to take down the site; upon request, Cyberint can carry out both the report and the take down request on behalf of Barclays.",
"ref_id": "ARG-4",
"related_entities": [],
"severity": "very_high",
"source": "",
"source_category": "online_protection",
"status": "open",
"tags": [
"Phishing Kit",
"Finance"
],
"targeted_brands": [],
"targeted_vectors": [
"customer"
],
"threat_actor": "",
"ticket_id": null,
"title": "Active Phishing Website Targeting Company",
"type": "phishing_website"
},
{
"acknowledged_by": null,
"acknowledged_date": null,
"alert_data": {
"service": "Azure",
"subdomain": "s7k.paymebiz.hsbc.com.hk",
"vulnerable_cname_record": "s7k-paymebiz.trafficmanager.net"
},
"analysis_report": null,
"attachments": [],
"category": "vulnerabilities",
"closed_by": null,
"closure_date": null,
"closure_reason": null,
"confidence": 100,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-12T00:01:12",
"description": "CyberInt discovered a misconfiguration on an HSBC subdomain which exposes it to takeover.\nCurrently, the domain names refer to the CNAME records listed above. However, those CNAME records are no longer owned by Target, and they may have expired. This situation allows others to obtain the record, and practically get access to the HSBC subdomain.\n\nTaking over HSBC subdomains could be used to conduct complex phishing attack on the organization's employees and customers, as well potentially hijack sessions of logged-in users in any service using the vulnerable domains.",
"environment": "Argos Demo",
"impacts": [
"data_compromise",
"unauthorized_access",
"account_takeover"
],
"iocs": [],
"modification_date": "2021-04-12T00:01:12",
"publish_date": "2020-11-24T20:28:00",
"recommendation": "CyberInt advises HSBC to choose either of the following courses of action:\n1. Update the CNAME record of the subdomains so that they no longer redirect traffic to the vulnerable subdomains.\n2. Re-purchase the record and thus avoid contradiction between the CNAME record and the Fastly interface.",
"ref_id": "ARG-8",
"related_entities": [],
"severity": "very_high",
"source": "",
"source_category": "my_digital_presence",
"status": "open",
"tags": [],
"targeted_brands": [
"HSBC"
],
"targeted_vectors": [
"business"
],
"threat_actor": "",
"ticket_id": null,
"title": "Company Subdomain Vulnerable to Hijacking",
"type": "hijackable_subdomains"
},
{
"acknowledged_by": {
"email": "avital@cyberint.com"
},
"acknowledged_date": "2021-04-09T15:47:12",
"alert_data": {
"author_email_address": null,
"code_leak_sample": "# Working credentials, no need to replace\nawesome_sauce:\n login: 'test-api'\n password: 'c271ee995dd79671dc19f3ba4bb435e26bee68b0e831b7e9e4ae858c1584e0a33bc93b8d9ca3cedc'\n\n# Working credentials, no need to replace\nbalanced:\n login: 'e1c5ad38d1c711e1b36c026ba7e239a9'",
"exposed_code_link": "https://github.com/brpandey/active-merchant-sample-gateway-adapter/blob/b18b7faa10e1b4a6b6347b95933ce92ada600a17/test/fixtures.yml"
},
"analysis_report": null,
"attachments": [
{
"id": 15,
"mimetype": "image/png",
"name": "Argos Intel Item Containing Exposed Information.png"
}
],
"category": "data",
"closed_by": null,
"closure_date": null,
"closure_reason": null,
"confidence": 90,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-08T00:01:12",
"description": "CyberInt detected exposed credentials and RSA private key of a developer working with a Barclays API, which were published on a Github repository.\nThese credentials can allow an attacker to gain access to sensitive internal information of Barclays.\n",
"environment": "Argos Demo",
"impacts": [
"data_compromise",
"competitive_advantage_loss"
],
"iocs": [],
"modification_date": "2021-04-08T00:01:12",
"publish_date": "2017-01-08T05:21:51",
"recommendation": "CyberInt recommends Barclays validate the authenticity of the credentials and key and in case they are relevant, reset them immediately.\nUpon request, CyberInt can take down the code on behalf of Barclays.",
"ref_id": "ARG-2",
"related_entities": [],
"severity": "very_high",
"source": "github.com",
"source_category": "code_repository",
"status": "acknowledged",
"tags": [],
"targeted_brands": [
"Barclays"
],
"targeted_vectors": [],
"threat_actor": "Bibek Pandey",
"ticket_id": null,
"title": "Company Source Code Exposed",
"type": "internal_information_disclosure"
},
{
"acknowledged_by": {
"email": "avital@cyberint.com"
},
"acknowledged_date": "2021-04-09T15:47:12",
"alert_data": {
"tool_name": null
},
"analysis_report": {
"id": 106,
"mimetype": "application/pdf",
"name": "Expert Analysis - Fraudulent Refund Services Targeting Company.pdf"
},
"attachments": [],
"category": "fraud",
"closed_by": null,
"closure_date": null,
"closure_reason": null,
"confidence": 100,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-08T00:01:12",
"description": "Argos detected a thread published in a fraudsters' forum, concerning fraudulent refund services against various US retailers, including Nike and Costco. \nThe thread contains vouches from dozens of satisfied customers, who used the TA's refunding service.\n\nRefund fraud refers to the process of abusing a company\u2019s refund policy using social engineering techniques to receive a partial or complete refund on an order. Threat actors who offer this service are usually paid 7-20% of the order\u2019s value, and usually require a minimum of $15 per order to start the process. Given the commonness of the service, refund fraud may result in significant financial loss to organizations.",
"environment": "Argos Demo",
"impacts": [
"revenue_loss"
],
"iocs": [],
"modification_date": "2021-04-08T00:01:12",
"publish_date": "2020-11-16T09:09:44",
"recommendation": "CyberInt advises Costco to search their systems for refunds accepted in recent months, and try to cross-reference similarities and IOCs between the transactions. Such investigation can help identify potentially fraudulent patterns.\nAdditionally, as part of a full engagement, CyberInt can further investigate the TA in order to gain more information about their methods.",
"ref_id": "ARG-6",
"related_entities": [],
"severity": "medium",
"source": "nulled.to",
"source_category": "forum",
"status": "acknowledged",
"tags": [
"Retail"
],
"targeted_brands": [
"Target"
],
"targeted_vectors": [
"business"
],
"threat_actor": "BigBoi",
"ticket_id": null,
"title": "Fraudulent Refund Services Targeting Company",
"type": "refund_fraud"
},
{
"acknowledged_by": {
"email": "avital@cyberint.com"
},
"acknowledged_date": "2021-04-09T05:47:12",
"alert_data": {
"detection_reasons": [
"similar_logo_detected",
"source_code_mentioned_assets"
],
"has_ssl_certificate": null,
"ip_reputation": null,
"requests_user_details": true,
"site_status": null,
"url": "http://hacking.enterprises/PayPal/banks/bank.barclays.co.uk/",
"url_reputation": "malicious",
"whois_created_date": null
},
"analysis_report": null,
"attachments": [],
"category": "phishing",
"closed_by": {
"email": "avital@cyberint.com"
},
"closure_date": "2021-04-11T10:19:12",
"closure_reason": "resolved",
"confidence": 100,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-08T00:01:12",
"description": "CyberInt detected an active phishing website impersonating Barclays login page while abusing the brand\u2019s name, logo and photos.\nThe website contains login, registration and checkout forms, where unsuspecting victims could be lured to fill in their PII, credentials and payment details.\nPhishing websites such as the above are often used by attackers to obtain users' credentials and PII. This information can be utilized to take over customers' accounts, causing customer churn and damage to the brand's reputation.",
"environment": "Argos Demo",
"impacts": [
"brand_degradation",
"account_takeover",
"user_data_compromise",
"data_compromise",
"unauthorized_access"
],
"iocs": [],
"modification_date": "2021-04-08T00:01:12",
"publish_date": "2020-11-29T05:00:38",
"recommendation": "CyberInt recommends Barclays take down the site; upon request, CyberInt can submit the take down request on behalf of the bank.",
"ref_id": "ARG-15",
"related_entities": [],
"severity": "very_high",
"source": "",
"source_category": "online_protection",
"status": "closed",
"tags": [],
"targeted_brands": [
"Barclays"
],
"targeted_vectors": [
"customer"
],
"threat_actor": "",
"ticket_id": null,
"title": "Active Phishing Website Targeting Company",
"type": "phishing_website"
},
{
"acknowledged_by": {
"email": "avital@cyberint.com"
},
"acknowledged_date": "2021-04-09T05:47:12",
"alert_data": {
"tool_name": null
},
"analysis_report": null,
"attachments": [],
"category": "fraud",
"closed_by": {
"email": "avital@cyberint.com"
},
"closure_date": "2021-04-11T10:19:12",
"closure_reason": "resolved",
"confidence": 100,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-08T00:01:12",
"description": "Argos detected a thread published in a fraudsters' forum, concerning fraudulent refund services against various US retailers, including Apple, Sam's Club and more.\nThe thread contains vouches from dozens of satisfied customers, who used the TA's refunding service.\n\nRefund fraud refers to the process of abusing a company\u2019s refund policy using social engineering techniques to receive a partial or complete refund on an order. Threat actors who offer this service are usually paid 7-20% of the order\u2019s value, and usually require a minimum of $15 per order to start the process. Given the commonness of the service, refund fraud may result in significant financial loss to organizations.",
"environment": "Argos Demo",
"impacts": [
"revenue_loss"
],
"iocs": [],
"modification_date": "2021-04-08T00:01:12",
"publish_date": "2020-11-29T20:42:29",
"recommendation": "CyberInt advises Apple to search their systems for refunds accepted in recent months, and try to cross-reference similarities and IOCs between the transactions. Such investigation can help identify potentially fraudulent patterns.\nAdditionally, as part of a full engagement, CyberInt can further investigate the TA in order to gain more information about their methods.",
"ref_id": "ARG-16",
"related_entities": [],
"severity": "medium",
"source": "mpgh.net",
"source_category": "forum",
"status": "closed",
"tags": [],
"targeted_brands": [
"Apple"
],
"targeted_vectors": [
"business"
],
"threat_actor": "Felix_dsp",
"ticket_id": null,
"title": "Fraudulent Refund Services Targeting Company",
"type": "refund_fraud"
},
{
"acknowledged_by": {
"email": "avital@cyberint.com"
},
"acknowledged_date": "2021-04-09T00:01:12",
"alert_data": {
"tool_name": null
},
"analysis_report": null,
"attachments": [
{
"id": 14,
"mimetype": "image/png",
"name": "Company Customer Payment Cards Offered for Sale.png"
}
],
"category": "data",
"closed_by": null,
"closure_date": null,
"closure_reason": null,
"confidence": 100,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-06T00:01:12",
"description": "Cyberint detected payment cards belonging to customers being offered for sale online for 18$. The cards' information, published by a threat actors named Dolly, includes the BIN number of the card, expiration date and CVV digits as well as some PII of the card owner.\nCompromised payment card details, especially when combined with exposed PII, can be purchased and abused by threat actors for illegitimate and fraudulent activities. Those, in turn, will result in chargeback costs for the bank and potential customer churn.",
"environment": "Argos Demo",
"impacts": [
"revenue_loss",
"brand_degradation",
"customer_churn",
"financial_penalties"
],
"iocs": [],
"modification_date": "2021-04-06T00:01:12",
"publish_date": "2020-08-17T00:00:00",
"recommendation": "Cyberint recommends Joe purchase one of the payment cards in order to then verify validity. Upon confirmation, Cyberint recommends cancelling the payment cards in order to prevent their abuse, and informing the card holders of the cancellation.\nCyberint can make the test purchase on behalf of the bank.",
"ref_id": "ARG-1",
"related_entities": [],
"severity": "medium",
"source": "bestvalid.onion",
"source_category": "darknet",
"status": "acknowledged",
"tags": [],
"targeted_brands": [],
"targeted_vectors": [],
"threat_actor": "Dolly",
"ticket_id": null,
"title": "Company Customer Payment Cards Offered for Sale",
"type": "compromised_payment_cards"
},
{
"acknowledged_by": {
"email": "avital@cyberint.com"
},
"acknowledged_date": "2021-04-09T00:01:12",
"alert_data": {
"tool_name": null
},
"analysis_report": null,
"attachments": [
{
"id": 186,
"mimetype": "image/png",
"name": "AAX's post with full link.png"
}
],
"category": "data",
"closed_by": null,
"closure_date": null,
"closure_reason": null,
"confidence": 100,
"created_by": {
"email": "avital@cyberint.com"
},
"created_date": "2021-04-06T00:01:12",
"description": "Cyberint identified 40 accounts of Gucci customers being offered for sale in a hacking forum. It is unclear where the threat actors had obtained the accounts, but the thread been commented on by 20 interested buyers.\nThose are later abused by the buyers for account takeovers, to make fraudulent purchases on the victims\u2019 behalf. Account takeovers result in financial loss to the organization and may cause customer churn.",
"environment": "Argos Demo",
"impacts": [
"data_compromise",
"unauthorized_access",
"account_takeover",
"revenue_loss",
"brand_degradation",
"customer_churn",
"financial_penalties"
],
"iocs": [],
"modification_date": "2021-04-06T00:01:12",
"publish_date": "2020-10-15T11:31:43",
"recommendation": "Cyberint can contact the threat actor on behalf of Gucci, using an Avatar, in order to lure them into sharing how they had obtained the accounts. If relevant, Cyberint recommends Gucci consider purchasing a sample of the compromised accounts, to verify their validity and whether the rest of the batch could be worth purchasing as well.",
"ref_id": "ARG-5",
"related_entities": [],
"severity": "medium",
"source": "cracked.to",
"source_category": "forum",
"status": "acknowledged",
"tags": [],
"targeted_brands": [
"Gucci"
],
"targeted_vectors": [
"customer"
],
"threat_actor": "aax",
"ticket_id": null,
"title": "Company Customer Credentials Offered for Sale",
"type": "compromised_customer_credentials"
}
]
}
}
Human Readable Output
Total alerts: 9
Current page: 1
CyberInt alerts
ref_id title status severity created_date type environment ARG-3 Company Customer Credentials Exposed open high 2021-04-12T00:01:12 compromised_customer_credentials Argos Demo ARG-4 Active Phishing Website Targeting Company open very_high 2021-04-12T00:01:12 phishing_website Argos Demo ARG-8 Company Subdomain Vulnerable to Hijacking open very_high 2021-04-12T00:01:12 hijackable_subdomains Argos Demo ARG-2 Company Source Code Exposed acknowledged very_high 2021-04-08T00:01:12 internal_information_disclosure Argos Demo ARG-6 Fraudulent Refund Services Targeting Company acknowledged medium 2021-04-08T00:01:12 refund_fraud Argos Demo ARG-15 Active Phishing Website Targeting Company closed very_high 2021-04-08T00:01:12 phishing_website Argos Demo ARG-16 Fraudulent Refund Services Targeting Company closed medium 2021-04-08T00:01:12 refund_fraud Argos Demo ARG-1 Company Customer Payment Cards Offered for Sale acknowledged medium 2021-04-06T00:01:12 compromised_payment_cards Argos Demo ARG-5 Company Customer Credentials Offered for Sale acknowledged medium 2021-04-06T00:01:12 compromised_customer_credentials Argos Demo
cyberint-alerts-status-update
Update the status of one or more alerts.
Base Command
cyberint-alerts-status-update
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_ref_ids | Reference IDs for the alert(s). | Required |
| status | Desired status to update for the alert(s). Possible values are: open, acknowledged, closed. | Required |
| closure_reason | Reason for updating the alerts status to closed. Required when status is closed. Possible values are: resolved, irrelevant, false_positive. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cyberint.Alert.ref_id | String | Reference ID of the alert. |
| Cyberint.Alert.status | String | Status of the alert. |
| Cyberint.Alert.closure_reason | String | Reason for updating the alert to closed if closed. |
Command Example
!cyberint-alerts-status-update alert_ref_ids="ADS10-3" status="acknowledged"
Context Example
{
"Cyberint": {
"Alert": {
"closure_reason": null,
"ref_id": "ADS10-3",
"status": "acknowledged"
}
}
}
Human Readable Output
CyberInt alerts updated information
ref_id status ADS10-3 acknowledged
cyberint-alerts-get-attachment
Get alert attachment.
Base Command
cyberint-alerts-get-attachment
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_ref_id | Reference ID of the alert. | Required |
| attachment_id | Attachment ID. | Required |
| attachment_name | Attachment file name. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| File.Size | Number | The size of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA256 hash of the file. |
| File.Name | String | The name of the file. |
| File.SSDeep | String | The SSDeep hash of the file. |
| File.EntryID | String | The entry ID of the file. |
| File.Info | String | File information. |
| File.Type | String | The file type. |
| File.MD5 | String | The MD5 hash of the file. |
| File.Extension | String | The file extension. |
Command Example
!cyberint-alerts-get-attachment alert_ref_id="ARG-3" attachment_id="18" attachment_name="Compromised Account As Appears On Argos.png"
Context Example
{
"File": {
"EntryID": "1071@01674117-479d-4af5-89d6-cbf4584ae0e8",
"Extension": "png",
"Info": "image/png",
"MD5": "1bcab0883881e84802d859baea3810f5",
"Name": "Compromised Account As Appears On Argos.png",
"SHA1": "a232483be0ff6f912a2367e96c399dc311c8cfb1",
"SHA256": "635d7c00bb5f11f13b2fd2cab1b352c45f758467b7a00fed13e2d4669c83f35d",
"SHA512": "72128496b305dbd9e2e916bb92feea6314cfc9990da0ffbdf6364c8676ea5b681ab01c9f5ee97b9e5ec22999bf5795ed3424517ffbf6f0ad8455a4c2cce5e60c",
"SSDeep": "768:FYCj7zNaryIJPjoLRRQeD6hFvqvWOu5sUzdVL9EfNj0Aof:FpnNKyMPjoLzQE+DHVhoNj1of",
"Size": 35665,
"Type": "PNG image data, 711 x 531, 8-bit/color RGB, non-interlaced"
}
}
cyberint-alerts-analysis-report
Get alert analysis report.
Base Command
cyberint-alerts-analysis-report
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_ref_id | Reference ID of the alert. | Required |
| report_name | Analysis report file name. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| File.Size | Number | The size of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA256 hash of the file. |
| File.Name | String | The name of the file. |
| File.SSDeep | String | The SSDeep hash of the file. |
| File.EntryID | String | The entry ID of the file. |
| File.Info | String | File information. |
| File.Type | String | The file type. |
| File.MD5 | String | The MD5 hash of the file. |
| File.Extension | String | The file extension. |
Command Example
!cyberint-alerts-analysis-report alert_ref_id="ARG-4" report_name="Expert Analysis - Active Phishing Website Targeting Company.pdf"
Context Example
{
"File": {
"EntryID": "1075@01674117-479d-4af5-89d6-cbf4584ae0e8",
"Extension": "pdf",
"Info": "application/pdf",
"MD5": "6786164b6cfb00c54622b2f974dc53f4",
"Name": "Expert Analysis - Active Phishing Website Targeting Company.pdf",
"SHA1": "e7a6ceca8a216ba81527d423cf50d0dbf01bce5f",
"SHA256": "1890ad48da918d3f416a14b2fd22c1ca144cc5b47da4835c32e1341e0e2e880a",
"SHA512": "8228a78cc0a0411436b6086801654fdf2bff9a25bdf3bdbdec16692bd2fe6b4edf99012af68559900f6680456e1aaa84179c38df192adc866595298ebbc4b767",
"SSDeep": "6144:mMI1j8kEaLJviZ1dK80NyPUqZq/32pSjQNu1KwDaacfurJgT2vJWIp+YGh:mMIaa1UTLYVYuS2vJ3p+Lh",
"Size": 279550,
"Type": "PDF document, version 1.3"
}
}
Configuration parameters
client_name— Company Name (required)access_token— Cyberint Access Token (required)environment— Cyberint API URL (required)region— Data residencyduplicate_alert— Create an incident per CSV recordisFetch— Fetch incidentsfetch_severity— Fetch Severityfetch_status— Fetch Statusfetch_environment— Fetch Environmentmirror_direction— Incident Mirroring DirectionincidentType— Incident typeincidentFetchInterval— Incidents Fetch Intervalclose_incident— Close Mirrored XSOAR Incidentclose_alert— Close Mirrored Cyberint Alertfetch_type— Fetch Typesmax_fetch— Fetch Limitfirst_fetch— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)insecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (8)
-
cyberint-alerts-analysis-reportGet alert analysis report.
-
cyberint-alerts-fetchList alerts according to parameters.
-
cyberint-alerts-get-attachmentGet alert attachment.
-
cyberint-alerts-status-updateUpdate the status of one or more alerts.
-
get-mapping-fieldsReturns the list of fields for an incident type.
-
get-modified-remote-dataGets the list of incidents that were modified since the last update time. Note that this method is here for debugging purposes. The get-modified-remote-data command is used as part of a Mirroring feature, which is available in Cortex XSOAR from version 6.1.
-
get-remote-dataGets remote data from a remote incident. This method does not update the current incident, and should be used for debugging purposes.
-
update-remote-systemUpdates the remote incident or detection with local incident or detection changes. This method is only used for debugging purposes and will not update the current incident or detection.
# pylint: disable=unsubscriptable-object """IMPORTS""" import copy from collections.abc import Iterable from contextlib import closing from CommonServerPython import * from requests import Response """ CONSTANTS """ STRFTIME = "%Y-%m-%d" DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" SEVERITIES = {"low": 1, "medium": 2, "high": 3, "very_high": 4} CSV_FIELDS_TO_EXTRACT = ["Username", "Password"] MIRROR_DIRECTION_MAPPING = { "Incoming": "In", "Outgoing": "Out", "Incoming And Outgoing": "Both", } MIRRORING_FIELDS_XSOAR = [ "cyberintstatus", "cyberintclosurereason", "cyberintclosurereasondescription", ] MIRRORING_FIELDS_ARGOS = [ "status", "closure_reason", "closure_reason_description", ] MIRRORING_FIELDS_MAPPER = { "cyberintstatus": "status", "cyberintclosurereason": "closure_reason", "cyberintclosurereasondescription": "closure_reason_description", } # Mapping from human-readable UI values to API values CLOSURE_REASON_TO_API = { "None": "none", "Resolved": "resolved", "No Longer a Threat": "no_longer_a_threat", "Irrelevant Alert Subtype": "irrelevant_alert_subtype", "False Positive": "false_positive", "Other": "other", } # Reverse mapping from API values to human-readable UI values CLOSURE_REASON_TO_DISPLAY = {v: k for k, v in CLOSURE_REASON_TO_API.items()} EXPECTED_CLOSURE_REASON_SELECT_VALUES = [ "None", "Resolved", "No Longer a Threat", "Irrelevant Alert Subtype", "False Positive", "Other", ] class Client(BaseClient): """ API Client to communicate with Cyberint API endpoints. """ def __init__(self, base_url: str, access_token: str, verify_ssl: bool, proxy: bool): """ Client for Cyberint RESTful API. Args: base_url (str): URL to access when getting alerts. access_token (str): Access token for authentication. verify_ssl (bool): specifies whether to verify the SSL certificate or not. proxy (bool): specifies if to use XSOAR proxy settings. """ params = demisto.params() self._cookies = {"access_token": access_token} self._headers = { "X-Integration-Type": "XSOAR", "X-Integration-Instance-Name": demisto.integrationInstance(), "X-Integration-Instance-Id": "", "X-Integration-Customer-Name": params.get("client_name", ""), "X-Integration-Version": str(get_pack_version()), } super().__init__(base_url=base_url, verify=verify_ssl, proxy=proxy, headers=self._headers) @logger def list_alerts( self, page: str | None, page_size: int | None, created_date_from: str | None, created_date_to: str | None, modification_date_from: str | None, modification_date_to: str | None, update_date_from: str | None, update_date_to: str | None, environments: list[str] | None, statuses: list[str] | None, severities: list[str] | None, types: list[str] | None, ) -> dict: """ Retrieve a list of alerts according to parameters. Args: page (str): Index of page to return. page_size (int): Size of the page to return. created_date_from (str): Minimal ISO-Formatted creation date. created_date_to (str): Maximal ISO-Formatted creation date. modification_date_from (str): Minimal ISO-Formatted modification date. modification_date_to (str): Maximal ISO-Formatted modification date. update_date_from (str): Minimal ISO-Formatted update date. update_date_to (str): Maximal ISO-Formatted update date. environments (list(str)): Environments in which the alerts were created. statuses (list(str)): Alerts statuses. severities (list(str)): Alerts severities. types (list(str)): Alerts type. Returns: response (Response): API response from Cyberint. """ body = { "page": page, "size": page_size, "include_csv_attachments_as_json_content": True, "filters": { "created_date": {"from": created_date_from, "to": created_date_to}, "modification_date": {"from": modification_date_from, "to": modification_date_to}, "update_date": {"from": update_date_from, "to": update_date_to}, "environments": environments, "status": statuses, "severity": severities, "type": types, }, } body = remove_empty_elements(body) response = self._http_request(method="POST", json_data=body, cookies=self._cookies, url_suffix="api/v1/alerts") return response def update_alerts( self, alerts: list[str], status: str | None, closure_reason: str | None = None, closure_reason_description: str | None = None, ) -> dict: """ Update the status of one or more alerts Args: alerts (list(str)): Reference IDs for the alert(s) status (str): Desired status to update for the alert(s) closure_reason (str): Reason for updating the alerts status to closed. closure_reason_description (str): Reason for updating the alerts status to closed. Returns: response (Response): API response from Cyberint. """ body = { "alert_ref_ids": alerts, "data": { "status": status, "closure_reason": closure_reason, "closure_reason_description": closure_reason_description, }, } body = remove_empty_elements(body) response = self._http_request(method="PUT", json_data=body, cookies=self._cookies, url_suffix="api/v1/alerts/status") return response def get_csv_file(self, alert_id: str, attachment_id: str, delimiter: bytes = b"\r\n") -> Iterable[str]: """ Stream a CSV file attachment in order to extract data out of it. Args: alert_id (str): ID of the alert the CSV belongs to. attachment_id (str): ID of the specific CSV file. delimiter (str): Delimiter for the CSV file. Returns: row (generator(str)): Generator containing each line of the CSV. """ url_suffix = f"api/v1/alerts/{alert_id}/attachments/{attachment_id}" with closing( self._http_request(method="GET", url_suffix=url_suffix, cookies=self._cookies, resp_type="all", stream=True) ) as r: for line in r.iter_lines(delimiter=delimiter): yield line.decode("utf-8").strip('"') def get_alert_attachment(self, alert_ref_id: str, attachment_id: str) -> Response: """ Retrieve attachment by alert reference ID and attachment ID. Args: alert_ref_id (str): Reference ID of the alert. attachment_id (str): The ID of the attachment. Returns: Response: API response from Cyberint. """ url_suffix = f"api/v1/alerts/{alert_ref_id}/attachments/{attachment_id}" return self._http_request(method="GET", cookies=self._cookies, url_suffix=url_suffix, resp_type="response") def get_alert( self, alert_ref_id: str, ) -> dict: """ Retrieve attachment by alert reference ID and attachment ID. Args: alert_ref_id (str): Reference ID of the alert. Returns: Response: API response from Cyberint. """ url_suffix = f"api/v1/alerts/{alert_ref_id}" return self._http_request(method="GET", cookies=self._cookies, url_suffix=url_suffix) def get_analysis_report(self, alert_ref_id: str) -> Response: """ Retrieve analysis report by alert reference ID. Args: alert_ref_id (str): Reference ID of the alert. Returns: Response: API response from Cyberint. """ url_suffix = f"api/v1/alerts/{alert_ref_id}/analysis_report" return self._http_request(method="GET", cookies=self._cookies, url_suffix=url_suffix, resp_type="response") def test_module(client: Client): """ Test the connection to the API by sending a normal request. Args: client (Client): Cyberint API client Returns: 'ok' if test passed, anything else will fail the test. """ try: result = client.list_alerts(*([None] * 12)) if result: return "ok" except DemistoException as exception: if "Invalid token or token expired" in str(exception): error_message = "Error verifying access token and / or URL, make sure the configuration parameters are correct." else: error_message = str(exception) return error_message def verify_input_date_format(date: str | None) -> str | None: """ Make sure a date entered by the user is in the correct string format (with a Z at the end). Args: date (str): Date string given by the user. Can be None. Returns: str: Fixed date in the same format as the one needed by the API. """ if date and not date.endswith("Z"): date += "Z" return date def set_date_pair( start_date_arg: str | None, end_date_arg: str | None, date_range_arg: str | None ) -> tuple[str | None, str | None]: """ Calculate the date range to send to the API based on the arguments from the user. Args: start_date_arg (str): Optional start_date from the user. end_date_arg (str): Optional end_date from the user. date_range_arg (str): Optional date range from the user. Returns: start_date (str): Start date to send to the API. end_date (str): End date to send to the API. """ if date_range_arg: start_date, end_date = parse_date_range(date_range=date_range_arg, date_format=DATE_FORMAT, utc=False) return start_date, end_date min_date = datetime.fromisocalendar(2020, 2, 1) start_date_arg = verify_input_date_format(start_date_arg) end_date_arg = verify_input_date_format(end_date_arg) if start_date_arg and not end_date_arg: end_date_arg = datetime.strftime(datetime.now(), DATE_FORMAT) elif end_date_arg and not start_date_arg: start_date_arg = datetime.strftime(min_date, DATE_FORMAT) return start_date_arg, end_date_arg def extract_data_from_csv_stream(client: Client, alert_id: str, attachment_id: str, delimiter: bytes = b"\r\n") -> list[dict]: """ Call the attachment download API and parse required fields. Args: client (Client): Cyberint API client. alert_id (str): ID of the alert the attachment belongs to. attachment_id (str): ID of the attachment itself. delimiter (bytes): Delimeter for the CSV file. Returns: list(dict): List of all the data found using the wanted fields. """ first_line = True field_indexes = {} # {wanted_field_name: wanted_field_index...} information_found = [] for csv_line in client.get_csv_file(alert_id, attachment_id, delimiter): csv_line_separated = csv_line.split(",") if first_line: for field in CSV_FIELDS_TO_EXTRACT: try: field_indexes[field] = csv_line_separated.index(field) except ValueError: pass first_line = False else: try: extracted_field_data = { field_name.lower(): csv_line_separated[field_index] for field_name, field_index in field_indexes.items() } if extracted_field_data: information_found.append(extracted_field_data) except IndexError: pass return information_found def cyberint_alerts_fetch_command(client: Client, args: dict) -> CommandResults: """ List alerts on cyberint according to parameters. Args: client (Client): Cyberint API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ created_date_from, created_date_to = set_date_pair( args.get("created_date_from", None), args.get("created_date_to", None), args.get("created_date_range", None), ) modify_date_from, modify_date_to = set_date_pair( args.get("modification_date_from", None), args.get("modification_date_to", None), args.get("modification_date_range", None), ) update_date_from, update_date_to = set_date_pair( args.get("updated_date_from", None), args.get("updated_date_to", None), args.get("updated_date_range", None), ) if int(args.get("page_size", 10)) < 10 or int(args.get("page_size", 10)) > 100: raise DemistoException("Page size must be between 10 and 100.") result = client.list_alerts( args.get("page"), args.get("page_size"), created_date_from, created_date_to, modify_date_from, modify_date_to, update_date_from, update_date_to, argToList(args.get("environments")), argToList(args.get("statuses")), argToList(args.get("severities")), argToList(args.get("types")), ) alerts = result.get("alerts", []) outputs = [] for alert in alerts: alert_csv_id = alert.get("alert_data", {}).get("csv", {}).get("id", "") if alert_csv_id: alert["csv_data"] = { "csv_id": alert_csv_id, "name": dict_safe_get(alert, ["alert_data", "csv", "name"]), "content": dict_safe_get(alert, ["alert_data", "csv", "content"]), } extracted_csv_data = extract_data_from_csv_stream(client, alert.get("ref_id", ""), alert_csv_id) alert["alert_data"]["csv"] = extracted_csv_data outputs.append(alert) total_alerts = result.get("total") table_headers = ["id", "ref_id", "title", "status", "severity", "created_date", "update_date", "type", "environment"] readable_output = f"Total alerts: {total_alerts}\nCurrent page: {args.get('page', 1)}\n" readable_output += tableToMarkdown(name="Cyberint alerts:", t=outputs, headers=table_headers, removeNull=True) return CommandResults( outputs_key_field="ref_id", outputs_prefix="Cyberint.Alert", readable_output=readable_output, raw_response=result, outputs=outputs, ) def cyberint_alerts_status_update(client: Client, args: dict) -> CommandResults: """ Update the status of one or more alerts Args: client (Client): Cyberint API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ alert_ids = argToList(args.get("alert_ref_ids")) status = args.get("status") closure_reason = args.get("closure_reason") closure_reason_description = args.get("closure_reason_description") if status == "closed" and not closure_reason: raise DemistoException("You must supply a closure reason when closing an alert.") # Convert display value to API value closure_reason_api = CLOSURE_REASON_TO_API.get(closure_reason, closure_reason) if closure_reason else None if closure_reason_api == "other" and not closure_reason_description: raise DemistoException("You must supply a closure_reason_description when specify closure_reason to 'Other'.") response = client.update_alerts( alerts=alert_ids, status=status, closure_reason=closure_reason_api, closure_reason_description=closure_reason_description, ) table_headers = ["ref_id", "status", "closure_reason", "closure_reason_description"] outputs = [] for alert_id in alert_ids: outputs.append( { "ref_id": alert_id, "status": status, "closure_reason": closure_reason, "closure_reason_description": closure_reason_description, } ) readable_output = tableToMarkdown( name="Cyberint alerts updated information:", t=outputs, headers=table_headers, removeNull=True ) return CommandResults( outputs_key_field="ref_id", outputs_prefix="Cyberint.Alert", readable_output=readable_output, raw_response=response, outputs=outputs, ) def cyberint_alerts_get_attachment_command(client: Client, alert_ref_id: str, attachment_id: str, attachment_name: str) -> dict: """ Retrieve attachment by alert reference ID and attachment internal ID. Attachments includes: CSV files , Screenshots, and alert attachments files. Args: client (Client): Cyberint API client. alert_ref_id (str): Reference ID of the alert. attachment_id (str): The ID of the alert attachment. attachment_name (str): The file name of the alert attachment. Returns: Dict: Alert attachment file result. """ raw_response = client.get_alert_attachment(alert_ref_id, attachment_id) return fileResult(filename=attachment_name, data=raw_response.content) def cyberint_alerts_get_analysis_report_command(client: Client, alert_ref_id: str, report_name: str) -> dict: """ Retrieve expert analysis report by alert reference ID and report name. Args: client (Client): Cyberint API client. alert_ref_id (str): Reference ID of the alert. report_name (str): The name of the alert expert analysis report. Returns: Dict: Alert attachment file result. """ raw_response = client.get_analysis_report(alert_ref_id) return fileResult(filename=report_name, data=raw_response.content) def get_attachment_name(attachment_name: str) -> str: """ Retrieve attachment name or error string if none is provided. Args: attachment_name (str): Attachment name to retrieve. Returns: str: The attachment file name or 'xsoar_untitled_attachment' by default. """ if attachment_name is None or attachment_name == "": return "xsoar_untitled_attachment" return attachment_name def create_fetch_incident_attachment(raw_response: Response, attachment_file_name: str) -> dict: """ Create suitable attachment information dictionary object. This dictionary object will be used as an entry in the fetch-incidents attachments list. For each attachment file, it is necessary to save the relevant fields that return from this function, in order to represent the attachment in the layout. Args: raw_response (Response): Cyberint API response from retrieving the alert attachment. attachment_file_name (str): The name of the attachment. Returns: dict: Attachment file information.Includes - path, name, and showMediaFile. """ attachment_name = get_attachment_name(attachment_file_name) file_result = fileResult(filename=attachment_name, data=raw_response.content) return {"path": file_result["FileID"], "name": attachment_name, "showMediaFile": True} def get_alert_attachments(client: Client, attachment_list: list, attachment_type: str, alert_id: str) -> list: """ Retrieve all alert attachments files - Attachments, CSV, Screenshot, and Analysis report. For each attachment, we save and return the relevant fields in order to represent the attachment in the layout. Args: client (Client): Cyberint API client. attachment_list (List): Alert attachments list. Each element in the list contains id, mimetype and name fields. attachment_type (str): The type of the attachment. Can be 'attachment' or 'analysis_report'. alert_id (str): The ID of the alert. Returns: (List): incident attachments details - contains the file details of the attachment. """ incident_attachments = [] for attachment in attachment_list: if attachment: if attachment_type == "analysis_report": raw_response = client.get_analysis_report(alert_id) incidents_attachment = create_fetch_incident_attachment(raw_response, attachment.get("name", None)) else: raw_response = client.get_alert_attachment(alert_id, attachment.get("id", None)) incidents_attachment = create_fetch_incident_attachment(raw_response, attachment.get("name", None)) if incidents_attachment: incident_attachments.append(incidents_attachment) return incident_attachments def convert_date_time_args(date_time: str) -> str: """Convert str to datetime. Args: date_time (str): The datetime str. Returns: str: The updated datetime. """ if datetime_arg := arg_to_datetime(date_time, required=False): return datetime_arg.strftime(DATE_FORMAT) return "" def get_modified_remote_data(client: Client, args: dict[str, Any]) -> GetModifiedRemoteDataResponse: """ Queries for incidents that were modified since the last update. Args: client: Cyberint API client. args (Dict[str, Any]): command arguments. Returns: GetModifiedRemoteDataResponse: modified tickets from Cyberint. """ remote_args = GetModifiedRemoteDataArgs(args) last_update = remote_args.last_update demisto.debug(f"******** Get modified remote data from {last_update}") update_date_from = convert_date_time_args(last_update) update_date_to = datetime.strftime(datetime.now(), DATE_FORMAT) demisto.debug(f"******** Get modified remote data {update_date_from=} {update_date_to=}") modified_tickets = [] response = client.list_alerts( page="1", page_size=50, update_date_from=update_date_from, update_date_to=update_date_to, created_date_from=None, created_date_to=None, modification_date_from=None, modification_date_to=None, environments=None, statuses=None, severities=None, types=None, ) for ticket in response["alerts"]: modified_tickets.append(ticket["ref_id"]) demisto.debug(f"******** There are {len(modified_tickets)} modified incidents from Cyberint") return GetModifiedRemoteDataResponse(modified_tickets) def get_mapping_fields_command() -> GetMappingFieldsResponse: """ Pulls the remote schema for the different incident types, and their associated incident fields, from the remote system. Returns: GetMappingFieldsResponse: Dictionary with keys as field names. """ demisto.debug("******** Get Cyberint mapping fields") mapping_response = GetMappingFieldsResponse() incident_type_scheme = SchemeTypeMapping(type_name="Cyberint Incident") for field in MIRRORING_FIELDS_ARGOS: incident_type_scheme.add_field(field) mapping_response.add_scheme_type(incident_type_scheme) return mapping_response def update_remote_system( client: Client, args: dict[str, Any], ) -> str: """ This command pushes local changes to the remote system. Args: client: XSOAR Client to use. args: args['data']: the data to send to the remote system. args['entries']: the entries to send to the remote system. args['incident_changed']: boolean telling us if the local incident indeed changed or not. args['remote_incident_id']: the remote incident id. Returns: The remote incident id - ticket_id """ parsed_args = UpdateRemoteSystemArgs(args) incident_id = parsed_args.remote_incident_id inc_status = parsed_args.inc_status # XSOAR incident status (2 = Done/Closed) demisto.debug( f"******** Got the following delta keys {list(parsed_args.delta.keys())!s}" if parsed_args.delta else "******** There is no delta fields in Cyberint" ) try: if parsed_args.incident_changed: demisto.debug(f"******** Incident changed: {parsed_args.incident_changed}, {parsed_args.delta=}") update_args = parsed_args.delta demisto.debug(f"******** Sending incident with remote ID [{incident_id}] to Cyberint\n") updated_arguments: dict[str, Any] = {} updated_status = update_args.get("status") xsoar_incident_closed = inc_status == IncidentStatus.DONE if updated_status == "closed" or (not updated_status and xsoar_incident_closed): # Closing the alert - need closure_reason and description closure_reason = update_args.get("closure_reason") or update_args.get("cyberintclosurereason") or "Other" # Convert display value to API value closure_reason_api = CLOSURE_REASON_TO_API.get(closure_reason, closure_reason) updated_arguments["status"] = "closed" updated_arguments["closure_reason"] = closure_reason_api # Use description from XSOAR field, fallback to static text only if not provided closure_reason_description = ( update_args.get("closure_reason_description") or update_args.get("cyberintclosurereasondescription") or "Closed from XSOAR" ) updated_arguments["closure_reason_description"] = closure_reason_description elif updated_status: # Status change to non-closed state updated_arguments["status"] = updated_status else: # No status change from XSOAR, check current Cyberint status cyberint_response = client.get_alert(alert_ref_id=incident_id) cyberint_alert: dict[str, Any] = cyberint_response["alert"] cyberint_status = cyberint_alert.get("status") updated_arguments["status"] = cyberint_status if cyberint_status == "closed": updated_arguments["closure_reason"] = cyberint_alert.get("closure_reason", "other") updated_arguments["closure_reason_description"] = cyberint_alert.get( "closure_reason_description", "Closed from Cyberint" ) updated_arguments["alerts"] = [incident_id] demisto.debug(f"******** Remote ID [{incident_id}] to Cyberint. {updated_arguments=}|| {update_args=}") client.update_alerts(**updated_arguments) demisto.debug(f"******** Remote data of {incident_id}: {parsed_args.data}") except Exception as error: demisto.error(f"Error in Cyberint outgoing mirror for incident {incident_id}\nError message: {error}") finally: return incident_id def get_remote_data_command( client: Client, args: dict[str, Any], params: dict[str, Any], ) -> GetRemoteDataResponse: """ Gets new information about the incidents in the remote system and updates existing incidents in Cortex XSOAR. Args: client: Cyberint API client. args (Dict[str, Any]): command arguments. Returns: List[Dict[str, Any]]: first entry is the incident (which can be completely empty) and the new entries. """ parsed_args = GetRemoteDataArgs(args) incident_id = parsed_args.remote_incident_id last_update = date_to_epoch_for_fetch(arg_to_datetime(parsed_args.last_update)) demisto.debug(f"******** Check {incident_id} update from {last_update}") response = client.get_alert(alert_ref_id=incident_id) if not isinstance(response, dict): response = json.loads(response) if response is None: demisto.error("Invalid response from Cyberint") return GetRemoteDataResponse({}, []) mirrored_ticket: dict[str, Any] = response.get("alert", {}) if mirrored_ticket is None or not mirrored_ticket: return GetRemoteDataResponse({}, []) ticket_last_update = date_to_epoch_for_fetch(arg_to_datetime(mirrored_ticket.get("update_date"))) mirrored_ticket["cyberintstatus"] = MIRRORING_FIELDS_MAPPER.get(mirrored_ticket["status"]) # Convert API value to display value for closure_reason api_closure_reason = mirrored_ticket.get("closure_reason") mirrored_ticket["cyberintclosurereason"] = ( CLOSURE_REASON_TO_DISPLAY.get(api_closure_reason, api_closure_reason) if api_closure_reason else None ) mirrored_ticket["cyberintclosurereasondescription"] = mirrored_ticket.get("closure_reason_description") demisto.debug(f"******** Alert {incident_id} - {ticket_last_update=} {last_update=}") entries = [] if mirrored_ticket.get("status") == "closed" and params.get("close_incident"): entries.append( { "Type": EntryType.NOTE, "Contents": { "dbotIncidentClose": True, "closeReason": "Closed from Cyberint.", }, "ContentsFormat": EntryFormat.JSON, } ) return GetRemoteDataResponse(mirrored_ticket, entries) def date_to_epoch_for_fetch(date: datetime | None) -> int: """ Converts datetime object to date in epoch timestamp (in seconds), for fetch command. Args: date (Optional[datetime]): The datetime to convert. Returns: int: date in epoch timestamp. """ if date is None: return int(datetime.now().timestamp()) return date_to_timestamp(date) // 1000 def migrate_closure_fields_if_needed(last_run: dict[str, Any]) -> None: """ One-time migration: ensures the closure reason incident fields have the correct display names and user-friendly select values. Runs once and sets a flag in last_run so it won't repeat. """ if last_run.get("closure_fields_migrated"): return demisto.debug("******** Running one-time closure fields migration") field_updates = { "incident_cyberintclosurereason": { "name": "Cyberint Alert Close Reason", "selectValues": EXPECTED_CLOSURE_REASON_SELECT_VALUES, }, "incident_cyberintclosurereasondescription": { "name": "Cyberint Alert Close Reason Description", }, } for field_id, updates in field_updates.items(): try: get_result = demisto.executeCommand("core-api-get", {"uri": f"/incidentfield/{field_id}"}) if is_error(get_result): demisto.debug(f"******** Could not get field {field_id}: {get_error(get_result)}") continue current_field = get_result[0]["Contents"]["response"] current_field.update(updates) update_result = demisto.executeCommand("core-api-post", {"uri": f"/incidentfield/{field_id}", "body": current_field}) if is_error(update_result): demisto.debug(f"******** Could not update field {field_id}: {get_error(update_result)}") else: demisto.debug(f"******** Successfully migrated field {field_id}") except Exception as e: demisto.debug(f"******** Error migrating field {field_id}: {e}") last_run["closure_fields_migrated"] = True def fetch_incidents( client: Client, last_run: dict[str, int], first_fetch_time: str, fetch_severity: list[str] | None, fetch_status: list[str] | None, fetch_type: list[str] | None, fetch_environment: list[str] | None, max_fetch: int | None, duplicate_alert: bool, mirror_direction: str | None, close_alert: bool, ) -> tuple[dict[str, int], list[dict]]: """ Fetch incidents (alerts) each minute (by default). Args: client (Client): Cyberint Client. last_run (dict): Dict with last_fetch object, saving the last fetch time(in millisecond timestamp). first_fetch_time (dict): Dict with first fetch time in str (ex: 3 days ago). fetch_severity (list(str)): Severities to fetch. fetch_status (list(str)): Statuses to fetch. fetch_type (list(str)): Types to fetch. fetch_environment (list(str)): Environments to fetch. max_fetch (int): Max number of alerts to fetch. duplicate_alert (bool): Whether to duplicate alerts. mirror_direction (str): Direction to mirror. close_alert (bool): Whether to close alerts. Returns: Tuple of next_run (seconds timestamp) and the incidents list """ # Start by setting the time to fetch from. # use condition statement to avoid mypy error last_fetch_timestamp = last_run.get("last_fetch", None) if last_fetch_timestamp: last_fetch_date = datetime.fromtimestamp(last_fetch_timestamp / 1000) last_fetch = last_fetch_date else: first_fetch_date = dateparser.parse(first_fetch_time) last_fetch = first_fetch_date # type: ignore incidents = [] next_run = last_fetch # Send the API request to fetch the alerts. alerts = client.list_alerts( page="1", page_size=max_fetch, created_date_from=datetime.strftime(last_fetch, DATE_FORMAT), created_date_to=datetime.strftime(datetime.now(), DATE_FORMAT), modification_date_from=None, modification_date_to=None, update_date_from=None, update_date_to=None, environments=fetch_environment, statuses=fetch_status, severities=fetch_severity, types=fetch_type, ) for alert_object in alerts.get("alerts", []): alert = dict(alert_object) # Create the XS0AR incident. alert_created_time = datetime.strptime(alert.get("created_date"), "%Y-%m-%dT%H:%M:%S") # type: ignore alert_id = alert["ref_id"] alert_title = alert.get("title") attachments = [] incident_attachments = [] attachments_keys = { "attachment": [["attachments"], ["alert_data", "screenshot"], ["alert_data", "csv"]], "analysis_report": [["analysis_report"]], } for attachment_type, attachments_path in attachments_keys.items(): for path in attachments_path: current_attachments = dict_safe_get(alert, path, default_return_value=[]) attachment_list = current_attachments if isinstance(current_attachments, list) else [current_attachments] # Retrieve alert Incident attachments files - Attachments, CSV, Screenshot, and Analysis report. current_incident_attachments = get_alert_attachments(client, attachment_list, attachment_type, alert_id) # type: ignore incident_attachments.extend(current_incident_attachments) for tmp_attachment in attachment_list: if tmp_attachment: attachments.append(tmp_attachment) alert["attachments"] = attachments alert_data = dict_safe_get(alert, ["alert_data", "csv"], {}) incident_csv_records = alert_data.get("content", []) alert_csv_id = dict_safe_get(alert, ["alert_data", "csv", "id"]) if alert_csv_id: extracted_csv_data = extract_data_from_csv_stream(client, alert_id, alert_csv_id) # type: ignore alert["alert_data"]["csv"] = extracted_csv_data # add alert_name key to alert response alert_name = f"Cyberint alert {alert_id}: {alert_title}" alert.update({"alert_name": alert_name}) alert["closure_reason_description"] = "none" alert["incident_id"] = alert_id alert["mirror_direction"] = mirror_direction alert["mirror_instance"] = demisto.integrationInstance() incident = { "name": alert_name, "occurred": datetime.strftime(alert_created_time, DATE_FORMAT), "rawJSON": json.dumps(alert), "severity": SEVERITIES.get(alert.get("severity", "low")), "attachment": incident_attachments, "mirror_direction": mirror_direction, "mirror_instance": demisto.integrationInstance(), } if duplicate_alert and incident_csv_records: for index, incident_csv_record in enumerate(incident_csv_records): alert_data.update({"content": incident_csv_record}) alert.update({"attachments": alert_data}) alert_name = f"Cyberint alert {alert_id} ({index + 1}): {alert_title}" alert.update({"alert_name": alert_name}) incident.update({"name": alert_name, "rawJSON": json.dumps(alert)}) incidents.append(copy.deepcopy(incident)) else: incidents.append(incident) # close Cyberint alert if required if close_alert: client.update_alerts( alerts=argToList(alert_id), status="closed", closure_reason="resolved", ) if incidents: # Update the time for the next fetch so that there won't be duplicates. last_incident_time = max(incidents, key=lambda item: item["occurred"]) next_run = datetime.strptime(str(last_incident_time["occurred"]), DATE_FORMAT) next_run += timedelta(seconds=1) next_run_timestamp = int(datetime.timestamp(next_run) * 1000) return {"last_fetch": next_run_timestamp}, incidents def main(): """ PARSE AND VALIDATE INTEGRATION PARAMS """ params = demisto.params() command = demisto.command() access_token = params.get("access_token") url = params.get("environment") verify_certificate = not params.get("insecure", False) first_fetch_time = params.get("first_fetch", "3 days").strip() proxy = params.get("proxy", False) base_url = f"{url}/alert/" demisto.info(f"Command being called is {command}") try: client = Client( base_url=base_url, verify_ssl=verify_certificate, access_token=access_token, proxy=proxy, ) if command == "test-module": result = test_module(client) return_results(result) elif command == "fetch-incidents": last_run = demisto.getLastRun() migrate_closure_fields_if_needed(last_run) fetch_environment = argToList(params.get("fetch_environment", "")) fetch_status = params.get("fetch_status", []) fetch_type = params.get("fetch_type", []) fetch_severity = params.get("fetch_severity", []) max_fetch = int(params.get("max_fetch", "50")) duplicate_alert = params.get("duplicate_alert", False) mirror_direction = ( None if params.get("mirror_direction") == "None" else MIRROR_DIRECTION_MAPPING[params["mirror_direction"]] ) close_alert = params.get("close_alert", False) next_run, incidents = fetch_incidents( client, last_run, first_fetch_time, fetch_severity, fetch_status, fetch_type, fetch_environment, max_fetch, duplicate_alert, mirror_direction, close_alert, ) next_run["closure_fields_migrated"] = last_run.get("closure_fields_migrated", False) demisto.setLastRun(next_run) demisto.incidents(incidents) elif command == "cyberint-alerts-fetch": return_results(cyberint_alerts_fetch_command(client, demisto.args())) elif command == "get-remote-data": return_results(get_remote_data_command(client, demisto.args(), params)) elif command == "get-modified-remote-data": return_results(get_modified_remote_data(client, demisto.args())) elif command == "update-remote-system": return_results(update_remote_system(client, demisto.args())) elif command == "get-mapping-fields": return_results(get_mapping_fields_command()) elif command == "cyberint-alerts-status-update": return_results(cyberint_alerts_status_update(client, demisto.args())) elif command == "cyberint-alerts-get-attachment": return_results(cyberint_alerts_get_attachment_command(client, **demisto.args())) elif command == "cyberint-alerts-analysis-report": return_results(cyberint_alerts_get_analysis_report_command(client, **demisto.args())) except Exception as e: if "Invalid token or token expired" in str(e): error_message = "Error verifying access token and / or URL, make sure the configuration parameters are correct." elif "datetime" in str(e).lower(): error_message = ( "Invalid time specified, " "make sure the arguments are correctly formatted and are not " "earlier than 2020 or later than the current time." ) elif "Unauthorized alerts requested" in str(e): error_message = "Some of the alerts selected to update are either blocked or not found." else: error_message = f"Failed to execute {command} command. Error: {e!s}" return_error(error_message) if __name__ in ("__main__", "__builtin__", "builtins"): main()