AlienVault OTX v2
Query Indicators of Compromise in AlienVault OTX.
Data Enrichment & Threat Intelligence · AlienVault OTX
Details
| ID | AlienVault OTX v2 |
|---|---|
| Provider | AT&T Cybersecurity |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Query Indicators of Compromise in AlienVault OTX.
Configure AlienVault OTX v2 in Cortex
| Parameter | Description | Required |
|---|---|---|
| Server address | True | |
| API Token | False | |
| Indicator Threshold. The minimum number of pulses to consider the indicator as malicious. | False | |
| Maximum number of relationships for indicators | If not provided, no relationships will be added. | False |
| Source Reliability | Reliability of the source providing the intelligence data. | True |
| Create relationships | Create relationships between indicators as part of Enrichment. | False |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False |
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.
Disclaimer
Retrieving Passive DNS history for high-volume indicators may time out and therefore be skipped.
ip
Queries an IP address in AlienVault OTX.
Base Command
ip
Input
| Argument Name | Description | Required |
|---|---|---|
| ip | The IP address to query. | Required |
| threshold | If the number of pulses is bigger than the threshold, the IP address is considered as malicious. If the threshold is not specified, the default indicator threshold is used, which is configured in the instance settings. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| IP.Address | String | The address of the IP. |
| IP.ASN | String | The autonomous system name for the IP address. For example, “AS8948”. |
| IP.Geo.Country | String | The country where the IP address is located. |
| IP.Geo.Location | String | The geolocation where the IP address is located, in the format: latitude:longitude. |
| AlienVaultOTX.IP.Reputation | String | The reputation of the IP address. |
| AlienVaultOTX.IP.IP | String | IP address |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| DBotScore.Indicator | String | The indicator that was tested. |
| IP.Relationships.EntityA | string | The source of the relationship. |
| IP.Relationships.EntityB | string | The destination of the relationship. |
| IP.Relationships.Relationship | string | The name of the relationship. |
| IP.Relationships.EntityAType | string | The type of the source of the relationship. |
| IP.Relationships.EntityBType | string | The type of the destination of the relationship. |
Command Example
!ip ip=98.136.103.23
Context Example
{
"AlienVaultOTX": {
"IP": {
"IP": {
"IP": "98.136.103.23",
"Reputation": 0
}
}
},
"DBotScore": {
"Indicator": "98.136.103.23",
"Reliability": "C - Fairly reliable",
"Score": 2,
"Type": "ip",
"Vendor": "AlienVault OTX v2"
},
"IP": {
"ASN": "AS36647 YAHOO-GQ1",
"Address": "98.136.103.23",
"Geo": {
"Country": "US",
"Location": "37.751:-97.822"
},
"Relationships": [
{
"EntityA": "98.136.103.23",
"EntityAType": "IP",
"EntityB": "T1140 - Deobfuscate/Decode Files or Information",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "98.136.103.23",
"EntityAType": "IP",
"EntityB": "T1040 - Network Sniffing",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "98.136.103.23",
"EntityAType": "IP",
"EntityB": "T1053 - Scheduled Task/Job",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "98.136.103.23",
"EntityAType": "IP",
"EntityB": "T1060 - Registry Run Keys / Startup Folder",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "98.136.103.23",
"EntityAType": "IP",
"EntityB": "T1071 - Application Layer Protocol",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "98.136.103.23",
"EntityAType": "IP",
"entityB": "mojorojorestaurante.com",
"entityBType": "URL",
"Relationship": "indicator-of"
},
{
"EntityA": "98.136.103.23",
"EntityAType": "IP",
"entityB": "nguyenhoangai-4g.xyz",
"entityBType": "Domain",
"Relationship": "indicator-of"
},
{
"EntityA": "98.136.103.23",
"EntityAType": "IP",
"entityB": "0b4d4a7c35a185680bc5102bdd98218297e2cdf0a552bde10e377345f3622c1c",
"entityBType": "File",
"Relationship": "indicator-of"
}
]
}
}
Human Readable Output
AlienVault OTX v2 - Results for ips query
ASN Address Geo Relationships AS36647 YAHOO-GQ1 98.136.103.23 Location: 37.751:-97.822
Country: US{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘98.136.103.23’, ‘EntityAType’: ‘IP’, ‘EntityB’: ‘T1140 - Deobfuscate/Decode Files or Information’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘98.136.103.23’, ‘EntityAType’: ‘IP’, ‘EntityB’: ‘T1040 - Network Sniffing’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘98.136.103.23’, ‘EntityAType’: ‘IP’, ‘EntityB’: ‘T1053 - Scheduled Task/Job’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘98.136.103.23’, ‘EntityAType’: ‘IP’, ‘EntityB’: ‘T1060 - Registry Run Keys / Startup Folder’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘98.136.103.23’, ‘EntityAType’: ‘IP’, ‘EntityB’: ‘T1071 - Application Layer Protocol’, ‘EntityBType’: ‘Attack Pattern’}
domain
Queries a domain in AlienVault OTX.
Base Command
domain
Input
| Argument Name | Description | Required |
|---|---|---|
| domain | The domain to query. | Required |
| threshold | If the number of pulses is bigger than the threshold, the domain is considered as malicious. If the threshold is not specified, the default indicator threshold is used, which is configured in the instance settings. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Domain.Name | String | The domain name. For example, “google.com”. |
| AlienVaultOTX.Domain.Alexa | String | Alexa URL for the domain data. |
| AlienVaultOTX.Domain.Whois | String | Whois URL for the domain data. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| Domain.Relationships.EntityA | string | The source of the relationship. |
| Domain.Relationships.EntityB | string | The destination of the relationship. |
| Domain.Relationships.Relationship | string | The name of the relationship. |
| Domain.Relationships.EntityAType | string | The type of the source of the relationship. |
| Domain.Relationships.EntityBType | string | The type of the destination of the relationship. |
Command Example
!domain domain=ahnlab.myfw.us
Context Example
{
"AlienVaultOTX": {
"Domain": {
"Alexa": "http://www.alexa.com/siteinfo/ahnlab.myfw.us",
"Name": "ahnlab.myfw.us",
"Whois": "http://whois.domaintools.com/ahnlab.myfw.us"
}
},
"DBotScore": {
"Indicator": "ahnlab.myfw.us",
"Reliability": "C - Fairly reliable",
"Score": 0,
"Type": "domain",
"Vendor": "AlienVault OTX v2"
},
"Domain": {
"Name": "ahnlab.myfw.us",
"Relationships":[
{
"EntityA": "ahnlab.myfw.us",
"EntityAType": "Domain",
"EntityB": "b3558ad9f46b72a0319f11889870457dfd611cc4020dbc63945a92869581f774",
"EntityBType": "File",
"Relationship": "indicator-of"
},
{
"EntityA": "ahnlab.myfw.us",
"EntityAType": "Domain",
"EntityB": "219c6da3c6555bba5a3c1138180351dd6d39bc14d3cb491e93a46bff6c5ca271",
"EntityBType": "File",
"Relationship": "indicator-of",
},
{
"EntityA": "ahnlab.myfw.us",
"EntityAType": "Domain",
"EntityB": "98.136.103.23",
"EntityBType": "IP",
"Relationship": "indicator-of"
},
{
"EntityA": "ahnlab.myfw.us",
"EntityAType": "Domain",
"EntityB": "ahnlab.myfw.us",
"EntityBType": "IP",
"Relationship": "indicator-of"
}
]
}
}
Human Readable Output
AlienVault OTX v2 - Results for Domain query
Alexa Name Whois http://www.alexa.com/siteinfo/ahnlab.myfw.us ahnlab.myfw.us http://whois.domaintools.com/ahnlab.myfw.us
alienvault-search-ipv6
Queries IPv6 in AlienVault OTX.
Base Command
alienvault-search-ipv6
Input
| Argument Name | Description | Required |
|---|---|---|
| ip | The IP address to query. | Required |
| threshold | If the number of pulses is bigger than the threshold, the IP address is considered as malicious. If the threshold is not specified, the default indicator threshold is used, which is configured in the instance settings. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| IP.Address | String | The IP address. |
| IP.ASN | String | The autonomous system name for the IP address. For example, “AS8948”. |
| IP.AlienVaultOTX.Reputation | String | The IP reputation in AlienVault OTX. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
Command Example
!alienvault-search-ipv6 ip=2001:4860:4860::8888
Context Example
{
"AlienVaultOTX": {
"IP": {
"IP": {
"IP": "2001:4860:4860::8888",
"Reputation": 0
}
}
},
"DBotScore": {
"Indicator": "2001:4860:4860::8888",
"Reliability": "C - Fairly reliable",
"Score": 2,
"Type": "ip",
"Vendor": "AlienVault OTX v2"
},
"IP": {
"ASN": "AS15169 GOOGLE",
"Address": "2001:4860:4860::8888",
"Geo": {
"Country": "US",
"Location": "37.751:-97.822"
},
"Relationships": [
{
"EntityA": "2001:4860:4860::8888",
"EntityAType": "IPv6",
"EntityB": "T1071.004 - DNS",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "2001:4860:4860::8888",
"EntityAType": "IPv6",
"EntityB": "T1071.001 - Web Protocols",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "2001:4860:4860::8888",
"EntityAType": "IPv6",
"EntityB": "T1071 - Application Layer Protocol",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "2001:4860:4860::8888",
"EntityAType": "IPv6",
"EntityB": "T1071.003 - Mail Protocols",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "2001:4860:4860::8888",
"EntityAType": "IPv6",
"EntityB": "T1071.002 - File Transfer Protocols",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "2001:4860:4860::8888",
"EntityAType": "IPv6",
"EntityB": "TA0011 - Command and Control",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "2001:4860:4860::8888",
"EntityAType": "IPv6",
"EntityB": "T1048 - Exfiltration Over Alternative Protocol",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
},
{
"EntityA": "2001:4860:4860::8888",
"EntityAType": "IPv6",
"EntityB": "T1041 - Exfiltration Over C2 Channel",
"EntityBType": "Attack Pattern",
"Relationship": "indicator-of"
}
]
}
}
Human Readable Output
AlienVault OTX v2 - Results for ips query
ASN Address Geo Relationships AS15169 GOOGLE 2001:4860:4860::8888 Location: 37.751:-97.822
Country: US{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘2001:4860:4860::8888’, ‘EntityAType’: ‘IPv6’, ‘EntityB’: ‘T1071.004 - DNS’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘2001:4860:4860::8888’, ‘EntityAType’: ‘IPv6’, ‘EntityB’: ‘T1071.001 - Web Protocols’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘2001:4860:4860::8888’, ‘EntityAType’: ‘IPv6’, ‘EntityB’: ‘T1071 - Application Layer Protocol’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘2001:4860:4860::8888’, ‘EntityAType’: ‘IPv6’, ‘EntityB’: ‘T1071.003 - Mail Protocols’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘2001:4860:4860::8888’, ‘EntityAType’: ‘IPv6’, ‘EntityB’: ‘T1071.002 - File Transfer Protocols’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘2001:4860:4860::8888’, ‘EntityAType’: ‘IPv6’, ‘EntityB’: ‘TA0011 - Command and Control’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘2001:4860:4860::8888’, ‘EntityAType’: ‘IPv6’, ‘EntityB’: ‘T1048 - Exfiltration Over Alternative Protocol’, ‘EntityBType’: ‘Attack Pattern’},
{‘Relationship’: ‘indicator-of’, ‘EntityA’: ‘2001:4860:4860::8888’, ‘EntityAType’: ‘IPv6’, ‘EntityB’: ‘T1041 - Exfiltration Over C2 Channel’, ‘EntityBType’: ‘Attack Pattern’}
alienvault-search-hostname
Searches for a host name in AlienVault OTX.
Base Command
alienvault-search-hostname
Input
| Argument Name | Description | Required |
|---|---|---|
| hostname | The host name to query. | Required |
| threshold | If the number of pulses is bigger than the threshold, the host name is considered as malicious. If the threshold is not specified, the default indicator threshold is used, which is configured in the instance settings. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Endpoint.Hostname | String | The hostname that is mapped to the endpoint. |
| AlienVaultOTX.Endpoint.Hostname | String | The hostname that is mapped to the endpoint. |
| AlienVaultOTX.Endpoint.Alexa | String | The Alexa URL endpoint. |
| AlienVaultOTX.Endpoint.Whois | String | The Whois URL endpoint. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| DBotScore.Indicator | String | The indicator that was tested. |
Command Example
!alienvault-search-hostname hostname=demisto.com
Context Example
{
"AlienVaultOTX": {
"Endpoint": {
"Alexa": "http://www.alexa.com/siteinfo/demisto.com",
"Hostname": "demisto.com",
"Whois": "http://whois.domaintools.com/demisto.com"
}
},
"DBotScore": {
"Indicator": "demisto.com",
"Reliability": "C - Fairly reliable",
"Score": 0,
"Type": "hostname",
"Vendor": "AlienVault OTX v2"
},
"Endpoint": {
"Hostname": "demisto.com"
}
}
Human Readable Output
AlienVault OTX v2 - Results for Hostname query
Alexa Hostname Whois http://www.alexa.com/siteinfo/demisto.com demisto.com http://whois.domaintools.com/demisto.com
file
Query a file in AlienVault OTX.
Base Command
file
Input
| Argument Name | Description | Required |
|---|---|---|
| file | The file hash to query. | Required |
| threshold | If the number of pulses is bigger than the threshold, the file is considered as malicious. If the threshold is not specified, the default indicator threshold is used, which is configured in the instance settings. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| File.MD5 | String | The MD5 hash of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA256 hash of the file. |
| File.Malicious.PulseIDs | String | IDs of pulses which are marked as malicious. |
| File.Type | String | The file type, as determined by libmagic (same as displayed in file entries). |
| File.Size | Number | The size of the file in bytes. |
| File.SSDeep | String | The SSDeep hash of the file (same as displayed in file entries). |
| File.Relationships.EntityA | string | The source of the relationship. |
| File.Relationships.EntityB | string | The destination of the relationship. |
| File.Relationships.Relationship | string | The name of the relationship. |
| File.Relationships.EntityAType | string | The type of the source of the relationship. |
| File.Relationships.EntityBType | string | The type of the destination of the relationship. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
Command Example
!file file=6c5360d41bd2b14b1565f5b18e5c203cf512e493
Context Example
{
"DBotScore": {
"Indicator": "6c5360d41bd2b14b1565f5b18e5c203cf512e493",
"Reliability": "C - Fairly reliable",
"Score": 0,
"Type": "file",
"Vendor": "AlienVault OTX v2"
},
"File": {
"MD5": "2eb14920c75d5e73264f77cfa273ad2c",
"Malicious": {
"PulseIDs": []
},
"SHA1": "6c5360d41bd2b14b1565f5b18e5c203cf512e493",
"SHA256": "4cf9322c49adebf63311a599dc225bbcbf16a253eca59bbe1a02e4ae1d824412",
"SSDeep": "",
"Size": "437760",
"Type": "PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows"
}
}
Human Readable Output
AlienVault OTX v2 - Results for File hash query
MD5 Malicious SHA1 SHA256 SSDeep Size Type 2eb14920c75d5e73264f77cfa273ad2c PulseIDs: 6c5360d41bd2b14b1565f5b18e5c203cf512e493 4cf9322c49adebf63311a599dc225bbcbf16a253eca59bbe1a02e4ae1d824412 437760 PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows
alienvault-search-cve
Query Common Vulnerabilities and Exposures (CVE) in AlienVault OTX.
Base Command
alienvault-search-cve
Input
| Argument Name | Description | Required |
|---|---|---|
| cve_id | The CVE to query. | Required |
| threshold | If the number of pulses is bigger than the threshold, the CVE is considered as malicious. If the threshold is not specified, the default indicator threshold is used, which is configured in the instance settings. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| CVE.ID | String | The ID of the CVE. For example, “CVE-2015-1653”. |
| CVE.CVSS | String | The CVSS of the CVE. For example, “10.0”. |
| CVE.Published | String | The timestamp of when the CVE was published. |
| CVE.Modified | String | The timestamp of when the CVE was last modified. |
| CVE.Description | String | A description of the CVE. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| DBotScore.Indicator | String | The indicator that was tested. |
Command Example
!alienvault-search-cve cve_id=CVE-2014-0160
Context Example
{
"CVE": {
"CVSS": "5.0",
"Description": "The (1) TLS and (2) DTLS implementations in OpenSSL 1.0.1 before 1.0.1g do not properly handle Heartbeat Extension packets, which allows remote attackers to obtain sensitive information from process memory via crafted packets that trigger a buffer over-read, as demonstrated by reading private keys, related to d1_both.c and t1_lib.c, aka the Heartbleed bug.",
"ID": "CVE-2014-0160",
"Modified": "2020-07-28T17:11:00",
"Published": "2014-04-07T22:55:00"
},
"DBotScore": {
"Indicator": "CVE-2014-0160",
"Reliability": "C - Fairly reliable",
"Score": 3,
"Type": "cve",
"Vendor": "AlienVault OTX v2"
}
}
Human Readable Output
AlienVault OTX v2 - Results for Hostname query
CVSS Description ID Modified Published 5.0 The (1) TLS and (2) DTLS implementations in OpenSSL 1.0.1 before 1.0.1g do not properly handle Heartbeat Extension packets, which allows remote attackers to obtain sensitive information from process memory via crafted packets that trigger a buffer over-read, as demonstrated by reading private keys, related to d1_both.c and t1_lib.c, aka the Heartbleed bug. CVE-2014-0160 2020-07-28T17:11:00 2014-04-07T22:55:00
alienvault-get-related-urls-by-indicator
Returns related URLs by indicator.
Base Command
alienvault-get-related-urls-by-indicator
Input
| Argument Name | Description | Required |
|---|---|---|
| indicator_type | The indicator type. Can be: “IPv4”, “IPv6”, “domain”, “hostname”, or “url”. Possible values are: IPv4, IPv6, domain, hostname, url. | Required |
| indicator | The indicator for which to search related URLs. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| AlienVaultOTX.URL.Data | Unknown | The path of the related URLs. |
Command Example
!alienvault-get-related-urls-by-indicator indicator=8.8.8.8 indicator_type=IPv4
Context Example
{
"AlienVaultOTX": {
"URL": [
{
"Data": "https://test.com"
}
]
}
}
Human Readable Output
AlienVault OTX v2 - Related url list to queried indicator
Data https://test.com
alienvault-get-related-hashes-by-indicator
Returns related hashes by indicator.
Base Command
alienvault-get-related-hashes-by-indicator
Input
| Argument Name | Description | Required |
|---|---|---|
| indicator | The indicator for which to search for related hashes. | Optional |
| indicator_type | The indicator type. Can be: “IPv4”, “IPv6”, “domain”, or “hostname”. Possible values are: IPv4, IPv6, domain, hostname. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AlienVaultOTX.File.Hash | Unknown | The path of the url. |
Command Example
!alienvault-get-related-hashes-by-indicator indicator=8.8.8.8 indicator_type=IPv4
Context Example
{
"AlienVaultOTX": {
"File": [
{
"Hash": "ffc2595aefa80b61621023252b5f0ccb22b6e31d7f1640913cd8ff74ddbd8b41"
},
{
"Hash": "0b4d4a7c35a185680bc5102bdd98218297e2cdf0a552bde10e377345f3622c1c"
},
{
"Hash": "d8b8a5c941b6a1c3cb58f7e59489b2554ed14e6c6655d1fbf6852e45404b7516"
},
{
"Hash": "b3d8adc185834ab858ebf55082828cb9fc1170bbe8de222821d225a6056ff5dc"
},
{
"Hash": "e43cf3f5fa5e14972ba3f159dee6e98330bd19dccc1267cfc91b1000aef975d9"
},
{
"Hash": "9e11b1e769da3c8059345b36c62b4a857845bd7e14c7c14af2945ce26570d91f"
},
{
"Hash": "ae695ce9b8ff4bb831721a8c60377c1757d6d4fe579640b54f3c7f62b175f506"
},
{
"Hash": "093bde5d50daba59bfe68b31251cf2c39353bdfe8ad510284935ca027f269637"
}
]
}
}
Human Readable Output
AlienVault OTX v2 - Related malware list to queried indicator
No entries.
alienvault-get-passive-dns-data-by-indicator
Returns passive DNS records by indicator.
Base Command
alienvault-get-passive-dns-data-by-indicator
Input
| Argument Name | Description | Required |
|---|---|---|
| indicator_type | The indicator type. Can be: “IPv4”, “IPv6”, “domain”, or “hostname”. Possible values are: IPv4, IPv6, domain, hostname. | Required |
| indicator | The indicator for which to search URLs. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| AlienVaultOTX.PassiveDNS.Hostname | String | The domain value. |
| AlienVaultOTX.PassiveDNS.IP | String | The IP passive DNS. |
| AlienVaultOTX.PassiveDNS.Domain | String | The domain value. |
| AlienVaultOTX.PassiveDNS.Type | String | The asset type. |
| AlienVaultOTX.PassiveDNS.FirstSeen | Date | The date first seen. |
| AlienVaultOTX.PassiveDNS.LastSeen | Date | The date last seen. |
Command Example
!alienvault-get-passive-dns-data-by-indicator indicator=8.8.8.8 indicator_type=IPv4
Context Example
{
"AlienVaultOTX": {
"PassiveDNS": [
{
"FirstSeen": "2021-04-27T09:48:48",
"Hostname": "www.heyheyitskateforay.com",
"IP": "8.8.8.8",
"LastSeen": "2021-04-27T09:49:05",
"Type": "hostname"
},
{
"FirstSeen": "2021-04-27T09:48:30",
"Hostname": "www.djjimmykennedy.com",
"IP": "8.8.8.8",
"LastSeen": "2021-04-27T09:49:11",
"Type": "hostname"
}
]
}
}
Human Readable Output
AlienVault OTX v2 - Related passive dns list to queried indicator
FirstSeen Hostname IP LastSeen Type 2021-04-27T09:48:48 www.heyheyitskateforay.com 8.8.8.8 2021-04-27T09:49:05 hostname 2021-04-27T09:48:30 www.djjimmykennedy.com 8.8.8.8 2021-04-27T09:49:11 hostname
alienvault-search-pulses
Searches for pulses in AlienVault OTX.
Base Command
alienvault-search-pulses
Input
| Argument Name | Description | Required |
|---|---|---|
| page | The page of the pulse to retrieve. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| AlienVaultOTX.Pulses.ID | String | The ID of the pulse. |
| AlienVaultOTX.Pulses.Author.ID | String | The ID of the Author. |
| AlienVaultOTX.Pulses.Author.Username | String | The username of the Author. |
| AlienVaultOTX.Pulses.Count | String | The pulse count. |
| AlienVaultOTX.Pulses.Modified | Date | The date of the pulse modification. |
| AlienVaultOTX.Pulses.Name | String | The name of the pulse. |
| AlienVaultOTX.Pulses.Source | String | The source of the Pulse. |
| AlienVaultOTX.Pulses.SubscriberCount | String | The count of the pulse subscriber. |
| AlienVaultOTX.Pulses.Tags | String | The tags of the pulse. |
| AlienVaultOTX.Pulses.Description | String | The description of the pulse. |
Command Example
!alienvault-search-pulses page=1
Context Example
{
"AlienVaultOTX": {
"Pulses": [
{
"Author": {
"ID": "2",
"Username": "AlienVault"
},
"Count": 28,
"ID": "546ce8eb11d40838dc6e43f1",
"Modified": "1273 days ago ",
"Name": "PoS Scammers Toolbox",
"Source": "web",
"SubscriberCount": 141735
},
{
"Author": {
"ID": "2",
"Username": "AlienVault"
},
"Count": 11,
"ID": "546cf5ba11d40839ea8821ca",
"Modified": "2098 days ago ",
"Name": " RAZOR BLADES IN THE CANDY JAR",
"Source": "web",
"SubscriberCount": 141715
},
{
"Author": {
"ID": "2",
"Username": "AlienVault"
},
"Count": 10,
"ID": "546e2e4f11d4083bc021c37d",
"Modified": "1342 days ago ",
"Name": "Linking Asprox, Zemot, Rovix and Rerdom Malware Families ",
"Source": "web",
"SubscriberCount": 141707,
"Tags": [
"Asprox",
"Zemot",
"Rovix"
]
},
{
"Author": {
"ID": "2",
"Username": "AlienVault"
},
"Count": 23,
"ID": "546fc7bf11d4083bc021c37f",
"Modified": "1341 days ago ",
"Name": "Operation Double Tap",
"Source": "web",
"SubscriberCount": 141711
},
{
"Author": {
"ID": "2",
"Username": "AlienVault"
},
"Count": 60,
"Description": "Regin is a multi-purpose data collection tool which dates back several years. Symantec first began looking into this threat in the fall of 2013. Multiple versions of Regin were found in the wild, targeting several corporations, institutions, academics, and individuals.\nRegin has a wide range of standard capabilities, particularly around monitoring targets and stealing data. It also has the ability to load custom features tailored to individual targets. Some of Regin\u2019s custom payloads point to a high level of specialist knowledge in particular sectors, such as telecoms infrastructure software, on the part of\nthe developers.",
"ID": "5473709d11d4083bc021c387",
"Modified": "824 days ago ",
"Name": "Regin",
"Source": "web",
"SubscriberCount": 141690
}
]
}
}
Human Readable Output
AlienVault OTX v2 - pulse page 1
Author Count ID Modified Name Source SubscriberCount ID: 2
Username: AlienVault28 546ce8eb11d40838dc6e43f1 1273 days ago PoS Scammers Toolbox web 141735 ID: 2
Username: AlienVault11 546cf5ba11d40839ea8821ca 2098 days ago RAZOR BLADES IN THE CANDY JAR web 141715 ID: 2
Username: AlienVault10 546e2e4f11d4083bc021c37d 1342 days ago Linking Asprox, Zemot, Rovix and Rerdom Malware Families web 141707 ID: 2
Username: AlienVault23 546fc7bf11d4083bc021c37f 1341 days ago Operation Double Tap web 141711 ID: 2
Username: AlienVault60 5473709d11d4083bc021c387 824 days ago Regin web 141690
alienvault-get-pulse-details
Returns pulse details.
Base Command
alienvault-get-pulse-details
Input
| Argument Name | Description | Required |
|---|---|---|
| pulse_id | The ID of the pulse. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| AlienVaultOTX.Pulses.Created | Date | The date the pulse was created. |
| AlienVaultOTX.Pulses.Author.Username | String | The author username of the pulse. |
| AlienVaultOTX.Pulses.ID | String | The ID of the pulse. |
| AlienVaultOTX.Pulses.Name | String | The name of the pulse. |
| AlienVaultOTX.Pulses.Tags | String | The tags of the pulse. |
| AlienVaultOTX.Pulses.TargetedCountries | String | The targeted countries of the pulse. |
| AlienVaultOTX.Pulses.Description | String | The description of the pulse. |
Command Example
!alienvault-get-pulse-details pulse_id=57204e9b3c4c3e015d93cb12
Context Example
{
"AlienVaultOTX": {
"Pulses": {
"Author": {
"Username": "AlienVault"
},
"Created": "2016-04-27T05:31:06.941000",
"Description": "The infamous Remote Access Trojan (RAT) Poison Ivy (hereafter referred to as PIVY) has resurfaced recently, and exhibits some new behaviors. PIVY has been observed targeting a number of Asian countries for various purposes over the past year. Palo Alto Networks\u2019 Unit 42 recently blogged about a new Poison Ivy variant targeting Hong Kong activists dubbed SPIVY that uses DLL sideloading and operates quite differently from a variant recently observed by ASERT that has been active for at least the past 12 months.",
"ID": "57204e9b3c4c3e015d93cb12",
"Name": "Poison Ivy Activity Targeting Myanmar, Asian Countries",
"Tags": [
"rat",
"remote access trojan",
"poison ivy",
"pivy",
"Myanmar",
"asia",
"Hong Kong",
"arbornetworks"
],
"TargetedCountries": []
}
}
}
Human Readable Output
AlienVault OTX v2 - pulse id details
Author Created Description ID Name Tags TargetedCountries Username: AlienVault 2016-04-27T05:31:06.941000 The infamous Remote Access Trojan (RAT) Poison Ivy (hereafter referred to as PIVY) has resurfaced recently, and exhibits some new behaviors. PIVY has been observed targeting a number of Asian countries for various purposes over the past year. Palo Alto Networks’ Unit 42 recently blogged about a new Poison Ivy variant targeting Hong Kong activists dubbed SPIVY that uses DLL sideloading and operates quite differently from a variant recently observed by ASERT that has been active for at least the past 12 months. 57204e9b3c4c3e015d93cb12 Poison Ivy Activity Targeting Myanmar, Asian Countries rat,
remote access trojan,
poison ivy,
pivy,
Myanmar,
asia,
Hong Kong,
arbornetworks
url
Queries a URL in AlienVault OTX.
Base Command
url
Input
| Argument Name | Description | Required |
|---|---|---|
| url | The URL to query. | Required |
| threshold | If the number of pulses is bigger than the threshold, the URL is considered as malicious. If threshold is not specified, the default indicator threshold is used, which is configured in the instance settings. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| URL.Data | String | The URL. |
| AlienVaultOTX.URL.Hostname | String | The host name of the URL. |
| AlienVaultOTX.URL.Domain | String | The domain of the URL. |
| AlienVaultOTX.URL.Alexa | String | The domain data for the Alexa URL. |
| AlienVaultOTX.URL.Url | String | Url |
| AlienVaultOTX.URL.Whois | String | The Whois URL for domain data. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| URL.Relationships.EntityA | string | The source of the relationship. |
| URL.Relationships.EntityB | string | The destination of the relationship. |
| URL.Relationships.Relationship | string | The name of the relationship. |
| URL.Relationships.EntityAType | string | The type of the source of the relationship. |
| URL.Relationships.EntityBType | string | The type of the destination of the relationship. |
Command Example
!url url="http://www.fotoidea.com/sport/4x4_san_ponso/slides/IMG_0068.html/url_list"
Context Example
{
"AlienVaultOTX": {
"URL": {
"Alexa": "http://www.alexa.com/siteinfo/fotoidea.com",
"Domain": "fotoidea.com",
"Hostname": "www.fotoidea.com",
"Url": "http://www.fotoidea.com/sport/4x4_san_ponso/slides/IMG_0068.html/url_list",
"Whois": "http://whois.domaintools.com/fotoidea.com"
}
},
"DBotScore": {
"Indicator": "http://www.fotoidea.com/sport/4x4_san_ponso/slides/IMG_0068.html/url_list",
"Reliability": "C - Fairly reliable",
"Score": 0,
"Type": "url",
"Vendor": "AlienVault OTX v2"
},
"URL": {
"Data": "http://www.fotoidea.com/sport/4x4_san_ponso/slides/IMG_0068.html/url_list",
"Relationships": [
{
"EntityA": "http://www.fotoidea.com/sport/4x4_san_ponso/slides/IMG_0068.html/url_list",
"EntityAType": "URL",
"EntityB": "fotoidea.com",
"EntityBType": "Domain",
"Relationship": "hosted-on"
}
]
}
}
Human Readable Output
AlienVault OTX v2 - Results for url query
Alexa Domain Hostname Url Whois http://www.alexa.com/siteinfo/fotoidea.com fotoidea.com www.fotoidea.com http://www.fotoidea.com/sport/4x4_san_ponso/slides/IMG_0068.html/url_list http://whois.domaintools.com/fotoidea.com
Additional Information
- AlienVault considers non lowercased URL protocol as invalid, e.g, HTTP://www.google.com. Hence such submissions will be lowercased to ensure a seamless usage of the integration.
Dbot score calculation method
In case AlienVault OTX API response contains accepted under the false_positive.assessment key, the DbotScore will be set to Good.
Otherwise, if the response includes one validation, DbotScore will be set to SUSPICIOUS, if there’s no validation in the response then the DbotScore will be set by the pulse_info length in the following manner:
- Bad - If the length of is greater or equal to the default threshold given by the user.
- SUSPICIOUS - If the length is shorter than the default threshold.
- None - If the length is zero.
In any other case, the DbotScore will be set to Good.
Configuration parameters
url— Server address (required)credentials—api_token— API Tokendefault_threshold— Indicator Threshold. The minimum number of pulses to consider the indicator as malicious.min_valid_sources— Minimum number of validated sources to consider the indicator as benign.max_indicator_relationships— Maximum number of relationships for indicatorsintegrationReliability— Source Reliability (required)create_relationships— Create relationshipsshould_error— Should Error When Not Foundinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (12)
-
alienvault-get-passive-dns-data-by-indicatorReturns passive DNS records by indicator.
-
alienvault-get-pulse-detailsReturns pulse details.
-
alienvault-get-related-hashes-by-indicatorReturns related hashes by indicator.
-
alienvault-get-related-urls-by-indicatorReturns related URLs by indicator.
-
alienvault-search-cveQuery Common Vulnerabilities and Exposures (CVE) in AlienVault OTX.
-
alienvault-search-hostnameSearches for a host name in AlienVault OTX.
-
alienvault-search-ipv6Queries IPv6 in AlienVault OTX.
-
alienvault-search-pulsesSearches for pulses in AlienVault OTX.
-
domainQueries a domain in AlienVault OTX.
-
fileQuery a file in AlienVault OTX.
-
ipQueries an IP address in AlienVault OTX.
-
urlQueries a URL in AlienVault OTX.
from CommonServerPython import * """ IMPORTS """ import urllib3 # Disable insecure warnings urllib3.disable_warnings() """GLOBALS/PARAMS Attributes: INTEGRATION_NAME: Name of the integration as shown in the integration UI, for example: Microsoft Graph User. INTEGRATION_COMMAND_NAME: Command names should be written in all lower-case letters, and each word separated with a hyphen, for example: msgraph-user. INTEGRATION_CONTEXT_NAME: Context output names should be written in camel case, for example: MSGraphUser. """ INTEGRATION_NAME = "AlienVault OTX v2" INTEGRATION_COMMAND_NAME = "alienvault" INTEGRATION_CONTEXT_NAME = "AlienVaultOTX" class Client(BaseClient): def __init__( self, base_url, headers, verify, proxy, default_threshold, min_valid_sources, max_indicator_relationships, reliability, create_relationships=True, should_error=True, ): BaseClient.__init__( self, base_url=base_url, headers=headers, verify=verify, proxy=proxy, ) self.reliability = reliability self.create_relationships = create_relationships self.min_valid_sources = min_valid_sources self.default_threshold = default_threshold self.max_indicator_relationships = max_indicator_relationships if max_indicator_relationships else 0 self.should_error = should_error def test_module(self) -> dict: """Performs basic GET request to check if the API is reachable and authentication is successful. Returns: Response json """ return self.query(section="IPv4", argument="8.8.8.8") def query(self, section: str, argument: str = None, sub_section: str = "general", params: dict = None) -> dict: """Query the specified kwargs. Args: section: indicator type argument: indicator value sub_section: sub section of api params: params to send in http request Returns: Response JSON """ # The service endpoint to request from demisto.debug(f"in query with section: {section}, sub section: {sub_section}, params: {params}") if section == "pulses": suffix = f"{section}/{argument}" elif argument and sub_section: suffix = f"indicators/{section}/{argument}/{sub_section}" else: suffix = f"{section}/{sub_section}" # Send a request using our http_request wrapper try: if sub_section == "passive_dns": return self._http_request("GET", url_suffix=suffix, params=params, timeout=30) result = self._http_request("GET", url_suffix=suffix, params=params, timeout=60) except DemistoException as e: demisto.debug("DemistoException was raised") if hasattr(e.res, "status_code"): res_status = e.res.status_code if res_status == 404: demisto.debug("The status code is 404") result = 404 elif res_status == 400: demisto.debug("The status code is 400") demisto.debug(f"{e.res.text} response received from server when trying to get api:{e.res.url}") if not self.should_error: return_warning(f"The command could not be execute: {argument} is invalid.", exit=True) raise Exception(f"The command could not be execute: {argument} is invalid.") elif res_status == 502: demisto.debug("The status code is 502") if self.should_error: raise e return_warning(f"{e.res.text} response received from server", exit=True) elif res_status in (500, 504): demisto.debug(f"The status code is {res_status}") if self.should_error: raise e result = {} else: demisto.debug("A DemistoException was raised but no status code was caught.") raise else: demisto.debug("A DemistoException was raised but there is no status code.") raise except requests.exceptions.ReadTimeout as e: demisto.debug(f"A ReadTimeout error was raised with suffix {suffix}. Error: {str(e)}.") if sub_section == "passive_dns": return {} return_warning(f"A ReadTimeout error was raised with suffix {suffix}.", exit=True) return result """ HELPER FUNCTIONS """ def calculate_dbot_score(client: Client, raw_response: dict | None) -> float: """ calculate DBot score for query Args: client: Client object with request raw_response: The response gotten from the server :returns: score - good (if 0), bad (if grater than default), suspicious if between """ default_threshold = int(client.default_threshold) min_valid_sources = int(client.min_valid_sources) false_Positive = {} pulase_info_dict = {} validation = [] if isinstance(raw_response, dict): false_Positive = raw_response.get("false_positive", {}) validation = raw_response.get("validation", []) pulase_info_dict = raw_response.get("pulse_info", {}) if false_Positive and false_Positive[0].get("assessment") == "accepted": return Common.DBotScore.GOOD else: if not validation and pulase_info_dict: count = int(pulase_info_dict.get("count", "0")) if count >= default_threshold: return Common.DBotScore.BAD elif 0 < count < default_threshold: return Common.DBotScore.SUSPICIOUS else: return Common.DBotScore.NONE elif validation and len(validation) < min_valid_sources: return Common.DBotScore.SUSPICIOUS else: return Common.DBotScore.GOOD def create_list_by_ec(list_entries: list, list_type: str) -> list: def create_entry_by_ec(entry: dict) -> dict: if list_type == "passive_dns": return { "Hostname": entry.get("hostname"), "IP": entry.get("address"), "Type": entry.get("asset_type"), "FirstSeen": entry.get("first"), "LastSeen": entry.get("last"), } if list_type == "url_list": return assign_params(Data=entry.get("url")) if list_type == "hash_list": return assign_params(Hash=entry.get("hash")) # should not get here return {} return [create_entry_by_ec(entry) for entry in list_entries] def create_pulse_by_ec(entry: dict) -> dict: pulse_by_ec = { "ID": entry.get("id"), "Author": {"ID": entry.get("author", {}).get("id"), "Username": entry.get("author", {}).get("username")}, "Count": entry.get("indicator_count"), "Modified": entry.get("modified_text"), "Name": entry.get("name"), "Source": entry.get("pulse_source"), "SubscriberCount": entry.get("subscriber_count"), "Tags": entry.get("tags"), "Description": entry.get("description"), } return assign_params(**pulse_by_ec) def extract_attack_ids(raw_response: dict): """ extract the attack_ids field from the raw response if exists Args: raw_response: The response gotten from the server :returns: the attack_ids list of all the attack_ids that exists in the response. """ pulses = dict_safe_get(raw_response, ["pulse_info", "pulses"]) or [{}] attack_ids = [] for pulse in pulses: if pulse.get("attack_ids"): attack_ids.extend(pulse.get("attack_ids")) return attack_ids def relationships_manager( client: Client, entity_a: str, entity_a_type: str, indicator_type: str, attack_ids: list, indicator: str, field_for_passive_dns_rs: str, feed_indicator_type_for_passive_dns_rs: str, ): """ manage the relationships creation Args: client: Client object with request entity_a: str the first entity of the relationship entity_a_type: str the type of the first entity indicator_type: str the indicator type to get the related information by entity_b_type: str the indicator to get the related information by :returns: a list of the relationships that were created """ relationships = create_relationships( client, attack_ids, entity_a, entity_a_type, "display_name", FeedIndicatorType.indicator_type_by_server_version("STIX Attack Pattern"), ) if client.max_indicator_relationships > 0: limit = str(client.max_indicator_relationships) _, _, urls_raw_response = alienvault_get_related_urls_by_indicator_command(client, indicator_type, indicator, limit) urls_raw_response = delete_duplicated_entities(urls_raw_response.get("url_list", []), "url") relationships += create_relationships(client, urls_raw_response, entity_a, entity_a_type, "url", FeedIndicatorType.URL) _, _, hash_raw_response = alienvault_get_related_hashes_by_indicator_command(client, indicator_type, indicator, limit) hash_raw_response = delete_duplicated_entities(hash_raw_response.get("data", []), "hash") relationships += create_relationships(client, hash_raw_response, entity_a, entity_a_type, "hash", FeedIndicatorType.File) _, _, passive_dns_raw_response = alienvault_get_passive_dns_data_by_indicator_command( client, indicator_type, indicator, limit ) passive_dns_raw_response = delete_duplicated_entities( passive_dns_raw_response.get("passive_dns", []), field_for_passive_dns_rs ) passive_dns_raw_response = validate_string_is_not_url(passive_dns_raw_response, field_for_passive_dns_rs) passive_dns_raw_response = passive_dns_raw_response[0 : client.max_indicator_relationships] relationships += create_relationships( client, passive_dns_raw_response, entity_a, entity_a_type, field_for_passive_dns_rs, feed_indicator_type_for_passive_dns_rs, ) return relationships def create_relationships( client: Client, relevant_field: list, entity_a: str, entity_a_type: str, relevant_id: str, entity_b_type: str ): """ create relationships list for the given fields Args: client: Client object with request relevant_field: the field that holds the relevant display name(entity_b) for the relationship entity_a: str the first entity of the relationship entity_a_type: str the type of the first entity relevant_id: str the exact key where the display name is located inside the relevant_field entity_b_type: str the type of the second entity :returns: a list of the relationships that were created """ relationships: list = [] if not client.create_relationships: return relationships if relevant_field and isinstance(relevant_field, list) and relevant_id in relevant_field[0]: display_names = [item.get(relevant_id) for item in relevant_field] if display_names: relationships = [ EntityRelationship( name=EntityRelationship.Relationships.INDICATOR_OF, entity_a=entity_a, entity_a_type=entity_a_type, entity_b=display_name, entity_b_type=entity_b_type, source_reliability=client.reliability, brand=INTEGRATION_NAME, ) for display_name in display_names ] return relationships def delete_duplicated_entities(entities_list: List[dict], field_name: str): """delete duplicated results from a response Args: entities_list: The list of the entities brought back from the query. field_name: The field to compare according to between the given entities. Returns: a list without duplicated entities. """ unique_dict: dict = {} for entity_dict in entities_list: if isinstance(entity_dict, dict) and (ind_value := entity_dict.get(field_name)) not in unique_dict: unique_dict[ind_value] = entity_dict return list(unique_dict.values()) def validate_string_is_not_url(entities_list: List[dict], field_name: str): """delete url type entities from a given list. Args: entities_list: The list of the entities brought back from the query. field_name: The field to compare according to between the given entities. Returns: a list without url type entities. """ return [dict for dict in entities_list if auto_detect_indicator_type(dict.get(field_name)) != "URL"] def lowercase_protocol_callback(pattern: re.Match) -> str: return pattern.group(0).lower() """ COMMANDS """ @logger def test_module_command(client: Client, *_) -> tuple[None, None, str]: """Performs a basic GET request to check if the API is reachable and authentication is successful. Args: client: Client object with request *_: Usually demisto.args() Returns: 'ok' if test successful. Raises: DemistoException: If test failed. """ results = client.test_module() if "city" in results: return None, None, "ok" raise DemistoException(f"Test module failed, {results}") @logger def ip_command(client: Client, ip_address: str, ip_version: str) -> List[CommandResults]: """Enrichment for IPv4/IPv6 Args: client: Client object with request ip_address: ip address ip_version: IPv4 or IPv6 Returns: List of CommandResults """ ips_list: list = argToList(ip_address) title = f"{INTEGRATION_NAME} - Results for ips query" command_results: List[CommandResults] = [] for ip_ in ips_list: raw_response = client.query(section=ip_version, argument=ip_) if raw_response and raw_response != 404: ip_version = FeedIndicatorType.IP if ip_version == "IPv4" else FeedIndicatorType.IPv6 relationships = relationships_manager( client, entity_a=ip_, entity_a_type=ip_version, indicator_type=ip_version, indicator=ip_, field_for_passive_dns_rs="hostname", feed_indicator_type_for_passive_dns_rs=FeedIndicatorType.Domain, attack_ids=extract_attack_ids(raw_response), ) dbot_score = Common.DBotScore( indicator=ip_, indicator_type=DBotScoreType.IP, integration_name=INTEGRATION_NAME, score=calculate_dbot_score(client, raw_response), reliability=client.reliability, ) ip_object = Common.IP( ip=ip_, dbot_score=dbot_score, asn=raw_response.get("asn"), geo_country=raw_response.get("country_code"), geo_latitude=raw_response.get("latitude"), geo_longitude=raw_response.get("longitude"), relationships=relationships, ) context = {"Reputation": raw_response.get("reputation"), "IP": ip_} human_readable_context = { "Address": ip_object.to_context().get("IP(val.Address && val.Address == obj.Address)").get("Address"), "Geo": ip_object.to_context().get("IP(val.Address && val.Address == obj.Address)").get("Geo"), } human_readable = tableToMarkdown(name=title, t=human_readable_context) command_results.append( CommandResults( readable_output=human_readable, outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.IP(val.IP && val.IP === obj.IP)", outputs={"IP": context}, indicator=ip_object, raw_response=raw_response, relationships=relationships, ) ) else: command_results.append( create_indicator_result_with_dbotscore_unknown( indicator=ip_, indicator_type=DBotScoreType.IP, reliability=client.reliability ) ) if not command_results: return [CommandResults(f"{INTEGRATION_NAME} - Could not find any results for given query.")] return command_results @logger def domain_command(client: Client, domain: str) -> List[CommandResults]: """Enrichment for domain Args: client: Client object with request domain: domains to query Returns: List of CommandResults """ domains_list: list = argToList(domain) title = f"{INTEGRATION_NAME} - Results for Domain query" command_results: List[CommandResults] = [] for domain in domains_list: raw_response = client.query(section="domain", argument=domain) if raw_response and raw_response != 404: relationships = relationships_manager( client, entity_a=domain, indicator_type="domain", entity_a_type=FeedIndicatorType.Domain, indicator=domain, field_for_passive_dns_rs="address", feed_indicator_type_for_passive_dns_rs=FeedIndicatorType.IP, attack_ids=extract_attack_ids(raw_response), ) dbot_score = Common.DBotScore( indicator=domain, indicator_type=DBotScoreType.DOMAIN, integration_name=INTEGRATION_NAME, score=calculate_dbot_score(client, raw_response), reliability=client.reliability, ) domain_object = Common.Domain(domain=domain, dbot_score=dbot_score, relationships=relationships) context = { "Name": raw_response.get("indicator"), "Alexa": raw_response.get("alexa"), "Whois": raw_response.get("whois"), } human_readable = tableToMarkdown(t=context, name=title) command_results.append( CommandResults( readable_output=human_readable, outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.Domain(val.Alexa && val.Alexa === obj.Alexa &&" f" val.Whois && val.Whois === obj.Whois)", outputs=context, indicator=domain_object, raw_response=raw_response, relationships=relationships, ) ) else: command_results.append( create_indicator_result_with_dbotscore_unknown( indicator=domain, indicator_type=DBotScoreType.DOMAIN, reliability=client.reliability ) ) if not command_results: return [CommandResults(f"{INTEGRATION_NAME} - Could not find any results for given query")] return command_results @logger def file_command(client: Client, file: str) -> List[CommandResults]: """Enrichment for file hash MD5/SHA1/SHA256 Args: client: Client object with request file: File hash MD5/SHA1/SHA256 Returns: List of CommandResults """ hashes_list: list = argToList(file) title = f"{INTEGRATION_NAME} - Results for File hash query" command_results: List[CommandResults] = [] for hash_ in hashes_list: raw_response_analysis = client.query(section="file", argument=hash_, sub_section="analysis") raw_response_general = client.query(section="file", argument=hash_) if ( raw_response_analysis and raw_response_general and (shortcut := dict_safe_get(raw_response_analysis, ["analysis", "info", "results"], {})) and raw_response_general != 404 and raw_response_analysis != 404 ): relationships = create_relationships( client, extract_attack_ids(raw_response_general), hash_, FeedIndicatorType.File, "display_name", FeedIndicatorType.indicator_type_by_server_version("STIX Attack Pattern"), ) dbot_score = Common.DBotScore( indicator=hash_, indicator_type=DBotScoreType.FILE, integration_name=INTEGRATION_NAME, score=calculate_dbot_score(client, raw_response_general), malicious_description=raw_response_general.get("pulse_info", {}).get("pulses"), reliability=client.reliability, ) file_object = Common.File( md5=shortcut.get("md5"), sha1=shortcut.get("sha1"), sha256=shortcut.get("sha256"), ssdeep=shortcut.get("ssdeep"), size=shortcut.get("filesize"), file_type=shortcut.get("file_type"), dbot_score=dbot_score, relationships=relationships, ) context = { "MD5": shortcut.get("md5"), "SHA1": shortcut.get("sha1"), "SHA256": shortcut.get("sha256"), "SSDeep": shortcut.get("ssdeep"), "Size": shortcut.get("filesize"), "Type": shortcut.get("file_type"), "Malicious": {"PulseIDs": raw_response_general.get("pulse_info", {}).get("pulses")}, } human_readable = tableToMarkdown(name=title, t=context) command_results.append( CommandResults( readable_output=human_readable, outputs_prefix=outputPaths.get("file"), outputs=context, indicator=file_object, raw_response=raw_response_general, relationships=relationships, ) ) else: command_results.append( create_indicator_result_with_dbotscore_unknown( indicator=hash_, indicator_type=DBotScoreType.FILE, reliability=client.reliability ) ) if not command_results: return [CommandResults(f"{INTEGRATION_NAME} - Could not find any results for given query")] return command_results @logger def url_command(client: Client, url: str) -> List[CommandResults]: """Enrichment for url Args: client: Client object with request url: url address Returns: List of CommandResults """ urls_list: list = argToList(url) title = f"{INTEGRATION_NAME} - Results for url query" command_results: List[CommandResults] = [] for url in urls_list: url = re.sub(r"(\w+)://", lowercase_protocol_callback, url) raw_response = client.query(section="url", argument=url) if raw_response: if raw_response == 404: command_results.append( create_indicator_result_with_dbotscore_unknown( indicator=url, indicator_type=DBotScoreType.URL, reliability=client.reliability ) ) else: relationships = [] if client.create_relationships: indicator = FeedIndicatorType.indicator_type_by_server_version("STIX Attack Pattern") relationships = create_relationships( client, extract_attack_ids(raw_response), url, FeedIndicatorType.URL, "display_name", indicator ) domain = raw_response.get("domain") if domain: relationships.extend( [ EntityRelationship( name=EntityRelationship.Relationships.HOSTED_ON, entity_a=url, entity_a_type=FeedIndicatorType.URL, entity_b=domain, entity_b_type=FeedIndicatorType.Domain, source_reliability=client.reliability, brand=INTEGRATION_NAME, ) ] ) dbot_score = Common.DBotScore( indicator=url, indicator_type=DBotScoreType.URL, integration_name=INTEGRATION_NAME, score=calculate_dbot_score(client, raw_response), reliability=client.reliability, ) url_object = Common.URL(url=url, dbot_score=dbot_score, relationships=relationships) context = { "Url": url, "Hostname": raw_response.get("hostname"), "Domain": raw_response.get("domain"), "Alexa": raw_response.get("alexa"), "Whois": raw_response.get("whois"), } human_readable = tableToMarkdown(name=title, t=context) command_results.append( CommandResults( readable_output=human_readable, outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.URL(val.Url && val.Url === obj.Url)", outputs=context, indicator=url_object, raw_response=raw_response, relationships=relationships, ) ) if not command_results: command_results.append(CommandResults(f"{INTEGRATION_NAME} - Could not find any results for given query")) return command_results @logger def alienvault_search_hostname_command(client: Client, hostname: str) -> tuple[str, dict, dict]: """Search for hostname details Args: client: Client object with request hostname: hostname address Returns: Outputs """ raw_response = client.query(section="hostname", argument=hostname) if raw_response and raw_response != 404: title = f"{INTEGRATION_NAME} - Results for Hostname query" context_entry: dict = { "Endpoint(val.Hostname && val.Hostname === obj.Hostname)": {"Hostname": raw_response.get("indicator")}, "AlienVaultOTX.Endpoint(val.Alexa && val.Alexa === obj.Alexa &&val.Whois && val.Whois === obj.Whois)": { "Hostname": raw_response.get("indicator"), "Alexa": raw_response.get("alexa"), "Whois": raw_response.get("whois"), }, outputPaths.get("dbotscore"): { "Indicator": raw_response.get("indicator"), "Score": calculate_dbot_score(client, raw_response), "Type": "hostname", "Vendor": "AlienVault OTX v2", "Reliability": client.reliability, }, } human_readable = tableToMarkdown( name=title, t=context_entry.get( "AlienVaultOTX.Endpoint(val.Alexa && val.Alexa === obj.Alexa &&val.Whois && val.Whois === obj.Whois)" ), ) return human_readable, context_entry, raw_response else: return f"{INTEGRATION_NAME} - Could not find any results for given query", {}, {} @logger def alienvault_search_cve_command(client: Client, cve_id: str) -> tuple[str, dict, dict]: """Get Common Vulnerabilities and Exposures by id Args: client: Client object with request cve_id: CVE id Returns: Outputs """ raw_response = client.query(section="cve", argument=cve_id) if raw_response and raw_response != 404: title = f"{INTEGRATION_NAME} - Results for Hostname query" context_entry: dict = { outputPaths.get("cve"): { "ID": raw_response.get("indicator"), "CVSS": raw_response.get("cvss", {}).get("Score"), "Published": raw_response.get("date_created"), "Modified": raw_response.get("date_modified"), "Description": raw_response.get("description"), }, outputPaths.get("dbotscore"): { "Indicator": raw_response.get("indicator"), "Score": calculate_dbot_score(client, raw_response), "Type": "cve", "Vendor": "AlienVault OTX v2", "Reliability": client.reliability, }, } human_readable = tableToMarkdown(t=context_entry.get(outputPaths.get("cve")), name=title) return human_readable, context_entry, raw_response else: return f"{INTEGRATION_NAME} - Could not find any results for given query", {}, {} @logger def alienvault_get_related_urls_by_indicator_command( client: Client, indicator_type: str, indicator: str, limit: str = "" ) -> tuple[str, dict, dict]: """Get related urls by indicator (IPv4,IPv6,domain,hostname,url) Args: client: Client object with request indicator_type: IPv4,IPv6,domain,hostname,url indicator: indicator its self (google.com) limit: the maximum number of indicators to fetch Returns: Outputs """ if indicator_type == "IP": indicator_type = "IPv4" params = {} if limit: params["limit"] = limit raw_response = client.query(section=indicator_type, argument=indicator, sub_section="url_list", params=params) if raw_response and raw_response != 404: title = f"{INTEGRATION_NAME} - Related url list to queried indicator" context_entry: list = create_list_by_ec(list_entries=raw_response.get("url_list", {}), list_type="url_list") context: dict = {"AlienVaultOTX.URL(val.URL.Data && val.URL.Data == obj.URL.Data)": context_entry} human_readable = tableToMarkdown(t=context_entry, name=title) return human_readable, context, raw_response else: return f"{INTEGRATION_NAME} - Could not find any results for given query", {}, {} @logger def alienvault_get_related_hashes_by_indicator_command( client: Client, indicator_type: str, indicator: str, limit: str = "" ) -> tuple[str, dict, dict]: """Get related file hashes by indicator (IPv4,IPv6,domain,hostname) Args: client: Client object with request indicator_type: IPv4,IPv6,domain,hostname indicator: indicator its self (google.com) limit: the maximum number of indicators to fetch Returns: Outputs """ if indicator_type == "IP": indicator_type = "IPv4" params = {} if limit: params["limit"] = limit raw_response = client.query(section=indicator_type, argument=indicator, sub_section="malware", params=params) if raw_response and raw_response != 404: title = f"{INTEGRATION_NAME} - Related malware list to queried indicator" context_entry: dict = { "AlienVaultOTX.File(val.File.Hash && val.File.Hash == obj.File.Hash)": create_list_by_ec( list_entries=raw_response.get("data", {}), list_type="hash_list" ) } human_readable = tableToMarkdown( t=context_entry.get( "AlienVaultOTX.File(val.File.Hash && val.File.Hash \ == obj.File.Hash)" ), name=title, ) return human_readable, context_entry, raw_response else: return f"{INTEGRATION_NAME} - Could not find any results for given query", {}, {} @logger def alienvault_get_passive_dns_data_by_indicator_command( client: Client, indicator_type: str, indicator: str, limit: str = "" ) -> tuple[str, dict, dict]: """Get related file hashes by indicator (IPv4,IPv6,domain,hostname) Args: client: Client object with request indicator_type: IPv4,IPv6,domain,hostname indicator: indicator its self (google.com) limit: the maximum number of indicators to fetch Returns: Outputs """ if indicator_type == "IP": indicator_type = "IPv4" params = {} if limit: params["limit"] = limit raw_response = client.query(section=indicator_type, argument=indicator, sub_section="passive_dns", params=params) if raw_response and raw_response != 404: title = f"{INTEGRATION_NAME} - Related passive dns list to queried indicator" context_entry: dict = { "AlienVaultOTX.PassiveDNS(val.PassiveDNS.Hostname && val.PassiveDNS.Hostname == obj.PassiveDNS.Hostname &&" "val.PassiveDNS.LastSeen && val.PassiveDNS.LastSeen == obj.PassiveDNS.LastSeen &&" "val.PassiveDNS.IP && val.PassiveDNS.IP == obj.PassiveDNS.IP)": create_list_by_ec( list_entries=raw_response.get("passive_dns", {}), list_type="passive_dns" ) } human_readable = tableToMarkdown( t=context_entry.get( "AlienVaultOTX.PassiveDNS(val.PassiveDNS.Hostname && val.PassiveDNS.Hostname == obj.PassiveDNS.Hostname &&" "val.PassiveDNS.LastSeen && val.PassiveDNS.LastSeen == obj.PassiveDNS.LastSeen &&" "val.PassiveDNS.IP && val.PassiveDNS.IP == obj.PassiveDNS.IP)" ), name=title, ) return human_readable, context_entry, raw_response else: return f"{INTEGRATION_NAME} - Could not find any results for given query", {}, {} @logger def alienvault_search_pulses_command(client: Client, page: str) -> tuple[str, dict, dict]: """Get pulse page by number of the page Args: client: Client object with request page: pulse page number Returns: Outputs """ raw_response = client.query(section="search", sub_section="pulses", params={"page": page}) if raw_response and raw_response != 404: title = f"{INTEGRATION_NAME} - pulse page {page}" context_entry: dict = { "AlienVaultOTX.Pulses(val.ID && val.ID == obj.ID && val.Modified && val.Modified == obj.Modified)": [ create_pulse_by_ec(entry) for entry in raw_response.get("results", {}) ] } human_readable = tableToMarkdown( t=context_entry.get( "AlienVaultOTX.Pulses(val.ID && val.ID == obj.ID && val.Modified && val.Modified == obj.Modified)" ), name=title, ) return human_readable, context_entry, raw_response else: return f"{INTEGRATION_NAME} - Could not find any results for given query", {}, {} @logger def alienvault_get_pulse_details_command(client: Client, pulse_id: str) -> tuple[str, dict, dict]: """Get pulse by ID Args: client: Client object with request pulse_id: pulse ID Returns: Outputs """ raw_response = client.query(section="pulses", argument=pulse_id) if raw_response and raw_response != 404: title = f"{INTEGRATION_NAME} - pulse id details" context_entry: dict = { "AlienVaultOTX.Pulses(val.ID && val.ID == obj.ID)": { "Description": raw_response.get("description"), "Created": raw_response.get("created"), "Author": {"Username": raw_response.get("author", {}).get("username")}, "ID": raw_response.get("id"), "Name": raw_response.get("name"), "Tags": raw_response.get("tags"), "TargetedCountries": raw_response.get("targeted_countries"), } } human_readable = tableToMarkdown(t=context_entry.get("AlienVaultOTX.Pulses(val.ID && val.ID == obj.ID)"), name=title) return human_readable, context_entry, raw_response else: return f"{INTEGRATION_NAME} - Could not find any results for given query", {}, {} """ COMMANDS MANAGER / SWITCH PANEL """ def main(): params = demisto.params() base_url = urljoin(params.get("url"), "/api/v1/") verify_ssl = not params.get("insecure", False) proxy = params.get("proxy") default_threshold = int(params.get("default_threshold", 2)) min_valid_sources = int(params.get("min_valid_sources", 2)) max_indicator_relationships = arg_to_number(params.get("max_indicator_relationships", 0)) token = params.get("credentials", {}).get("password", "") or params.get("api_token", "") reliability = params.get("integrationReliability") reliability = reliability if reliability else DBotScoreReliability.C if DBotScoreReliability.is_valid_type(reliability): reliability = DBotScoreReliability.get_dbot_score_reliability_from_str(reliability) else: Exception("Please provide a valid value for the Source Reliability parameter.") should_error = argToBoolean(params.get("should_error", True)) client = Client( base_url=base_url, headers={"X-OTX-API-KEY": token}, verify=verify_ssl, proxy=proxy, default_threshold=default_threshold, min_valid_sources=min_valid_sources, reliability=reliability, create_relationships=argToBoolean(params.get("create_relationships")), max_indicator_relationships=max_indicator_relationships, should_error=should_error, ) command = demisto.command() demisto.debug(f"Command being called is {command}") commands = { "test-module": test_module_command, "domain": domain_command, "file": file_command, "url": url_command, f"{INTEGRATION_COMMAND_NAME}-search-hostname": alienvault_search_hostname_command, f"{INTEGRATION_COMMAND_NAME}-search-cve": alienvault_search_cve_command, f"{INTEGRATION_COMMAND_NAME}-get-related-urls-by-indicator": alienvault_get_related_urls_by_indicator_command, f"{INTEGRATION_COMMAND_NAME}-get-related-hashes-by-indicator": alienvault_get_related_hashes_by_indicator_command, f"{INTEGRATION_COMMAND_NAME}-get-passive-dns-data-by-indicator": alienvault_get_passive_dns_data_by_indicator_command, f"{INTEGRATION_COMMAND_NAME}-search-pulses": alienvault_search_pulses_command, f"{INTEGRATION_COMMAND_NAME}-get-pulse-details": alienvault_get_pulse_details_command, } try: if command == f"{INTEGRATION_COMMAND_NAME}-search-ipv6": return_results(ip_command(client=client, ip_address=demisto.args().get("ip"), ip_version="IPv6")) elif command == "ip": return_results(ip_command(client=client, ip_address=demisto.args().get("ip"), ip_version="IPv4")) elif command in ["file", "domain", "url"]: return_results(commands[command](client=client, **demisto.args())) else: readable_output, outputs, raw_response = commands[command](client=client, **demisto.args()) return_outputs(readable_output, outputs, raw_response) # Log exceptions except Exception as e: err_msg = f"Error in {INTEGRATION_NAME} Integration [{e}]" return_error(err_msg, error=e) if __name__ in ("__main__", "__builtin__", "builtins"): main()