VMware Carbon Black Cloud¶
Table of Contents¶
Release Notes¶
Version |
Date |
Notes |
---|---|---|
1.0.0 |
06/2024 |
Initial Release |
Overview¶
IBM SOAR app bidirectional synchronization and functions for VMware Carbon Black Cloud
Provide bidirectional synchronization and functions for VMware Carbon Black Cloud
Key Features¶
Poll Carbon Black Cloud alerts and create and update the corresponding cases in SOAR
Set the Carbon Black Cloud alert workflow information (determination, status, closure reason) from the corresponding case in SOAR
Create artifacts from an alert in the corresponding case in SOAR
Synchronize notes between Carbon Black Cloud alert and corresponding SOAR case
View alert device details in the CBC Device data table
View alert proceses details in the Processes data table
Perform the following remediation task from the CBC Device data table:
Quarantine ON/OFF
Bypass ON/OFF
Background Scan ON/OFF
Post tags on the alert from SOAR
Write the alert and device full JSON data from Carbon Black Cloud to a note in SOAR
Kill a running process on the device from the Processes data table
Override the reputation of an application to approve or ban it for an organization from the Processes data table or a SHA256 artifact
Get alert observations from Carbon Black Cloud and write the JSON to a note in SOAR
Requirements¶
This app supports the IBM Security QRadar SOAR Platform and the IBM Security QRadar SOAR for IBM Cloud Pak for Security.
SOAR platform¶
The SOAR platform supports two app deployment mechanisms, Edge Gateway (also known as App Host) and integration server.
If deploying to a SOAR platform with an App Host, the requirements are:
SOAR platform >=
50.0.9097
.The app is in a container-based format (available from the AppExchange as a
zip
file).
If deploying to a SOAR platform with an integration server, the requirements are:
SOAR platform >=
50.0.9097
.The app is in the older integration format (available from the AppExchange as a
zip
file which contains atar.gz
file).Integration server is running
resilient-circuits>=51.0.1.1.0
.If using an API key account, make sure the account provides the following minimum permissions:
Name
Permissions
Org Data
Read
Function
Read
Incidents
Read, Create
Edit Incidents
Fields, Status
Layouts
Read, Edit
The following SOAR platform guides provide additional information:
Edge Gateway Deployment Guide or App Host Deployment Guide: provides installation, configuration, and troubleshooting information, including proxy server settings.
Integration Server Guide: provides installation, configuration, and troubleshooting information, including proxy server settings.
System Administrator Guide: provides the procedure to install, configure and deploy apps.
The above guides are available on the IBM Documentation website at ibm.biz/soar-docs. On this web page, select your SOAR platform version. On the follow-on page, you can find the Edge Gateway Deployment Guide, App Host Deployment Guide, or Integration Server Guide by expanding Apps in the Table of Contents pane. The System Administrator Guide is available by expanding System Administrator.
Cloud Pak for Security¶
If you are deploying to IBM Cloud Pak for Security, the requirements are:
IBM Cloud Pak for Security >=
1.10.15
.Cloud Pak is configured with an Edge Gateway.
The app is in a container-based format (available from the AppExchange as a
zip
file).
The following Cloud Pak guides provide additional information:
Edge Gateway Deployment Guide or App Host Deployment Guide: provides installation, configuration, and troubleshooting information, including proxy server settings. From the Table of Contents, select Case Management and Orchestration & Automation > Orchestration and Automation Apps.
System Administrator Guide: provides information to install, configure, and deploy apps. From the IBM Cloud Pak for Security IBM Documentation table of contents, select Case Management and Orchestration & Automation > System administrator.
These guides are available on the IBM Documentation website at ibm.biz/cp4s-docs. From this web page, select your IBM Cloud Pak for Security version. From the version-specific IBM Documentation page, select Case Management and Orchestration & Automation.
Proxy Server¶
The app does support a proxy server.
Python Environment¶
Python 3.9 and 3.11 are officially supported. When deployed as an app, the app runs on Python 3.11. Additional package dependencies may exist for each of these packages:
resilient-circuits>=51.0.1.1.0
VMware Carbon Black Cloud Development Version¶
This app has been implemented using:
Product Name |
Product Version |
API URL |
API Version |
---|---|---|---|
VMware Carbon Black Cloud |
NA |
Alerts |
v7 |
VMware Carbon Black Cloud |
NA |
Devices |
v6 |
VMware Carbon Black Cloud |
NA |
Observations |
v2 |
VMware Carbon Black Cloud |
NA |
Job Service |
v1 |
VMware Carbon Black Cloud |
NA |
Live Response |
v6 |
Prerequisites¶
A Carbon Black Cloud account with and API key and secret.
Permissions¶
The following Carbon Black Cloud Access Level API Key Permissions must be granted to run this SOAR app:
Name |
Permissions |
---|---|
org.alerts.tags |
CREATE, READ, DELETE |
org.xdr.metadata |
READ |
org.alerts.notes |
CREATE, READ, UPDATE, DELETE |
org.alerts |
READ |
org.alerts.close |
EXECUTE |
org.mdr.threathunts |
READ |
org.reputations |
CREATE |
org.liveresponse.session |
CREATE, READ, DELETE |
org.liveresponse.process |
READ, DELETE, EXECUTE |
org.search.events |
READ, CREATE |
device |
READ |
device.quarantine |
EXECUTE |
device.bypass |
EXECUTE |
device.bg-scan |
EXECUTE |
jobs.status |
READ |
Create a custom access levels by going to Settings->API Access and selecting the Access Levels tab and hit the Add Access Level. Edit the Access Level as shown below. Then create an API key/secret with the created custom access level.
¶
Installation¶
Install¶
To install or uninstall an App or Integration on the SOAR platform, see the documentation at ibm.biz/soar-docs.
To install or uninstall an App on IBM Cloud Pak for Security, see the documentation at ibm.biz/cp4s-docs and follow the instructions above to navigate to Orchestration and Automation.
App Configuration¶
The following table provides the settings you need to configure the app. These settings are made in the app.config file. See the documentation discussed in the Requirements section for the procedure.
Config |
Required |
Example |
Description |
---|---|---|---|
api_id |
Yes |
|
VMware Carbon Black Cloud API ID. |
api_secret |
Yes |
|
VMware Carbon Black Cloud API secret. |
hostname |
Yes |
|
VMware Carbon Black Cloud host name. |
org_key |
Yes |
|
VMware Carbon Black Cloud organization key. |
polling_filters_criteria_1 |
No |
|
First polling filter. |
polling_filters_criteria_2 |
No |
|
Second polling filter. |
polling_filters_criteria_3 |
No |
|
*Third polling filter. |
polling_interval |
Yes |
|
Poller interval time in seconds. Value of zero turns poller off. |
polling_lookback |
Yes |
|
Number of minutes to lookback for queries the first time the poller runs. |
soar_create_case_template |
No |
|
Path to override template for automatic case creation. See Poller Considerations. |
soar_update_case_template |
No |
|
Path to override template for automatic case updating. See Poller Considerations. |
soar_close_case_template |
No |
|
Path to override template for automatic case closing. See Poller Considerations. |
Custom Layouts¶
The app automatically creates a custom Carbon Black Cloud
tab on first run after installation:
Poller Considerations¶
The poller is just one way to escalate Carbon Black Cloud alerts to SOAR cases. It’s also possible to send Carbon Black Cloud alert information to another SIEM, such as IBM QRadar, which would then correlate cases into Offenses. With the QRadar Plugin for SOAR, offenses can then be escalated to SOAR cases. As long as the Carbon Black Cloud alert is preserved in the custom case field vmware_cbc_alert_id
, then all the remaining details about the case synchronize to the SOAR case. In the case of the QRadar Plugin for SOAR, you would modify the escalation templates to reference this custom field with the VMware Carbon Black Cloud Alert ID.
When using another source of Carbon Black Cloud alert escalation to IBM SOAR, disable the poller by changing the app.config setting to poller_interval=0
.
Poller Templates for SOAR Cases¶
It may be necessary to modify the templates used to create, update, or close SOAR cases based on your required custom fields in SOAR.
This is especially relevant if you have required custom close
fields that need to be filled when closing a case in SOAR. If that is the case, be sure to implement a custom close_case_template
and reference those required close fields in the template.
When overriding the template in App Host, specify the file path for each file as /var/rescircuits
.
Below are the default templates used which can be copied, modified, and used with app_config’s
soar_create_case_template
, soar_update_case_template
, and soar_close_case_template
settings to override the default templates.
Alert Filtering¶
To limit the number of alerts escalated to SOAR, consider using the optional polling_filters
parameter in the app configuration file. Each filter is a tuple in the following format: (“field”,”values”),
Where:
“field” is the Carbon Black Cloud alert field to be queried
“values” is a comma separated string of values to include in the query results returned
If more than one filter is needed, separate each tuple with a comma.
Denote lists enclosed in square brackets [].
NOTE: See Carbon Black Cloud documentation here
Carbon Black Alert Search Fields for all possible searchable alert fields.
Here are 3 polling filter criteria examples that add or update CBC alerts in SOAR
polling_filters_criteria_1 = ("group_results",False),("minimum_severity",4),("target_value",["LOW","MEDIUM"]),("alert_type",["CB_ANALYTICS","WATCHLIST"]),("workflow_status",["OPEN","IN_PROGRESS","CLOSED"])
polling_filters_criteria_2 = ("group_results",False),("minimum_severity",1),("target_value",["LOW","MEDIUM","HIGH","MISSION_CRITICAL"]),("alert_type",["CB_ANALYTICS"]),("reputation",["KNOWN_MALWARE","PUP","SUSPECT_MALWARE","COMPANY_BLACK_LIST"]),("workflow_status",["OPEN","IN_PROGRESS","CLOSED"])
polling_filters_criteria_3 = ("group_results",False),("minimum_severity",1),("target_value",["LOW","MEDIUM","HIGH","MISSION_CRITICAL"]),("alert_type",["CB_ANALYTICS"]),("reason_code",["T_DETECT_SUSPECT","T_RUN_MALWARE","T_REP_VIRUS","T_RUN_VIRUS"]),("workflow_status",["OPEN","IN_PROGRESS","CLOSED"])
NOTE: Each individual polling filter is first constructed by joining together the field and the desired values using OR statements. Each individual filter is then combined using AND. In the example above, only alerts with target_value LOW,MEDIUM, HIGH, MISSION_CRITICAL AND those with workflow statuses OPEN, IN_PROGRESS,or CLOSED are pulled into SOAR as cases.
Similar to alert search “criteria”, a search alert “exclusions” filter can be specified for each polling_filter_criteria_(1,2,3).¶
Function - VMware CBC: Get Alert By ID¶
Get the alert JSON from VMware Carbon Black Cloud by alert ID.
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
- |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"content": {
"alert": {
"alert_notes_present": false,
"alert_url": "my-cbc.net/alerts?s[c][query_string]=id:0730b7bc-7d51-9d64-68b1-d3e01d28a716\u0026orgKey=XXXXYYYY",
"asset_group": [],
"attack_tactic": "TA0001",
"backend_timestamp": "2024-04-18T17:16:50.606Z",
"backend_update_timestamp": "2024-04-18T17:17:48.151Z",
"childproc_cmdline": "",
"childproc_guid": "",
"childproc_username": "",
"detection_timestamp": "2024-04-18T17:15:47.419Z",
"determination": {
"change_timestamp": "2024-04-18T17:16:50.606Z",
"changed_by": "ALERT_CREATION",
"changed_by_type": "SYSTEM",
"value": "NONE"
},
"device_external_ip": "170.225.223.20",
"device_id": 206046519,
"device_internal_ip": "9.30.87.128",
"device_location": "OFFSITE",
"device_name": "winserver221",
"device_os": "WINDOWS",
"device_os_version": "Windows Server 2022 x64",
"device_policy": "Standard",
"device_policy_id": 493371,
"device_target_value": "MEDIUM",
"device_uem_id": "",
"device_username": "Administrator",
"first_event_timestamp": "2024-04-18T17:15:45.021Z",
"id": "0730b7bc-7d51-9d64-68b1-d3e01d28a716",
"is_updated": true,
"last_event_timestamp": "2024-04-18T17:15:45.398Z",
"mdr_alert": false,
"mdr_alert_notes_present": false,
"mdr_threat_notes_present": false,
"org_key": "7M9FP6WE",
"parent_cmdline": "",
"parent_effective_reputation": "TRUSTED_WHITE_LIST",
"parent_guid": "7M9FP6WE-0c480537-00000344-00000000-1da8f6c97aa305d",
"parent_md5": "",
"parent_name": "c:\\windows\\system32\\services.exe",
"parent_pid": 836,
"parent_reputation": "TRUSTED_WHITE_LIST",
"parent_sha256": "854780206e7abbf5a46704f1e75c5075881e4e914c7b44bea45fac1677781096",
"parent_username": "NT AUTHORITY\\SYSTEM",
"policy_applied": "APPLIED",
"primary_event_id": "6f097307fda711eeb0bd491bae9facb0",
"process_cmdline": "\"C:\\Program Files\\Confer\\RepMgr.exe\"",
"process_effective_reputation": "IGNORE",
"process_guid": "7M9FP6WE-0c480537-000008e8-00000000-1da8f6c9a23e47a",
"process_issuer": [
"Sectigo RSA Code Signing CA 2"
],
"process_md5": "8a87acebc21e2cc5eeb24af602b32b30",
"process_name": "c:\\program files\\confer\\repmgr.exe",
"process_pid": 2280,
"process_publisher": [
"Carbon Black, Inc."
],
"process_reputation": "TRUSTED_WHITE_LIST",
"process_sha256": "f6ec44f025c67ab18170da47c1c610a94a9c84741f3cdfceb20cee565579868a",
"process_username": "NT AUTHORITY\\SYSTEM",
"reason": "A file (340s.exe) with a reputation of known malware was found on disk.",
"reason_code": "T_DETECT_MALWARE",
"run_state": "RAN",
"sensor_action": "ALLOW",
"severity": 3,
"tags": null,
"threat_id": "32120969ccb74e5382fd587417e23e1c",
"threat_notes_present": false,
"ttps": [
"DETECTED_MALWARE_APP",
"UNKNOWN_APP",
"POLICY_DENY"
],
"type": "CB_ANALYTICS",
"user_update_timestamp": null,
"workflow": {
"change_timestamp": "2024-04-18T17:16:50.606Z",
"changed_by": "ALERT_CREATION",
"changed_by_type": "SYSTEM",
"closure_reason": "NO_REASON",
"status": "OPEN"
}
}
},
"inputs": {
"vmware_cbc_alert_id": "0730b7bc-7d51-9d64-68b1-d3e01d28a716"
},
"metrics": {
"execution_time_ms": 213,
"host": "myhost",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"timestamp": "2024-04-19 10:53:10",
"version": "1.0"
},
"raw": null,
"reason": null,
"success": true,
"version": 2.0
}
Example Function Input Script:
inputs.vmware_cbc_alert_id = incident.properties.vmware_cbc_alert_id
Example Function Post Process Script:
from urllib.parse import quote as url_encode
# Map VMware CBC workflow closure_reason values SOAR resolution id values.
MAPPING_CLOSURE_REASON = {
"Resolved" : "Resolved",
"No Reason": "Resolved",
"Resolved Benign Known Good": "Resolved",
"Duplicate Cleanup" : "Duplicate",
"Other": "Resolved"
}
# Map VMware CBC determination values to SOAR resolution id values.
MAPPING_DETERMINATION_ON_CLOSE = {
"None": "Resolved",
"False positive": "Not an Issue",
"True positive": "Resolved"
}
results = playbook.functions.results.get_alert_by_id_results
if not results.success:
incident.addNote("<b>VMware CBC: Update Case Manual:</b> Write Alert Custom Fields did NOT complete.")
else:
content = results.get("content", {})
if content:
alert = content.get("alert")
alert_url = alert.get("alert_url", None)
if alert_url:
# url encode the bracket characters []
alert_url_urlencoded = alert_url.replace("[", "%5B").replace("]", "%5D")
incident.properties.vmware_cbc_alert_link = "<a target='_blank' href='https://{0}'>Alert Link</a>".format(alert_url_urlencoded)
incident.properties.vmware_cbc_alert_type = alert.get("type", None)
incident.properties.vmware_cbc_alert_reason_code = alert.get("reason_code", None)
incident.properties.vmware_cbc_threat_id = alert.get("threat_id", None)
workflow = alert.get("workflow", None) or None
status = workflow.get("status", None) if workflow.get("status", None) else None
closure_reason = workflow.get("closure_reason") if workflow.get("closure_reason") else None
incident.properties.vmware_cbc_workflow_status = status.replace("_", " ").title() if status else None
incident.properties.vmware_cbc_workflow_closure_reason = closure_reason.replace("_", " ").title() if status else None
determination = alert.get("determination", None)
determination_value = determination.get("value") if determination.get("value", None) else None
incident.properties.vmware_cbc_determination_value = determination_value.replace("_", " ").title() if determination_value else None
tags = alert.get("tags", None)
incident.properties.vmware_cbc_tags = ", ".join(tags) if isinstance(tags, list) else None
incident.properties.vmware_cbc_attack_tactic = alert.get("attack_tactic", None)
incident.addNote("<b>VMware CBC: Update Case Manual:</b> Write Alert Custom Fields completed.")
if incident.properties.vmware_cbc_workflow_status.lower() == "closed":
incident.plan_status = "C"
incident.resolution_id = MAPPING_CLOSURE_REASON.get(incident.properties.vmware_cbc_workflow_closure_reason, "Resolved")
incident.resolution_summary = "Case {0} Closed in SOAR".format(incident.id)
else:
incident.addNote("<b>VMware CBC: Update Case Manual:</b> Write Alert Custom Fields did NOT complete.")
Function - VMware CBC: Get CBC Notes¶
Get the alert or threat notes of the specified Carbon Black Cloud alert or threat id.
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
Can be a CBC alert or threat ID |
|
|
Yes |
|
- |
|
|
Yes |
|
- |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"content": {
"count": 1
},
"inputs": {
"vmware_cbc_id": "0730b7bc-7d51-9d64-68b1-d3e01d28a716",
"vmware_cbc_incident_id": 2186,
"vmware_cbc_note_type": "alert"
},
"metrics": {
"execution_time_ms": 56753,
"host": "myapphost.com",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"timestamp": "2024-04-24 16:15:57",
"version": "1.0"
},
"raw": null,
"reason": null,
"success": true,
"version": 2.0
}
Example Function Input Script:
inputs.vmware_cbc_id = incident.properties.vmware_cbc_alert_id
inputs.vmware_cbc_incident_id = incident.id
inputs.vmware_cbc_note_type = "alert"
Example Function Post Process Script:
results = playbook.functions.results.get_alert_notes_results
if results.get("success"):
content = results.get("content")
if content:
note_text = "<b>VMware CBC: Get Alert Notes</b> playbook created {0} notes in SOAR".format(content.get("count"))
else:
note_text = "<b>VMware CBC: Get Alert Notes</b> function failed to get notes from Carbon Black Cloud"
else:
note_text = "<b>VMware CBC: Get Alert Notes</b> function failed to get notes from Carbon Black Cloud"
incident.addNote(note_text)
Function - VMware CBC: Get Device By ID¶
Get the device information from Carbon Black Cloud by the specified device ID.
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
- |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"content": {
"device": {
"activation_code": null,
"activation_code_expiry_time": "2024-04-22T19:31:18.358Z",
"ad_group_id": 0,
"appliance_name": null,
"appliance_uuid": null,
"asset_group": [],
"auto_scaling_group_name": null,
"av_ave_version": null,
"av_engine": null,
"av_last_scan_time": null,
"av_master": false,
"av_pack_version": null,
"av_product_version": null,
"av_status": [
"AV_ACTIVE",
"ONDEMAND_SCAN_DISABLED"
],
"av_update_servers": null,
"av_vdf_version": null,
"base_device": null,
"cloud_provider_account_id": null,
"cloud_provider_resource_id": null,
"cloud_provider_tags": [],
"cluster_name": null,
"compliance_status": "NOT_ASSESSED",
"current_sensor_policy_name": "Standard",
"datacenter_name": null,
"deployment_type": "ENDPOINT",
"deregistered_time": null,
"device_meta_data_item_list": [
{
"key_name": "OS_MAJOR_VERSION",
"key_value": "Windows 11",
"position": 0
},
{
"key_name": "SUBNET",
"key_value": "9.30.43.0",
"position": 0
}
],
"device_owner_id": 16421859,
"email": "Administrator",
"esx_host_name": null,
"esx_host_uuid": null,
"first_name": null,
"golden_device": null,
"golden_device_id": null,
"id": 210476652,
"infrastructure_provider": "NONE",
"last_contact_time": "2024-05-10T17:31:19.521Z",
"last_device_policy_changed_time": null,
"last_device_policy_requested_time": "2024-05-10T17:06:42.474Z",
"last_external_ip_address": "70.25.23.23",
"last_internal_ip_address": "9.35.43.10",
"last_location": "OFFSITE",
"last_name": null,
"last_policy_updated_time": "2023-12-13T07:03:23.711Z",
"last_reported_time": "2024-05-10T17:11:52.177Z",
"last_reset_time": null,
"last_shutdown_time": "2024-05-10T17:06:03.515Z",
"login_user_name": "WINSERVER2022\\Administrator",
"mac_address": "00000a0b67eb",
"middle_name": null,
"name": "winserver2022",
"nsx_distributed_firewall_policy": null,
"nsx_enabled": null,
"organization_id": 72676,
"organization_name": "myorg.com",
"os": "WINDOWS",
"os_version": "Windows Server 2022 x64",
"passive_mode": false,
"policy_id": 493371,
"policy_name": "Standard",
"policy_override": false,
"quarantined": false,
"registered_time": "2024-05-10T16:59:59.563Z",
"scan_last_action_time": null,
"scan_last_complete_time": null,
"scan_status": null,
"sensor_gateway_url": null,
"sensor_gateway_uuid": null,
"sensor_kit_type": "WINDOWS",
"sensor_out_of_date": false,
"sensor_pending_update": false,
"sensor_states": [
"ACTIVE",
"LIVE_RESPONSE_NOT_RUNNING",
"LIVE_RESPONSE_NOT_KILLED",
"LIVE_RESPONSE_DISABLED",
"CB_FIREWALL_INACTIVE"
],
"sensor_version": "4.0.1.1428",
"status": "REGISTERED",
"target_priority": "MEDIUM",
"uninstall_code": "XXXX",
"vcenter_host_url": null,
"vcenter_name": null,
"vcenter_uuid": null,
"vdi_base_device": null,
"vdi_provider": "NONE",
"virtual_machine": false,
"virtual_private_cloud_id": null,
"virtualization_provider": "OTHER",
"vm_ip": null,
"vm_name": null,
"vm_uuid": null,
"vulnerability_score": 8.8,
"vulnerability_severity": "IMPORTANT",
"windows_platform": null
}
},
"inputs": {
"vmware_cbc_device_id": "210476652"
},
"metrics": {
"execution_time_ms": 15881,
"host": "my.app.host",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"timestamp": "2024-05-10 13:34:09",
"version": "1.0"
},
"raw": null,
"reason": null,
"success": true,
"version": 2.0
}
Example Function Input Script:
inputs.vmware_cbc_device_id = row.cbc_device_id
Example Function Post Process Script:
results = playbook.functions.results.get_device_by_id_results
if results.get("success", False):
content = results.get("content", {})
if content:
device = content.get("device", None)
if device:
row.cbc_device_quarantined = device.get("quarantined", None)
row.cbc_device_status = device.get("status", None)
sensor_states = device.get("sensor_states", None)
row.cbc_sensor_state = ", ".join(sensor_states) if isinstance(sensor_states, list) else None
note_text = f"<b>VMware CBC: Populate Device Row from Device:</b> Update complete."
else:
note_text = f"<b>VMware CBC: Populate Device Row from Device:</b> Unable to get device data to update CBC Device data table."
else:
note_text = f"<b>VMware CBC: Populate Device Row from Device:</b> Unable to get device data to update CBC Device data table - no content."
else:
reason = results.get("reason", None)
note_text = f"<b>VMware CBC: Populate Device Row from Device:</b> Unable to get device data to update CBC Device data table.<br> reason: {reason}"
incident.addNote(note_text)
Function - VMware CBC: Post Alert Workflow Data¶
Update the alert workflow information in VMware Carbon Black Cloud.
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
- |
|
|
No |
|
Reason code for why the alert is being updated. Optional |
|
|
No |
|
- |
|
|
No |
|
Custom message to add to the note added to modified alert |
|
|
No |
|
Alert workflow status in CBC |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"content": {
"connector_id": "FRIYZADN2K",
"create_time": "2024-05-29T15:09:30.535479Z",
"id": 14374209,
"job_parameters": {
"job_parameters": {
"request": {
"criteria": {
"id": [
"35081d68-29d0-7592-f408-33939ed01882"
]
},
"determination": "TRUE_POSITIVE",
"status": "IN_PROGRESS"
},
"userWorkflowDto": {
"change_timestamp": "2024-05-29T15:09:30.461Z",
"changed_by": "FRZADN2KIK",
"changed_by_type": "API",
"closure_reason": "NO_REASON",
"status": "IN_PROGRESS"
}
}
},
"last_update_time": "2024-05-29T15:09:31.340546Z",
"org_key": "7M9FP6WE",
"progress": {
"message": "Dismissal completed",
"num_completed": 1,
"num_total": 1
},
"status": "COMPLETED",
"type": "user_workflow_update"
},
"inputs": {
"vmware_cbc_alert_id": "35081d68-29d0-7592-f408-33939ed01882",
"vmware_cbc_closure_reason": null,
"vmware_cbc_determination": "TRUE_POSITIVE",
"vmware_cbc_note_text": null,
"vmware_cbc_status": "IN_PROGRESS"
},
"metrics": {
"execution_time_ms": 1599,
"host": "my.app.host",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"timestamp": "2024-05-29 11:09:31",
"version": "1.0"
},
"raw": null,
"reason": null,
"success": true,
"version": 2.0
}
Example Function Input Script:
STATUS_MAPPING = {
"Unresolved": "CLOSED", # Unresolved
"Duplicate": "CLOSED", # Duplicate
"Not an Issue": "CLOSED", # Not an Issue
"Resolved": "CLOSED" # Resolved
}
CLOSURE_REASON_MAPPING = {
"Unresolved": "NO_REASON", # Unresolved
"Duplicate": "DUPLICATE_CLEANUP", # Duplicate
"Not an Issue": "RESOLVED_BENIGN_KNOWN_GOOD", # Not an Issue
"Resolved": "RESOLVED" # Resolved
}
inputs.vmware_cbc_alert_id = incident.properties.vmware_cbc_alert_id
inputs.vmware_cbc_status = STATUS_MAPPING.get(incident.resolution_id, "CLOSED")
inputs.vmware_cbc_closure_reason = CLOSURE_REASON_MAPPING.get(incident.resolution_id, "NO_REASON")
inputs.vmware_cbc_note_text = incident.resolution_summary.content if incident.resolution_summary.content else f"Case {incident.id} was closed in IBM QRadar SOAR"
# Note determination is set to the custom field value. Make sure to set determination before closing the case in SOAR
inputs.vmware_cbc_determination = incident.properties.vmware_cbc_determination_value.replace(" ", "_").upper() if incident.properties.vmware_cbc_determination_value else "NONE"
# Set custom fields in SOAR with values sent to CBC.
incident.properties.vmware_cbc_workflow_status = inputs.vmware_cbc_status
if incident.properties.vmware_cbc_workflow_status == "CLOSED" and inputs.vmware_cbc_closure_reason:
incident.properties.vmware_cbc_workflow_closure_reason = inputs.vmware_cbc_closure_reason.replace(" ", "_").title()
Example Function Post Process Script:
results = playbook.functions.results.close_alert_results
if results.get("success"):
note_text = "<b>VMware CBC: Close Alert on Case Close:</b> Closed alert in Carbon Black Cloud."
else:
reason = results.get("reason", None)
note_text = "<b>VMware CBC: Close Alert on Case Close:</b> Failed function to set status. Reason = {reason}"
incident.addNote(note_text)
Function - VMware CBC: Post Device Action¶
Post an action to the device monitored by Carbon Black Cloud.
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
- |
|
|
Yes |
|
- |
|
|
No |
|
- |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"content": {
"device_status": "success"
},
"inputs": {
"vmware_cbc_device_action": "QUARANTINE",
"vmware_cbc_device_id": "212389492",
"vmware_cbc_device_toggle": "OFF"
},
"metrics": {
"execution_time_ms": 54545,
"host": "my.app.host",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"timestamp": "2024-05-31 12:31:34",
"version": "1.0"
},
"raw": null,
"reason": null,
"success": true,
"version": 2.0
}
Example Function Input Script:
inputs.vmware_cbc_device_id = row.cbc_device_id
inputs.vmware_cbc_device_action = "QUARANTINE"
inputs.vmware_cbc_device_toggle = "OFF"
Example Function Post Process Script:
results = playbook.functions.results.post_device_action_results
if results.get("success"):
note_text = f"<b>VMware CBC: Quarantine OFF:</b> Post action to device successful."
else:
reason = results.get("reason")
note_text = f"<b>VMware CBC: Quarantine OFF:</b> Failed to post action Quarantine OFF: {reason}."
incident.addNote(note_text)
Function - VMware CBC: Post Note to CBC Alert¶
Post a note to the specified Carbon Black Cloud alert in CBC.
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
- |
|
|
Yes |
|
- |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"content": {
"author": "FR6K2KN2K",
"create_timestamp": "2024-04-26T20:30:03.905Z",
"id": "50c323c1-8495-4fe4-815f-7bc411976ec9",
"last_update_timestamp": "2024-04-26T20:30:03.905Z",
"note": "Created by IBM SOAR: VMware CBC Update Case on Creation: populated Process data table 2 artifact(s) added to the case.",
"note_json_format": null,
"source": "CUSTOMER"
},
"inputs": {
"vmware_cbc_alert_id": "0730b7bc-7d51-9d64-68b1-d3e01d28a716",
"vmware_cbc_alert_note_text": "\u003cb\u003eVMware CBC Update Case on Creation:\u003c/b\u003e populated Process data table\u003cbr /\u003e\u003cb\u003e2\u003c/b\u003e artifact(s) added to the case."
},
"metrics": {
"execution_time_ms": 19794,
"host": "myapphost",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"timestamp": "2024-04-26 16:30:04",
"version": "1.0"
},
"raw": null,
"reason": null,
"success": true,
"version": 2.0
}
Example Function Input Script:
inputs.vmware_cbc_alert_id = incident.properties.vmware_cbc_alert_id
inputs.vmware_cbc_alert_note_text = note.text.content
Example Function Post Process Script:
from datetime import datetime
results = playbook.functions.results.post_note_to_alert_results
# Edit note in SOAR to indicate it was sent to CBC alert
if results.get("success"):
# Get the current time
now = datetime.now()
note.text = u"<b>Post note to CBC alert at {0}</b><br>{1}".format(now, note.text.content)
Function - VMware CBC: Post Observations Detail Job¶
Creates an Observations details job and returns the result of the job. The details include information about the given event that’s not normally accessible during a search. The observations parameter is a JSON object converted to a string. See the Carbon Black Cloud documentation on the Observation Detail Job: Create Detail Job request body. The full JSON is defined :
{
“alert_id”: “
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
JSON value dumped to a string. |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"version": 2.0,
"success": true,
"reason": null,
"content": {
"results": [
{
"alert_category": [
"THREAT"
],
"alert_id": [
"3680c6ba-c645-6779-3976-f9ed69326ca1"
],
"backend_timestamp": "2024-06-12T14:35:37.015Z",
"device_external_ip": "170.225.223.22",
"device_group_id": 0,
"device_id": 212389492,
"device_installed_by": "Administrator",
"device_internal_ip": "9.30.34.190",
"device_location": "OFFSITE",
"device_name": "cb-win221",
"device_os": "WINDOWS",
"device_os_version": "Windows Server 2022 x64",
"device_policy": "standard",
"device_policy_id": 493371,
"device_sensor_version": "4.0.0.1292",
"device_target_priority": "MEDIUM",
"device_timestamp": "2024-06-12T14:32:57.641Z",
"document_guid": "djeE5vZFSoS1Xar6Gn_S1A",
"enriched": true,
"enriched_event_type": "FILE_CREATE",
"event_attack_stage": [
"DELIVER_EXPLOIT"
],
"event_description": "The file \"<share><link hash=\"761de33e1c3d08865f5f2d0cfc84c3b5401c7915a2953ca6b8c2fddbb007556b\">c:\\users\\administrator\\downloads\\temp4\\340s.exe\\340s.exe</link></share>\" was first detected on a local disk. The device was off the corporate network using the public address 170.225.223.22 (located in San Jose CA, United States). The file is not signed. The file was created by the application \"<share><link hash=\"f6ec44f025c67ab18170da47c1c610a94a9c84741f3cdfceb20cee565579868a\">c:\\program files\\confer\\repmgr.exe</link></share>\".",
"event_id": "cda5cca328c811efb47ec103488c1130",
"event_threat_score": [
3
],
"event_type": "filemod",
"file_scan_result": "/ZBot",
"filemod_action": "ACTION_FILE_CREATE",
"filemod_hash": [
"48cd89827939b3a8976d9bb0993bc338",
"761de33e1c3d08865f5f2d0cfc84c3b5401c7915a2953ca6b8c2fddbb007556b"
],
"filemod_issuer": [
"jhjkkkaaaaaaaaaaa"
],
"filemod_name": "c:\\users\\administrator\\downloads\\temp4\\340s.exe\\340s.exe",
"filemod_publisher": [
"jhjkkkaaaaaaaaaaa"
],
"filemod_publisher_state": [
"FILE_SIGNATURE_STATE_SIGNED"
],
"filemod_reputation": "KNOWN_MALWARE",
"ingress_time": 1718202925831,
"legacy": true,
"observation_description": "A known virus (Trojan: ZBot) was detected in file c:\\users\\administrator\\downloads\\temp4\\340s.exe\\340s.exe.",
"observation_id": "cda5cca328c811efb47ec103488c1130:3680c6ba-c645-6779-3976-f9ed69326ca1",
"observation_type": "CB_ANALYTICS",
"org_id": "7M9FP6WE",
"parent_effective_reputation": "TRUSTED_WHITE_LIST",
"parent_effective_reputation_source": "CLOUD",
"parent_guid": "7M9FP6WE-0ca8ce74-00000318-00000000-1daac4e9610e992",
"parent_hash": [
"7477c64706110f4d0652b28c6618b8b83d27fef94ca9d112965f8c9a62ca3d7e"
],
"parent_issuer": [
"Microsoft Windows Production PCA 2011"
],
"parent_name": "c:\\windows\\system32\\services.exe",
"parent_pid": 792,
"parent_publisher": [
"Microsoft Windows Publisher"
],
"parent_publisher_state": [
"FILE_SIGNATURE_STATE_VERIFIED",
"FILE_SIGNATURE_STATE_SIGNED"
],
"parent_reputation": "TRUSTED_WHITE_LIST",
"parent_user_id": "S-1-5-18",
"parent_username": "NT AUTHORITY\\SYSTEM",
"process_cmdline": [
"\"C:\\Program Files\\Confer\\RepMgr.exe\""
],
"process_cmdline_length": [
36
],
"process_effective_reputation": "IGNORE",
"process_effective_reputation_source": "IGNORE",
"process_guid": "7M9FP6WE-0ca8ce74-00000460-00000000-1daac5135bc76a4",
"process_hash": [
"8a87acebc21e2cc5eeb24af602b32b30",
"f6ec44f025c67ab18170da47c1c610a94a9c84741f3cdfceb20cee565579868a"
],
"process_issuer": [
"Sectigo RSA Code Signing CA 2"
],
"process_name": "c:\\program files\\confer\\repmgr.exe",
"process_pid": [
1120
],
"process_publisher": [
"Carbon Black, Inc."
],
"process_publisher_state": [
"FILE_SIGNATURE_STATE_VERIFIED",
"FILE_SIGNATURE_STATE_SIGNED"
],
"process_reputation": "TRUSTED_WHITE_LIST",
"process_sha256": "f6ec44f025c67ab18170da47c1c610a94a9c84741f3cdfceb20cee565579868a",
"process_start_time": "2024-05-22T14:06:15.472Z",
"process_user_id": "S-1-5-18",
"process_username": [
"NT AUTHORITY\\SYSTEM"
],
"ttp": [
"DETECTED_MALWARE_APP"
]
}
],
"num_found": 1,
"num_available": 1,
"approximate_unaggregated": 1,
"num_aggregated": 1,
"contacted": 21,
"completed": 21,
"message": ""
},
"raw": null,
"inputs": {
"vmware_cbc_observations": "{\"alert_id\": \"3680c6ba-c645-6779-3976-f9ed69326ca1\"}"
},
"metrics": {
"version": "1.0",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"host": "my.app.host",
"execution_time_ms": 95538,
"timestamp": "2024-06-17 11:25:10"
}
}
Example Function Input Script:
from json import dumps
if not incident.properties.vmware_cbc_alert_id:
helper.fail("VMware CBC alert ID is required!")
# Note: Either observation_ids or alert_id is required however only one can be specified.
# This playbook specifies an alert_id.
observations ={
"alert_id": incident.properties.vmware_cbc_alert_id
}
inputs.vmware_cbc_observations = dumps(observations)
Example Function Post Process Script:
results = playbook.functions.results.observation_detail_job_results
inputs = results.get("inputs", None)
observation = inputs.get("vmware_cbc_observation", None)
header = u"<b>VMware CBC: Observation Detail Job:</b> {0}".format(observation)
json_note = {
"version": "1.3",
"header": header,
"json": results.content,
"sort": False
}
playbook.addProperty('convert_json_to_rich_text', json_note)
Function - VMware CBC: Post Reputation Override¶
Override the reputation of an application by adding a SHA-256 hash, a certificate signer or a path to a known IT tool application or directory of IT tools to an Approved or Banned list. The reputation override parameter is a JSON object converted to a string. See the Carbon Black Cloud documentation on Configure Reputation Override.
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
JSON value dumped to a string. |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"content": {
"create_time": "2024-05-20T19:56:01.742Z",
"created_by": "FRIYZADN2K",
"description": null,
"filename": null,
"id": "fbf657a616e211efaf639b4554e04742",
"override_list": "WHITE_LIST",
"override_type": "SHA256",
"sha256_hash": "8f7c9555176248ea553d9ac4c8df5a4b5e36ba43d2b02c153e3ee322970877e0",
"source": "APP",
"source_ref": null
},
"inputs": {
"vmware_cbc_reputation_override": "{\"override_list\": \"WHITE_LIST\", \"override_type\": \"SHA256\", \"sha256_hash\": \"8f7c9555176248ea553d9ac4c8df5a4b5e36ba43d2b02c153e3ee322970877e0\"}"
},
"metrics": {
"execution_time_ms": 6481,
"host": "my.app.host",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"timestamp": "2024-05-20 15:56:01",
"version": "1.0"
},
"raw": null,
"reason": null,
"success": true,
"version": 2.0
}
Example Function Input Script:
from json import dumps
if not row.cbc_process_sha256:
helper.fail("SHA256 is required to override reputation")
SHA256 = row.cbc_process_sha256
override_list = playbook.inputs.vmware_cbc_override_list
description = playbook.inputs.vmware_cbc_description if playbook.inputs.vmware_cbc_description else None
filename = row.cbc_process_name if row.cbc_process_name else None
reputation = {
"override_list": override_list,
"override_type": "SHA256",
"sha256_hash": SHA256,
"description": description,
"filename": filename
}
inputs.vmware_cbc_reputation_override = dumps(reputation)
Example Function Post Process Script:
from json import dumps
results = playbook.functions.results.override_reputation_results
reason = results.get("reason")
if results.get("success"):
content = results.get("content")
if content:
note_text = "<b>VMware CBC: Override Reputation:</b><br> {0}.".format(dumps(content, indent=4))
else:
note_text = f"<b>VMware CBC: Override Reputation:</b> failed to post override_reputation - no content: {reason}."
else:
note_text = f"<b>VMware CBC: Override Reputation:</b> failed to post override reputation: {reason}."
incident.addNote(note_text)
Function - VMware: CBC Kill Process¶
Kill the process on the specified Carbon Black Cloud device, given the process ID (pid) on the device.
NOTE: This function uses the Carbon Black Live Response API to issue commands on a remote device. All Live Response commands must be issued in the context of a session and the session must have an ACTIVE state. When using the VMware CBC: Kill Process playbook, there may be a delay when the session becomes ACTIVE and the resulting incident note instructs the user to “try again later”. It may take a number of minutes for the session to become ACTIVE.
Inputs:
Name |
Type |
Required |
Example |
Tooltip |
---|---|---|---|---|
|
|
Yes |
|
- |
|
|
Yes |
|
- |
Outputs:
NOTE: This example might be in JSON format, but
results
is a Python Dictionary on the SOAR platform.
results = {
"content": {
"create_time": "2024-05-31T16:15:33Z",
"files": [],
"finish_time": "2024-05-31T16:15:33.377707Z",
"id": 0,
"input": {
"name": "kill",
"object": 2264
},
"name": "kill",
"result_code": 0,
"result_desc": "",
"status": "PENDING",
"sub_keys": [],
"values": []
},
"inputs": {
"vmware_cbc_device_id": "212389492",
"vmware_cbc_process_id": "2264"
},
"metrics": {
"execution_time_ms": 40888,
"host": "my.app.host",
"package": "fn-vmware-cbc",
"package_version": "1.0.0",
"timestamp": "2024-05-31 12:15:47",
"version": "1.0"
},
"raw": null,
"reason": null,
"success": true,
"version": 2.0
}
Example Function Input Script:
inputs.vmware_cbc_device_id = row.cbc_device_id
inputs.vmware_cbc_process_id = row.cbc_process_pid
Example Function Post Process Script:
results = playbook.functions.results.kill_process_results
reason = results.get("reason", None)
inputs = results.get("inputs", None)
pid = inputs.get("vmware_cbc_process_id") if inputs else None
if results.get("success"):
content = results.get("content", None)
if content:
result = content.get("result", None)
if result:
status = result.get("status", None)
result_code = result.get("result_code", None)
if status == "COMPLETE" and result_code == 0:
note_text = f"<b>VMware CBC: Kill Process:</b> PID: {pid} was terminated:<br>{result}."
else:
note_text = f"<b>VMware CBC: Kill Process:</b> ERROR: PID: {pid} was not terminated:<br>{result}<br>{reason}."
else:
note_text = f"<b>VMware CBC: Kill Process:</b> Failed to kill PID {pid} - no result: {reason}."
else:
note_text = f"<b>VMware CBC: Kill Process:</b> Failed to kill PID {pid} - no content: {reason}."
else:
reason = results.get("reason")
note_text = f"<b>VMware CBC: Kill Process:</b> Failed to kill PID {pid}: {reason}."
incident.addNote(note_text)
Script - Convert JSON to rich text v1.3¶
This script converts a json object into a hierarchical display of rich text and adds the rich text to an incident’s rich text (custom) field or an incident note. A workflow property is used to share the json to convert and identify parameters used on how to perform the conversion. Typically, a function will create workflow property and this script will run after that function to perform the conversion.
Features:
Display the hierarchical nature of json, presenting the json keys (sorted if specified) as bold labels
Provide links to found URLs
Create either an incident note or add results to an incident (custom) rich text field.
Object: incident
Script Text:
# (c) Copyright IBM Corp. 2010, 2023. All Rights Reserved.
VERSION = 1.3
"""
This script converts a json object into a hierarchical display of rich text and adds the rich text to an incident's rich text (custom) field or an incident note.
A workflow property is used to define the json to convert and identify parameters used on how to perform the conversion.
Typically, a function will create workflow property and this script will run after that function to perform the conversion.
Features:
* Display the hierarchical nature of json, presenting the json keys as bold labels
* Provide links to found URLs
* Create either an incident note or add results to an incident (custom) rich text field.
In order to use this script, define a workflow property called: convert_json_to_rich_text, to define the json and parameters to use for the conversion.
Workflow properties can be added using a command similar to this:
workflow.addProperty('convert_json_to_rich_text', {
"version": 1.3,
"header": "Artifact scan results for: {}".format(artifact.value),
"padding": 10,
"separator": u"<br />",
"sort": True,
"json": results.content,
"json_omit_list": ["omit"],
"incident_field": None
})
Format of workflow.property.convert_json_to_rich_text:
{
"version": 1.3, [this is for future compatibility]
"header": str, [header line to add to converted json produced or None. Ex: Results from scanning artifact: xxx. The header may contain rich text tags]
"padding": 10, [padding for nested json elements, or defaults to 10]
"separator": u"<br />"|list such as ['<span>','</span>'], [html separator between json keys and lists or defaults to html break: '<br />'.
If a list, then the data is brackets by the pair specified]
"sort": True|False, [sort the json keys at each level when displayed]
"json": json, [required json to convert]
"json_omit_list": [list of json keys to exclude or None]
"incident_field": "<incident_field>" [indicates a builtin rich text incident field, such as 'description'
or a custom rich text field in the format: 'properties.<field>'. default: create an incident note]
}
For playbooks, use playbook.addProperty() with the same format as workflow.addProperty()
Playbooks can also use playbook.functions.results.convert_json_to_rich_text using the standard function output which contains the 'content' json element.
When using playbook.functions.results.convert_json_to_rich_text with standard function results, all the defaults for padding, separator, etc. are used.
"""
import re
# needed for python 3
try:
unicode("abc") # fails in py3
py2 = True
except:
unicode = str
py2 = False
rc = re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+#\?]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')
class ConvertJson:
"""Class to hold the conversion parameters and perform the conversion"""
def __init__(self, omit_keys=[], padding=10, separator=u"<br />", sort_keys=False):
self.omit_keys = omit_keys
self.padding = padding
self.separator = separator
self.sort_keys = sort_keys
def format_link(self, item):
"""[summary]
Find embedded urls (http(s)) and add html anchor tags to display as links
Args:
item ([string])
Returns:
[str]: None|original text if no links|text with html links
"""
formatted_item = item
if py2:
num_type = bool(item and isinstance(item, (int, long, bool, float)))
else:
num_type = bool(item and isinstance(item, (int, bool, float)))
if item and not num_type:
list = rc.findall(item)
if list:
for link in list:
formatted_item = formatted_item.replace(link, u"<a target='blank' href='{0}'>{0}</a>".format(link))
return formatted_item
def expand_list(self, list_value, is_list=False):
"""[summary]
convert items to html, adding indents to nested dictionaries.
Args:
list_value ([dict|list]): json element
Returns:
[str]: html converted code
"""
if not isinstance(list_value, list):
return self.format_link(list_value)
elif not list_value:
return u"None<br>"
try:
items_list = [] # this will ensure list starts on second line of key label
for item in list_value:
if isinstance(item, dict):
result = self.convert_json_to_rich_text(item)
if is_list:
items_list.append(u"<li>{}</li>".format(result))
else:
items_list.append(result)
elif isinstance(item, list):
items_list.append(self.expand_list(item, is_list=True))
elif is_list:
items_list.append(u"<li>{}</li>".format(self.format_link(unicode(item))))
else:
items_list.append(self.format_link(unicode(item)))
expand_list_result = self.add_separator(self.separator if not is_list else u"",
items_list,
is_list=is_list)
if is_list:
return u"<ul>{}</ul>".format(expand_list_result)
else:
return u"<div style='padding:5px'>{}</div>".format(expand_list_result)
except Exception as err:
return str(err)
def convert_json_to_rich_text(self, sub_dict):
"""[summary]
Walk dictionary tree and convert to html for better display
Args:
sub_dict ([type]): [description]
Returns:
[type]: [description]
"""
notes = []
if sub_dict and isinstance(sub_dict, (list, dict)):
if isinstance(sub_dict, list):
expanded_list = self.expand_list(sub_dict, is_list=True)
notes.append(self.add_separator(self.separator, expanded_list))
else:
keys = sorted (sub_dict.keys()) if self.sort_keys else sub_dict.keys()
for key in keys:
if key not in self.omit_keys:
value = sub_dict[key]
is_list = isinstance(value, list)
item_list = [u"<strong>{0}</strong>: ".format(key)]
if isinstance(value, dict):
convert_result = self.convert_json_to_rich_text(value)
if convert_result:
item_list.append(u"<div style='padding:{}px'>{}</div>".format(self.padding, convert_result))
else:
item_list.append(u"None<br>")
else:
item_list.append(self.expand_list(value, is_list=is_list))
notes.append(self.add_separator(self.separator, u"".join(make_unicode(v) for v in item_list), is_list=is_list))
result_notes = u"".join(notes)
if isinstance(self.separator, list):
return result_notes
else:
return result_notes.replace(
u"</div>{0}".format(self.separator), u"</div>").replace(
u"{0}</div>".format(self.separator), u"</div>"
) # tighten up result
def add_separator(self, separator, items, is_list=False):
"""
apply the separator to the data
:param separator: None, str or list such as ['<span>', '</span>']
:param items: str or list to add separator
:return: text with separator applied
"""
_items = items
if not _items:
return "<br>"
if not isinstance(_items, list):
_items = [_items]
if isinstance(separator, list):
return u"".join([u"{}{}{}".format(separator[0], item, separator[1]) for item in _items])
return u"{}{}".format(separator.join(_items), separator if not is_list else u"")
def make_unicode(value):
if value is None:
return 'None'
return unicode(value)
def get_results(property_name):
if playbook and playbook.functions.results[property_name] is not None:
return playbook.functions.results[property_name]
elif playbook and playbook.properties[property_name] is not None:
return playbook.properties[property_name]
elif workflow and workflow.properties[property_name] is not None:
return workflow.properties[property_name]
return None
def get_properties(property_name):
"""
Logic to collect the json and parameters from a workflow property.
Args:
property_name: workflow property to reference
Returns:
padding, separator, header, json_omit_list, incident_field, json, sort_keys
"""
result_properties = get_results(property_name)
if not result_properties:
helper.fail("Playbook/workflow property not found: {}".format(property_name))
padding = int(result_properties.get("padding", 10))
separator = result_properties.get("separator", u"<br />")
if isinstance(separator, list) and len(separator) != 2:
helper.fail("list of separators should be specified as a pair such as ['<div>', '</div>']: {}".format(separator))
header = result_properties.get("header")
sort_keys = bool(result_properties.get("sort", False))
json_omit_list = result_properties.get("json_omit_list")
if not json_omit_list:
json_omit_list = []
incident_field = result_properties.get("incident_field")
# workflow formatted content is 'json'. Standard functions is 'content'
json = result_properties.get("json") if result_properties.get("json") else result_properties.get("content")
json_err = None
# is there an issue we need handle now?
if not json and \
result_properties.get("success") == False and result_properties.get("reason"):
json_err = result_properties.get("reason")
return padding, separator, header, json_omit_list, incident_field, json, json_err, sort_keys
## S T A R T
padding, separator, header, json_omit_list, incident_field, json, json_err, sort_keys = get_properties('convert_json_to_rich_text')
if json_err:
result = "Result error: {}".format(json_err)
else:
if header:
if isinstance(separator, list):
hdr = u"{0}{1}{2}".format(separator[0], header, separator[1])
else:
hdr = u"{0}{1}".format(header, separator)
else:
hdr = u""
convert = ConvertJson(omit_keys=json_omit_list, padding=padding, separator=separator, sort_keys=sort_keys)
converted_json = convert.convert_json_to_rich_text(json)
result = u"{}{}".format(hdr, converted_json if converted_json else "\nNone")
rich_text_note = helper.createRichText(result)
if incident_field:
incident[incident_field] = rich_text_note
else:
incident.addNote(rich_text_note)
Script - VMware CBC: Populate CBC Device Row from Alert¶
Write the function results of get device into the data table in a new row.
Object: incident
Script Text:
from datetime import datetime
results = playbook.functions.results.get_alert_by_id_results
if results.get("success", False):
content = results.get("content", {})
if content:
alert = content.get("alert", [])
device_row = incident.addRow("vmware_cbc_device_dt")
device_row.cbc_query_date = datetime.now()
device_row.cbc_device_id = alert.get("device_id", None)
device_row.cbc_device_name = alert.get("device_name", None)
device_row.cbc_device_policy = alert.get("device_policy", None)
device_row.cbc_device_policy_id = alert.get("device_policy_id", None)
device_row.cbc_device_os_version = alert.get("device_os_version", None)
device_row.cbc_device_username = alert.get("device_username", None)
device_row.cbc_device_external_ip = alert.get("device_external_ip", None)
device_row.cbc_device_internal_ip = alert.get("device_internal_ip", None)
device_row.cbc_device_location = alert.get("device_location", None)
note_text = "<b>VMware CBC: Populate CBC Device Row from Alert:</b> Update complete."
else:
note_text = "<b>VMware CBC: Populate CBC Device Row from Alert:</b> No alert found (no content)."
else:
note_text = "<bVMware CBC: Populate CBC Device Row from Alert:</b> Update did not complete. Reason = {0}".format(results.get("reason", None))
incident.addNote(note_text)
Script - VMware CBC: Populate CBC Device Row from Device¶
Write the results of get device by ID function to the Device data table.
Object: vmware_cbc_device_dt
Script Text:
results = playbook.functions.results.get_device_by_id_results
if results.get("success", False):
content = results.get("content", {})
if content:
device = content.get("device", None)
if device:
row.cbc_device_quarantined = device.get("quarantined", None)
row.cbc_device_status = device.get("status", None)
sensor_states = device.get("sensor_states", None)
row.cbc_sensor_state = ", ".join(sensor_states) if isinstance(sensor_states, list) else None
note_text = f"<b>VMware CBC: Populate Device Row from Device:</b> Update complete."
else:
note_text = f"<b>VMware CBC: Populate Device Row from Device:</b> Unable to get device data to update CBC Device data table."
else:
note_text = f"<b>VMware CBC: Populate Device Row from Device:</b> Unable to get device data to update CBC Device data table - no content."
else:
reason = results.get("reason", None)
note_text = f"<b>VMware CBC: Populate Device Row from Device:</b> Unable to get device data to update CBC Device data table.<br> reason: {reason}"
incident.addNote(note_text)
Script - VMware CBC: Populate Observations Data Table¶
Write observations function results to the Observations data table and create hash artifacts.
Object: Incident
Script Text:
from datetime import datetime
def hash_is_sha256(_hash) -> bool:
return len(_hash) == 64
def hash_is_sha1(_hash) -> bool:
return len(_hash) == 40
def hash_is_md5(_hash) -> bool:
return len(_hash) == 32
results = playbook.functions.results.observation_detail_job_results
if results.get("success", False):
content = results.get("content", {})
if content:
observations = content.get("results", [])
num_rows = 0
num_observations = len(observations)
num_artifacts = 0
for observation in observations:
file_scan_result = observation.get("file_scan_result", None)
# To limit results only add to the data table observations that have file scan results.
if file_scan_result:
row = incident.addRow("vmware_cbc_observations_dt")
num_rows += 1
row.cbc_query_date = datetime.now()
row.cbc_device_time = observation.get("device_timestamp", None)
row.cbc_filemod_name = observation.get("filemod_name", None)
row.cbc_filemod_reputation = observation.get("filemod_reputation", None)
row.cbc_file_scan_result = file_scan_result
filemod_hash = observation.get("filemod_hash", [])
if filemod_hash:
row.filemod_hash =', '.join(str(f_hash) for f_hash in filemod_hash)
for f_hash in filemod_hash:
if hash_is_md5(f_hash):
incident.addArtifact("Malware MD5 Hash", f_hash, f"VMware Carbon Black Observations: MD5 hash from from file {row.cbc_filemod_name}.")
num_artifacts += 1
elif hash_is_sha256(f_hash):
incident.addArtifact("Malware SHA-256 Hash", f_hash, f"VMware Carbon Black Observations: SHA-256 hash from file {row.cbc_filemod_name}.")
num_artifacts += 1
elif hash_is_sha1(f_hash):
incident.addArtifact("Malware SHA-1 Hash", f_hash, f"VMware Carbon Black Observations: SHA-1 hash from file {row.cbc_filemod_name}.")
num_artifacts += 1
note_text = f"<b>VMware CBC: Update Observation DT:</b> Update complete:<br> {num_observations} observations found, {num_rows} rows added to data table and {num_artifacts} artifacts added."
else:
note_text = "<bVMware CBC: Update Observation DT:</b> Failed function to get observations for Observations data table - no content."
else:
reason = results.get("reason", None)
note_text = f"<bVMware CBC: Update Observation DT:</b> Failed function to get observations for Observations data table.<br>Reason = {reason}"
incident.addNote(note_text)
Playbooks¶
Playbook Name |
Description |
Activation Type |
Object |
Status |
Condition |
---|---|---|---|---|---|
VMware CBC: Background Scan OFF |
Manual playbook to turn off background scan on a device from the CBC Device data table. |
Manual |
vmware_cbc_device_dt |
|
`vmware_cbc_device_dt.cbc_device_id has_a_value |
VMware CBC: Background Scan ON |
Manual playbook to enable background scan on a device from the CBC Device data table. |
Manual |
vmware_cbc_device_dt |
|
|
VMware CBC: Bypass OFF |
Manual playbook to turn OFF BYPASS on the device from the CBC Device data table. |
Manual |
vmware_cbc_device_dt |
|
|
VMware CBC: Bypass ON |
Manual playbook to turn ON BYPASS on the device from the CBC Device data table. |
Manual |
vmware_cbc_device_dt |
|
|
VMware CBC: Close Alert on Case Close |
Automatic playbook that updates the Workflow Status and Determination of the associated alert in VMware Carbon Black Cloud when the case is closed in SOAR. The SOAR case resolution summary is written as a note to the Carbon Black Cloud alert. |
Automatic |
incident |
|
|
VMware CBC: Kill Process |
Kill the specified process from the row in the Processes data table. Write the results to a note. |
Manual |
vmware_cbc_processes_dt |
|
|
VMware CBC: Override Reputation |
Override reputation of a SHA256 hash from the Processes data table row. |
Manual |
vmware_cbc_processes_dt |
|
|
VMware CBC: Override Reputation Artifact |
Override reputation of a SHA256 hash from a SHA256 artifact. |
Manual |
artifact |
|
|
VMware CBC: Post Note to CBC Alert |
Manual playbook to post a note to CBC alert in Carbon Black Cloud. |
Manual |
note |
|
|
VMware CBC: Post Tags |
Manual playbook to add a list of tags to a Carbon Black Cloud alert in CBC. |
Manual |
incident |
|
|
VMware CBC: Quarantine OFF |
Manual playbook to remove a device from Quarantine from the CBC Device data table. |
Manual |
vmware_cbc_device_dt |
|
|
VMware CBC: Quarantine ON |
Manual playbook to Quarantine a device from the CBC Device data table. |
Manual |
vmware_cbc_device_dt |
|
|
VMware CBC: Refetch Device in Row |
Get the device data on the device in this row and add to a new row in the data table. Use to compare device query results over time. |
Manual |
vmware_cbc_device_dt |
|
|
VMware CBC: Update Alert Workflow Data |
Update the alert workflow information in VMware Carbon Black Cloud. |
Manual |
incident |
|
|
VMware CBC: Update Case |
Manual playbook to update a VMware Carbon Black Cloud case. Custom fields and comments are updated in SOAR. |
Manual |
incident |
|
|
VMware CBC: Update Case on Creation |
Automatic playbook to update VMware Carbon Black Cloud case when the case is created. Custom fields, data tables and comments are updated in the SOAR case. |
Automatic |
incident |
|
|
VMware CBC: Update Device Row from Device |
Automatic playbook to populate the columns of the CBC Device data table that from the devices endpoint. Note: Other columns of the CBC Device data table are populated from fields of the alert. |
Automatic |
vmware_cbc_device_dt |
|
|
VMware CBC: Update Observations DT |
Manual playbook to get the Observations of an alert and update the Observations data table. |
Manual |
incident |
|
|
VMware CBC: Write Alert JSON to Note |
Write the VMware Carbon Black Cloud alert JSON to an incident note in SOAR. |
Manual |
incident |
|
|
VMware CBC: Write Device JSON to Note |
Write the VMware Carbon Black Cloud device JSON to an incident note in SOAR. |
Manual |
vmware_cbc_device_dt |
|
|
VMware CBC: Write Observations JSON to Note |
Write the VMware Carbon Black Cloud Observations of an alert JSON to an incident note in SOAR. |
Manual |
incident |
|
|
Custom Layouts¶
Import the Data Tables and Custom Fields like the screenshot below:
Data Table - CBC Device¶
API Name:¶
vmware_cbc_device_dt
Columns:¶
Column Name |
API Access Name |
Type |
Tooltip |
---|---|---|---|
Device ID |
|
|
- |
External IP |
|
|
- |
Internal IP |
|
|
- |
Location |
|
|
- |
Name |
|
|
- |
OS Version |
|
|
- |
Policy |
|
|
- |
Policy ID |
|
|
- |
Quarantined |
|
|
- |
Query Date |
|
|
- |
Sensor State |
|
|
- |
Status |
|
|
- |
Username |
|
|
- |
Data Table - Observations¶
API Name:¶
vmware_cbc_observations_dt
Columns:¶
Column Name |
API Access Name |
Type |
Tooltip |
---|---|---|---|
Device Time |
|
|
- |
File Scan Result |
|
|
- |
Filemod Hash |
|
|
- |
Filemod Name |
|
|
- |
Filemod Reputation |
|
|
- |
Query Date |
|
|
- |
Data Table - Processes¶
API Name:¶
vmware_cbc_processes_dt
Columns:¶
Column Name |
API Access Name |
Type |
Tooltip |
---|---|---|---|
CMD |
|
|
- |
Device ID |
|
|
- |
Name |
|
|
- |
PID |
|
|
Process ID |
Policy Action |
|
|
- |
Reputation |
|
|
- |
Sha256 |
|
|
- |
Type |
|
|
Process type: Parent or Child |
Username |
|
|
- |
Custom Fields¶
Label |
API Access Name |
Type |
Prefix |
Placeholder |
Tooltip |
---|---|---|---|---|---|
CBC Alert ID |
|
|
|
- |
- |
CBC Alert Link |
|
|
|
- |
- |
CBC Alert Reason Code |
|
|
|
- |
- |
CBC Alert Type |
|
|
|
- |
- |
CBC Attack Tactic |
|
|
|
- |
- |
CBC Determination |
|
|
|
- |
- |
CBC Tags |
|
|
|
- |
- |
CBC Threat ID |
|
|
|
- |
- |
CBC Workflow Closure Reason |
|
|
|
- |
- |
CBC Workflow Status |
|
|
|
- |
- |
Templates for SOAR Cases¶
It may necessary to modify the templates used to create or close SOAR cases based on a customer’s required custom fields. Below are the default templates used which can be copied, modified and used with app_config’s
soar_create_case_template
and soar_close_case_template
settings to override the default templates.
soar_create_case.jinja¶
When overriding the template in App Host, specify the file path as /var/rescircuits
.
{
{# JINJA template for creating a new SOAR incident from an endpoint #}
{# See https://ibmresilient.github.io/resilient-python-api/pages/resilient-lib/resilient-lib.html#module-resilient_lib.components.templates_common
for details on available jinja methods. Examples for `soar_substitute` and more are included below.
#}
{# modify to specify your specific **data** fields #}
"name": "CBC Alert - {{ reason | replace('"', '\'') | replace('\\', '\\\\') }}",
"description": "Device: {{ device_name | replace('\\', '\\\\') }}<br>Alert type: {{ type }}<br>Reason Code: {{ reason_code }}",
{# start_date cannot be after discovered_date #}
{% set start_date = first_event_timestamp if first_event_timestamp <= detection_timestamp else detection_timestamp %}
"discovered_date": {{ detection_timestamp | soar_datetimeformat(split_at='.') }},
"start_date": {{ start_date | soar_datetimeformat(split_at='.') }},
{# if alert users are different than SOAR users, consider using a mapping table using soar_substitute: #}
{# "owner_id": "{{ **assignedTo** |soar_substitute('{"Automation": "soar_user1@example.com", "default_user@example.com": "soar_user2@example.com", "DEFAULT": "default_user@example.com" }') }}", #}
"plan_status": "A",
{% if severity < 3 %}
"severity_code": "Low",
{% elif severity < 6%}
"severity_code": "Medium",
{% else %}
"severity_code": "High",
{% endif %}
{# specify your custom fields for your endpoint solution #}
"properties": {
"vmware_cbc_alert_id": "{{ id }}",
"vmware_cbc_alert_link": "<a target='_blank' href='https://{{ alert_url.replace("[", "%5B").replace("]", "%5D") }}'>Link</a>"
}
}
soar_close_case.jinja¶
When overriding the template in App Host, specify the file path as /var/rescircuits
.
{
{# JINJA template for closing a SOAR incident using endpoint data #}
{% set closure_reason_mapping = '''{
"NO_REASON": "Resolved",
"RESOLVED": "Resolved",
"RESOLVED_BENIGN_KNOWN_GOOD": "Resolved",
"DUPLICATE_CLEANUP": "Duplicate",
"OTHER": "Not an Issue"
}'''
%}
"plan_status": "C",
"resolution_id": "{{ workflow.closure_reason | soar_substitute(closure_reason_mapping) }}",
"resolution_summary": "Closed by VMware Carbon Black Cloud, Alert Determination: {{ determination.value.replace('_', ' ')|title }}"
{# add additional fields based on your 'on close' field requirements #}
,"properties": {
"vmware_cbc_workflow_status": "{{ workflow.status.replace('_', ' ')|title }}",
"vmware_cbc_workflow_closure_reason": "{{ workflow.closure_reason.replace('_', ' ')|title }}",
"vmware_cbc_determination_value": "{{ determination.value.replace('_', ' ')|title }}"
}
}
soar_update_case.jinja¶
When overriding the template in App Host, specify the file path as /var/rescircuits
.
{
{# JINJA template for updating a new SOAR incident from an endpoint #}
{% if severity < 3 %}
"severity_code": "Low",
{% elif severity < 6%}
"severity_code": "Medium",
{% else %}
"severity_code": "High",
{% endif %}
{# specify your custom fields for your endpoint solution #}
"properties": {
"vmware_cbc_alert_id": "{{ id }}",
"vmware_cbc_alert_type": "{{ type }}",
"vmware_cbc_alert_reason_code" : "{{ reason_code }}",
"vmware_cbc_alert_link": "<a target='_blank' href='https://{{ alert_url.replace("[", "%5B").replace("]", "%5D") }}'>Link</a>",
"vmware_cbc_attack_tactic": "{{ attack_tactic }}",
"vmware_cbc_threat_id": "{{ threat_id }}",
"vmware_cbc_workflow_status": "{{ workflow.status.replace('_', ' ') |lower|title }}",
"vmware_cbc_workflow_closure_reason": "{{ workflow.closure_reason.replace('_', ' ') |lower|title }}",
"vmware_cbc_determination_value": "{{ determination.value.replace('_', ' ') |lower|title}}"
{% if tags %}
,"vmware_cbc_tags": "{{ tags | join(', ') }}"
{% endif %}
}
}
Troubleshooting & Support¶
Refer to the documentation listed in the Requirements section for troubleshooting information.
For Support¶
This is an IBM supported app. Please search ibm.com/mysupport for assistance.