'
+
+strFromLineRegex = r'.*From\w*:(.*)'
+# example to match: From: john.doe@gmail.com
+
+strTagRegex = r'<[^>]*>'
+strEmailRegex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b'
+
+entries = demisto.executeCommand('getEntries', {})
+mailbody = demisto.incidents()[0]['details']
+mailbody = mailbody.replace('
','\n')
+# defaults
+mailbox=''
+sender=''
+attachmentname=''
+subject=''
+
+query =''
+
+
+# Get the mailbox
+if demisto.args().has_key('mailbox'):
+ mailbox = demisto.args()['mailbox']
+else:
+ for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email/from':
+ mailbox = t['value']
+ break
+ if mailbox == '':
+ for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email':
+ mailbox = t['value']
+ break
+
+if not mailbox:
+ demisto.results( { 'Type' : entryTypes['error'], 'ContentsFormat' : formats['text'], 'Contents' : 'No mailbox specified. Cannot continue.' } )
+ sys.exit(0)
+
+
+# Get the mail's subject
+if demisto.args().has_key('subject'):
+ subject = demisto.args()['subject']
+else:
+ matches = re.findall(strSubjectLineRegex, mailbody, re.I)
+ if len(matches) > 0:
+ subject = re.sub(strTagRegex,'',matches[0])
+
+qsubject = 'subject:"' + subject.strip() +'"' if subject else ''
+
+
+# Get the attachment file name
+if demisto.get(demisto.args(), 'attachmentName'):
+ attachmentname = demisto.args()['attachmentName']
+else:
+ for entry in entries:
+ if entry['File'] and demisto.get(entry, 'File'):
+ attachmentname = entry['File']
+
+qattach = 'attachment:"'+attachmentname+'"' if attachmentname else ''
+
+
+# Get the sender
+if demisto.args().has_key('sender'):
+ sender = demisto.args()['sender']
+else:
+ matches = re.findall(strFromLineRegex, mailbody, re.I)
+ if len(matches) > 0:
+ emails = re.findall(strEmailRegex, matches[0], re.I)
+ if len(emails) > 0:
+ sender = emails[0]
+
+qsender = 'from:"'+sender+'"' if sender else ''
+
+folder = 'inbox'
+parts = [qsubject, qsender,qattach]
+query = ' AND '.join([x for x in parts if not x==''])
+
+
+resultText +='Searching for emails using query:\n**' + query + '**\n'
+
+FoundIDs = []
+def AddID(id):
+ resultText += 'Found Item ID:\t' + id + '\n'
+ FoundIDs.append(id)
+
+respSearch = demisto.executeCommand('ews-search-mailbox', {'target-mailbox': mailbox, 'folders-ids': folder, 'query': query })
+
+if isError(respSearch[0]) or not respSearch[0]['ContentsFormat'] == formats['json']:
+ err = [{ 'Type' : entryTypes['error'], 'ContentsFormat' : formats['text'], 'Contents' : 'Unexpected response from ews-search-mailbox.' }]
+ demisto.results( [err] + respSearch)
+ sys.exit(0)
+
+
+c = respSearch[0]['Contents']
+#demisto.log(json.dumps(c)+'\n')
+fault = demisto.get(c, 'Envelope.Body.Fault')
+if fault:
+ if 'ErrorNonExistentMailbox' == demisto.gets(fault, 'detail.ResponseCode.#text'):
+ resultText += 'No mails found.\n'
+ finished=True
+ else:
+ msgXmlName = demisto.gets(fault, 'detail.MessageXml.Value.-Name')
+ msgXmlText = demisto.gets(fault, 'detail.MessageXml.Value.#text')
+ mdErr = '### Received error from Exchange Web Services :\n'
+ mdErr += 'faultcode|faultstring|Message|ResponseCode' + ('|' + msgXmlName if type(msgXmlName)==str else '') + '\n'
+ mdErr += '-|-|-|-|-\n'
+ mdErr += '|'.join( [str(demisto.gets(fault, loc)) for loc in 'faultcode.#text', 'faultstring.#text', 'detail.Message.#text', 'detail.ResponseCode.#text' ] + ([msgXmlText] if msgXmlText else [])) + '\n'
+ demisto.results( { 'Type' : entryTypes['error'], 'ContentsFormat' : formats['markdown'], 'Contents' : mdErr} )
+ sys.exit(0)
+else:
+ resp = demisto.get(c, 'Envelope.Body.FindItemResponse.ResponseMessages.FindItemResponseMessage')
+ if resp and demisto.get(resp, '-ResponseClass') == 'Success':
+ itemcount = int(resp['RootFolder']['-TotalItemsInView']) # todo handle parsing error for ints
+ if itemcount==0:
+ resultText += 'No mails found.\n'
+ finished=True
+ elif itemcount>1:
+ items = resp['RootFolder']['Items']['Message']
+ for item in items:
+ AddID(item['ItemId']['-Id'])
+ else:
+ AddID(resp['RootFolder']['Items']['Message']['ItemId']['-Id'])
+ else:
+ resultText += "Received empty or unsuccessful response with error:" + str(resp) +'\n'
+ finished = True
+
+if not finished:
+ resultText += 'Deleting all founds emails...\n'
+ itemids = ','.join(FoundIDs)
+ delres = demisto.executeCommand('ews-delete-items', {'target-mailbox': mailbox, 'item-ids' :itemids })
+
+ delres = delres[0]
+ if delres['Type'] != 4 and delres['ContentsFormat'] == 'json':
+ delresp = demisto.get(delres, 'Contents.Envelope.Body.DeleteItemResponse.ResponseMessages.DeleteItemResponseMessage')
+ if delresp:
+ if delresp['-ResponseClass'] == 'Success':
+ resultText += 'Delete successful.\n'
+ else:
+ resultText += "Delete failed: " + delresp['MessageText'] + "\nResponse Code:" + delresp['-ResponseCode']+'\n'
+ else:
+ resultText += "Delete failed with missing response\n"
+ else:
+ demisto.results(delres)
+
+demisto.results( { 'Type' : entryTypes['note'], 'ContentsFormat' : formats['markdown'], 'Contents': resultText } )
diff --git a/Scripts/FPDeleteRule.py b/Scripts/FPDeleteRule.py
new file mode 100644
index 000000000000..d7d46874a509
--- /dev/null
+++ b/Scripts/FPDeleteRule.py
@@ -0,0 +1,30 @@
+from re import escape
+FILTER_CONFIG_PATH = "/opt/WCG/config/filter.config"
+CMD_DEL_RULE_FORMAT = "sed -i '/^{0}={1} action=[A-Za-z]*$/d' {2}"
+CMD_TRITON_RELOAD_CONFIG = "/opt/WCG/bin/content_line -x" # && /opt/WCG/WCGAdmin runds"
+ruleType = demisto.args()["type"]
+if not ruleType in ["dest_domain", "dest_ip", "dest_host", "url_regex"]:
+ demisto.results( { "Type" : entryTypes["error"], "ContentsFormat" : formats["text"], "Contents" : 'Type argument must be "dest_domain", "dest_ip", "dest_host" or "url_regex". Invalid value: ' + ruleType } )
+else:
+ valueFormat = escape(demisto.args()["value"])
+ if ruleType in ["dest_domain", "url_regex"]:
+ valueFormat = r'\"' + valueFormat + r'\"'
+ # sed command that deletes the rule
+ cmdDelRule = CMD_DEL_RULE_FORMAT.format(ruleType, valueFormat, FILTER_CONFIG_PATH)
+ sshArgs = {"cmd": cmdDelRule + " && " + CMD_TRITON_RELOAD_CONFIG}
+ if "tritonsystem" in demisto.args():
+ if "remoteaccessname" in demisto.args():
+ demisto.results({ "Type" : entryTypes["error"], "ContentsFormat": formats["markdown"], "Contents": "You cannot uses both **tritonsystem** and **remoteaccessname**. Please choose one." })
+ sys.exit(0)
+ sshArgs["system"] = demisto.args()["tritonsystem"]
+ elif "remoteaccessname" in demisto.args():
+ sshArgs["using"] = demisto.args()["remoteaccessname"]
+ else:
+ demisto.results({ "Type" : entryTypes["error"], "ContentsFormat": formats["markdown"], "Contents": "You must provide either **tritonsystem** or **remoteaccessname** as arguments." })
+ sys.exit(0)
+ if "using" in sshArgs or "system" in sshArgs:
+ resSSH = demisto.executeCommand("ssh", sshArgs)
+ if not isError(resSSH[0]) and demisto.gets(resSSH[0], "Contents.success"):
+ demisto.results("Command executed successfully.")
+ else:
+ demisto.results(resSSH)
diff --git a/Scripts/FPSetRule.py b/Scripts/FPSetRule.py
new file mode 100644
index 000000000000..458cc62845f5
--- /dev/null
+++ b/Scripts/FPSetRule.py
@@ -0,0 +1,33 @@
+from re import escape
+FILTER_CONFIG_PATH = "/opt/WCG/config/filter.config"
+CMD_SET_RULE_FORMAT = "sed -i '/^{0}={1} action=/{{h;s/{0}={1} action=[A-Za-z]*$/{0}={1} action={2}/}};${{x;/^$/{{s//{0}={1} action={2}/;H}};x}}' {3}"
+CMD_TRITON_RELOAD_CONFIG = "/opt/WCG/bin/content_line -x" # && /opt/WCG/WCGAdmin runds"
+policy = demisto.args()["policy"]
+ruleType = demisto.args()["type"]
+if not policy in ["allow", "deny"]:
+ demisto.results({"Type": entryTypes["error"], "ContentsFormat": formats["text"], "Contents": 'Policy argument must be "allow" or "deny". Invalid value: ' + policy } )
+if not ruleType in ["dest_domain", "dest_ip", "dest_host", "url_regex"]:
+ demisto.results({"Type": entryTypes["error"], "ContentsFormat": formats["text"], "Contents": 'Type argument must be "dest_domain", "dest_ip", "dest_host" or "url_regex". Invalid value: ' + ruleType } )
+else:
+ valueFormat = escape(demisto.args()["value"])
+ if ruleType in ["dest_domain", "url_regex"]:
+ valueFormat = r'\"' + valueFormat + r'\"'
+ # sed command that modifies the action of the rule if found, otherwise it adds it in a new line
+ cmdSetRule = CMD_SET_RULE_FORMAT.format(ruleType, valueFormat, policy, FILTER_CONFIG_PATH)
+ sshArgs = {"cmd": cmdSetRule + " && " + CMD_TRITON_RELOAD_CONFIG}
+ if "tritonsystem" in demisto.args():
+ if "remoteaccessname" in demisto.args():
+ demisto.results({ "Type" : entryTypes["error"], "ContentsFormat": formats["markdown"], "Contents": "You cannot uses both **tritonsystem** and **remoteaccessname**. Please choose one." })
+ sys.exit(0)
+ sshArgs["system"] = demisto.args()["tritonsystem"]
+ elif "remoteaccessname" in demisto.args():
+ sshArgs["using"] = demisto.args()["remoteaccessname"]
+ else:
+ demisto.results({ "Type" : entryTypes["error"], "ContentsFormat": formats["markdown"], "Contents": "You must provide either **tritonsystem** or **remoteaccessname** as arguments." })
+ sys.exit(0)
+ if "using" in sshArgs or "system" in sshArgs:
+ resSSH = demisto.executeCommand("ssh", sshArgs)
+ if not isError(resSSH[0]) and demisto.gets(resSSH[0], "Contents.success"):
+ demisto.results("Command executed successfully.")
+ else:
+ demisto.results(resSSH)
diff --git a/Scripts/IPExtract.py b/Scripts/IPExtract.py
new file mode 100644
index 000000000000..76e7640e8d21
--- /dev/null
+++ b/Scripts/IPExtract.py
@@ -0,0 +1,39 @@
+import re
+import socket
+
+def is_valid_ipv4_address(address):
+ try:
+ socket.inet_pton(socket.AF_INET, address)
+ except AttributeError: # no inet_pton here, sorry
+ try:
+ socket.inet_aton(address)
+ except socket.error:
+ return False
+ return address.count('.') == 3
+ except socket.error: # not a valid address
+ return False
+ return True
+
+res = []
+ips = []
+
+data = demisto.args()['text']
+
+for m in re.finditer(r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b', data, re.I):
+ ip = m.group(0)
+ if ip in ips:
+ continue
+ if not is_valid_ipv4_address(ip):
+ continue
+ ips.append(ip)
+
+res.append('IPs found:\n' + '\n'.join(ips))
+currIPs = demisto.get(demisto.context(), 'ips')
+if currIPs and isinstance(currIPs, list):
+ for i in ips:
+ if i not in currIPs:
+ currIPs.append(i)
+else:
+ currIPs = ips
+demisto.setContext('ips', currIPs)
+demisto.results(res)
diff --git a/Scripts/IncidentSet.py b/Scripts/IncidentSet.py
new file mode 100644
index 000000000000..4931ceec9448
--- /dev/null
+++ b/Scripts/IncidentSet.py
@@ -0,0 +1,26 @@
+res = []
+dArgs = {}
+inArgs= demisto.args()
+if demisto.get(inArgs, 'owner'):
+ res += demisto.executeCommand("setOwner", {'owner': inArgs['owner']})
+if demisto.get(inArgs, 'playbook'):
+ res += demisto.executeCommand("setPlaybook", {'name': inArgs['playbook']})
+if demisto.get(inArgs, 'stage'):
+ res += demisto.executeCommand("setStage", {'stage': inArgs['stage']})
+if demisto.get(inArgs, 'name'):
+ dArgs['incName'] = inArgs['name']
+if demisto.get(inArgs, 'details'):
+ dArgs['details'] = inArgs['details']
+if demisto.get(inArgs, 'severity'):
+ dArgs['severity'] = inArgs['severity']
+if demisto.get(inArgs, 'labels'):
+ dArgs['labels'] = inArgs['labels']
+if demisto.get(inArgs, 'addLabels'):
+ dArgs['addLabels'] = inArgs['addLabels']
+if demisto.get(inArgs, 'type'):
+ dArgs['type'] = inArgs['type']
+ if (not demisto.get(inArgs, 'updatePlaybookForType') or demisto.get(inArgs, 'updatePlaybookForType') == 'yes') and not demisto.get(inArgs, 'playbook'):
+ demisto.executeCommand("setPlaybookAccordingToType", {'type': inArgs['type']})
+if dArgs:
+ res += demisto.executeCommand("setIncident", dArgs)
+demisto.results(res)
diff --git a/Scripts/IncidentToContext.py b/Scripts/IncidentToContext.py
new file mode 100644
index 000000000000..d2c9a1aa4636
--- /dev/null
+++ b/Scripts/IncidentToContext.py
@@ -0,0 +1,42 @@
+# These comments are required to make sure that all pre-defined labels are selectable in the playbook
+# demisto.setContext('Label/Application', '')
+# demisto.setContext('Label/Database', '')
+# demisto.setContext('Label/Directory', '')
+# demisto.setContext('Label/Email', '')
+# demisto.setContext('Label/Email/cc', '')
+# demisto.setContext('Label/Email/from', '')
+# demisto.setContext('Label/IP', '')
+# demisto.setContext('Label/System', '')
+# demisto.setContext('Label/URL', '')
+# demisto.setContext('Label/User', '')
+
+i = demisto.incidents()[0]
+demisto.setContext('id', i['id'])
+demisto.setContext('created', i['created'])
+demisto.setContext('modified', i['modified'])
+demisto.setContext('occurred', i['occurred'])
+demisto.setContext('dueDate', i['dueDate'])
+demisto.setContext('name', i['name'])
+demisto.setContext('owner', i['owner'])
+demisto.setContext('type', i['type'])
+demisto.setContext('severity', i['severity'])
+demisto.setContext('stage', i['stage'])
+demisto.setContext('status', i['status'])
+demisto.setContext('details', i['details'])
+
+# Setting initial score based on severity. Severity "Unknown" yields score 0.
+score = i['severity'] * 25
+demisto.setContext('score', score)
+
+labels = {}
+for l in i['labels']:
+ name = 'Label/' + l['type']
+ if demisto.get(labels, name):
+ labels[name] = labels[name].append(l['value'])
+ else:
+ labels[name] = [l['value']]
+
+for k, v in labels.iteritems():
+ demisto.setContext(k, v if len(v) > 1 else v[0])
+
+demisto.results('Incident context set')
diff --git a/Scripts/MD5Extract.py b/Scripts/MD5Extract.py
new file mode 100644
index 000000000000..575c99c34b29
--- /dev/null
+++ b/Scripts/MD5Extract.py
@@ -0,0 +1,24 @@
+import re
+
+md5Regex = r'\b[a-fA-F\d]{32}\b'
+
+res = []
+hashes = []
+
+data = demisto.args()['text']
+
+for m in re.finditer(md5Regex, data, re.I):
+ h = m.group(0)
+ if h not in hashes:
+ hashes.append(h)
+
+res.append('MD5s found:\n' + '\n'.join(hashes))
+currHashes = demisto.get(demisto.context(), 'md5s')
+if currHashes and isinstance(currHashes, list):
+ for h in hashes:
+ if h not in currHashes:
+ currHashes.append(h)
+else:
+ currHashes = hashes
+demisto.setContext('md5s', currHashes)
+demisto.results(res)
diff --git a/Scripts/NexposeEmailParser.js b/Scripts/NexposeEmailParser.js
new file mode 100644
index 000000000000..535adc76991f
--- /dev/null
+++ b/Scripts/NexposeEmailParser.js
@@ -0,0 +1,62 @@
+var entries = executeCommand('getEntries', {});
+var report = 0;
+if (!args.entryID) {
+ for (var i = 0; i < entries.length; i++) {
+ if (entries[i].File) {
+ var entryIDFromInv = entries[i].ID;
+ report = getFileByEntryID(entryIDFromInv);
+ if (/$/);
+for (var i=0; i < nodes.length; i++) {
+ var nodeRegexp = //g;
+ var match = nodeRegexp.exec(nodes[i]);
+ if (nodes[i] && match) {
+ var testRegexp = //g ;
+ var vulnCount = nodes[i].match(testRegexp) ? nodes[i].match(testRegexp).length : '0';
+ var data = {
+ Address: match[1],
+ Name: match[4],
+ Risk: match[6],
+ Status: match[2],
+ Importance: match[5],
+ Vulnerabilities: vulnCount
+ };
+ if ((args.minRiskScore && args.minRiskScore <= data.Risk) || !args.minRiskScore) {
+ if ((args.minVulnCount && args.minVulnCount <= data.Vulnerabilities) || !args.minVulnCount) {
+ contents.push(data);
+
+ var sev = "low";
+ if (data.Risk > 3000) {
+ sev = "critical";
+ } else if (data.Risk > 2500) {
+ sev = "high";
+ } else if (data.Risk > 2000) {
+ sev = "medium";
+ }
+
+ createNewIncident({
+ type: 'Nexpose alert',
+ details: nodes[i],
+ severity: sev,
+ incName: data.Address + ' vulnerability count ' + data.Vulnerabilities + ' risk score ' + data.Risk,
+ systems: data.Address
+ });
+
+ }
+ }
+ }
+}
+
+return [{Contents: contents,
+ ContentsFormat: formats.table,
+ Type: entryTypes.note},
+ closeInvestigation({Reason: 'Spawned ' + contents.length + ' child incidents'})];
\ No newline at end of file
diff --git a/Scripts/NexposeEmailParserForVuln.js b/Scripts/NexposeEmailParserForVuln.js
new file mode 100644
index 000000000000..e5626b976fc8
--- /dev/null
+++ b/Scripts/NexposeEmailParserForVuln.js
@@ -0,0 +1,51 @@
+var report = args.report ? args.report : incidents[0].details;
+var contents = {};
+var nodes = report.split(/<\/node>/);
+for (var i=0; i < nodes.length; i++) {
+ var nodeRegexp = //g;
+ var vulnRegexp = /()/g;
+ var res = nodes[i].match(/()/g);
+ var match = nodeRegexp.exec(nodes[i]);
+ if (nodes[i] && match) {
+ var vulnCount = nodes[i].match(vulnRegexp) ? nodes[i].match(vulnRegexp).length : '0';
+ var data = {
+ Address: match[1],
+ Name: match[4],
+ Risk: match[6],
+ Status: match[2],
+ Importance: match[5],
+ Vulnerabilities: vulnCount
+ };
+ for (var j = 0; res && j < res.length; j++) {
+ var current = vulnRegexp.exec(res[j]);
+ if (current) {
+ if (contents[current[2]]) {
+ contents[current[2]].labels.push(data);
+ } else {
+ contents[current[2]] = {labels: [], Name: current[2]};
+ }
+ }
+ }
+ }
+}
+
+var k = 0;
+for (var key in contents) {
+ var element = contents[key];
+ var labels = '';
+ for (var j = 0; j < element.labels.length; j++) {
+ labels += element.labels[j].Address + ',';
+ }
+ labels = labels.slice(0, -1);
+ if (labels.length > 0) {
+ createNewIncident({
+ type: 'Nexpose alert',
+ details: JSON.stringify(element.labels),
+ severity: args.defaultNexposeSeverity,
+ incName: key,
+ systems: labels
+ });
+ }
+}
+
+return closeInvestigation({Reason: 'Spawned ' + k + ' child incidents'});
\ No newline at end of file
diff --git a/Scripts/NexposeVulnExtractor.js b/Scripts/NexposeVulnExtractor.js
new file mode 100644
index 000000000000..ef9b820450ed
--- /dev/null
+++ b/Scripts/NexposeVulnExtractor.js
@@ -0,0 +1,11 @@
+var report = args.report ? args.report : incidents[0].details;
+var res = report.match(/()/g);
+var contents = [];
+for (var i =0; i < res.length; i++) {
+ var current = /()/g.exec(res[i]);
+ contents.push({Name: current[2], Status: current[4], Since: current[6], Compliance: current[7]});
+}
+
+return {Contents: contents,
+ ContentsFormat: formats.table,
+ Type: entryTypes.note};
\ No newline at end of file
diff --git a/Scripts/PrintContext.py b/Scripts/PrintContext.py
new file mode 100644
index 000000000000..c1f03c61e3d0
--- /dev/null
+++ b/Scripts/PrintContext.py
@@ -0,0 +1,6 @@
+res = demisto.executeCommand('getContext', {})
+if isError(res[0]):
+ demisto.results(res)
+else:
+ md = "**Context data**:\n" + json.dumps(res[0]['Contents'], indent=4)
+ demisto.results({'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': md})
diff --git a/Scripts/RegCollectValues.py b/Scripts/RegCollectValues.py
new file mode 100644
index 000000000000..aed3adf144d7
--- /dev/null
+++ b/Scripts/RegCollectValues.py
@@ -0,0 +1,31 @@
+def Reg2Markdown(baseKey,regResp):
+ res, mdRows = [], ''
+ if isError(regResp[0]):
+ res += regResp
+ elif len(regResp) > 1 :
+ res.append( { 'Type' : entryTypes['error'], 'ContentsFormat' : formats['text'], 'Contents' : 'Unexpected output from D2RegQuery - more than one entry returned:\n'+'\n'.join( [ str(x) for x in regResp ] ) } )
+ else:
+ subkeys = regResp[0]['Contents']
+ for sk in subkeys:
+ if type(subkeys[sk]) == dict:
+ for ssk in subkeys[sk]:
+ mdRows += '\n|' + str(baseKey) + '\\' + str(sk) + '|' + str(ssk) + '|' + str( subkeys[sk][ssk] ) + '|'
+ else:
+ mdRows += '\n|' + str(baseKey) + '|' + str(sk) + '|' + str( subkeys[sk] ) + '|'
+ return ( res, mdRows )
+
+
+# Note: Don't use !d2_status as that only shows systems with generated agents - the correct way is below:
+res = []
+regPath = NormalizeRegistryPath( demisto.args()['regpath'] )
+for system in demisto.investigation()['systems']:
+ if system['os'] == 'windows':
+ mdTable = '### Values retrieved from system *' + system['name'] + '*'
+ mdTable += '\n|Key|SubKey|Value|'
+ mdTable += '\n|-----|------|-----|'
+ cmdResp = demisto.executeCommand( 'D2RegQuery', { 'using' : system['name'], 'path' : regPath } )
+ moreEntries, moreRows = Reg2Markdown( regPath , cmdResp )
+ mdTable += moreRows
+ res += moreEntries
+ res.append( { 'Type' : entryTypes['note'], 'ContentsFormat' : formats['markdown'], 'Contents' : mdTable } )
+demisto.results(res)
diff --git a/Scripts/RegPathReputationBasicLists.py b/Scripts/RegPathReputationBasicLists.py
new file mode 100644
index 000000000000..89f2ebc8f54c
--- /dev/null
+++ b/Scripts/RegPathReputationBasicLists.py
@@ -0,0 +1,112 @@
+whitelist = [ r'HKEY_LOCAL_MACHINE\Software\wow6432node\Microsoft\Windows\CurrentVersion\Run\vmware-tray.exe' ]
+
+blacklist = [ r'HKEY_CURRENT_USER\Software\Locky',
+r'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run\Locky' ]
+
+suspicious = [ r"HKCU\Software\Microsoft\Internet Explorer\UrlSearchHooks",
+r"HKCU\Software\Microsoft\Windows\CurrentVersion\Run",
+r"HKLM\SOFTWARE\Classes\Htmlfile\Shell\Open\Command",
+r"HKLM\SOFTWARE\Classes\Htmlfile\Shell\Open\Command\(Default)",
+r"HKLM\SOFTWARE\Classes\Protocols\Filter",
+r"HKLM\SOFTWARE\Classes\Protocols\Handler",
+r"HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components",
+r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_Dlls",
+r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon",
+r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell",
+r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit",
+r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\VmApplet",
+r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Provider Filters",
+r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers",
+r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\PLAP Providers",
+r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellServiceObjects",
+r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
+r"HKLM\SOFTWARE\Wow6432Node\Microsoft\Active Setup\Installed Components",
+r"HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_Dlls",
+r"HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\ShellServiceObjects",
+r"HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run",
+r"HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Authentication Packages",
+r"HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Notification Packages",
+r"HKLM\SYSTEM\CurrentControlSet\Control\Lsa\OSConfig\Security Packages",
+r"HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages",
+r"HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order",
+r"HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors",
+r"HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot",
+r"HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\AlternateShell",
+r"HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders",
+r"HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SecurityProviders",
+r"HKLM\Software\Classes\AllFileSystemObjects\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Classes\AllFileSystemObjects\ShellEx\PropertySheetHandlers",
+r"HKLM\Software\Classes\CLSID\{083863F1-70DE-11d0-BD40-00A0C911CE86}\Instance",
+r"HKLM\Software\Classes\CLSID\{7ED96837-96F0-4812-B211-F13C24117ED3}\Instance",
+r"HKLM\Software\Classes\Directory\Background\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Classes\Directory\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Classes\Directory\Shellex\CopyHookHandlers",
+r"HKLM\Software\Classes\Directory\Shellex\DragDropHandlers",
+r"HKLM\Software\Classes\Directory\Shellex\PropertySheetHandlers",
+r"HKLM\Software\Classes\Drive\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Classes\Folder\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Classes\Folder\ShellEx\DragDropHandlers",
+r"HKLM\Software\Classes\Folder\ShellEx\PropertySheetHandlers",
+r"HKLM\Software\Classes\Folder\Shellex\ColumnHandlers",
+r"HKLM\Software\Microsoft\Windows NT\CurrentVersion\Drivers32",
+r"HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows",
+r"HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\IconServiceLib",
+r"HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects",
+r"HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers",
+r"HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce",
+r"HKLM\Software\Wow6432Node\Classes\AllFileSystemObjects\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Wow6432Node\Classes\AllFileSystemObjects\ShellEx\PropertySheetHandlers",
+r"HKLM\Software\Wow6432Node\Classes\CLSID\{083863F1-70DE-11d0-BD40-00A0C911CE86}\Instance",
+r"HKLM\Software\Wow6432Node\Classes\CLSID\{7ED96837-96F0-4812-B211-F13C24117ED3}\Instance",
+r"HKLM\Software\Wow6432Node\Classes\Directory\Background\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Directory\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Directory\Shellex\CopyHookHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Directory\Shellex\DragDropHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Directory\Shellex\PropertySheetHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Drive\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Folder\ShellEx\ContextMenuHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Folder\ShellEx\DragDropHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Folder\ShellEx\PropertySheetHandlers",
+r"HKLM\Software\Wow6432Node\Classes\Folder\Shellex\ColumnHandlers",
+r"HKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Drivers32",
+r"HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects",
+r"HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers",
+r"HKLM\System\CurrentControlSet\Control",
+r"HKLM\System\CurrentControlSet\Control\ServiceControlManagerExtension",
+r"HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute",
+r"HKLM\System\CurrentControlSet\Control\Session Manager\KnownDlls",
+r"HKLM\System\CurrentControlSet\Control\Terminal Server\Wds\rdpwd",
+r"HKLM\System\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\StartupPrograms",
+r"HKLM\System\CurrentControlSet\Services",
+r"HKLM\System\CurrentControlSet\Services\WinSock2\Parameters\NameSpace_Catalog5\Catalog_Entries",
+r"HKLM\System\CurrentControlSet\Services\WinSock2\Parameters\NameSpace_Catalog5\Catalog_Entries64",
+r"HKLM\System\CurrentControlSet\Services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries",
+r"HKLM\System\CurrentControlSet\Services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries64",
+r"HKLM\software\microsoft\windows\currentversion\policies\explorer\run",
+r"HKCU\Software\Microsoft\Windows\CurrentVersion\RunServices",
+r"HKLM\Software\Microsoft\Windows\CurrentVersion\RunServices",
+r"HKLM\software\microsoft\wbem\ess\//./root/cimv2\win32clockprovider",
+r"HKLM\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce",
+r"HKCU\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce",
+r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify",
+r"HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell",
+r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad",
+r"HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnceEx",
+r"HKCU\Software\Microsoft\Windows\CurrentVersion\Run",
+r"HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce",
+r"HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run",
+r"HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\load",
+r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SharedTaskScheduler" ]
+
+regPath = NormalizeRegistryPath( demisto.args()['input'] )
+regPath = regPath.upper()
+if regPath in (key.upper() for key in whitelist):
+ score = 1
+elif regPath in (key.upper() for key in blacklist):
+ score = 3
+elif regPath in (key.upper() for key in suspicious):
+ score = 2
+else:
+ score = 0
+demisto.results(score)
+
diff --git a/Scripts/RegProbeBasic.py b/Scripts/RegProbeBasic.py
new file mode 100644
index 000000000000..54d84e362e5a
--- /dev/null
+++ b/Scripts/RegProbeBasic.py
@@ -0,0 +1,45 @@
+keys = [ r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
+r"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run",
+r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Active Setup\Installed Components",
+r"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Active Setup\Installed Components",
+r"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
+r"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce" ]
+
+
+def Reg2Markdown(baseKey,regResp):
+ res, mdRows = [], ''
+ if isError(regResp[0]):
+ res += regResp
+ elif len(regResp) > 1 :
+ res.append( { 'Type' : entryTypes['error'], 'ContentsFormat' : formats['text'], 'Contents' : 'Unexpected output from D2RegQuery - more than one entry returned:\n'+'\n'.join( [ str(x) for x in regResp ] ) } )
+ else:
+ subkeys = regResp[0]['Contents']
+ for sk in subkeys:
+ if type(subkeys[sk]) == dict:
+ for ssk in subkeys[sk]:
+ mdRows += '\n|' + str(baseKey) + '\\' + str(sk) + '|' + str(ssk) + '|' + str( subkeys[sk][ssk] ) + '|'
+ else:
+ mdRows += '\n|' + str(baseKey) + '|' + str(sk) + '|' + str( subkeys[sk] ) + '|'
+ return ( res, mdRows )
+
+
+res = []
+if 'system' in demisto.args():
+ system = demisto.args()['system']
+elif 'using' in demisto.args():
+ system = demisto.args()['using']
+else:
+ demisto.results( { 'Type' : entryTypes['error'], 'ContentsFormat' : formats['text'], 'Contents' : 'You must provide "system" or "using" as arguments.'} )
+ sys.exit(0)
+
+mdTable = '### Results for short registry probe of system *' + system + '*'
+mdTable += '\n|Key|SubKey|Value|'
+mdTable += '\n|-----|------|-----|'
+for k in keys:
+ regPath = NormalizeRegistryPath( k )
+ cmdResp = demisto.executeCommand( 'D2RegQuery', { 'using' : system, 'regpath' : regPath } )
+ moreEntries , moreRows = Reg2Markdown( regPath, cmdResp )
+ mdTable += moreRows
+ res += moreEntries
+res.append ( { 'Type' : entryTypes['note'], 'ContentsFormat' : formats['markdown'], 'Contents' : mdTable } )
+demisto.results(res)
diff --git a/Scripts/RemoteExec.js b/Scripts/RemoteExec.js
new file mode 100644
index 000000000000..af66d4a4a24f
--- /dev/null
+++ b/Scripts/RemoteExec.js
@@ -0,0 +1,16 @@
+//Execute a command on a remote machine
+var output = [];
+var entries = executeCommand("ssh", {system: args.system, cmd: args.cmd});
+var ret = entries[0];
+if (ret === null) {
+ output.push({ContentsFormat: formats.text, Type: entryTypes.error, Contents: "Failed to execute remote command."});
+} else {
+ result = ret.Contents;
+ if (!result.success) {
+ output.push({ContentsFormat: formats.text, Type: entryTypes.error, Contents: result.error});
+ } else {
+ output.push({ContentsFormat: formats.text, Type: entryTypes.note, Contents: result.output});
+ }
+}
+return output;
+
\ No newline at end of file
diff --git a/Scripts/SendEmail.py b/Scripts/SendEmail.py
new file mode 100644
index 000000000000..bd59f66eb33b
--- /dev/null
+++ b/Scripts/SendEmail.py
@@ -0,0 +1,7 @@
+from string import Template
+body = Template(demisto.args()['body'].replace('\\n', '\n'))
+subject = Template(demisto.args()['subject'])
+to = demisto.args()['to']
+subject = subject.safe_substitute(name=demisto.incidents()[0]['name'])
+body = body.safe_substitute(recipient=to)
+demisto.results(demisto.executeCommand('send-mail', {'to': to, 'subject': subject , 'body': body}))
diff --git a/Scripts/SeverityByReputationsScore.py b/Scripts/SeverityByReputationsScore.py
new file mode 100644
index 000000000000..6ae2bb75999c
--- /dev/null
+++ b/Scripts/SeverityByReputationsScore.py
@@ -0,0 +1,34 @@
+for k in demisto.args():
+ if demisto.args()[k] == '':
+ demisto.results({'Type': entryTypes['error'], 'ContentsFormat': formats['text'], 'Contents': 'Please provide non-empty value for arg ' + k})
+ sys.exit(0)
+
+badWeights = {"bad_urls": demisto.args()['bad_url_weight'],
+ "bad_ips": demisto.args()['bad_ip_weight'],
+ "bad_hashes": demisto.args()['bad_hash_weight']}
+tCritical = demisto.args()['threshold_critical']
+tHigh = demisto.args()['threshold_high']
+tMed = demisto.args()['threshold_medium']
+
+score = demisto.get(demisto.context(), 'score')
+# Must explicitly compare to None since 0 is a valid score
+if score is None:
+ # Setting initial score based on severity. Severity "Unknown" yields score 0.
+ score = i['severity'] * 25
+
+for badKey in badWeights:
+ v = demisto.get(demisto.context(), badKey)
+ v = [v] if isinstance(v, list) else v
+ score += len(v) * badWeights[badKey]
+
+demisto.setContext('score', score)
+if score >= tMed:
+ if score >= tHigh:
+ if score >= tCritical:
+ demisto.executeCommand('IncidentSet', {'severity': 4})
+ else:
+ demisto.executeCommand('IncidentSet', {'severity': 3})
+ else:
+ demisto.executeCommand('IncidentSet', {'severity': 2})
+else:
+ demisto.executeCommand('IncidentSet', {'severity': 1})
diff --git a/Scripts/SlackSend.py b/Scripts/SlackSend.py
new file mode 100644
index 000000000000..5413a582ddf7
--- /dev/null
+++ b/Scripts/SlackSend.py
@@ -0,0 +1,6 @@
+dArgs = {}
+for argName in ['message', 'to', 'channel', 'group', 'entry']:
+ if demisto.get(demisto.args(), argName):
+ dArgs[argName] = demisto.args()[argName]
+
+demisto.results( demisto.executeCommand( 'slack-send', dArgs ) )
diff --git a/Scripts/SplunkEmailParser.js b/Scripts/SplunkEmailParser.js
new file mode 100644
index 000000000000..d0639366f476
--- /dev/null
+++ b/Scripts/SplunkEmailParser.js
@@ -0,0 +1,65 @@
+var body = args.body ? args.body : incidents[0].details;
+var subject = args.subject ? args.subject : incidents[0].name;
+
+function parseBody() {
+ var lines = body.split(/\r*\n/);
+ var header="";
+ var content = "";
+ for (var i =0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.toLowerCase().indexOf("view results") >=0&& lines.length > i+3) {
+ header = lines[i+1];
+ content = lines[i+3];
+ break;
+ }
+ }
+ return {header: header,content: content};
+}
+
+function headerIndex(header, column) {
+ lowerHeader = header.toLowerCase();
+ lowerColumn = column.toLowerCase();
+ columns = lowerHeader.split(/\s+/);
+ for (var i = 0; i < columns.length; i++){
+ if (columns[i]===lowerColumn) {
+ var start = lowerHeader.indexOf(lowerColumn);
+ var end = -1;
+ if (columns.length > i+1 && columns[i + 1]!=="") {
+ end = lowerHeader.indexOf(columns[i + 1], start);
+ }
+ return {start:start,end:end};
+ }
+ }
+ return {start:-1,end:-1};
+}
+
+function getContent(content,start,end) {
+ lastIndex = end ;
+ if (lastIndex===-1) {
+ lastIndex = content.length
+ }
+ if (start === -1) {
+ return "";
+ }
+ return content.substring(start,lastIndex).trim();
+}
+
+function getField(field) {
+ mail = parseBody();
+ dataBounderies = headerIndex(mail.header,field);
+ return getContent(mail.content,dataBounderies.start,dataBounderies.end);
+}
+
+var type = getField('sourcetype');
+var details = getField('_raw');
+var severity = getField('level');
+var systems = getField('host');
+var incName = subject;
+
+return setIncident({
+ type: type,
+ details: details,
+ severity: severity,
+ incName: incName,
+ systems: systems
+ });
\ No newline at end of file
diff --git a/Scripts/SplunkSearchJson.py b/Scripts/SplunkSearchJson.py
new file mode 100644
index 000000000000..03c11823049a
--- /dev/null
+++ b/Scripts/SplunkSearchJson.py
@@ -0,0 +1,31 @@
+rows = 30
+if 'rows' in demisto.args():
+ rows = demisto.args()['rows']
+
+query = demisto.args()['query']
+if '|' not in query:
+ query = query + ' | head ' + str(rows)
+
+res = demisto.executeCommand('search', {'query': query})
+md = {
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['markdown'],
+ 'Contents': '# Splunk search result'
+}
+
+for result in res:
+ if result['Brand'] == 'splunk':
+ for r in res[0]['Contents']:
+ data = demisto.get(r, 'result._raw')
+ if data:
+ md['Contents'] += '\n|Time|Host|Source|\n|-|-|-|'
+ md['Contents'] += '\n|' + str(demisto.get(r, 'result._time')) + '|' + str(demisto.get(r, 'result.host')) + '|' + str(demisto.get(r, 'result.source')) + '|\n'
+ try:
+ j = json.loads(data)
+ for f in j:
+ md['Contents'] += '\n- ' + f + ': ' + str(j[f])
+ except:
+ md['Contents'] += '\n- Raw data: ' + str(data)
+ md['Contents'] += '\n'
+
+demisto.results(md)
diff --git a/Scripts/TextFromHTML.py b/Scripts/TextFromHTML.py
new file mode 100644
index 000000000000..8c6acd9f99df
--- /dev/null
+++ b/Scripts/TextFromHTML.py
@@ -0,0 +1,10 @@
+import re
+body = re.search(r'', demisto.args()['html'], re.M + re.S + re.I)
+if body and body.group(0):
+ data = re.sub(r'<.*?>', '', body.group(0))
+ entities = {'quot': '"', 'amp': '&', 'apos': "'", 'lt': '<', 'gt': '>', 'nbsp': ' ', 'copy': '(C)', 'reg': '(R)', 'tilde': '~', 'ldquo': '"', 'rdquo': '"', 'hellip': '...'}
+ for e in entities:
+ data = data.replace('&' + e + ';', entities[e])
+ demisto.results(data)
+else:
+ demisto.results('Could not extract text')
diff --git a/Scripts/URLExtract.py b/Scripts/URLExtract.py
new file mode 100644
index 000000000000..c3fbdbc2fece
--- /dev/null
+++ b/Scripts/URLExtract.py
@@ -0,0 +1,30 @@
+import re
+
+strURLRegex = r'(?i)(?:(?:https?|ftp):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])'
+
+res = []
+urls = []
+filtered = ['http://schemas.microsoft.com/office/2004/12/omml', 'http://www.w3.org/TR/REC-html40']
+
+data = demisto.args()['text']
+
+for m in re.finditer(strURLRegex, data, re.I):
+ u = m.group(0)
+ if u in filtered:
+ continue
+ if u in urls:
+ continue
+ if 'mailto:' in u:
+ continue
+ urls.append(u)
+
+res.append('URLs found:\n' + '\n'.join(urls))
+currUrls = demisto.get(demisto.context(), 'urls')
+if currUrls and isinstance(currUrls, list):
+ for u in urls:
+ if u not in currUrls:
+ currUrls.append(u)
+else:
+ currUrls = urls
+demisto.setContext('urls', currUrls)
+demisto.results(res)
diff --git a/Scripts/analyzememimage.js b/Scripts/analyzememimage.js
new file mode 100644
index 000000000000..49e97e874ee3
--- /dev/null
+++ b/Scripts/analyzememimage.js
@@ -0,0 +1,17 @@
+var imginfo = executeCommand('Vol', {file:args.memdump, system: args.system, cmd:'imageinfo'});
+if (imginfo) {
+ imginfo[0].Contents = '================ Image Info for ' + args.memdump + '==============\n' + imginfo[0].Contents;
+}
+var pstree = executeCommand('Vol', {file:args.memdump, system: args.system, cmd:'pstree'});
+if (pstree) {
+ pstree[0].Contents = '================ Process Tree for ' + args.memdump + '==============\n' + pstree[0].Contents;
+}
+var connscan = executeCommand('Vol', {file:args.memdump, system: args.system, cmd:'connscan'});
+if (connscan) {
+ connscan[0].Contents = '================ Past Network Connections for ' + args.memdump + '==============\n' + connscan[0].Contents;
+}
+var res = [];
+res.push(imginfo);
+res.push(pstree);
+res.push(connscan);
+return res;
diff --git a/Scripts/analyzeosx.js b/Scripts/analyzeosx.js
new file mode 100644
index 000000000000..7e0fe1f65264
--- /dev/null
+++ b/Scripts/analyzeosx.js
@@ -0,0 +1,40 @@
+var osx_report = executeCommand('Osxcollector', {section: args.section, system: args.system, timeout: args.timeout});
+var res = [];
+var maxchecks = 10;
+if (args.maxchecks) {
+ maxchecks = args.maxchecks;
+}
+for (var i=0; i 0) {
+ return res;
+}
+return 'No infected files or malicious urls detected on OSX machine: '+args.system;
diff --git a/Scripts/binaryreputation.py b/Scripts/binaryreputation.py
new file mode 100644
index 000000000000..5298991ee2b2
--- /dev/null
+++ b/Scripts/binaryreputation.py
@@ -0,0 +1,49 @@
+# Look for various hashes in the incident
+# Inspect labels and attachments for hashes and check the hash reputation
+import re
+
+strHashRegex = r'\b[a-fA-F\d]{32}\b'
+
+# Iterate on all the labels and find hashes
+hashRe = re.compile(strHashRegex, re.I)
+hashes = set()
+for t in demisto.incidents()[0]['labels']:
+ for h in hashRe.finditer(t['value']):
+ hashes.add(h.group(0))
+
+# Find hashes in the details
+for h in hashRe.finditer(demisto.incidents()[0]['details']):
+ hashes.add(h.group(0))
+
+
+fileNames = []
+if 'fileNames' in demisto.args():
+ fileNames = demisto.args()['fileNames'].split(',')
+
+# Also get hashes of files in war room entries
+entries = demisto.executeCommand('getEntries', {})
+for entry in entries:
+ if entry['File'] and demisto.get(entry, 'FileMetadata.md5') and (len(fileNames) == 0 or entry['File'] in fileNames):
+ hashes.add(demisto.get(entry, 'FileMetadata.md5'))
+
+badHashes = []
+res = []
+for h in hashes:
+ rep = demisto.executeCommand('file', {'file': h})
+ for r in rep:
+ if positiveFile(r):
+ badHashes.append(h)
+ res.append(shortFile(r))
+
+if len(res) > 0:
+ res.extend(['yes', 'Found malicious hashes!'])
+ currHashes = demisto.get(demisto.context(), 'bad_hashes')
+ if currHashes and isinstance(currHashes, list):
+ currHashes += [h for h in badHashes if h not in currHashes]
+ else:
+ currHashes = badHashes
+ demisto.setContext('bad_hashes', currHashes)
+else:
+ res.extend(['No suspicious files found', 'no'])
+
+demisto.results(res)
diff --git a/Scripts/binarysearch.py b/Scripts/binarysearch.py
new file mode 100644
index 000000000000..a73b9d8fc463
--- /dev/null
+++ b/Scripts/binarysearch.py
@@ -0,0 +1,40 @@
+# Look for various hashes in the incident
+# Inspect labels and attachments for hashes and check if we have this process running anywhere using Carbon Black
+import re
+
+# Iterate on all the labels and see which one has hashes
+hashRe = re.compile(r'\b[a-fA-F\d]{32}\b', re.I)
+hashes = set()
+for t in demisto.incidents()[0]['labels']:
+ for h in hashRe.finditer(t['value']):
+ hashes.add(h.group(0))
+
+# Find hashes in the details
+for h in hashRe.finditer(demisto.incidents()[0]['details']):
+ hashes.add(h.group(0))
+
+# Get also hashes from files in entries
+entries = demisto.executeCommand('getEntries', {})
+for entry in entries:
+ if entry['File'] and demisto.get(entry, 'FileMetadata.md5'):
+ hashes.add(demisto.get(entry, 'FileMetadata.md5'))
+
+res = []
+
+for h in hashes:
+ processes = demisto.executeCommand('process', {'query': 'md5:' + h})
+ if len(processes) > 0 and processes[0]['Type'] == entryTypes['note'] and processes[0]['ContentsFormat'] == formats['json']:
+ process = demisto.get(processes[0], 'Contents.results')
+ if process:
+ res.append({
+ '1. MD5': h,
+ '2. Name': demisto.get(process, 'process_name'),
+ '3. Hostname': demisto.get(process, 'hostname'),
+ '4. Path': demisto.get(process, 'path'),
+ '5. Updated': demisto.get(process, 'last_update'),
+ '6. Terminated': demisto.get(process, 'terminated')})
+
+if len(res) > 0:
+ demisto.results(['yes', {'Type': entryTypes['note'], 'ContentsFormat': formats['table'], 'Contents': res}])
+else:
+ demisto.results(['no', {'Type': entryTypes['note'], 'ContentsFormat': formats['text'], 'Contents': 'No process hashes found'}])
diff --git a/Scripts/cbevents.js b/Scripts/cbevents.js
new file mode 100644
index 000000000000..1bb506b23463
--- /dev/null
+++ b/Scripts/cbevents.js
@@ -0,0 +1,48 @@
+//Searches for all processes with a given query - and lists events associated with processes.
+//Args - process search query
+
+var output = [];
+
+function formatEvents(header, pname, pid, segid, arr) {
+ var entry = [];
+ if (typeof (arr) !== 'undefined') {
+
+ for (var i = 0; i < arr.length; i++) {
+ var items = arr[i].split("|");
+ var row = {};
+ row["name"]=pname;
+ row["pid"]=pid;
+ row["segid"]=segid;
+ for (var k = 0; k < items.length; k++){
+ row[header[k]]=items[k];
+ }
+ entry.push(row);
+ }
+ output.push({ContentsFormat: formats.table, Type: entryTypes.note, Contents: entry});
+ }
+}
+
+var res = executeCommand("cb-process", {session: args.sessionid, query: args.query});
+if (res.length > 0) {
+ var list = res[0].Contents.results;
+ if (typeof (list) !== 'undefined') {
+ if (list.length === 0) {
+ output.push({ContentsFormat: formats.text, Type: entryTypes.note, Contents: "No results"});
+ }
+ for (var i = 0; i < list.length; i++) {
+ var process = list[i];
+ var res1 = executeCommand("process-events", {pid: process.id.toString(), segid: process.segment_id.toString()});
+ var pevent = res1[0].Contents;
+ formatEvents(["time","md5","path"],pevent.process.process_name,process.id, process.segment_id, pevent.process.modload_complete);
+ formatEvents(["type","time","reg path"],pevent.process.process_name,process.id, process.segment_id, pevent.process.regmod_complete);
+ formatEvents(["type","time","file path","last md5","file type","tamper"],pevent.process.process_name,process.id, process.segment_id, pevent.process.filemod_complete);
+ formatEvents(["time","remote ip","remote port","proto","dns name","outbound"],pevent.process.process_name,process.id, process.segment_id, pevent.process.netconn_complete);
+ formatEvents(["time","uid","md5","path","pid","started","tamper"],pevent.process.process_name,process.id, process.segment_id, pevent.process.childproc_complete);
+ formatEvents(["type","time","uid","md5","path","sub-type","access","tamper"],pevent.process.process_name,process.id, process.segment_id, pevent.process.crossproc_complete);
+ }
+ } else {
+ output.push(res);
+ }
+}
+
+return output;
diff --git a/Scripts/cbliveprocesslist.js b/Scripts/cbliveprocesslist.js
new file mode 100644
index 000000000000..d976af2f86b2
--- /dev/null
+++ b/Scripts/cbliveprocesslist.js
@@ -0,0 +1,9 @@
+//Execute 'process list' command on a sensor
+//Args - sensor ID, session ID
+var output = [];
+
+var cmd = executeCommand("cb-command-create",{session: args.sessionid, wait: "true", name: "process list"});
+var id = cmd[0].Contents.id;
+var result = executeCommand("cb-command-info",{session: args.sessionid, command: id.toString()});
+output.push({ContentsFormat: formats.table, Type: entryTypes.note, Contents: result[0].Contents.processes});
+return output;
\ No newline at end of file
diff --git a/Scripts/cbsearch.js b/Scripts/cbsearch.js
new file mode 100644
index 000000000000..b270b5474f15
--- /dev/null
+++ b/Scripts/cbsearch.js
@@ -0,0 +1,53 @@
+//Script to query carbon black servers:
+//type must be either 'process' or 'binary'
+//can provide query & number of rows to return
+
+function formatresults(rows, columns) {
+ var table = [];
+ for (var index in rows) {
+ var row = rows[index];
+ if (typeof (row) === "object") {
+ tablerow = {};
+ for (var name in row) {
+ var value = row[name];
+ if (typeof (value) === "object") {
+ value = JSON.stringify(value);
+ }
+ var columnIndex = columns.indexOf(name);
+ var columnName;
+ if (columnIndex !== -1) {
+ columnName = columnIndex.toString() + ". " + name;
+ } else {
+ columnName = name;
+ }
+ tablerow[columnName] = value;
+ }
+ table.push(tablerow);
+ }
+ }
+ return table;
+}
+
+var output = [];
+var searchType = args.type ? args.type : 'process';
+if ((searchType !== 'process') && (searchType !== 'binary')) {
+ output.push({ContentsFormat: formats.text, Type: entryTypes.error, Contents: "Error! type must be 'process' or 'binary"});
+} else {
+ var res = [];
+ var columns = [];
+ if (searchType === 'process') {
+ columns = ["hostname", "username", "process_pid", "path", "process_md5", "start", "os_type", "parent_pid", "sensor_id"];
+ } else {
+ columns = ["md5", "observed_filename", "original_filename", "is_executable_image", "endpoint", "signed", "os_type"];
+ }
+ myArgs = {
+ start: args.start ? args.start : '0',
+ rows: args.rows ? args.rows : '20'
+ };
+ if(args.query)
+ myArgs.query = args.query;
+ res = executeCommand('cb-' + searchType, myArgs);
+ var table = formatresults(res[0].Contents.results, columns);
+ output.push({ContentsFormat: formats.table, Type: entryTypes.note, Contents: table});
+}
+return output;
diff --git a/Scripts/cbsensors.js b/Scripts/cbsensors.js
new file mode 100644
index 000000000000..0f5fa84dfeeb
--- /dev/null
+++ b/Scripts/cbsensors.js
@@ -0,0 +1,5 @@
+//Show a list of carbon black sensors.
+var sensors = executeCommand("cb-list-sensors",{})
+var output = sensors[0];
+output.ContentsFormat = formats.table;
+return output;
\ No newline at end of file
diff --git a/Scripts/cbsessions.js b/Scripts/cbsessions.js
new file mode 100644
index 000000000000..66eb093eb0ad
--- /dev/null
+++ b/Scripts/cbsessions.js
@@ -0,0 +1,26 @@
+//Show a list of carbon black sessions.
+//Argument: Verbose = true, to show all properties.
+var output = [];
+var entries = executeCommand("cb-list-sessions", {});
+var array = entries[0].Contents;
+var outTable = [];
+for (var index in array) {
+ var item = array[index];
+ var outItem = {};
+ if (args.verbose === "true") {
+ for (var name in item) {
+ var value = item[name];
+ if (typeof (value) !== "object") {
+ outItem[name] = value;
+ }
+ }
+ } else {
+ outItem["status"] = item["status"];
+ outItem["id"] = item["id"];
+ outItem["sensor"]=item["sensor_id"];
+ }
+ outTable.push(outItem);
+}
+output.push({ContentsFormat: formats.table, Type: entryTypes.note, Contents: outTable});
+
+return output;
\ No newline at end of file
diff --git a/Scripts/checkfiles.js b/Scripts/checkfiles.js
new file mode 100644
index 000000000000..407c9db5b5eb
--- /dev/null
+++ b/Scripts/checkfiles.js
@@ -0,0 +1,23 @@
+var fileNames = [];
+if (args.fileNames) {
+ fileNames = args.fileNames.split(',');
+}
+var entries = executeCommand('getEntries', {});
+var res = [];
+for (var i=0; i= 0)) {
+ var rep = executeCommand('file', {file: entries[i].FileMetadata.MD5});
+ if (rep && Array.isArray(rep)) {
+ for (var r = 0; r < rep.length; r++) {
+ if (positiveFile(rep[r])) {
+ res.push(shortFile(rep[r]));
+ }
+ }
+ }
+ }
+}
+if (res.length > 0) {
+ res.push('yes');
+ return res;
+}
+return ['No suspicious files found', 'no'];
diff --git a/Scripts/checkfileswildfire.py b/Scripts/checkfileswildfire.py
new file mode 100644
index 000000000000..d701a3d1708f
--- /dev/null
+++ b/Scripts/checkfileswildfire.py
@@ -0,0 +1,37 @@
+import time
+
+fileNames = []
+if 'fileNames' in demisto.args():
+ fileNames = demisto.args()['fileNames'].split(',')
+
+res = []
+uploaded = []
+entries = demisto.executeCommand('getEntries', {})
+for entry in entries:
+ if entry['File'] and demisto.get(entry, 'FileMetadata.md5') and (len(fileNames) == 0 or entry['File'] in fileNames):
+ demisto.log('Checking - ' + demisto.get(entry, 'FileMetadata.md5'))
+ rep = demisto.executeCommand('wildfire-report', {'md5': demisto.get(entry, 'FileMetadata.md5')})
+ for r in rep:
+ if positiveFile(r):
+ res.append(shortFile(r))
+ elif r['Type'] == entryTypes['error'] and '404' in r['Contents']:
+ upReply = demisto.executeCommand('wildfire-upload', {'upload': entry['ID']})
+ if upReply[0]['Type'] != entryTypes['error']:
+ uploaded.append(demisto.get(entry, 'FileMetadata.md5'))
+
+# Wait for the uploaded files to be processed - 15 min
+if len(uploaded) > 0:
+ time.sleep(15 * 60)
+
+for u in uploaded:
+ rep = demisto.executeCommand('wildfire-report', {'md5': u})
+ notFound = False
+ for r in rep:
+ if positiveFile(r):
+ res.append(shortFile(r))
+
+if len(res) > 0:
+ res.append('yes')
+else:
+ res.extend(['No suspicious files found', 'no'])
+demisto.results(res)
diff --git a/Scripts/checkips.py b/Scripts/checkips.py
new file mode 100644
index 000000000000..46fe387adce0
--- /dev/null
+++ b/Scripts/checkips.py
@@ -0,0 +1,54 @@
+import re
+import socket
+
+strIpRegex = r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b'
+
+def is_valid_ipv4_address(address):
+ try:
+ socket.inet_pton(socket.AF_INET, address)
+ except AttributeError: # no inet_pton here, sorry
+ try:
+ socket.inet_aton(address)
+ except socket.error:
+ return False
+ return address.count('.') == 3
+ except socket.error: # not a valid address
+ return False
+ return True
+
+res = []
+ips = []
+badIps = []
+
+data = demisto.args()['data'] if demisto.get(demisto.args(), 'data') else demisto.incidents()[0]['details']
+
+if isinstance(data, list):
+ ips = data[:]
+else:
+ for m in re.finditer(strIpRegex, data, re.I):
+ ip = m.group(0)
+ if ip in ips:
+ continue
+ if not is_valid_ipv4_address(ip):
+ continue
+ ips.append(ip)
+for ip in ips:
+ rep = demisto.executeCommand('ip', {'ip': ip})
+ for r in rep:
+ if positiveIp(r):
+ badIps.append(ip)
+ res.append(shortIp(r))
+
+if len(res) > 0:
+ res.extend(['yes', 'Found malicious IPs!'])
+ currIps = demisto.get(demisto.context(), 'bad_ips')
+ if currIps and isinstance(currIps, list):
+ currIps += [i for i in badIps if i not in currIps]
+ else:
+ currIps = badIps
+ demisto.setContext('bad_ips', currIps)
+else:
+ res.append('no')
+ res.append('Only clean IPs found: \n' + '\n'.join(ips))
+
+demisto.results(res)
diff --git a/Scripts/checksender.js b/Scripts/checksender.js
new file mode 100644
index 000000000000..d173a767d480
--- /dev/null
+++ b/Scripts/checksender.js
@@ -0,0 +1,7 @@
+var fromRE = /From:.*href="mailto:(.*)"/ig;
+var from = fromRE.exec(incidents[0].details);
+var res = [];
+var rep = executeCommand('pipl-search', {email: from[1]});
+Array.prototype.push.apply(res, rep);
+res.push({Contents: 'no', Type: entryTypes.note, ContentsFormat: formats.text});
+return res;
diff --git a/Scripts/checksender.py b/Scripts/checksender.py
new file mode 100644
index 000000000000..57d84439cf55
--- /dev/null
+++ b/Scripts/checksender.py
@@ -0,0 +1,13 @@
+import re
+
+email = ''
+if 'email' in demisto.args():
+ email = demisto.args()['email']
+else:
+ sender = re.search('From:.*href="mailto:(.*)"', demisto.incidents()[0]['details'], re.I)
+ if sender:
+ email = sender.group(1)
+if email:
+ demisto.results(demisto.executeCommand('pipl-search', {'email': email}))
+else:
+ demisto.results('Could not find the sender data')
diff --git a/Scripts/checksenderdomaindistance.py b/Scripts/checksenderdomaindistance.py
new file mode 100644
index 000000000000..760bd0316223
--- /dev/null
+++ b/Scripts/checksenderdomaindistance.py
@@ -0,0 +1,44 @@
+import re
+
+domain = 'demisto.com'
+if 'domain' in demisto.args():
+ domain = demisto.args()['domain']
+
+def levenshtein(s1, s2):
+ l1 = len(s1)
+ l2 = len(s2)
+
+ matrix = [range(l1 + 1)] * (l2 + 1)
+ for zz in range(l2 + 1):
+ matrix[zz] = range(zz,zz + l1 + 1)
+ for zz in range(0,l2):
+ for sz in range(0,l1):
+ if s1[sz] == s2[zz]:
+ matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz])
+ else:
+ matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz] + 1)
+ return matrix[l2][l1]
+
+res = []
+found = False
+sender = demisto.get(demisto.args(), 'sender')
+if not sender:
+ sender = re.search(r".*From\w*:.*\b([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})\b", demisto.incidents()[0]['details'], re.I)
+if sender:
+ parts = sender.group(1).split('@')
+ if len(parts) == 2:
+ distance = levenshtein(domain, parts[1])
+ if distance > 0 and distance < 3:
+ res.append({'Type': entryTypes['note'], 'ContentsFormat': formats['text'], 'Contents': 'Domain ' + parts[1] + ' is suspicioiusly close to ' + domain})
+ found = True
+ else:
+ res.append({'Type': entryTypes['error'], 'ContentsFormat': formats['text'], 'Contents': 'Unable to extract domain from sender - ' + sender.group(1)})
+else:
+ res.append({'Type': entryTypes['error'], 'ContentsFormat': formats['text'], 'Contents': 'Unable to find sender in email'})
+
+if found:
+ res.append('yes')
+else:
+ res.append('no')
+
+demisto.results(res)
diff --git a/Scripts/checkurls.py b/Scripts/checkurls.py
new file mode 100644
index 000000000000..211f95d25f18
--- /dev/null
+++ b/Scripts/checkurls.py
@@ -0,0 +1,44 @@
+import re
+
+strURLRegex = r'(?i)(?:(?:https?|ftp):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])'
+
+res = []
+urls = []
+badUrls = []
+filtered = ['http://schemas.microsoft.com/office/2004/12/omml', 'http://www.w3.org/TR/REC-html40']
+
+data = demisto.args()['data'] if demisto.get(demisto.args(), 'data') else demisto.incidents()[0]['details']
+
+if isinstance(data, list):
+ urls = data[:]
+else:
+ for m in re.finditer(strURLRegex, data, re.I):
+ u = m.group(0)
+ if u in filtered:
+ continue
+ if u in urls:
+ continue
+ if 'mailto:' in u:
+ continue
+ urls.append(u)
+
+for u in urls:
+ rep = demisto.executeCommand('url', {'url': u})
+ for r in rep:
+ if positiveUrl(r):
+ badUrls.append(u)
+ res.append(shortUrl(r))
+
+if len(res) > 0:
+ res.extend(['yes', 'Found malicious URLs!'])
+ currUrls = demisto.get(demisto.context(), 'bad_urls')
+ if currUrls and isinstance(currUrls, list):
+ currUrls += [u for u in badUrls if u not in currUrls]
+ else:
+ currUrls = badUrls
+ demisto.setContext('bad_urls', currUrls)
+else:
+ res.append('no')
+ res.append('Only clean URLs found: \n' + '\n'.join(urls))
+
+demisto.results(res)
diff --git a/Scripts/common-d2.js b/Scripts/common-d2.js
new file mode 100644
index 000000000000..a04474810d2e
--- /dev/null
+++ b/Scripts/common-d2.js
@@ -0,0 +1,36 @@
+// Common functions script
+// =======================
+// This script will be appended to each d2 agent script before being executed.
+// Place here all common functions you'd like to share between d2 agent scripts.
+
+/**
+ * Checks if the given string represents a valid IPv4 address
+ * @param {String} ip - the string to check
+ * @returns {Boolean} true if valid IPv4 address
+ */
+function isIp(ip) {
+ var d = ip.split('.'), i = d.length;
+ if (i !== 4) {
+ return false;
+ }
+ var ok = true;
+ while (i-- && ok) {
+ ok = d[i].length !== 0 && !isNaN(parseInt(d[i])) && d[i] > -1 && d[i] < 256;
+ }
+ return ok;
+}
+
+/**
+ * Execute a command and pack the standard output or throw the error
+ * @param {String} command - the command to execute on the OS
+ * @returns {Void}
+ */
+function packOutput(command){
+ var output = execute(command);
+ if (output.Success) {
+ pack(output.Stdout);
+ } else {
+ throw 'Errcode:' + output.Error + '\nStderr:' + output.Stderr + '\nStdout: ' + output.Stdout;
+
+ }
+}
diff --git a/Scripts/common.js b/Scripts/common.js
new file mode 100644
index 000000000000..296d2c009dfe
--- /dev/null
+++ b/Scripts/common.js
@@ -0,0 +1,373 @@
+// Common functions script
+// =======================
+// This script will be appended to each server script before being executed.
+// Place here all common functions you'd like to share between server scripts.
+
+/**
+ * Checks if the given string represents a valid IPv4 address
+ * @param {String} ip - the string to check
+ * @returns {Boolean} true if valid IPv4 address
+ */
+function isIp(ip) {
+ var d = ip.split('.'), i = d.length;
+ if (i !== 4) {
+ return false;
+ }
+ var ok = true;
+ while (i-- && ok) {
+ ok = d[i].length !== 0 && !isNaN(parseInt(d[i])) && d[i] > -1 && d[i] < 256;
+ }
+ return ok;
+}
+
+var entryTypes = {note: 1, downloadAgent: 2, file: 3, error: 4, pinned: 5, userManagement: 6, image: 7, plagroundError: 8};
+var formats = {table: 'table', json: 'json', text: 'text', dbotResponse: 'dbotCommandResponse', markdown: 'markdown'};
+var brands = {xfe: 'xfe', vt: 'virustotal', cy: 'cylance', wf: 'wildfire', cs: 'crowdstrike-intel'};
+var providers = {xfe: 'IBM X-Force Exchange', vt: 'VirusTotal', cy: 'Cylance', wf: 'WildFire', cs: 'CrowdStrike'};
+
+/**
+ * Returns the name of the file as stored in our investigation artifacts on disk.
+ * This should be used when sending files to d2 scripts as you can see in StaticAnalyze.
+ * @param {String} entryId - the entry ID containing the file
+ * @returns {String} the name of the file in our artifact repository
+ */
+function fileNameFromEntry(entryId) {
+ var parts = entryId.split('@');
+ if (parts.length !== 2) {
+ return null;
+ }
+ var res = executeCommand('getEntry', {id: entryId});
+ if (res && Array.isArray(res) && res.length === 1) {
+ return parts[1] + '_' + res[0].FileID;
+ }
+ return null;
+}
+
+/**
+ * Closes the current investigation
+ * @param {Object} args - arguments for the close (what happened, damange, etc.)
+ * @returns {Array} an array with error entry if there is an error or empty array
+ */
+function closeInvestigation(args) {
+ return executeCommand('closeInvestigation', args);
+}
+
+// Thresholds for the various reputation services to mark something as positive
+var thresholds = {xfeScore: 3, vtPositives: 10, vtPositiveUrlsForIP: 10};
+
+/**
+ * Checks if the given entry from a URL reputation query is positive (known bad)
+ * @param {Object} entry - reputation entry
+ * @returns {Boolean} true if positive, false otherwise
+ */
+function positiveUrl(entry) {
+ if (entry.Type !== entryTypes.error && entry.ContentsFormat === formats.json) {
+ var c = entry.Contents;
+ if (entry.Brand === brands.xfe && c && c.url.result.score && c.url.result.score > thresholds.xfeScore) {
+ return true;
+ } else if (entry.Brand === brands.vt && c && c.positives && c.positives > thresholds.vtPositives) {
+ return true;
+ } else if (entry.Brand === brands.cs && c && c.length && c[0].indicator && (c[0].malicious_confidence === 'high' || c[0].malicious_confidence === 'medium')) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Checks if the given entry from a file reputation query is positive (known bad)
+ * @param {Object} entry - reputation entry
+ * @returns {Boolean} true if positive, false otherwise
+ */
+function positiveFile(entry) {
+ if (entry.Type !== entryTypes.error && entry.ContentsFormat === formats.json) {
+ var c = entry.Contents;
+ if (entry.Brand === brands.xfe && c && c.malware.family) {
+ return true;
+ } else if (entry.Brand === brands.vt && c && c.positives && c.positives > thresholds.vtPositives) {
+ return true;
+ } else if (entry.Brand === brands.wf && c && c.wildfire && c.wildfire.file_info) {
+ return c.wildfire.file_info.malware === 'yes';
+ } else if (entry.Brand === brands.cy && c) {
+ var k = Object.keys(c);
+ if (k && k.length > 0) {
+ var v = c[k[0]];
+ if (v && v.generalscore) {
+ return v.generalscore < -0.5;
+ }
+ }
+ } else if (entry.Brand === brands.cs && c && c.length && c[0].indicator && (c[0].malicious_confidence === 'high' || c[0].malicious_confidence === 'medium')) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Checks if the given entry from an IP reputation query is positive (known bad)
+ * @param {Object} entry - reputation entry
+ * @returns {Boolean} true if positive, false otherwise
+ */
+function positiveIP(entry) {
+ if (entry.Type !== entryTypes.error && entry.ContentsFormat === formats.json) {
+ var c = entry.Contents;
+ if (entry.Brand === brands.xfe && c && c.reputation.score && c.reputation.score > thresholds.xfeScore) {
+ return true;
+ } else if (entry.Brand === brands.vt && c && c.detected_urls) {
+ var positives = 0;
+ for (var i = 0; i < c.detected_urls.length; i++) {
+ if (c.detected_urls[i].positives > thresholds.vtPositives) {
+ positives++;
+ }
+ }
+ return positives > thresholds.vtPositiveUrlsForIP;
+ } else if (entry.Brand === brands.cs && c && c.length && c[0].indicator && (c[0].malicious_confidence === 'high' || c[0].malicious_confidence === 'medium')) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Display CrowdStrike Intel results in Markdown
+ * @param {Object} entry - reputation entry
+ * @returns {Object} the markdown entry
+ */
+function shortCrowdStrike(entry) {
+ if (entry.Type !== entryTypes.error && entry.ContentsFormat === formats.json) {
+ var c = entry.Contents;
+ if (entry.Brand === brands.cs && c && c.length && c[0].indicator) {
+ var csRes = '## CrowdStrike Falcon Intelligence';
+ csRes += '\n\n### Indicator - ' + c[0].indicator;
+ if (c[0].labels && c[0].labels.length) {
+ csRes += '\n### Labels';
+ csRes += '\nName|Created|Last Valid';
+ csRes += '\n----|-------|----------';
+ for (var l = 0; l < c[0].labels.length; l++) {
+ csRes += '\n' + c[0].labels[l].name + '|' + new Date(c[0].labels[l].created_on * 1000) + '|' + new Date(c[0].labels[l].last_valid_on * 1000);
+ }
+ }
+ if (c[0].relations && c[0].relations.length) {
+ csRes += '\n### Relations';
+ csRes += '\nIndicator|Type|Created|Last Valid';
+ csRes += '\n---------|----|-------|----------';
+ for (var r = 0; r < c[0].relations.length; r++) {
+ csRes += '\n' + c[0].relations[r].indicator + '|' + c[0].relations[r].type + '|' + new Date(c[0].relations[r].created_date * 1000) + '|' + new Date(c[0].relations[r].last_valid_date * 1000);
+ }
+ }
+ return {ContentsFormat: formats.markdown, Type: entryTypes.note, Contents: csRes};
+ }
+ }
+ return entry;
+}
+
+/**
+ * Formats a URL reputation entry into a short table
+ * @param {Object} entry - reputation entry
+ * @returns {Object} the table entry
+ */
+function shortUrl(entry) {
+ if (entry.Type !== entryTypes.error && entry.ContentsFormat === formats.json) {
+ var c = entry.Contents;
+ if (entry.Brand === brands.xfe && c) {
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ Country: c.country, MalwareCount: c.malware.count, A: c.resolution.A ? c.resolution.A.join(',') : '',
+ AAAA: c.resolution.AAAA ? c.resolution.AAAA.join(',') : '', Score: c.url.result.score,
+ Categories: c.url.result.cats ? Object.keys(c.url.result.cats).join(',') : '',
+ URL: c.url.result.url, Provider: providers.xfe, ProviderLink: 'https://exchange.xforce.ibmcloud.com/url/' + c.url.result.url
+ }};
+ } else if (entry.Brand === brands.vt && c) {
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ ScanDate: c.scan_date, Positives: c.positives, Total: c.total, URL: c.url, Provider: providers.vt, ProviderLink: c.permalink
+ }};
+ } else if (entry.Brand === brands.cs && c && c.length && c[0].indicator) {
+ return shortCrowdStrike(entry);
+ }
+ }
+ return entry;
+}
+
+/**
+ * Formats a file reputation entry into a short table
+ * @param {Object} entry - reputation entry
+ * @returns {Object} the table entry
+ */
+function shortFile(entry) {
+ if (entry.Type !== entryTypes.error && entry.ContentsFormat === formats.json) {
+ var c = entry.Contents;
+ if (entry.Brand === brands.xfe && entry.Contents) {
+ var cm = c.malware;
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ Family: cm.family, MIMEType: cm.mimetype, MD5: cm.md5 ? cm.md5.substring(2) : '',
+ CnCServers: cm.origins.CnCServers.count, DownloadServers: cm.origins.downloadServers.count,
+ Emails: cm.origins.emails.count, ExternalFamily: cm.origins.external && cm.origins.external.family ? cm.origins.external.family.join(',') : '',
+ ExternalCoverage: cm.origins.external.detectionCoverage, Provider: providers.xfe,
+ ProviderLink: 'https://exchange.xforce.ibmcloud.com/malware/' + cm.md5.replace(/^(0x)/,"")
+ }};
+ } else if (entry.Brand === brands.vt && entry.Contents) {
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ Resource: c.resource, ScanDate: c.scan_date, Positives: c.positives, Total: c.total, SHA1: c.sha1, SHA256: c.sha256, Provider: providers.vt, ProviderLink: c.permalink
+ }};
+ } else if (entry.Brand === brands.cy && entry.Contents) {
+ var k = Object.keys(entry.Contents);
+ if (k && k.length > 0) {
+ var v = entry.Contents[k[0]];
+ if (v) {
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ Status: v.status, Code: v.statuscode, Score: v.generalscore, Classifiers: JSON.stringify(v.classifiers), ConfirmCode: v.confirmcode, Error: v.error, Provider: providers.cy
+ }};
+ }
+ }
+ } else if (entry.Brand === brands.wf && entry.Contents && entry.Contents.wildfire && entry.Contents.wildfire.file_info) {
+ var c = entry.Contents.wildfire.file_info;
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ Type: c.filetype, Malware: c.malware, MD5: c.md5, SHA256: c.sha256, Size: c.size, Provider: providers.wf
+ }};
+ } else if (entry.Brand === brands.cs && c && c.length && c[0].indicator) {
+ return shortCrowdStrike(entry);
+ }
+ }
+ return entry;
+}
+
+/**
+ * Formats an ip reputation entry into a short table
+ * @param {Object} entry - reputation entry
+ * @returns {Object} the table entry
+ */
+function shortIP(entry) {
+ if (entry.Type !== entryTypes.error && entry.ContentsFormat === formats.json) {
+ var c = entry.Contents;
+ if (entry.Brand === brands.xfe && entry.Contents) {
+ var cr = c.reputation;
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ IP: cr.ip, Score: cr.score, Geo: cr.geo && cr.geo['country'] ? cr.geo['country'] : '',
+ Categories: cr.cats ? JSON.stringify(cr.cats) : '', Provider: providers.xfe
+ }};
+ } else if (entry.Brand === brands.vt && entry.Contents) {
+ var positives = 0;
+ for (var i = 0; i < entry.Contents.detected_urls.length; i++) {
+ if (entry.Contents.detected_urls[i].positives > thresholds.vtPositives) {
+ positives++;
+ }
+ }
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ DetectedURLs: positives, Provider: providers.vt
+ }};
+ } else if (entry.Brand === brands.cs && c && c.length && c[0].indicator) {
+ return shortCrowdStrike(entry);
+ }
+ }
+ return entry;
+}
+
+/**
+ * Formats a domain reputation entry into a short table
+ * @param {Object} entry - reputation entry
+ * @returns {Object} the table entry
+ */
+function shortDomain(entry) {
+ if (entry.Type !== entryTypes.error && entry.ContentsFormat === formats.json) {
+ if (entry.Brand === brands.vt && entry.Contents) {
+ var c = entry.Contents;
+ var positives = 0;
+ for (var i = 0; i < entry.Contents.detected_urls.length; i++) {
+ if (entry.Contents.detected_urls[i].positives > 20) {
+ positives++;
+ }
+ }
+ return {ContentsFormat: formats.table, Type: entryTypes.note, Contents: {
+ DetectedURLs: positives, Provider: providers.vt
+ }};
+ }
+ }
+ return entry;
+}
+
+/**
+ * Sets severity an incident. The incident must be related to current investigation.
+ * @param {Object} arg - has 2 keys, 'id' - the incident id, 'severity' - the new severity value (Critical, High, Medium etc.).
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function setSeverity(arg) {
+ return executeCommand('setSeverity', arg);
+}
+
+/**
+ * Sets fields of the incident. The incident must be related to current investigation and be the only incident in it.
+ * @param {Object} args - has 5 optional keys: type, severity, details, incName and systems of the incident.
+ * systems should follow this string template: '1.1.1.1,10.10.10.10'
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function setIncident(args) {
+ return executeCommand('setIncident', args);
+}
+
+/**
+ * Create a new incident with the fields specified, only if an incident with the same name does not exist as an active incident.
+ * If an active incident with the same name exists, ignore the request.
+ * @param {Object} args - has 5 optional keys: type, severity, details, incName and system of the incident.
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function createNewIncident(args) {
+ return executeCommand('createNewIncident', args);
+}
+
+/**
+ * Sets playbook according to type.
+ * @param {String} type - the incident type, which the playbook is set accordingly.
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function setPlaybookAccordingToType(type) {
+ return executeCommand('setPlaybookAccordingToType', {type: type});
+}
+
+/**
+ * Sets Owner to an incident. The incident must be related to current investigation.
+ * @param {Object} name - the owner user name.
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function setOwner(name) {
+ return executeCommand('setOwner', { owner: name });
+}
+
+/**
+ * Assigns a playbook task to a user.
+ * @param {Object} arg - has 2 keys, 'id' - the task id, 'assignee' - assignee user name.
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function taskAssign(arg) {
+ return executeCommand('taskAssign', arg);
+}
+
+/**
+ * Sets task due date.
+ * @param {Object} arg - has 2 keys, 'id' - the task id, 'dueDate' - time string in UTC format (To get current time use: 'new Date().toUTCString()').
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function setTaskDueDate(arg) {
+ return executeCommand('setTaskDueDate', arg);
+}
+
+/**
+ * Sets investigation playbook
+ * @param {String} name - the playbook name.
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function setPlaybook(name) {
+ return executeCommand('setPlaybook', { name: name });
+}
+
+/**
+ * Adds task to Workplan
+ * @param {Object} arg - has 5 keys:
+ * 'name' - (mandatory) The new task's name.
+ * 'description' - (optional) The new task's description.
+ * 'script' - (optional) Name of script to be run by task.
+ * 'addBefore' - (optional, refers to task id) Insert new task before specified task (when using this argument do not use afterTask)
+ * 'addAfter' - (optional, refers to task id) Insert new task after specified task (when using this argument do not use beforeTask)
+ * @returns {Array} in case of error will contain the error entry. In Case of success will return an empty array.
+ */
+function addTask(arg) {
+ return executeCommand('addTask', arg);
+}
diff --git a/Scripts/common.py b/Scripts/common.py
new file mode 100644
index 000000000000..3e302c385d5d
--- /dev/null
+++ b/Scripts/common.py
@@ -0,0 +1,185 @@
+# Common functions script
+# =======================
+# This script will be appended to each server script before being executed.
+# Please notice that to add custom common code, add it to the CommonServerUserPython script
+from datetime import datetime, timedelta
+import time
+
+entryTypes = {'note': 1, 'downloadAgent': 2, 'file': 3, 'error': 4, 'pinned': 5, 'userManagement': 6, 'image': 7, 'plagroundError': 8}
+formats = {'table': 'table', 'json': 'json', 'text': 'text', 'dbotResponse': 'dbotCommandResponse', 'markdown': 'markdown'}
+brands = {'xfe': 'xfe', 'vt': 'virustotal', 'wf': 'wildfire', 'cy': 'cylance', 'cs': 'crowdstrike-intel'}
+providers = {'xfe': 'IBM X-Force Exchange', 'vt': 'VirusTotal', 'wf': 'Wildfire', 'cy': 'Cylance', 'cs': 'CrowdStrike'}
+thresholds = {'xfeScore': 4, 'vtPositives': 10, 'vtPositiveUrlsForIP': 30}
+
+# Checks if the given entry from a URL reputation query is positive (known bad)
+def positiveUrl(entry):
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ if entry['Brand'] == brands['xfe']:
+ return demisto.get(entry, 'Contents.url.result.score') > thresholds['xfeScore']
+ if entry['Brand'] == brands['vt']:
+ return demisto.get(entry, 'Contents.positives') > thresholds['vtPositives']
+ if entry['Brand'] == brands['cs'] and demisto.get(entry, 'Contents'):
+ c = demisto.get(entry, 'Contents')[0]
+ return demisto.get(c, 'indicator') and demisto.get(c, 'malicious_confidence') in ['high', 'medium']
+ return False
+
+def positiveFile(entry):
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ if entry['Brand'] == brands['xfe'] and (demisto.get(entry, 'Contents.malware.family') or demisto.gets(entry, 'Contents.malware.origins.external.family')):
+ return True
+ if entry['Brand'] == brands['vt']:
+ return demisto.get(entry, 'Contents.positives') > thresholds['vtPositives']
+ if entry['Brand'] == brands['wf']:
+ return demisto.get(entry, 'Contents.wildfire.file_info.malware') == 'yes'
+ if entry['Brand'] == brands['cy'] and demisto.get(entry, 'Contents'):
+ contents = demisto.get(entry, 'Contents')
+ k = contents.keys()
+ if k and len(k) > 0:
+ v = contents[k[0]]
+ if v and demisto.get(v, 'generalscore'):
+ return v['generalscore'] < -0.5
+ if entry['Brand'] == brands['cs'] and demisto.get(entry, 'Contents'):
+ c = demisto.get(entry, 'Contents')[0]
+ return demisto.get(c, 'indicator') and demisto.get(c, 'malicious_confidence') in ['high', 'medium']
+ return False
+
+def vtCountPositives(entry):
+ positives = 0
+ if demisto.get(entry, 'Contents.detected_urls'):
+ for detected in demisto.get(entry, 'Contents.detected_urls'):
+ if demisto.get(detected, 'positives') > thresholds['vtPositives']:
+ positives += 1
+ return positives
+
+def positiveIp(entry):
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ if entry['Brand'] == brands['xfe']:
+ return demisto.get(entry, 'Contents.reputation.score') > thresholds['xfeScore']
+ if entry['Brand'] == brands['vt'] and demisto.get(entry, 'Contents.detected_urls'):
+ return vtCountPositives(entry) > thresholds['vtPositiveUrlsForIP']
+ if entry['Brand'] == brands['cs'] and demisto.get(entry, 'Contents'):
+ c = demisto.get(entry, 'Contents')[0]
+ return demisto.get(c, 'indicator') and demisto.get(c, 'malicious_confidence') in ['high', 'medium']
+ return False
+
+def formatEpochDate(t):
+ if t:
+ return time.ctime(t)
+ return ''
+
+def shortCrowdStrike(entry):
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ if entry['Brand'] == brands['cs'] and demisto.get(entry, 'Contents'):
+ c = demisto.get(entry, 'Contents')[0]
+ csRes = '## CrowdStrike Falcon Intelligence'
+ csRes += '\n\n### Indicator - ' + demisto.gets(c, 'indicator')
+ labels = demisto.get(c, 'labels')
+ if labels:
+ csRes += '\n### Labels'
+ csRes += '\nName|Created|Last Valid'
+ csRes += '\n----|-------|----------'
+ for label in labels:
+ csRes += '\n' + demisto.gets(label, 'name') + '|' + formatEpochDate(demisto.get(label, 'created_on')) + '|' + formatEpochDate(demisto.get(label, 'last_valid_on'))
+ relations = demisto.get(c, 'relations')
+ if relations:
+ csRes += '\n### Relations'
+ csRes += '\nIndicator|Type|Created|Last Valid'
+ csRes += '\n---------|----|-------|----------'
+ for r in relations:
+ csRes += '\n' + demisto.gets(r, 'indicator') + '|' + demisto.gets(r, 'type') + '|' + formatEpochDate(demisto.get(label, 'created_date')) + '|' + formatEpochDate(demisto.get(label, 'last_valid_date'))
+ return {'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': csRes}
+ return entry
+
+def shortUrl(entry):
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ c = entry['Contents']
+ if entry['Brand'] == brands['xfe']:
+ return {'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': {
+ 'Country': c['country'], 'MalwareCount': demisto.get(c, 'malware.count'),
+ 'A': demisto.gets(c, 'resolution.A'), 'AAAA': demisto.gets(c, 'resolution.AAAA'),
+ 'Score': demisto.get(c, 'url.result.score'), 'Categories': demisto.gets(c, 'url.result.cats'),
+ 'URL': demisto.get(c, 'url.result.url'), 'Provider': providers['xfe'], 'ProviderLink': 'https://exchange.xforce.ibmcloud.com/url/' + demisto.get(c, 'url.result.url')}}
+ if entry['Brand'] == brands['vt']:
+ return {'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': {
+ 'ScanDate': c['scan_date'], 'Positives': c['positives'], 'Total': c['total'],
+ 'URL': c['url'], 'Provider': providers['vt'], 'ProviderLink': c['permalink']}}
+ if entry['Brand'] == brands['cs'] and demisto.get(entry, 'Contents'):
+ return shortCrowdStrike(entry)
+ return {'ContentsFormat': 'text', 'Type': 4, 'Contents': 'Unknown provider for result: ' + entry['Brand']}
+
+def shortFile(entry):
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ c = entry['Contents']
+ if entry['Brand'] == brands['xfe']:
+ cm = c['malware']
+ return {'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': {
+ 'Family': cm['family'], 'MIMEType': cm['mimetype'], 'MD5': cm['md5'][2:] if 'md5' in cm else '',
+ 'CnCServers': demisto.get(cm, 'origins.CncServers.count'), 'DownloadServers': demisto.get(cm, 'origins.downloadServers.count'),
+ 'Emails': demisto.get(cm, 'origins.emails.count'), 'ExternalFamily': demisto.gets(cm, 'origins.external.family'),
+ 'ExternalCoverage': demisto.get(cm, 'origins.external.detectionCoverage'), 'Provider': providers['xfe'],
+ 'ProviderLink': 'https://exchange.xforce.ibmcloud.com/malware/' + cm['md5'].replace('0x', '')}}
+ if entry['Brand'] == brands['vt']:
+ return {'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': {
+ 'Resource': c['resource'], 'ScanDate': c['scan_date'], 'Positives': c['positives'],
+ 'Total': c['total'], 'SHA1': c['sha1'], 'SHA256': c['sha256'], 'Provider': providers['vt'], 'ProviderLink': c['permalink']}}
+ if entry['Brand'] == brands['wf']:
+ c = demisto.get(entry, 'Contents.wildfire.file_info')
+ if c:
+ return {'Contents': {'Type': c['filetype'], 'Malware': c['malware'], 'MD5': c['md5'], 'SHA256': c['sha256'], 'Size': c['size'], 'Provider': providers['wf']},
+ 'ContentsFormat': formats['table'], 'Type': entryTypes['note']}
+ if entry['Brand'] == brands['cy'] and demisto.get(entry, 'Contents'):
+ contents = demisto.get(entry, 'Contents')
+ k = contents.keys()
+ if k and len(k) > 0:
+ v = contents[k[0]]
+ if v and demisto.get(v, 'generalscore'):
+ return {'Contents': {'Status': v['status'], 'Code': v['statuscode'], 'Score': v['generalscore'], 'Classifiers': str(v['classifiers']), 'ConfirmCode': v['confirmcode'], 'Error': v['error'], 'Provider': providers['cy']},
+ 'ContentsFormat': formats['table'], 'Type': entryTypes['note']}
+ if entry['Brand'] == brands['cs'] and demisto.get(entry, 'Contents'):
+ return shortCrowdStrike(entry)
+ return {'ContentsFormat': formats['text'], 'Type': entryTypes['error'], 'Contents': 'Unknown provider for result: ' + entry['Brand']}
+
+def shortIp(entry):
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ c = entry['Contents']
+ if entry['Brand'] == brands['xfe']:
+ cr = c['reputation']
+ return {'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': {
+ 'IP': cr['ip'], 'Score': cr['score'], 'Geo': str(cr['geo']), 'Categories': str(cr['cats']),
+ 'Provider': providers['xfe']}}
+ if entry['Brand'] == brands['vt']:
+ return {'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': {'Positive URLs': vtCountPositives(entry), 'Provider': providers['vt']}}
+ if entry['Brand'] == brands['cs'] and demisto.get(entry, 'Contents'):
+ return shortCrowdStrike(entry)
+ return {'ContentsFormat': formats['text'], 'Type': entryTypes['error'], 'Contents': 'Unknown provider for result: ' + entry['Brand']}
+
+def shortDomain(entry):
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ if entry['Brand'] == brands['vt']:
+ return {'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': {'Positive URLs': vtCountPositives(entry), 'Provider': providers['vt']}}
+ return {'ContentsFormat': formats['text'], 'Type': entryTypes['error'], 'Contents': 'Unknown provider for result: ' + entry['Brand']}
+
+def isError(entry):
+ return type(entry)==dict and entry['Type']==entryTypes['error']
+
+
+def FormatADTimestamp(ts):
+ return ( datetime(year=1601, month=1, day=1) + timedelta(seconds = int(ts)/10**7) ).ctime()
+
+def PrettifyCompactedTimestamp(x):
+ return '%s-%s-%sT%s:%s:%s' % (x[:4], x[4:6], x[6:8], x[8:10], x[10:12], x[12:])
+
+def NormalizeRegistryPath(strRegistryPath):
+ dSub = {
+ 'HKCR' : 'HKEY_CLASSES_ROOT',
+ 'HKCU' : 'HKEY_CURRENT_USER',
+ 'HKLM' : 'HKEY_LOCAL_MACHINE',
+ 'HKU' : 'HKEY_USERS',
+ 'HKCC' : 'HKEY_CURRENT_CONFIG',
+ 'HKPD' : 'HKEY_PERFORMANCE_DATA'
+ }
+ for k in dSub:
+ if strRegistryPath[:len(k)] == k:
+ return dSub[k] + strRegistryPath[len(k):]
+ return strRegistryPath
+
diff --git a/Scripts/csactors.py b/Scripts/csactors.py
new file mode 100644
index 000000000000..b7f4ad0ce606
--- /dev/null
+++ b/Scripts/csactors.py
@@ -0,0 +1,41 @@
+import time
+
+def formatDate(t):
+ if t:
+ return time.ctime(t)
+ return ''
+
+def formatSlugs(slugs):
+ res = ''
+ first = True
+ if slugs:
+ for s in slugs:
+ if first:
+ first = False
+ else:
+ res += ', '
+ res += demisto.gets(s, 'value')
+ return res
+
+res = '## CrowdStrike Falcon Intelligence'
+entry = demisto.executeCommand('cs-actors', demisto.args())[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ meta = demisto.get(entry, 'Contents.meta')
+ if meta:
+ res += '\n\n### Metadata'
+ res += '\n|Total|Offset|Limit|Time|'
+ res += '\n|-----|------|-----|----|'
+ res += '\n| ' + demisto.gets(meta, 'paging.total') + ' | ' + demisto.gets(meta, 'paging.offset') + ' | ' + demisto.gets(meta, 'paging.limit') + ' | ' + demisto.gets(meta, 'query_time') + ' |'
+ resources = demisto.get(entry, 'Contents.resources')
+ if resources:
+ res += '\n\n### Actors'
+ res += '\n|ID|Name|Short Description|URL|Known As|Create Date|First Date|Last Date|Origins|Target Countries|Target Industries|Motivations|'
+ res += '\n|--|----|-----------------|---|--------|-----------|----------|---------|-------|----------------|-----------------|-----------|'
+ for r in resources:
+ res += '\n| ' + demisto.gets(r, 'id') + ' | ' + demisto.gets(r, 'name') + ' | ' + demisto.gets(r, 'short_description') + ' | ' + demisto.gets(r, 'url') + ' | ' + \
+ demisto.gets(r, 'known_as') + ' | ' + formatDate(demisto.get(r, 'created_date')) + ' | ' + formatDate(demisto.get(r, 'first_activity_date')) + ' | ' + \
+ formatDate(demisto.get(r, 'last_activity_date')) + ' | ' + formatSlugs(demisto.get(r, 'origins')) + ' | ' + formatSlugs(demisto.get(r, 'target_countries')) + ' | ' + \
+ formatSlugs(demisto.get(r, 'target_industries')) + ' | ' + formatSlugs(demisto.get(r, 'motivations')) + ' |'
+ demisto.results({'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': res})
+else:
+ demisto.results(entry)
\ No newline at end of file
diff --git a/Scripts/csindicators.py b/Scripts/csindicators.py
new file mode 100644
index 000000000000..0a5a01a5fb81
--- /dev/null
+++ b/Scripts/csindicators.py
@@ -0,0 +1,35 @@
+import time
+
+def formatDate(t):
+ if t:
+ return time.ctime(t)
+ return ''
+
+def formatLabels(labels):
+ res = ''
+ first = True
+ if labels:
+ for l in labels:
+ if first:
+ first = False
+ else:
+ res += ', '
+ res += demisto.gets(l, 'name')
+ return res
+
+res = '## CrowdStrike Falcon Intelligence'
+entry = demisto.executeCommand('cs-indicators', demisto.args())[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ indicators = demisto.get(entry, 'Contents')
+ if indicators:
+ res += '\n\n### Indicators'
+ res += '\n|Indicator|Type|Published|Updated|Confidence|Reports|Actors|Malware Families|Kill Chains|Domain Types|IP Address Types|Labels|'
+ res += '\n|---------|----|---------|-------|----------|-------|------|----------------|-----------|------------|----------------|------|'
+ for i in indicators:
+ res += '\n| ' + demisto.gets(i, 'indicator') + ' | ' + demisto.gets(i, 'type') + ' | ' + formatDate(demisto.get(i, 'published_date')) + ' | ' + \
+ formatDate(demisto.get(i, 'last_updated')) + ' | ' + demisto.gets(i, 'malicious_confidence') + ' | ' + ','.join(demisto.get(i, 'reports')) + ' | ' + \
+ ','.join(demisto.get(i, 'actors')) + ' | ' + ','.join(demisto.get(i, 'malware_families')) + ' | ' + ','.join(demisto.get(i, 'kill_chains')) + ' | ' + \
+ ','.join(demisto.get(i, 'domain_types')) + ' | ' + ','.join(demisto.get(i, 'ip_address_types')) + ' | ' + formatLabels(demisto.get(i, 'labels')) + ' |'
+ demisto.results({'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': res})
+else:
+ demisto.results(entry)
\ No newline at end of file
diff --git a/Scripts/cyfilerep.py b/Scripts/cyfilerep.py
new file mode 100644
index 000000000000..229eb1dcab19
--- /dev/null
+++ b/Scripts/cyfilerep.py
@@ -0,0 +1,41 @@
+# Retrieve file entry reputation using Cylance
+# First, get the file entry, check with Cylance if the file is known and if not, upload the file, wait 5 seconds and check again
+import time
+
+e = demisto.args()['entry']
+fileEntry = demisto.executeCommand('getEntry', {'id': e})
+if fileEntry and len(fileEntry) == 1 and fileEntry[0]['Type'] != entryTypes['error']:
+ fe = fileEntry[0]
+ if fe['File'] and demisto.get(fe, 'FileMetadata.md5'):
+ rep = demisto.executeCommand('file', {'file': demisto.get(fe, 'FileMetadata.md5'), 'using-brand': brands['cy']})
+ if rep and len(rep) == 1 and rep[0]['Type'] != entryTypes['error']:
+ contents = demisto.get(rep[0], 'Contents')
+ k = contents.keys()
+ if k and len(k) > 0:
+ v = contents[k[0]]
+ if demisto.get(v, 'status') == 'NEEDFILE' and demisto.get(v, 'confirmcode'):
+ upload = demisto.executeCommand('cy-upload', {'entry': e, 'confirmCode': demisto.get(v, 'confirmcode')})
+ if upload and len(upload) == 1 and upload[0]['Type'] != entryTypes['error']:
+ contents = demisto.get(upload[0], 'Contents')
+ k = contents.keys()
+ if k and len(k) > 0:
+ v1 = contents[k[0]]
+ if demisto.get(v1, 'status') == 'ACCEPTED':
+ time.sleep(10)
+ rep = demisto.executeCommand('file', {'file': demisto.get(fe, 'FileMetadata.md5'), 'using-brand': brands['cy']})
+ if rep and len(rep) == 1 and rep[0]['Type'] != entryTypes['error']:
+ demisto.results(shortFile(rep[0]))
+ else:
+ demisto.results(rep)
+ else:
+ demisto.results(upload)
+ else:
+ demisto.results(upload)
+ else:
+ demisto.results(shortFile(rep[0]))
+ else:
+ demisto.results(rep)
+ else:
+ demisto.results('Entry is not a file')
+else:
+ demisto.results('Unable to retrieve entry')
diff --git a/Scripts/datahashrep.js b/Scripts/datahashrep.js
new file mode 100644
index 000000000000..80395927fab4
--- /dev/null
+++ b/Scripts/datahashrep.js
@@ -0,0 +1,48 @@
+var xfeScore = -1;
+var vtScore = -1;
+var cyScore = -1;
+var wfScore = -1;
+var csScore = -1;
+var rep = executeCommand('file', {file: args.input});
+if (rep) {
+ for (var r = 0; r < rep.length; r++) {
+ if (rep[r].Type !== entryTypes.error && rep[r].ContentsFormat === formats.json) {
+ if (rep[r].Brand === brands.xfe && rep[r].Contents && rep[r].Contents.malware.family) {
+ xfeScore = 3;
+ } else if (rep[r].Brand === brands.vt && rep[r].Contents && rep[r].Contents.positives) {
+ var detected = rep[r].Contents.positives;
+ if (detected > 15) {
+ vtScore = 3;
+ } else if (detected > 4) {
+ vtScore = 2;
+ } else {
+ vtScore = 1;
+ }
+ } else if (rep[r].Brand === brands.cy && rep[r].Contents) {
+ var k = Object.keys(rep[r].Contents);
+ if (k && k.length > 0) {
+ var v = rep[r].Contents[k[0]];
+ if (v && v.generalscore) {
+ var score = v.generalscore;
+ if (score < -0.7) {
+ cyScore = 3;
+ } else if (score < -0.1) {
+ cyScore = 2;
+ } else {
+ cyScore = 1;
+ }
+ }
+ }
+ } else if (rep[r].Brand === brands.wf && rep[r].Contents) {
+ if (positiveFile(rep[r])) {
+ wfScore = 3;
+ }
+ } else if (rep[r].Brand === brands.cs && rep[r].Contents && rep[r].Contents.length) {
+ csScore = rep[r].Contents[0].malicious_confidence === 'high' ? 3 : rep[r].Contents[0].malicious_confidence === 'medium' ? 2 : 1;
+ }
+ }
+ }
+}
+
+var score = Math.max(xfeScore, vtScore, cyScore, wfScore, csScore);
+return score < 0 ? 0 : score;
diff --git a/Scripts/dataiprep.js b/Scripts/dataiprep.js
new file mode 100644
index 000000000000..8acc3921b4b7
--- /dev/null
+++ b/Scripts/dataiprep.js
@@ -0,0 +1,42 @@
+var xfeScore = -1;
+var vtScore = -1;
+var csScore = -1;
+var rep = executeCommand('ip', {ip: args.input});
+if (rep) {
+ for (var r = 0; r < rep.length; r++) {
+ if (rep[r].Type !== entryTypes.error && rep[r].ContentsFormat === formats.json) {
+ if (rep[r].Brand === brands.xfe && rep[r].Contents && rep[r].Contents.reputation.score) {
+ var s = rep[r].Contents.reputation.score;
+ if (s >= 6) {
+ xfeScore = 3;
+ } else if (s >= 3) {
+ xfeScore = 2;
+ } else {
+ xfeScore = 1;
+ }
+ } else if (rep[r].Brand === brands.vt && rep[r].Contents && rep[r].Contents.detected_urls) {
+ var positives = 0;
+ var suspect = 0;
+ for (var i = 0; i < rep[r].Contents.detected_urls.length; i++) {
+ if (rep[r].Contents.detected_urls[i].positives > 20) {
+ positives++;
+ } else if (rep[r].Contents.detected_urls[i].positives > 10) {
+ suspect++;
+ }
+ }
+ if (positives > 20) {
+ vtScore = 3;
+ } else if (positives > 10 || suspect > 20) {
+ vtScore = 2;
+ } else {
+ vtScore = 1;
+ }
+ } else if (rep[r].Brand === brands.cs && rep[r].Contents && rep[r].Contents.length) {
+ csScore = rep[r].Contents[0].malicious_confidence === 'high' ? 3 : rep[r].Contents[0].malicious_confidence === 'medium' ? 2 : 1;
+ }
+ }
+ }
+}
+
+var score = Math.max(xfeScore, vtScore, csScore);
+return score < 0 ? 0 : score;
diff --git a/Scripts/dataurlrep.js b/Scripts/dataurlrep.js
new file mode 100644
index 000000000000..6905d1577bf7
--- /dev/null
+++ b/Scripts/dataurlrep.js
@@ -0,0 +1,34 @@
+var xfeScore = -1;
+var vtScore = -1;
+var csScore = -1;
+var rep = executeCommand('url', {url: args.input});
+if (rep) {
+ for (var r = 0; r < rep.length; r++) {
+ if (rep[r].Type !== entryTypes.error && rep[r].ContentsFormat === formats.json) {
+ if (rep[r].Brand === brands.xfe && rep[r].Contents && rep[r].Contents.url.result.score) {
+ var s = rep[r].Contents.url.result.score;
+ if (s >= 6) {
+ xfeScore = 3;
+ } else if (s >= 3) {
+ xfeScore = 2;
+ } else {
+ xfeScore = 1;
+ }
+ } else if (rep[r].Brand === brands.vt && rep[r].Contents && rep[r].Contents.positives) {
+ var detected = rep[r].Contents.positives;
+ if (detected > 20) {
+ vtScore = 3;
+ } else if (detected > 8) {
+ vtScore = 2;
+ } else {
+ vtScore = 1;
+ }
+ } else if (rep[r].Brand === brands.cs && rep[r].Contents && rep[r].Contents.length) {
+ csScore = rep[r].Contents[0].malicious_confidence === 'high' ? 3 : rep[r].Contents[0].malicious_confidence === 'medium' ? 2 : 1;
+ }
+ }
+ }
+}
+
+var score = Math.max(xfeScore, vtScore, csScore);
+return score < 0 ? 0 : score;
diff --git a/Scripts/emailrep.js b/Scripts/emailrep.js
new file mode 100644
index 000000000000..370c58c24ac4
--- /dev/null
+++ b/Scripts/emailrep.js
@@ -0,0 +1 @@
+return executeCommand('pipl-search', {email: args.email});
diff --git a/Scripts/example.js b/Scripts/example.js
new file mode 100644
index 000000000000..ddab9de60b45
--- /dev/null
+++ b/Scripts/example.js
@@ -0,0 +1,78 @@
+// This is an Example server-side script that shows how to use and create JavaScript scripts in the Demisto server
+// The log function allows you to print data during script execution
+log('Hello World');
+
+// You can use arguments (that are added via the CLI) inside scripts in the following ways, (in our example,
+// the argument name is : 'ArgumentExample')
+// e.g. run this script !ExampleJSScript ArgumentExample=MD5
+log('Your argument value = ' + args.ArgumentExample);
+
+// You can view (but not change directly) incidents and investigation properties relevant to your current investigation
+log('Investigation name is: ' + investigation.name);
+
+// Please note incidents can be empty (e.g. if you run the from playground) the following returns the incident
+// details only if the incidents are not empty
+if (incidents && typeof incidents === 'object' && incidents !== null) {
+ log('First incident details: ' + incidents[0].details);
+}
+
+// You can run specific integrations if they are already configured. The following is an example of a script
+// running on a particular splunk instance called "Splunk1".
+// The first example brings back the first event offset.
+// log('Splunk query result: ' + executeCommand('search', {'using': 'Splunk1', 'query':' * | head 2 '})[0]['offset']);
+
+// executeCommand is used to run a command without specifying the entity that will execute it.
+// For example, if you want to run a single command that runs on every integration that supports the search command:
+// log('Search query result: ' + executeCommand('search', {'query':' * | head 2 '}));
+
+// Another example for executeCommand enables running an internal command that will get all entries of this investigation
+log('First Entry content: ' + executeCommand('getEntries', {})[0]);
+
+// You can check for all supported commands using the getAllSupportedCommands function.
+// The following will return all supported commands and the entities supported
+var cmds = getAllSupportedCommands();
+for (var key in cmds) {
+ if (cmds.hasOwnProperty(key)) {
+ log('supporting integration: ' + key);
+ }
+}
+
+// Util function isIp
+log(isIp('8.8.8.8'));
+
+// Util function http
+var res = http('http://www.demisto.com');
+log('Http GET call result StatusCode = ' +res.StatusCode);
+
+// You can perform operations on current investigation and incidents, see follow example,
+// Methods descriptions available in CommonServer script.
+/*
+var user = 'David';
+var time = new Date();
+var taskId = '3';
+
+setOwner(user);
+
+setPlaybook('Phishing Playbook');
+
+setSeverity({id: incidents[0].id, severity: 'Critical'});
+
+setTaskDueDate({id: taskId, dueDate: time.toUTCString()});
+
+taskAssign({id: taskId, assignee: user});
+
+You can change incident type, name, severity, details and systems for an investigation with a single incident.
+This can be done by sending an object to setIncident with the details.
+setIncident({type: 'Phishing', details: 'Phishing on my computer', severity: 'Critical', incName: 'Phishing incident',
+ systems: '2.2.2.2,10.10.10.10'});
+
+You can also spawn a new incident from the current one, in the same manner as changing the details of the incident:
+createNewIncident({type: 'Phishing', details: 'Phishing on your computer', severity: 'Critical', incName: 'Phishing incident',
+ systems: '2.2.2.2,10.10.10.10'});
+
+
+return;
+*/
+
+// You can also return either string or JSON objects to be printed to the screen as script results
+return 'Script success! GO and write new scripts :)';
diff --git a/Scripts/exassignrole.js b/Scripts/exassignrole.js
new file mode 100644
index 000000000000..aca67265f559
--- /dev/null
+++ b/Scripts/exassignrole.js
@@ -0,0 +1,33 @@
+//+ exchange/assign.ps1
+
+//Params:
+//See here: https://technet.microsoft.com/en-us/library/dd298173(v=exchg.160).aspx
+//query is the string provided to the -SearchQuery parameter
+//to-mailbox is the string provided to -TargetMailbox parameter
+//to-folder is the string provided to -TargetFolder parameter
+//if server is provided, it'll be used as -ServerFQDN parameter to Connect-ExchangeServer cmdlet
+
+if ((env.ARCH !== "amd64") && (env.OS !== "windows")) {
+ throw("Script can run only in 64bit Windows Agents");
+}
+
+var command = [];
+command.push("powershell.exe");
+command.push("-version 2.0");
+command.push("-NonInteractive");
+command.push("-NoLogo");
+command.push(which("assign.ps1"));
+if (typeof (args.role) !== "undefined") {
+ command.push("-role");
+ command.push(args.role);
+}
+if (typeof (args.username) !== "undefined") {
+ command.push("-username");
+ command.push(args.username);
+}
+if (typeof (args.server) !== "undefined") {
+ command.push("-server");
+ command.push(args.server);
+}
+
+pack(execute(command.join(" ")), 'table');
diff --git a/Scripts/exchange/assign.ps1 b/Scripts/exchange/assign.ps1
new file mode 100644
index 000000000000..ff5b25a4c120
--- /dev/null
+++ b/Scripts/exchange/assign.ps1
@@ -0,0 +1,27 @@
+param (
+ [string]$server = $null,
+ [string]$username = $env:USERNAME,
+ [string]$role = "Mailbox Import Export"
+ )
+
+. RemoteExchange.ps1
+
+if (!$server){
+ Connect-ExchangeServer -auto
+}
+else {
+ Connect-ExchangeServer -ServerFqdn $server
+}
+
+if (!$username){
+ $username=whoami
+}
+
+$assignment = New-ManagementRoleAssignment -Role $role -User:$username
+
+if (!$assignment){
+ $msg ="Failed to assign '$username' to role: '$role'"
+ throw $msg
+}
+
+Remove-PSSession $remoteSession
diff --git a/Scripts/exchange/search.ps1 b/Scripts/exchange/search.ps1
new file mode 100644
index 000000000000..19a8bcd6fce2
--- /dev/null
+++ b/Scripts/exchange/search.ps1
@@ -0,0 +1,36 @@
+param (
+ [string]$server = $null,
+ [string]$username = $env:USERNAME,
+ [string]$query = $null,
+ [string]$targetmbx = $null,
+ [string]$targetFolder = $null,
+ [switch]$delete = $false
+ )
+
+. RemoteExchange.ps1
+
+if (!$query){
+ throw "Missing parameter: -query"
+}
+
+if (!$server){
+ Connect-ExchangeServer -auto
+}
+else {
+ Connect-ExchangeServer -ServerFqdn $server
+}
+
+if ($delete){
+ Get-Mailbox -ResultSize Unlimited | Search-Mailbox -SearchQuery '$query' -Force:$true -DeleteContent
+}
+else {
+ if (!$targetmbx){
+ throw "Missing parameter: -targetmbx"
+ }
+ if (!$targetFolder){
+ throw "Missing parameter: -targetFolder"
+ }
+ Get-Mailbox -ResultSize Unlimited | Search-Mailbox -SearchQuery '$query' -TargetMailbox $targetmbx -TargetFolder $targetFolder
+}
+
+Remove-PSSession $remoteSession
diff --git a/Scripts/exdeletemail.js b/Scripts/exdeletemail.js
new file mode 100644
index 000000000000..5f410a569ffa
--- /dev/null
+++ b/Scripts/exdeletemail.js
@@ -0,0 +1,27 @@
+//+ exchange/search.ps1
+
+//Params:
+//See here: https://technet.microsoft.com/en-us/library/dd298173(v=exchg.160).aspx
+//query is the string provided to the -SearchQuery parameter
+
+if ((env.ARCH !== "amd64") && (env.OS !== "windows")) {
+ throw("Script can ran only in 64bit Windows Agents");
+}
+
+var command = [];
+command.push("powershell.exe");
+command.push("-version 2.0");
+command.push("-NonInteractive");
+command.push("-NoLogo");
+command.push(which("search.ps1"));
+command.push("-query");
+command.push(args.query);
+command.push("-delete");
+
+if (typeof (args.server) !== "undefined") {
+ command.push("-server");
+ command.push(args.server);
+}
+
+pack(execute(command.join(" ")), 'table');
+
diff --git a/Scripts/exsearchmailbox.js b/Scripts/exsearchmailbox.js
new file mode 100644
index 000000000000..63fdd22bb29f
--- /dev/null
+++ b/Scripts/exsearchmailbox.js
@@ -0,0 +1,33 @@
+//+ exchange/search.ps1
+
+//Params:
+//See here: https://technet.microsoft.com/en-us/library/dd298173(v=exchg.160).aspx
+//query is the string provided to the -SearchQuery parameter
+//to-mailbox is the string provided to -TargetMailbox parameter
+//to-folder is the string provided to -TargetFolder parameter
+//if server is provided, it'll be used as -ServerFQDN parameter to Connect-ExchangeServer cmdlet
+
+if ((env.ARCH !== "amd64") && (env.OS !== "windows")) {
+ throw("Script can ran only in 64bit Windows Agents");
+}
+
+var command = [];
+command.push("powershell.exe");
+command.push("-version 2.0");
+command.push("-NonInteractive");
+command.push("-NoLogo");
+command.push(which("search.ps1"));
+command.push("-query");
+command.push(args.query);
+command.push("-targetmbx");
+command.push(args["to-mailbox"]);
+command.push("-targetFolder");
+command.push(args["to-folder"]);
+
+if (typeof (args.server) !== "undefined") {
+ command.push("-server");
+ command.push(args.server);
+}
+
+pack(execute(command.join(" ")), 'table');
+
diff --git a/Scripts/filerep.js b/Scripts/filerep.js
new file mode 100644
index 000000000000..ca8dace0cd8b
--- /dev/null
+++ b/Scripts/filerep.js
@@ -0,0 +1,8 @@
+var res = [];
+var rep = executeCommand('file', {file: args.file});
+if (rep && Array.isArray(rep)) {
+ for (var i = 0; i < rep.length; i++) {
+ res.push(shortFile(rep[i]));
+ }
+}
+return res;
diff --git a/Scripts/iprep.js b/Scripts/iprep.js
new file mode 100644
index 000000000000..b806ce7fbd7e
--- /dev/null
+++ b/Scripts/iprep.js
@@ -0,0 +1,8 @@
+var res = [];
+var rep = executeCommand('ip', {ip: args.ip});
+if (rep && Array.isArray(rep)) {
+ for (var i = 0; i < rep.length; i++) {
+ res.push(shortIP(rep[i]));
+ }
+}
+return res;
diff --git a/Scripts/lcfinddomain.js b/Scripts/lcfinddomain.js
new file mode 100644
index 000000000000..b2bcbba6127c
--- /dev/null
+++ b/Scripts/lcfinddomain.js
@@ -0,0 +1,16 @@
+// Demisto script for LimaCharlie
+var r = executeCommand('objectloc', {obj_name: args.domain, obj_type: 'DOMAIN_NAME'});
+
+if (r && Array.isArray(r) && r.length === 1 && r[0].ContentsFormat === formats.json && r[0].Contents && r[0].Contents && Array.isArray(r[0].Contents)) {
+ var res = {Contents: [], ContentsFormat: formats.table, Type: entryTypes.note};
+ for (var i=0; i 0) {
+ return res;
+ }
+}
+return r;
diff --git a/Scripts/lcfindprocess.js b/Scripts/lcfindprocess.js
new file mode 100644
index 000000000000..896ad776c235
--- /dev/null
+++ b/Scripts/lcfindprocess.js
@@ -0,0 +1,17 @@
+// Demisto script for LimaCharlie
+var r = executeCommand('objectloc', {obj_name: args.name, obj_type: 'PROCESS_NAME'});
+
+if (r && Array.isArray(r) && r.length === 1 && r[0].ContentsFormat === formats.json && r[0].Contents && r[0].Contents && Array.isArray(r[0].Contents)) {
+ var res = {Contents: [], ContentsFormat: formats.table, Type: entryTypes.note};
+ for (var i=0; i 0) {
+ return res;
+ }
+}
+
+return r;
diff --git a/Scripts/lcnewprocess.js b/Scripts/lcnewprocess.js
new file mode 100644
index 000000000000..8800b74f03bf
--- /dev/null
+++ b/Scripts/lcnewprocess.js
@@ -0,0 +1,37 @@
+// Demisto script for LimaCharlie
+var before = Math.round((new Date()).getTime() / 1000).toString();
+var days = (args.days) ? args.days : 1;
+var after = (before - days * 24 * 60 * 60).toString();
+var size = (args.size) ? args.size : '4096';
+
+var r = executeCommand('timeline', {before: before, after: after, sensor_id: args.sensor, max_size: size});
+
+if (r && Array.isArray(r) && r.length === 1 && r[0].ContentsFormat === formats.json && r[0].Contents && r[0].Contents.events) {
+ var res = {Contents: [], ContentsFormat: formats.table, Type: entryTypes.note};
+ for (var i=0; i 0) {
+ return res;
+ }
+}
+
+return r;
diff --git a/Scripts/osxcollector.js b/Scripts/osxcollector.js
new file mode 100644
index 000000000000..4ebb016624e5
--- /dev/null
+++ b/Scripts/osxcollector.js
@@ -0,0 +1,32 @@
+var res = http('https://raw.githubusercontent.com/Yelp/osxcollector/master/osxcollector/osxcollector.py', {SaveToFile: true});
+
+execute('chmod +x '+res.Path);
+
+var cmd = res.Path ;
+if (args.section) {
+ cmd = cmd + " -s " + args.section ;
+}
+var timeout = 600 ;
+if (args.timeout) {
+ timeout = args.timeout ;
+}
+pack(cmd);
+
+var output = execute(cmd,timeout);
+pack(output);
+
+var result = output.Stderr;
+var fileNameStartIndex = result.lastIndexOf("osxcollect");
+var fileName = result.substring(fileNameStartIndex, result.length-1);
+
+pack_file(String(fileName));
+
+fileNameWithoutExtension = fileName.substring(0, fileName.length-7);
+
+execute('tar -zxvf '+fileName+' ./'+fileNameWithoutExtension+'/'+fileNameWithoutExtension+'.json');
+
+var jsonResultStr = read_file(fileNameWithoutExtension+'/'+fileNameWithoutExtension+'.json');
+var arr = jsonResultStr.split('\n');
+str = arr.join(',');
+var result = '{"osxcollector_result":['+str.substring(0, str.length-1)+']}';
+pack(JSON.parse(result));
\ No newline at end of file
diff --git a/Scripts/pagerdutyalert.js b/Scripts/pagerdutyalert.js
new file mode 100644
index 000000000000..5143429b2cce
--- /dev/null
+++ b/Scripts/pagerdutyalert.js
@@ -0,0 +1,4 @@
+if (incidents[0].severity == 4) {
+ return executeCommand('pagerDutySubmitEvent', {description: incidents[0].name,details: '{"incidentDetails":"'+incidents[0].details+'"}'});
+}
+return 'Incident severity not high enough to wake up user' ;
\ No newline at end of file
diff --git a/Scripts/pagerdutyassign.js b/Scripts/pagerdutyassign.js
new file mode 100644
index 000000000000..8ca96718f976
--- /dev/null
+++ b/Scripts/pagerdutyassign.js
@@ -0,0 +1,25 @@
+var query = '' ;
+if (args.query) {
+ query = args.query ;
+}
+var res = executeCommand('PagerDutyGetUsersOnCallNow', {query: query});
+if (res[0].Type == entryTypes.error) {
+ return res[0]
+}
+
+var usersOnCall = res[0].Contents;
+
+var selectedUser = usersOnCall[0];
+
+
+if (selectedUser === null) {
+ return 'error : could not find user from PagerDuty OnCall now!';
+}
+res = executeCommand('getUserByEmail', {userEmail: selectedUser.email});
+if (res[0].Type == entryTypes.error) {
+ return res[0];
+}
+var userId = res[0].Contents.id;
+
+setOwner(userId);
+return 'User ' + userId + ' was set as owner to incidents of this investigation';
diff --git a/Scripts/panoramacommit.js b/Scripts/panoramacommit.js
new file mode 100644
index 000000000000..4fae2cdb325d
--- /dev/null
+++ b/Scripts/panoramacommit.js
@@ -0,0 +1,5 @@
+var reqArgs = {
+ type: 'commit',
+ cmd: ''
+ };
+return executeCommand('panorama', reqArgs)[0].Contents.response['-status'];
\ No newline at end of file
diff --git a/Scripts/panoramaconfig.js b/Scripts/panoramaconfig.js
new file mode 100644
index 000000000000..c6c6d9a1a890
--- /dev/null
+++ b/Scripts/panoramaconfig.js
@@ -0,0 +1,38 @@
+function AddArgumentOpen(arg, fieldName, member) {
+ if (arg) {
+ if (member) {
+ return '<'+fieldName+'>' + arg+ ''+fieldName+'>';
+ } else {
+ return '<'+fieldName+'>' + arg + ''+fieldName+'>';
+ }
+ }
+ return '';
+}
+
+function AddArgumentYesNo(arg, fieldName) {
+ if (arg !== undefined) {
+ return '<'+fieldName+'>' + (arg ? 'yes' : 'no') + ''+fieldName+'>';
+ }
+ return '';
+}
+var req = {
+ type: 'config',
+ action: 'set',
+ key: 'keyvalue',
+ xpath: args.xpath || '/config/devices/entry/vsys/entry/rulebase/security/rules/entry[@name=\'' + args.ruleName +'\']',
+ element: AddArgumentOpen(args.action, 'action') +
+ AddArgumentOpen(args.description, 'description') +
+ AddArgumentOpen(args.srcIP, 'source', true) +
+ AddArgumentOpen(args.dstIP, 'destination', true) +
+ AddArgumentOpen(args.application, 'application', true) +
+ AddArgumentOpen(args.srcUser, 'source-user', true) +
+ AddArgumentOpen(args.from, 'from', true) +
+ AddArgumentOpen(args.to, 'to', true) +
+ AddArgumentOpen(args.service, 'service', true) +
+ AddArgumentYesNo(args.negateSrc, 'negate-source') +
+ AddArgumentYesNo(args.negateDst, 'negate-destination') +
+ AddArgumentYesNo(args.disable, 'disabled') +
+ AddArgumentYesNo(args.disableServerResponseInspection, 'disable-server-response-inspection')
+ };
+var raw = executeCommand('panorama', req);
+return raw[0].Contents.response['-status'];
\ No newline at end of file
diff --git a/Scripts/panoramamove.js b/Scripts/panoramamove.js
new file mode 100644
index 000000000000..54d50868c0db
--- /dev/null
+++ b/Scripts/panoramamove.js
@@ -0,0 +1,12 @@
+var req = {
+ type: 'config',
+ action: 'move',
+ key: 'keyvalue',
+ xpath: args.xpath || '/config/devices/entry/vsys/entry/rulebase/security/rules/entry[@name=\'' + args.src +'\']',
+ where: args.where,
+ };
+if (args.dst) {
+ req.dst = args.dst
+}
+var raw = executeCommand('panorama', req);
+return raw[0].Contents.response['-status'];
\ No newline at end of file
diff --git a/Scripts/panoramapcaps.js b/Scripts/panoramapcaps.js
new file mode 100644
index 000000000000..d1bfadd87825
--- /dev/null
+++ b/Scripts/panoramapcaps.js
@@ -0,0 +1,34 @@
+function AddArgument(arg, argName,req) {
+ if (arg) {
+ req[argName] = arg;
+ }
+ return req;
+}
+
+var reqArgs = {
+ type: 'export',
+ category: args.pcapType,
+ };
+if (args.password) {
+ reqArgs['dlp-password'] = args.password;
+} else if (args.pcapType === 'dlp-pcap') {
+ return 'can not provide dlp-pcap without password';
+}
+
+AddArgument(args.from, 'from', reqArgs);
+AddArgument(args.to, 'to', reqArgs);
+AddArgument(args.serialNo, 'serialno', reqArgs);
+AddArgument(args.searchTime, 'search-time', reqArgs);
+AddArgument(args.pcapID, 'pcap-id', reqArgs);
+
+raw = executeCommand('panorama', reqArgs);
+if (raw[0].ContentsFormat === 'json') {
+ var content = raw[0].Contents.response;
+ if (content['-status'] === 'success') {
+ return content.result;
+ } else {
+ return content['-status'];
+ }
+} else {
+ return raw;
+}
\ No newline at end of file
diff --git a/Scripts/scripts.json b/Scripts/scripts.json
new file mode 100644
index 000000000000..1bdddbd149bf
--- /dev/null
+++ b/Scripts/scripts.json
@@ -0,0 +1,3325 @@
+{
+ "server": [
+ {
+ "name": "ExampleJSScript",
+ "script": "example.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["example"],
+ "comment": "This is only an example script, to showcase how to use and write JavaScript scripts",
+ "system": true,
+ "arguments": [
+ {
+ "name": "ArgumentExample",
+ "description": "An example argument that will be passed into and used by the script",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "WhoisSummary",
+ "script": "whoissummary.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["threat-intel", "whois", "server"],
+ "comment": "A simple script that outputs a shorter summary of the !whois command output",
+ "system": true,
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Input for the WHOIS query",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["whois"] }
+ },
+ {
+ "name": "Whois",
+ "script": "whois.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["threat-intel", "whois", "server"],
+ "comment": "A simple script that returns WHOIS info for a domain",
+ "system": true,
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Input for the WHOIS query",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["whois"] }
+ },
+ {
+ "name": "VolLDRModules",
+ "script": "volldrmodules.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for command ldrmodules",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolPSList",
+ "script": "volpslist.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for command pslist",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolGetProcWithMalNetConn",
+ "script": "volgetprocwithmalnetconn.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for getting the list of processes that have connections to ip address with bad reputation.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "repthreshold",
+ "description": "Reputation threshold - any IP addresses up to and including this score are considered malicious",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "repscript",
+ "description": "Reputation script to use for checking IP addresses",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolConnscan",
+ "script": "volconnscan.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for command connscan",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolNetworkConnections",
+ "script": "volnetworkconnections.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for finding all the network connections. This script runs through different commands based on the profile provided.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolImageinfo",
+ "script": "volimageinfo.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for command imageinfo",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolMalfind",
+ "script": "volmalfind.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for command ldrmodules",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "pid",
+ "description": "Process ID to pass to volatility as a parameter of the ldrmodules command",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolApihooks",
+ "script": "volapihooks.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for command apihooks",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "pid",
+ "description": "Process ID to pass to volatility as a parameter of the apihooks command",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolDlllist",
+ "script": "voldlllist.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Volatility script for command ldrmodules",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "pid",
+ "description": "Process ID to pass to volatility as a parameter of the dlllist command",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "CommonServer",
+ "script": "common.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["infra", "server"],
+ "comment": "Common code that will be merged into each server script when it runs",
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "CommonUserServer",
+ "script": "",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["infra", "server"],
+ "comment": "Common user defined code that will be merged into each server script when it runs",
+ "system": false,
+ "scriptTarget": 0
+ },
+ {
+ "name": "CommonServerPython",
+ "script": "common.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["infra", "server"],
+ "comment": "Common code that will be merged into each server script when it runs",
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "CommonServerUserPython",
+ "script": "",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["infra", "server"],
+ "comment": "Common user defined code that will be merged into each server script when it runs",
+ "system": false,
+ "scriptTarget": 0
+ },
+ {
+ "name": "Volatility",
+ "script": "volatilitygeneric.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Execute volatility with command and return tabular output. Incase where proper json output is not supported, scripts returns error. User should use raw command.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "cmd",
+ "description": "The volatility command/module you want to use",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "SplunkEmailParser",
+ "script": "SplunkEmailParser.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["splunk","ingestion"],
+ "comment": "Classify an incident created from an email originating from Splunk.\nThe mail type should be in plain text, and inline: table should be selected.\nParsing is done in the following manner -\ntype is the header sourcetype, severity is the mail importance level, \nthe incident name is the mail subject and the systems are taken from host.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "body",
+ "description": "Contents (body) of the email",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "subject",
+ "description": "Subject of the email",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "DefaultIncidentClassifier",
+ "script": "DefaultIncidentClassifier.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["ingestion"],
+ "comment": "Classify an incident from mail.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "splunkSender",
+ "description": "Email address from which Splunk sends emails to our mail listener",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "nexposeSender",
+ "description": "Email address from which Nexpose sends emails to our mail listener",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "defaultIncidentType",
+ "description": "The incident type to be set in case the email is neither from Splunk nor Nexpose",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "minRiskScore",
+ "description": "Argument passed as-is to NexposeEmailParser. See its documentation for details.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "minVulnCount",
+ "description": "Argument passed as-is to NexposeEmailParser. See its documentation for details.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "sentinelOneSender",
+ "description": "Email address from which sentinel one sends emails to our mail listener",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "sentinelOneIncidentType",
+ "description": "Incident type to classify sentinel one events to",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "NexposeEmailParser",
+ "script": "NexposeEmailParser.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["nexpose","ingestion"],
+ "comment": "Parses nexpose report into a clear table that contain risk score and vulnerability count for each server,\nAnd creates a new incident for each server.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "entryID",
+ "description": "ID of the entry containing the Nexpose report. If none is provided, the script will iterate and find a relevant entry.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "minRiskScore",
+ "description": "Minimal Risk Score an item in the report needs to reach in order to trigger an incident. Leave empty to trigger for any risk score.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "minVulnCount",
+ "description": "Minimal Vulnerability Count an item in the report needs to reach in order to trigger an incident. Leave empty to trigger for any count.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "defaultNexposeSeverity",
+ "description": "Severity to be set on triggered incidents",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "NexposeEmailParserForVuln",
+ "script": "NexposeEmailParserForVuln.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["nexpose","ingestion"],
+ "comment": "Parses nexpose report into a clear table that contain risk score and vulnerability count for each server,\nAnd creates a new incident for each server.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "report",
+ "description": "Full XML contents of the Nexpose report. If not provided, it will be taken from the Incident Details.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "NexposeVulnExtractor",
+ "script": "NexposeVulnExtractor.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["nexpose"],
+ "comment": "Parse a specific server nexpose response in to a table of vulnerabilities.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "report",
+ "description": "Full XML contents of the Nexpose report. If not provided, it will be taken from the Incident Details.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "VolRunCmds",
+ "script": "volruncmds.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Execute volatility with command and return tabular output. Incase where proper json output is not supported, scripts returns error. User should use raw command.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "cmds",
+ "description": "Comma-separated list of volatility commands/modules to run",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "profile",
+ "description": "Volatility profile to use",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "CheckFiles",
+ "script": "checkfiles.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["server", "threat-intel"],
+ "comment": "Iterate on all file artifacts in the investigation and return details of positives",
+ "system": true,
+ "arguments": [
+ {
+ "name": "fileNames",
+ "description": "If provided, checks only files whose names are in the list. The names should be comma-separated.",
+ "required": false,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["file"] }
+ },
+ {
+ "name": "CheckSender",
+ "script": "checksender.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["phishing"],
+ "comment": "For phishing incidents, check the sender of the email via Pipl search",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["pipl-search"] }
+ },
+ {
+ "name": "StaticAnalyze",
+ "script": "staticanalyze.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["phishing"],
+ "comment": "For phishing incidents, iterate on all attachments and run PE dump on each",
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "AnalyzeOSX",
+ "script": "analyzeosx.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["osx"],
+ "comment": "Get file and url reputation for osxcollector result.\n will use VirusTotal for Url checks, and IBM XForce for MD5 checks.\n maxchecks : for \n system : system name to run agent on.\n section : the type check that OSXCollector should run.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "section",
+ "description": "Ask OSXCollector for a specific section",
+ "required": false,
+ "default": true
+ },
+ {
+ "name": "timeout",
+ "description": "Timeout to be passed to OSXCollector script",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "maxchecks",
+ "description": "Maximum amount of files/urls to verify",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "system",
+ "description": "OSX System to be used",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["file", "url"] }
+ },
+ {
+ "name": "LCNewProcess",
+ "script": "lcnewprocess.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["lima charlie"],
+ "comment": "Using Lima Charlie, find all new process launches for a given sensor",
+ "system": true,
+ "arguments": [
+ {
+ "name": "sensor",
+ "description": "Sensor to query",
+ "required": true,
+ "default": true
+ },
+ {
+ "name": "size",
+ "description": "Size for the response",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "days",
+ "description": "Size of time window, in days",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["timeline"] }
+ },
+ {
+ "name": "LCFindProcess",
+ "script": "lcfindprocess.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["lima charlie"],
+ "comment": "Using Lima Charlie, find a given process name across all sensors",
+ "system": true,
+ "arguments": [
+ {
+ "name": "name",
+ "description": "Process name to find",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["objectloc"] }
+ },
+ {
+ "name": "LCFindDomain",
+ "script": "lcfinddomain.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["lima charlie"],
+ "comment": "Using Lima Charlie, find which sensor saw access to given domain",
+ "system": true,
+ "arguments": [
+ {
+ "name": "domain",
+ "description": "Domain to be found",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["objectloc"] }
+ },
+ {
+ "name": "SplunkSearch",
+ "script": "splunksearch.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["enhancement","splunk"],
+ "comment": "Run a query through Splunk and format the results as a table",
+ "system": true,
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Splunk query to execute",
+ "required": true,
+ "default": true
+ },
+ {
+ "name": "rows",
+ "description": "Return up to X first rows. If omitted, defaults to 30.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["search"] }
+ },
+ {
+ "name": "SplunkSearchJsonPy",
+ "script": "SplunkSearchJson.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["enhancement"],
+ "comment": "Run a query through Splunk and format the results as a markdown with raw data parsed as JSON",
+ "system": true,
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Splunk query to execute",
+ "required": true,
+ "default": true
+ },
+ {
+ "name": "rows",
+ "description": "Return up to X first rows. If omitted, defaults to 30.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["search"] }
+ },
+ {
+ "name": "TriagePhishing",
+ "script": "triagephishing.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["phishing"],
+ "comment": "Process a suspected email and check URLs, attachments and sender via reputation services",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": [], "should": ["url", "pipl-search", "file"] }
+ },
+ {
+ "name": "DataIPReputation",
+ "script": "dataiprep.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["reputation"],
+ "comment": "Evaluate reputation of an IP address and return a score between 1 to 5",
+ "system": true,
+ "arguments": [
+ {
+ "name": "input",
+ "description": "IP address to look up",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ip"] }
+ },
+ {
+ "name": "DataURLReputation",
+ "script": "dataurlrep.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["reputation"],
+ "comment": "Evaluate reputation of a URL and return a score between 1 to 5",
+ "system": true,
+ "arguments": [
+ {
+ "name": "input",
+ "description": "URL to look up",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["url"] }
+ },
+ {
+ "name": "DataHashReputation",
+ "script": "datahashrep.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["reputation"],
+ "comment": "Evaluate reputation of the given hash and return a score between 1 to 5",
+ "system": true,
+ "arguments": [
+ {
+ "name": "input",
+ "description": "Hash value to look up",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["file"] }
+ },
+ {
+ "name": "IPReputation",
+ "script": "iprep.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["enhancement"],
+ "comment": "A context script for IP entities",
+ "system": true,
+ "arguments": [
+ {
+ "name": "ip",
+ "description": "IP address to look up",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ip"] }
+ },
+ {
+ "name": "FileReputation",
+ "script": "filerep.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["enhancement"],
+ "comment": "A context script for MD5 entities",
+ "system": true,
+ "arguments": [
+ {
+ "name": "file",
+ "description": "File MD5 hash to look up",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["file"] }
+ },
+ {
+ "name": "EmailReputation",
+ "script": "emailrep.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["enhancement"],
+ "comment": "A context script for Email entities",
+ "system": true,
+ "arguments": [
+ {
+ "name": "email",
+ "description": "Email address to look up",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["pipl-search"] }
+ },
+ {
+ "name": "URLReputation",
+ "script": "urlrep.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["enhancement"],
+ "comment": "A context script for URL entities",
+ "system": true,
+ "arguments": [
+ {
+ "name": "url",
+ "description": "URL to look up",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["url"] }
+ },
+ {
+ "name": "CBSearch",
+ "script": "cbsearch.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["carbon-black", "endpoint", "enhancement"],
+ "comment": "Search Carbon Black for process & binary information",
+ "system": true,
+ "arguments": [
+ {
+ "name": "type",
+ "description": "\"process\" or \"binary\" according to the type of search you want to run",
+ "required": false,
+ "default": false
+ }, {
+ "name": "query",
+ "description": "Query to be run - in Carbon Black syntax",
+ "required": false,
+ "default": true
+ },
+ {
+ "name": "rows",
+ "description": "Number of rows to return. If omitted, default is as stated in the Carbon Black API documentation, currently 10.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "start",
+ "description": "Start at this row #. Allows pagination through large response. If omitted, default is as stated in the Carbon Black API documentation, currently 0. ",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": [], "should": ["cb-process", "cb-binary"] }
+ },
+ {
+ "name": "CBSensors",
+ "script": "cbsensors.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["carbon-black", "endpoint"],
+ "comment": "List Carbon Black sensors",
+ "system": true,
+ "arguments": [],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cb-list-sensors"] }
+ },
+ {
+ "name": "CBSessions",
+ "script": "cbsessions.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["carbon-black", "endpoint"],
+ "comment": "List Carbon Black sessions",
+ "system": true,
+ "arguments": [
+ {
+ "name": "verbose",
+ "description": "Show more detailed output",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cb-list-sessions"] }
+ },
+ {
+ "name": "CBLiveProcessList",
+ "script": "cbliveprocesslist.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["carbon-black", "endpoint"],
+ "comment": "Runs 'process list' command on a remote Carbon Black sensor",
+ "system": true,
+ "arguments": [
+ {
+ "name": "sessionid",
+ "description": "ID of an active Carbon Black session",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cb-command-create", "cb-command-info"] }
+ },
+ {
+ "name": "CBEvents",
+ "script": "cbevents.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["carbon-black", "endpoint", "enhancement"],
+ "comment": "Returns all events associated with a process query",
+ "system": true,
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Query to match against events",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cb-process", "process-events"] }
+ },
+ {
+ "name": "AnalyzeMemImage",
+ "script": "analyzememimage.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["memory", "forensics", "volatility", "server"],
+ "comment": "Use Volatility to run common memory image analysis commands",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": true
+ },
+ {
+ "name": "system",
+ "description": "System with Volatility installed to be used for the analysis",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "EsmExample",
+ "script": "ESMNitroExample.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["esm", "nitro", "siem", "example"],
+ "comment": "Example of using McAfee ESM (Nitro) with advanced filters",
+ "system": true,
+ "arguments": [
+ {
+ "name": "ip",
+ "description": "IP Address to find",
+ "required": true,
+ "default": true
+ },
+ {
+ "name": "limit",
+ "description": "Query limit. If omitted, defaults to 100.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "PagerDutyAssignOnCallUser",
+ "script": "pagerdutyassign.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["pagerduty", "communication"],
+ "comment": "By default assigns the first on-call user to an investigation (all incidents in the investigation will be owned by the on call user)",
+ "system": true,
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Filter user. E.g. query=dan will find the first on call user with the string \"dan\" in their name",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["PagerDutyGetUsersOnCallNow"] }
+ },
+ {
+ "name": "PagerDutyAlertOnIncident",
+ "script": "pagerdutyalert.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["pagerduty", "communication"],
+ "comment": "Send incident details to pagerduty (useful to include in playbooks)",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["pagerDutySubmitEvent"] }
+ },
+ {
+ "name": "WildfireReport",
+ "script": "wildfirereport.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["enhancement", "wildfire", "sandbox"],
+ "comment": "Use wildfire file analysis and parse result",
+ "system": true,
+ "arguments": [
+ {
+ "name": "md5",
+ "description": "MD5 Hash to look up",
+ "required": false,
+ "default": true
+ },
+ {
+ "name": "hash",
+ "description": "Hash to look up",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["wildfire-report"] }
+ },
+ {
+ "name": "WildfireUpload",
+ "script": "wildfireupload.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["wildfire", "sandbox"],
+ "comment": "Upload file to wildfire for analysis.\nExample usage: !WildfireUpload upload=2@3",
+ "system": true,
+ "arguments": [
+ {
+ "name": "upload",
+ "description": "ID of a war room entry containing a file to be uploaded to the WildFire service",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["wildfire-upload"] }
+ },
+ {
+ "name": "PanoramaConfig",
+ "script": "panoramaconfig.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["panorama", "firewall"],
+ "comment": "Set panorama configuration",
+ "system": true,
+ "arguments": [
+ {
+ "name": "ruleName",
+ "description": "Name for the rule to be configured",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "xpath",
+ "description": "Usually not required as ruleName is sufficient. Direct XPath to the rule entry to be modified.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "srcIP",
+ "description": "Source IP address",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "dstIP",
+ "description": "Destination IP address",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "negateSrc",
+ "description": "Yes/No - whether to negate the source IP address (match all except the specified address)",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "negateDst",
+ "description": "Yes/No - whether to negate the destination IP address (match all except the specified address)",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "action",
+ "description": "Action for the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "service",
+ "description": "Service for the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "disable",
+ "description": "Yes/No - whether to disable the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "application",
+ "description": "Application for the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "srcUser",
+ "description": "Source user for the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "from",
+ "description": "\"From\" value for the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "to",
+ "description": "\"To\" value for the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "disableServerResponseInspection",
+ "description": "Value for the \"disable-server-response-inspection\" option",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "description",
+ "description": "Rule description",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["panorama"] }
+ },
+ {
+ "name": "PanoramaPcaps",
+ "script": "panoramapcaps.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["panorama", "firewall"],
+ "comment": "Get panorama pcaps",
+ "system": true,
+ "arguments": [
+ {
+ "name": "pcapType",
+ "description": "Type of Packet Capture",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "from",
+ "description": "\"From\" value for the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "to",
+ "description": "\"To\" value for the rule",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "serialNo",
+ "description": "Serial number for the request. For further information, see the Panorama XML API Documentation.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "searchTime",
+ "description": "Search time for the request. For further information, see the Panorama XML API Documentation.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "pcapID",
+ "description": "ID of the Pcap for the request. For further information, see the Panorama XML API Documentation.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "password",
+ "description": "Password for Panorama.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["panorama"] }
+ },
+ {
+ "name": "PanoramaMove",
+ "script": "panoramamove.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["panorama", "firewall"],
+ "comment": "Move panorama rules",
+ "system": true,
+ "arguments": [
+ {
+ "name": "xpath",
+ "description": "Usually not required as \"src\" is sufficient. Full XPath to the source rule to be moved.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "src",
+ "description": "Source value for the request. For further information, see the Panorama XML API Documentation.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "where",
+ "description": "Values for the \"where\" parameter include \"before\", \"after\", \"top\" or \"bottom\". For further information, see the Panorama XML API Documentation.",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "dst",
+ "description": "Destination xpath to move the rule. For further information, see the Panorama XML API Documentation.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["panorama"] }
+ },
+ {
+ "name": "PanoramaCommit",
+ "script": "panoramacommit.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["panorama", "firewall"],
+ "comment": "Commit configuration to panorama",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["panorama"] }
+ },
+ {
+ "name": "RemoteExec",
+ "script": "RemoteExec.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["endpoint"],
+ "arguments": [
+ {
+ "name": "system",
+ "description": "Name of system on which to run the command",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "cmd",
+ "description": "Command to run",
+ "required": true,
+ "default": false
+ }
+ ],
+ "comment": "Execute a command on a remote machine (without installing a D2 agent)",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ssh"] }
+ },
+ {
+ "name": "BinaryReputationPy",
+ "script": "binaryreputation.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["hash", "server", "threat-intel", "virustotal", "xfe", "wildfire"],
+ "arguments": [
+ {
+ "name": "fileNames",
+ "description": "If provided, checks only files whose names are in the list. The names should be comma-separated.",
+ "required": false,
+ "default": true
+ }
+ ],
+ "comment": "Get reputation for any hash or file in the incident details",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["file"] }
+ },
+ {
+ "name": "BinarySearchPy",
+ "script": "binarysearch.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["hash", "server", "endpoint", "carbon-black"],
+ "arguments": [],
+ "comment": "Search for a binary on an endpoint using Carbon Black",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["process"] }
+ },
+ {
+ "name": "CheckFilesWildfirePy",
+ "script": "checkfileswildfire.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["hash", "server", "threat-intel", "wildfire"],
+ "arguments": [
+ {
+ "name": "fileNames",
+ "description": "If provided, checks only files whose names are in the list. The names should be comma-separated.",
+ "required": false,
+ "default": true
+ }
+ ],
+ "comment": "Get reputation for attachments using wildfire and if the file is not known upload it to wildfire, wait 15 min and get reputation again",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["wildfire-upload", "wildfire-report"]}
+ },
+ {
+ "name": "CheckIPs",
+ "script": "checkips.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "threat-intel", "virustotal", "xfe"],
+ "arguments": [
+ {
+ "name": "data",
+ "description": "Raw text from which to extract IP addresses using the appropriate regular expression. If omitted, will scan Incident Details instead.",
+ "required": false,
+ "default": true
+ }
+ ],
+ "comment": "Get reputation for IPs in the incident or given raw text",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ips"] }
+ },
+ {
+ "name": "CheckSenderDomainDistance",
+ "script": "checksenderdomaindistance.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "phishing"],
+ "arguments": [
+ {
+ "name": "domain",
+ "description": "The domain to be measured against the domain in the sender's email address.\nUsually the domain used by the company for email, e.g. acme.com when users are assigned jane@acme.com",
+ "required": false,
+ "default": true
+ }
+ ],
+ "comment": "Get the string distance for the sender from our domain",
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "CheckSenderPy",
+ "script": "checksender.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "phishing"],
+ "arguments": [
+ {
+ "name": "email",
+ "description": "Email address to look up. If omitted, will instead extract with regular expression from Incident Details, where the phishing email should be provided.",
+ "required": false,
+ "default": true
+ }
+ ],
+ "comment": "For phishing incidents, check the sender of the email via Pipl search",
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "CheckURLs",
+ "script": "checkurls.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "threat-intel", "xfe", "virustotal"],
+ "arguments": [
+ {
+ "name": "data",
+ "description": "Raw text from which to extract URLs using the appropriate regular expression. If omitted, will scan Incident Details instead.",
+ "required": false,
+ "default": true
+ }
+ ],
+ "comment": "Check the URLs in the incident, or raw text provided as argument, for malicious URLs",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["url"] }
+ },
+ {
+ "name": "ExchangeFindAndDelete",
+ "script": "ExchangeFindAndDelete.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "response", "ews","exchange","email"],
+ "arguments": [
+ {
+ "name": "mailbox",
+ "description": "Mailbox to be searched, e.g. dave@acme.com",
+ "required": false,
+ "default": true
+ },
+ {
+ "name": "subject",
+ "description": "Only match mails containing this Subject",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "attachmentName",
+ "description": "Only match mails containing an attachment with this name",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "sender",
+ "description": "Only match mails from this sender (Email address)",
+ "required": false,
+ "default": false
+ }
+ ],
+ "comment": "Search for the given mail details and in the given mailbox delete found emails",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ews-delete-items", "ews-search-mailbox"] }
+ },
+ {
+ "name": "CloseInvestigation",
+ "script": "CloseInvestigation.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "management"],
+ "arguments": [
+ {
+ "name": "reason",
+ "description": "Reason for closing the investigation",
+ "required": false,
+ "default": true
+ }
+ ],
+ "comment": "Close an investigation",
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "XBInfo",
+ "script": "xbinfo.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "analytics", "exabeam"],
+ "arguments": [],
+ "comment": "Retrieve global Exabeam info about number of users, assets and events",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": [], "should": ["xb-users", "xb-assets", "xb-events"] }
+ },
+ {
+ "name": "XBNotable",
+ "script": "xbnotable.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "analytics", "exabeam"],
+ "arguments": [
+ {
+ "name": "rows",
+ "description": "Maximum number of result rows to return",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "days",
+ "description": "Number of days back to include in results",
+ "required": false,
+ "default": false
+ }
+ ],
+ "comment": "Retrieve notable Exabeam users",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["xb-notable"] }
+ },
+ {
+ "name": "XBLockouts",
+ "script": "xblockouts.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "analytics", "exabeam"],
+ "arguments": [
+ {
+ "name": "rows",
+ "description": "Maximum number of result rows to return",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "days",
+ "description": "Number of days back to include in results",
+ "required": false,
+ "default": false
+ }
+ ],
+ "comment": "Retrieve lockouts from Exabeam",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["xb-lockouts"] }
+ },
+ {
+ "name": "XBUser",
+ "script": "xbuser.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "analytics", "exabeam"],
+ "arguments": [
+ {
+ "name": "username",
+ "description": "Username for query",
+ "required": true,
+ "default": true
+ }
+ ],
+ "comment": "Retrieve user info from Exabeam",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["xb-user"] }
+ },
+ {
+ "name": "XBTimeline",
+ "script": "xbtimeline.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "analytics", "exabeam"],
+ "arguments": [
+ {
+ "name": "username",
+ "description": "Username for query",
+ "required": true,
+ "default": true
+ }
+ ],
+ "comment": "Retrieve timeline of a user",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["xb-timeline"] }
+ },
+ {
+ "name": "XBTriggeredRules",
+ "script": "xbtriggeredrules.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "analytics", "exabeam"],
+ "arguments": [
+ {
+ "name": "session",
+ "description": "Session to query",
+ "required": true,
+ "default": true
+ }
+ ],
+ "comment": "Retrieve the triggered rules for a session",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["xb-triggered-rules"] }
+ },
+ {
+ "name": "CYFileRep",
+ "script": "cyfilerep.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "threat-intel", "cylance", "file"],
+ "arguments": [
+ {
+ "name": "entry",
+ "description": "The ID of a file entry to upload",
+ "required": true,
+ "default": true
+ }
+ ],
+ "comment": "Retrieve file reputation and upload the file if required for analysis",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["file", "cy-upload"] }
+ },
+ {
+ "name": "CSActors",
+ "script": "csactors.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "threat-intel", "crowdstrike"],
+ "arguments": [
+ {
+ "name": "q",
+ "description": "Search all fields for the given data",
+ "required": false,
+ "default": true
+ },
+ {
+ "name": "name",
+ "description": "Search based on actor name",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "description",
+ "description": "Search based on description",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "minLastModifiedDate",
+ "description": "Search range from modified date. Dates are formatted as YYYY-MM-DD.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "maxLastModifiedDate",
+ "description": "Search range to modified date. Dates are formatted as YYYY-MM-DD.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "minLastActivityDate",
+ "description": "Search range from activity date. Dates are formatted as YYYY-MM-DD.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "maxLastActivityDate",
+ "description": "Search range to activity date. Dates are formatted as YYYY-MM-DD.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "origins",
+ "description": "Search by origins - takes a comma-separated list",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "targetCountries",
+ "description": "Search by target countries - takes a comma-separated list",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "targetIndustries",
+ "description": "Search by target industries - takes a comma-separated list",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "motivations",
+ "description": "Search by motivations - takes a comma-separated list",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "sort",
+ "description": "Sort is field_name.order, field_name.order where order is either asc or desc.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "offset",
+ "description": "Which page of the results to retrieve. It is 0 based.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "limit",
+ "description": "Number of results for the page",
+ "required": false,
+ "default": false
+ }
+ ],
+ "comment": "Query CrowdStrike actors based on given parameters. For fields like countries and industries, multiple values can be passed separated by ','.",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cs-actors"] }
+ },
+ {
+ "name": "CSIndicators",
+ "script": "csindicators.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "threat-intel", "crowdstrike"],
+ "arguments": [
+ {
+ "name": "parameter",
+ "description": "Based on what parameter to search.\nSee CrowdStrike documentation for details.\nCan be one of indicator, type, report, actor, malicious_confidence, published_date, last_updated, malware_family, kill_chain, labels, DomainType, EmailAddressType, IntelNews, IPAddressType, Malware, Status, Target, ThreatType, Vulnerability",
+ "required": true,
+ "default": true
+ },
+ {
+ "name": "filter",
+ "description": "Can be either match, equal, gt(e), lt(e)",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "value",
+ "description": "The value for the given parameter",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "sort",
+ "description": "Sort by a field. Should be field_name.order where order is either asc or desc. Fields are indicator, type, report, actor, malicious_confidence, published_date, last_updated.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "page",
+ "description": "The page to retrieve - 1 based",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "pageSize",
+ "description": "The size of the page to retrieve",
+ "required": false,
+ "default": false
+ }
+ ],
+ "comment": "Query CrowdStrike indicators based on given parameters.",
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cs-indicators"] }
+ },
+ {
+ "name": "SendEmail",
+ "script": "SendEmail.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["response", "email", "server"],
+ "comment": "Send an email with the specified parameters.\nAttachments are provided as a comma-separated list of entry IDs.\nExample usage: !SendEmail subject=\"File from war room\" body=\"Please see the attached file. --DBot\" to=jane@acme.com cc=john@acme.com attachIDs=89@3,46@3",
+ "arguments": [
+ {
+ "name": "to",
+ "description": "Email addresses for the 'to' field",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "cc",
+ "description": "Email addresses for the 'cc' field",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "bcc",
+ "description": "Email addresses for the 'bcc' field",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "subject",
+ "description": "Subject for the email to be sent",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "body",
+ "description": "The contents (body) of the email to be sent",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "attachIDs",
+ "description": "A comma-separated list of IDs of war room entries that contain files. Used to attach files to the outgoing email. Example: attachIDs=15@8,19@8",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["send-mail"] }
+ },
+ {
+ "name": "CopyFileD2",
+ "script": "CopyFileD2.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["util", "server"],
+ "comment": "Copy a file from an entry to the destination path on the specified system. This uses the dissolvable agent's HTTPS communication channel rather than scp or other out-of-band methods.\nExample usage: !CopyFileD2 destpath=/home/sansforensics/collectedbinaries/inv8_suspiciousPE1.exe.evil entryid=21@8 system=Analyst1",
+ "arguments": [
+ {
+ "name": "system",
+ "description": "System to which we want to copy the file",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "destpath",
+ "description": "Full filesystem path and filename under which to save the file",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "entryid",
+ "description": "ID of the war room entry containing the file to copy",
+ "required": true,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "ADGetUserGroups",
+ "script": "ADGetUserGroups.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Use Active Directory to retrieve the groups in which the specified user is a member. The user can be specified by name, email or as an Active Directory Distinguished Name (DN).",
+ "arguments": [
+ {
+ "name": "dn",
+ "description": "Active Directory Distinguished Name of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "name",
+ "description": "Name of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "email",
+ "description": "Email address of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADGetUsersByEmail",
+ "script": "ADGetUsersByEmail.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Use Active Directory to retrieve the user associated with the specified email address.",
+ "arguments": [
+ {
+ "name": "email",
+ "description": "Email address by which to search",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADGetEmailForUser",
+ "script": "ADGetEmailForUser.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Use Active Directory to retrieve the email address associated with the specified user. The user can be specified by name, email or as an Active Directory Distinguished Name (DN).",
+ "arguments": [
+ {
+ "name": "dn",
+ "description": "Active Directory Distinguished Name of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "name",
+ "description": "Name of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADGetComputerGroups",
+ "script": "ADGetComputerGroups.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Use Active Directory to retrieve the groups in which the specified computer is a member. The member computer can be specified by name or by DN.",
+ "arguments": [
+ {
+ "name": "dn",
+ "description": "Active Directory Distinguished Name for the desired computer",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "name",
+ "description": "Name of the desired computer",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADUserLogonInfo",
+ "script": "ADUserLogonInfo.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Use Active Directory to retrieve detailed information about a user account. The user can be specified by name, email or as an Active Directory Distinguished Name (DN).",
+ "arguments": [
+ {
+ "name": "dn",
+ "description": "Active Directory Distinguished Name for the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "name",
+ "description": "Name of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "email",
+ "description": "Email address of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADGetGroupComputers",
+ "script": "ADGetGroupComputers.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Use Active Directory to retrieve the list of computers that are members of the specified group. Group must be given by its AD Distinguished Name. The \"attributes\" argument receives a comma-separated list of additional attributes you wish to be displayed in the results.\nExample usage: !ADGetGroupComputers groupdn=\"CN=ImportantComputers,DC=demisto,DC=com\" attributes=operatingsystem ",
+ "arguments": [
+ {
+ "name": "groupdn",
+ "description": "Active Directory Distinguished Name for the desired group",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADGetGroupUsers",
+ "script": "ADGetGroupUsers.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Use Active Directory to retrieve the list of users who are members of the specified group. Group must be given by its AD Distinguished Name. The \"attributes\" argument receives a comma-separated list of additional attributes you wish to be displayed in the results.\nExample usage: !ADGetGroupUsers groupdn=\"CN=Domain Admins,CN=Users,DC=demisto,DC=com\" attributes=badPwdCount,memberOf ",
+ "arguments": [
+ {
+ "name": "groupdn",
+ "description": "Active Directory Distinguished Name for the desired group",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADListComputers",
+ "script": "ADListComputers.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Retrieve the list of Computer objects stored in Active Directory. Use the \"attributes\" argument to include specific attributes in the results. ",
+ "arguments": [
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADListUsers",
+ "script": "ADListUsers.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Retrieve the list of User objects stored in Active Directory. Use the \"attributes\" argument to include specific attributes in the results. ",
+ "arguments": [
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ADListUsersEx",
+ "script": "ADListUsersEx.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Retrieve the list of User objects stored in Active Directory and include an extended list of attributes and information about each user. Use the \"attributes\" argument to include additional specific attributes in the results.",
+ "arguments": [
+ {
+ "name": "attributes",
+ "description": "Include these AD attributes of the resulting objects in addition to the default ones",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "Strings",
+ "script": "strings.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["server", "file"],
+ "comment": "Extract strings from a file with optional filter - similar to binutils strings command",
+ "arguments": [
+ {
+ "name": "entry",
+ "required": true,
+ "default": true,
+ "description": "Entry ID of a file entry to retrieve strings from"
+ },
+ {
+ "name": "chars",
+ "required": false,
+ "default": false,
+ "description": "Number of consecutive characters to be considered a string - default is 4"
+ },
+ {
+ "name": "size",
+ "required": false,
+ "default": false,
+ "description": "Display first 'size' results - default is 1024"
+ },
+ {
+ "name": "filter",
+ "required": false,
+ "default": false,
+ "description": "Regex to filter the strings - compiled with ignore case"
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "RegProbeBasic",
+ "script": "RegProbeBasic.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["registry"],
+ "comment": "Perform a short probe of the specified system's registry - retrieve and display the values of a list of interesting keys ",
+ "arguments": [
+ {
+ "name": "system",
+ "required": true,
+ "default": true,
+ "description": "Name of the system to be queried"
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "RegCollectValues",
+ "script": "RegCollectValues.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["registry","enhancement"],
+ "comment": "Collect values for the given registry path from all Windows systems in this investigation.",
+ "arguments": [
+ {
+ "name": "regpath",
+ "required": true,
+ "default": true,
+ "description": "The registry path of the key to query"
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "RegPathReputationBasicLists",
+ "script": "RegPathReputationBasicLists.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["registry","reputation"],
+ "comment": "Check the given registry path against a small blacklist and whitelist. If the key matches neither, returns an answer of 2 meaning \"Unknown\".",
+ "arguments": [
+ {
+ "name": "input",
+ "required": true,
+ "default": true,
+ "description": "Registry path to be checked"
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "TaniumDeployAction",
+ "script": "taniumDeployAction.js",
+ "type": "javascript",
+ "visualScript": "",
+ "comment": "Execute an action, optionally with parameters, and filtering - based on an existing package. See https://kb.tanium.com/SOAP for more information",
+ "system": true,
+ "tags": ["tanium"],
+ "arguments": [
+ {
+ "name": "packageName",
+ "description": "Specify the package name. e.g. !TaniumDeployAction packageName=\"Clean Stale Tanium Client Data\"",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "packageID",
+ "description": "Use the package ID instead of its name",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "parameters",
+ "description": "Use parameters with the package. See https://kb.tanium.com/SOAP for more details",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "comment",
+ "description": "Comment to be added to the action history in the Tanium server",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["tn-deploy-package"] }
+ },
+ {
+ "name": "TaniumAskQuestion",
+ "script": "taniumAskQuestion.js",
+ "type": "javascript",
+ "visualScript": "",
+ "comment": "Send a request for a formatted result of a saved question. To receive the most up to date data, run the same command twice. See https://kb.tanium.com/SOAP for more information",
+ "system": true,
+ "tags": ["tanium"],
+ "arguments": [
+ {
+ "name": "name",
+ "description": "Retrieve the question by its name. E.g. !TaniumAskQuestion name=\"Running Services\"",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "id",
+ "description": "Retrieve the question by its ID. To view ID in the Tanium console go to: Authoring -> Saved Questions tab. This shows you a list of Saved Questions. Click the edit button on a Saved Question and the ID will be shown in the lower left corner",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "timeout",
+ "description": "Force Tanium to respond after x seconds (even if data was not collected fully by Tanium)",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["tn-result-data", "tn-result-info"] }
+ },
+ {
+ "name": "TaniumAskQuestionComplex",
+ "script": "taniumAskQuestionComplex.js",
+ "type": "javascript",
+ "visualScript": "",
+ "comment": "TaniumAskQuestionComplex - same as the AskQuestion command with additional filtering prepared by the script (an XML subsection added to the request).",
+ "system": true,
+ "tags": ["tanium"],
+ "arguments": [
+ {
+ "name": "timeout",
+ "description": "Force Tanium to respond after x seconds (even if data was not collected fully by Tanium)",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["tn-result-data", "tn-result-info", "tn-add-question-complex"] }
+ },
+ {
+ "name": "ADIsUserMember",
+ "script": "ADIsUserMember.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["active directory"],
+ "comment": "Use Active Directory to check if the specified user is a member of the specified group. Returns simply yes/no. The user can be specified by name, email or as an Active Directory Distinguished Name (DN).",
+ "system": true,
+ "arguments": [
+ {
+ "name": "dn",
+ "description": "Active Directory Distinguished Name of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "name",
+ "description": "Name of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "email",
+ "description": "Email address of the desired user",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "groupname",
+ "description": "Name of the AD group to check",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ad-search"] }
+ },
+ {
+ "name": "ConferIncidentDetails",
+ "script": "ConferIncidentDetails.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["confer"],
+ "comment": "Display the incident details retrieved from Confer in a readable format",
+ "arguments": [],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["confer"] }
+ },
+ {
+ "name": "ConferSetSeverity",
+ "script": "ConferSetSeverity.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["confer"],
+ "comment": "Set incident severity according to indicators found in an confer alert",
+ "arguments": [],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["confer"] }
+ },
+ {
+ "name": "CuckooDetonateFile",
+ "script": "CuckooDetonateFile.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["cuckoo"],
+ "comment": "Detonate the file in Cuckoo sandbox.",
+ "arguments": [
+ {
+ "name": "entryID",
+ "description": "ID of the entry containing the file to detonate.",
+ "required": true,
+ "default": true
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ck-file"] }
+ },
+ {
+ "name": "CuckooGetReport",
+ "script": "CuckooGetReport.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["cuckoo"],
+ "comment": "Get the report for a completed analysis.",
+ "arguments": [
+ {
+ "name": "taskID",
+ "description": "ID of the task in Cuckoo.",
+ "required": true,
+ "default": true
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ck-report"] }
+ },
+ {
+ "name": "CuckooTaskStatus",
+ "script": "CuckooTaskStatus.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["cuckoo"],
+ "comment": "Check the current status of a task in Cuckoo sandbox.",
+ "arguments": [
+ {
+ "name": "taskID",
+ "description": "ID of the task to check.",
+ "required": true,
+ "default": true
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["ck-view"] }
+ },
+ {
+ "name": "FPSetRule",
+ "script": "FPSetRule.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["forcepoint","triton"],
+ "comment": "Adds (or updates existing) rule in Forcepoint Triton. Preserves order of rules and modifies policy in-place if a rule exists with the exact type and value.",
+ "arguments": [
+ {
+ "name": "policy",
+ "description": "Policy/action assigned to the rule - \"allow\" or \"deny\" only.",
+ "required": true,
+ "default": true
+ },
+ {
+ "name": "type",
+ "description": "The Triton rule type - \"dest_domain\", \"dest_ip\", \"dest_host\" or \"url_regex\"",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "value",
+ "description": "The value to match for this rule (domain, regex, etc. depending on the type)",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "remoteaccessname",
+ "description": "If the Forcepoint Triton instance is configured as a RemoteAccess integration instance ‐ insert its name here. Replaces argument \"tritonsystem\".",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "tritonsystem",
+ "description": "System name of the linux host on which Forcepoint Triton is installed. Only use if not working with Triton as a RemoteAccess integration instance ‐ if so, use the \"remoteaccessname\" argument instead.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "FPDeleteRule",
+ "script": "FPDeleteRule.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["forcepoint","triton"],
+ "comment": "Deletes a rule in Forcepoint Triton.",
+ "arguments": [
+ {
+ "name": "type",
+ "description": "The Triton rule type - \"dest_domain\", \"dest_ip\", \"dest_host\" or \"url_regex\"",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "value",
+ "description": "The value to match for this rule (domain, regex, etc. depending on the type)",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "remoteaccessname",
+ "description": "If the Forcepoint Triton instance is configured as a RemoteAccess integration instance ‐ insert its name here. Replaces argument \"tritonsystem\".",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "tritonsystem",
+ "description": "System name of the linux host on which Forcepoint Triton is installed. Only use if not working with Triton as a RemoteAccess integration instance ‐ if so, use the \"remoteaccessname\" argument instead.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0
+ },
+ {
+ "name": "CBPApproveHash",
+ "script": "CBPApproveHash.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["carbon-black-protection","bit9"],
+ "comment": "Approve/whitelist a hash in CBEP/Bit9.",
+ "arguments": [
+ {
+ "name": "hash",
+ "description": "The hash value to approve.",
+ "required": true,
+ "default": true
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cbp-fileRule-update"] }
+ },
+ {
+ "name": "CBPBanHash",
+ "script": "CBPBanHash.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["carbon-black-protection","bit9"],
+ "comment": "Ban/blacklist a hash in CBEP/Bit9.",
+ "arguments": [
+ {
+ "name": "hash",
+ "description": "The hash value to ban.",
+ "required": true,
+ "default": true
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cbp-fileRule-update"] }
+ },
+ {
+ "name": "CBPFindComputer",
+ "script": "CBPFindComputer.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["carbon-black-protection","bit9"],
+ "comment": "Find a computer in CBEP/Bit9.",
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Only show computers matching this query. If omitted, displays all computers. Query is in CBEP/Bit9 syntax documented in https://developer.carbonblack.com/reference/enterprise-protection/7.2/rest-api/#query-condition - e.g. \"name:*srv*\"",
+ "required": false,
+ "default": true
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cbp-computer-search"] }
+ },
+ {
+ "name": "CBPFindRule",
+ "script": "CBPFindRule.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["carbon-black-protection","bit9"],
+ "comment": "Find the rule state for a hash value in CBEP/Bit9.",
+ "arguments": [
+ {
+ "name": "hash",
+ "description": "The hash value to check.",
+ "required": true,
+ "default": true
+ }
+ ],
+ "system": true,
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cbp-fileRule-search"] }
+ },
+ {
+ "name": "CBAlerts",
+ "script": "CBAlerts.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["carbon-black"],
+ "comment": "Get the list of Alerts from Carbon Black Enterprise Response. Supports the same arguments as the cb-alerts command.",
+ "system": true,
+ "arguments": [],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cb-alerts"] }
+ },
+ {
+ "name": "CBWatchlists",
+ "script": "CBWatchlists.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["carbon-black"],
+ "comment": "Display all watchlists and their details, queries, etc.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "id",
+ "description": "Display a specific watchlist by watchlist ID (numeric)",
+ "required": false,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0,
+ "dependsOn": { "must": ["cb-watchlist-get"] }
+ },
+ {
+ "name": "IncidentSet",
+ "script": "IncidentSet.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["management"],
+ "comment": "Modify incident info such as name, owner, type, etc.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "owner",
+ "description": "Incident owner - must be an existing user in the platform",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "playbook",
+ "description": "Assigned new playbook name",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "stage",
+ "description": "Incident stage - must be from a predefined list of stages",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "details",
+ "description": "Incident details",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "severity",
+ "description": "The severity to set. Can be \"low\",\"medium\",\"high\" or \"critical\".",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "type",
+ "description": "Incident type",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "name",
+ "description": "Incident name",
+ "required": false,
+ "default": true
+ },
+ {
+ "name": "updatePlaybookForType",
+ "description": "Should we also update the playbook according to the new given type. Can be yes or no. Default is yes.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "labels",
+ "description": "Set and override the labels for the incident. Labels expected format is [{\"labelName\": \"labelValue\"}, {\"labelName1\": \"labelValue1\"}] (JSON).",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "addLabels",
+ "description": "Add to the list of labels for the incident. Labels expected format is [{\"labelName\": \"labelValue\"}, {\"labelName1\": \"labelValue1\"}] (JSON).",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "SendURLDetailsByEmail",
+ "script": "sendurldetails.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["threat-intel"],
+ "comment": "Get all details about URL (reputation, number of ads, etc.) and send them via email",
+ "system": true,
+ "arguments": [
+ {
+ "name": "url",
+ "description": "The URL to get details for. If not provided, will be taken from incident details.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "subject",
+ "description": "The subject of the email. If not provided will be the incident name.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "recipient",
+ "description": "The recipient of the email",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "SendEmailToManager",
+ "script": "sendemailtomanager.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["communication"],
+ "comment": "Send an approval email to the manager of the employee with the given email allowing the manager to reply directly into the incident",
+ "system": true,
+ "arguments": [
+ {
+ "name": "email",
+ "description": "The employee email. We will send an email to his manager. If not provided will be taken from incident label 'Email/from'",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "manager",
+ "description": "The manager attribute in Active Directory. Default is 'manager'.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "entitlement",
+ "description": "If provided (any value), we will add an entitlement to the subject allowing manager to reply to war room",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "body",
+ "description": "The contents of the email body. It's a template that can include $empName and $managerName which will be replaced with actual values.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "request",
+ "description": "The contents of the request from the manager. Will be added below the body. If none is provided, incident details will be taken.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "URLExtract",
+ "script": "URLExtract.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["url", "infra"],
+ "comment": "Extract URLs from the given text and place them both as output and in the context of a playbook",
+ "system": true,
+ "arguments": [
+ {
+ "name": "text",
+ "description": "The text to extract URLs from",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "IPExtract",
+ "script": "IPExtract.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["ip", "infra"],
+ "comment": "Extract IPs from the given text and place them both as output and in the context of a playbook",
+ "system": true,
+ "arguments": [
+ {
+ "name": "text",
+ "description": "The text to extract ip from",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "MD5Extract",
+ "script": "MD5Extract.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["hash", "infra"],
+ "comment": "Extract md5s from the given text and place them both as output and in the context of a playbook",
+ "system": true,
+ "arguments": [
+ {
+ "name": "text",
+ "description": "The text to extract md5 from",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "TextFromHTML",
+ "script": "TextFromHTML.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["infra"],
+ "comment": "Extract regular text from the given HTML",
+ "system": true,
+ "arguments": [
+ {
+ "name": "html",
+ "description": "The HTML to strip tags from",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0
+ },
+ {
+ "name": "PrintContext",
+ "script": "PrintContext.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["infra"],
+ "comment": "Pretty-print the contents of the playbook context",
+ "system": true,
+ "arguments": [],
+ "scriptTarget": 0
+ },
+ {
+ "name": "IncidentToContext",
+ "script": "IncidentToContext.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["infra"],
+ "comment": "Inserts incident info and labels into context for use inside playbooks.",
+ "system": true,
+ "arguments": [],
+ "scriptTarget": 0
+ },
+ {
+ "name": "SlackSend",
+ "script": "SlackSend.py",
+ "type": "python",
+ "visualScript": "",
+ "tags": ["slack"],
+ "comment": "Send messages to Slack teams",
+ "system": true,
+ "arguments": [
+ {
+ "name": "to",
+ "description": "Send a Direct message by specifying a Slack user name",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "channel",
+ "description": "Specify a Slack channel where the message will appear.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "entry",
+ "description": "Send a link to this entry in the War Room",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "group",
+ "description": "Specify a private channel",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "message",
+ "description": "Message contents to send.",
+ "required": false,
+ "default": true
+ }
+ ],
+ "scriptTarget": 0
+ }
+ ],
+ "agent": [
+ {
+ "name": "VolMalfindDumpAgent",
+ "script": "volmalfinddumpagent.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["volatility"],
+ "comment": "Volatility script for command ldrmodules",
+ "system": true,
+ "arguments": [
+ {
+ "name": "memdump",
+ "description": "Path to memory dump file on the system being used",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "pid",
+ "description": "Process ID to pass to volatility malfind command",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "dumpdir",
+ "description": "Path to directory in which to save dumped memory sections",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2Processes",
+ "script": "D2processes.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","endpoint"],
+ "comment": "Show running processes",
+ "system": true,
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2Hardware",
+ "script": "D2hardware.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","endpoint"],
+ "comment": "Show system information",
+ "system": true,
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2Users",
+ "script": "D2users.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","endpoint"],
+ "comment": "Show local accounts",
+ "system": true,
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2GetSystemLog",
+ "script": "D2GetSystemLog.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","endpoint"],
+ "comment": "Copy a log file. Works on Windows and Unix (differently - take a peek at the script itself to see how).",
+ "system": true,
+ "arguments": [
+ {
+ "name": "logName",
+ "description": "Name of the log to retrieve",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2Services",
+ "script": "D2services.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","endpoint"],
+ "comment": "Show system services",
+ "system": true,
+ "scriptTarget": 1
+ },
+ {
+ "name": "CommonD2",
+ "script": "common-d2.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["infra","agent"],
+ "comment": "Common code that will be merged into each D2 agent script when it runs",
+ "system": false,
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2Exec",
+ "script": "D2exec.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","endpoint"],
+ "comment": "Execute the command and pack the output back to server",
+ "system": true,
+ "arguments": [
+ {
+ "name": "cmd",
+ "description": "System command to execute",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "VolRaw",
+ "script": "vol.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","volatility"],
+ "comment": "Execute volatility with command and file as parameters and returns raw output from stdout.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "cmd",
+ "description": "Volatility command to run",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "file",
+ "description": "Path of file to pass as argument to Volatility",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "VolJson",
+ "script": "voljson.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["volatility","agent"],
+ "comment": "Execute volatility with command and file as parameters and return output as json.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "cmd",
+ "description": "Volatility command to run",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "file",
+ "description": "Path of file to pass as argument to Volatility",
+ "required": true,
+ "default": false
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2PEDump",
+ "script": "D2pedump.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["executable","agent"],
+ "comment": "Execute PE Dump on a file that is under /tmp somewhere. Used internally by StaticAnalyze",
+ "system": true,
+ "arguments": [
+ {
+ "name": "file",
+ "description": "Path to the PE file to analyze, relative to /tmp",
+ "required": true,
+ "default": true
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "Osxcollector",
+ "script": "osxcollector.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["osx","agent"],
+ "comment": "Execute osxcollector on machine, can run ONLY on OSX",
+ "system": true,
+ "arguments": [
+ {
+ "name": "section",
+ "description": "Ask OSXCollector for a specific section",
+ "required": false,
+ "default": true
+ },
+ {
+ "name": "timeout",
+ "description": "Timeout for the OSXCollector execution to complete. If omitted, defaults to 10 minutes.",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "ExchangeSearchMailbox",
+ "script": "exsearchmailbox.js",
+ "type": "javascript",
+ "tags": ["exchange","email"],
+ "visualScript": "",
+ "comment": "Search all mailboxes on an Exchange server and copy the results to a specified target mailbox. This script runs through the agent on a Windows machine, pulls and executes a PowerShell script - which talks to the Exchange server.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Exchange query to match against emails",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "toMailbox",
+ "description": "Destination mailbox",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "toFolder",
+ "description": "Folder within destination mailbox in which to place matched emails",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "server",
+ "description": "Hostname of the Exchange server",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "ExchangeDeleteMail",
+ "script": "exdeletemail.js",
+ "type": "javascript",
+ "tags": ["exchange","email"],
+ "visualScript": "",
+ "comment": "Search all mailboxes on an Exchange server for a query and deletes all messages in which query is satisfied. This script runs through the agent on a Windows machine, pulls and executes a PowerShell script - which talks to the Exchange server.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "query",
+ "description": "Exchange query to match against emails",
+ "required": true,
+ "default": false
+ },
+ {
+ "name": "server",
+ "description": "Hostname of the Exchange server",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "ExchangeAssignRole",
+ "script": "exassignrole.js",
+ "type": "javascript",
+ "tags": ["exchange","email"],
+ "visualScript": "",
+ "comment": "Assign a 'Mailbox Import Export' management role to a user. This script runs through the agent on a Windows machine, pulls and executes a PowerShell script - which talks to the Exchange server.",
+ "system": true,
+ "arguments": [
+ {
+ "name": "username",
+ "description": "Username to whom to assign the role",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "role",
+ "description": "Choose a different role to assign to the user. Default is 'Mailbox Import Export'.",
+ "required": false,
+ "default": false
+ },
+ {
+ "name": "server",
+ "description": "Hostname of the Exchange server",
+ "required": false,
+ "default": false
+ }
+ ],
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2Drop",
+ "script": "D2Drop.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","util"],
+ "comment": "Drop a file to a target system by providing its path on the server. Use CopyFileD2 instead in most cases.\nThis is a utility agent script to be used inside server scripts. See CopyFileD2 for an example.",
+ "arguments": [
+ {
+ "name": "destpath",
+ "description": "Full filesystem path and filename under which to save the file",
+ "required": true,
+ "default": false
+ }
+ ],
+ "system": true,
+ "scriptTarget": 1
+ },
+ {
+ "name": "D2RegQuery",
+ "script": "D2RegQuery.js",
+ "type": "javascript",
+ "visualScript": "",
+ "tags": ["agent","registry"],
+ "comment": "Use the D2 agent to retrieve the value of the given registry key.",
+ "arguments": [
+ {
+ "name": "regpath",
+ "required": true,
+ "default": true,
+ "description": "The registry path of the key to be queried"
+ }
+ ],
+ "system": true,
+ "scriptTarget": 1
+ }
+
+ ]
+}
diff --git a/Scripts/searchmailbox.js b/Scripts/searchmailbox.js
new file mode 100644
index 000000000000..63fdd22bb29f
--- /dev/null
+++ b/Scripts/searchmailbox.js
@@ -0,0 +1,33 @@
+//+ exchange/search.ps1
+
+//Params:
+//See here: https://technet.microsoft.com/en-us/library/dd298173(v=exchg.160).aspx
+//query is the string provided to the -SearchQuery parameter
+//to-mailbox is the string provided to -TargetMailbox parameter
+//to-folder is the string provided to -TargetFolder parameter
+//if server is provided, it'll be used as -ServerFQDN parameter to Connect-ExchangeServer cmdlet
+
+if ((env.ARCH !== "amd64") && (env.OS !== "windows")) {
+ throw("Script can ran only in 64bit Windows Agents");
+}
+
+var command = [];
+command.push("powershell.exe");
+command.push("-version 2.0");
+command.push("-NonInteractive");
+command.push("-NoLogo");
+command.push(which("search.ps1"));
+command.push("-query");
+command.push(args.query);
+command.push("-targetmbx");
+command.push(args["to-mailbox"]);
+command.push("-targetFolder");
+command.push(args["to-folder"]);
+
+if (typeof (args.server) !== "undefined") {
+ command.push("-server");
+ command.push(args.server);
+}
+
+pack(execute(command.join(" ")), 'table');
+
diff --git a/Scripts/sendemailtomanager.py b/Scripts/sendemailtomanager.py
new file mode 100644
index 000000000000..10ba6c4f1830
--- /dev/null
+++ b/Scripts/sendemailtomanager.py
@@ -0,0 +1,71 @@
+email = demisto.get(demisto.args(), 'email')
+if not email:
+ for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email/from':
+ email = t['value'].lower()
+
+if not email:
+ demisto.results('Could not find employee email. Quiting.')
+ sys.exit(0)
+
+managerAttrubute = demisto.get(demisto.args(), 'manager')
+if not managerAttrubute:
+ managerAttrubute = 'manager'
+
+res = demisto.executeCommand('ad-search', {'filter': r'(&(objectClass=user)(mail=' + email + '))', 'attributes': 'displayname,' + managerAttrubute})
+if isError(res[0]):
+ demisto.results(res)
+ sys.exit(0)
+
+managerDN = demisto.get(res[0]['Contents'][0], managerAttrubute)
+empName = demisto.get(res[0]['Contents'][0], 'displayname')
+
+if not managerDN:
+ demisto.results('Unable to get manager email')
+ sys.exit(0)
+
+filterstr = r'(&(objectClass=User)(distinguishedName=' + managerDN + '))'
+res = demisto.executeCommand('ad-search', {'filter': filterstr, 'attributes': 'displayname,mail'})
+if isError(res[0]):
+ demisto.results(res)
+ sys.exit(0)
+
+managerEmail = demisto.get(res[0]['Contents'][0], 'mail')
+managerName = demisto.get(res[0]['Contents'][0], 'displayname')
+if not managerDN:
+ demisto.results('Unable to get manager email from DN - ' + managerDN)
+ sys.exit(0)
+
+entitlement = demisto.get(demisto.args(), 'entitlement')
+if entitlement:
+ res = demisto.executeCommand('addOneTimeEntitlement', {})
+ if isError(res[0]):
+ demisto.results(res)
+ sys.exit(0)
+ entitlement = demisto.get(res[0], 'Contents')
+ if not entitlement:
+ demisto.results('Unable to get entitlement')
+ sys.exit(0)
+ subject = demisto.gets(demisto.incidents()[0], 'name') + ' - #' + demisto.investigation()['id'] + ' ' + entitlement
+else:
+ subject = demisto.gets(demisto.incidents()[0], 'name') + ' - #' + demisto.investigation()['id']
+
+from string import Template
+import textwrap
+
+body = demisto.get(demisto.args(), 'body')
+if not body:
+ body = """\
+ Hi $managerName,
+ We've received the following request below from $empName. Please reply to this email with either "approve" or "deny".
+
+ Cheers,
+ Your friendly security team"""
+
+actualBody = Template(body)
+
+empRequest = demisto.get(demisto.args(), 'request')
+if not empRequest:
+ empRequest = demisto.incidents()[0]['details']
+
+demisto.results(demisto.executeCommand('send-mail', {'to': managerEmail, 'subject': subject, 'body': textwrap.dedent(actualBody.safe_substitute(managerName=managerName, empName=empName)) + '\n----------' + empRequest}))
diff --git a/Scripts/sendgotitemailtotarget.py b/Scripts/sendgotitemailtotarget.py
new file mode 100644
index 000000000000..e5ca85623d19
--- /dev/null
+++ b/Scripts/sendgotitemailtotarget.py
@@ -0,0 +1,28 @@
+# Find the recipient of the email - should be the target
+target = ''
+for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email/from':
+ target = t['value']
+ break
+
+if target == '':
+ for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email':
+ target = t['value']
+ break
+
+if target == '':
+ demisto.results({'Type': entryTypes['error'], 'ContentsFormat': formats['text'], 'Contents': 'Could not find the target email'})
+else:
+ from string import Template
+ import textwrap
+ defaultBody = """\
+ Hi $target,
+ We've received your email and are investigating. Please do not touch the email until further notice.
+
+ Cheers,
+ Your friendly security team"""
+ body = demisto.args()['body'] if demisto.get(demisto.args(), 'body') else defaultBody
+ actualBody = Template(body)
+ subject = demisto.args()['subject'] if demisto.get(demisto.args(), 'subject') else 'Security Email Re: ' + demisto.incidents()[0]['name']
+ demisto.results(demisto.executeCommand('send-mail', {'to': target, 'subject': subject, 'body': textwrap.dedent(actualBody.safe_substitute(target=target))}))
diff --git a/Scripts/senditsbademailtotarget.py b/Scripts/senditsbademailtotarget.py
new file mode 100644
index 000000000000..9c8d74179d57
--- /dev/null
+++ b/Scripts/senditsbademailtotarget.py
@@ -0,0 +1,29 @@
+# Find the recipient of the email - should be the target
+target = ''
+for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email/from':
+ target = t['value']
+ break
+
+if target == '':
+ for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email':
+ target = t['value']
+ break
+
+if target == '':
+ demisto.results({'Type': entryTypes['error'], 'ContentsFormat': formats['text'], 'Contents': 'Could not find the target email'})
+else:
+ from string import Template
+ import textwrap
+ defaultBody = """\
+ Hi $target,
+ We've concluded that the email you forwarded to us is malicious. We've taken steps to blacklist the sender and quarantine the email.
+ Good job on detecting and forwarding it to us!
+
+ Cheers,
+ Your friendly security team"""
+ body = demisto.args()['body'] if demisto.get(demisto.args(), 'body') else defaultBody
+ actualBody = Template(body)
+ subject = demisto.args()['subject'] if demisto.get(demisto.args(), 'subject') else 'Security Email Re Malicious: ' + demisto.incidents()[0]['name']
+ demisto.results(demisto.executeCommand('send-mail', {'to': target, 'subject': subject, 'body': textwrap.dedent(actualBody.safe_substitute(target=target))}))
diff --git a/Scripts/senditsgoodemailtotarget.py b/Scripts/senditsgoodemailtotarget.py
new file mode 100644
index 000000000000..40fa47ede4b9
--- /dev/null
+++ b/Scripts/senditsgoodemailtotarget.py
@@ -0,0 +1,29 @@
+# Find the recipient of the email - should be the target
+target = ''
+for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email/from':
+ target = t['value']
+ break
+
+if target == '':
+ for t in demisto.incidents()[0]['labels']:
+ if t['type'] == 'Email':
+ target = t['value']
+ break
+
+if target == '':
+ demisto.results({'Type': entryTypes['note'], 'ContentsFormat': formats['text'], 'Contents': 'Could not find the target email'})
+else:
+ from string import Template
+ import textwrap
+ defaultBody = """\
+ Hi $target,
+ We've concluded that the email you forwarded to us is benign.
+ Good job on being alert and forwarding it to us!
+
+ Cheers,
+ Your friendly security team"""
+ body = demisto.args()['body'] if demisto.get(demisto.args(), 'body') else defaultBody
+ actualBody = Template(body)
+ subject = demisto.args()['subject'] if demisto.get(demisto.args(), 'subject') else 'Security Email Re Malicious: ' + demisto.incidents()[0]['name']
+ demisto.results(demisto.executeCommand('send-mail', {'to': target, 'subject': subject, 'body': textwrap.dedent(actualBody.safe_substitute(target=target))}))
diff --git a/Scripts/sendurldetails.py b/Scripts/sendurldetails.py
new file mode 100644
index 000000000000..38770a441451
--- /dev/null
+++ b/Scripts/sendurldetails.py
@@ -0,0 +1,97 @@
+import re
+import urllib2
+import ssl
+#import os
+
+strURLRegex = r'(?i)(?:(?:https?|ftp):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])'
+
+filtered = ['http://schemas.microsoft.com/office/2004/12/omml', 'http://www.w3.org/TR/REC-html40']
+res = []
+cleanUrls = []
+
+url = demisto.get(demisto.args(), 'url')
+if not url:
+ url = demisto.incidents()[0]['details']
+
+subject = demisto.get(demisto.args(), 'subject')
+if not subject:
+ subject = demisto.incidents()[0]['name']
+
+body = """\
+Hi whitelisting team,
+We've received the following request to whitelist the below. Here are the details we have about the URL."""
+
+suffix = """\
+
+
+Cheers,
+Your friendly security team"""
+
+def ads(html,termlist):
+ results = {}
+ tags = re.findall("<[^/][^>]*>",html)
+ for item in termlist.split('\n'):
+ if not item.strip():
+ continue
+ if item.startswith('!') or item.startswith('[Adbl') or item.startswith('@@'):
+ continue
+ if item.startswith('###'):
+ item = item[3:]
+ if item.startswith('||'):
+ item = item[2:item.find("^$")]
+ for t in tags:
+ if item in t:
+ results[item] = (results[item] + 1) if item in results else 1
+ return results
+
+ctx = ssl.create_default_context()
+ctx.check_hostname = False
+ctx.verify_mode = ssl.CERT_NONE
+
+#os.environ["http_proxy"] = ""
+#os.environ["https_proxy"] = ""
+
+easylist = urllib2.urlopen('https://easylist.github.io/easylist/easylist.txt', context=ctx).read()
+
+for m in re.finditer(strURLRegex, url, re.I):
+ u = m.group(0)
+ if u in filtered:
+ continue
+ if u in cleanUrls:
+ continue
+ if 'mailto:' in u:
+ continue
+ cleanUrls.append(u)
+ rep = demisto.executeCommand('url', {'url': u})
+ for entry in rep:
+ if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ c = entry['Contents']
+ if entry['Brand'] == brands['xfe']:
+ body += \
+ '\n\nURL: ' + demisto.gets(c, 'url.result.url') + \
+ '\nA: ' + demisto.gets(c, 'resolution.A') + \
+ '\nAAAA: ' + demisto.gets(c, 'resolution.AAAA') + \
+ '\nContry: ' + demisto.gets(c, 'country') + \
+ '\nCategories: ' + demisto.gets(c, 'url.result.cats') + \
+ '\nScore: ' + demisto.gets(c, 'url.result.score') + \
+ '\nMalwareCount: ' + demisto.gets(c, 'malware.count') + \
+ '\nProvider: ' + providers['xfe'] + \
+ '\nProviderLink: ' + 'https://exchange.xforce.ibmcloud.com/url/' + demisto.gets(c, 'url.result.url')
+ if entry['Brand'] == brands['vt']:
+ body += \
+ '\n\nURL: ' + demisto.gets(c, 'url') + \
+ '\nScanDate: ' + demisto.gets(c, 'scan_date') + \
+ '\nPositives: ' + demisto.gets(c, 'positives') + \
+ '\nTotal: ' + demisto.gets(c, 'total') + \
+ '\nProvider: ' + providers['vt'] + \
+ '\nProviderLink ' + demisto.gets(c, 'permalink')
+
+ # Now, get the number of ads and external links
+ html = urllib2.urlopen(u, context=ctx).read()
+ hits = ads(html, easylist)
+ score = sum(hits.values())
+ body += '\n\nTotal ads score is: ' + str(score)
+ body += '\nTotal number of links is: ' + str(len(re.findall(strURLRegex, html, re.I)))
+
+body += suffix
+demisto.results(demisto.executeCommand('send-mail', {'to': demisto.args()['recipient'], 'subject': subject, 'body': body + '\n\n--------------------\n\n' + url}))
diff --git a/Scripts/splunksearch.js b/Scripts/splunksearch.js
new file mode 100644
index 000000000000..830c25a87645
--- /dev/null
+++ b/Scripts/splunksearch.js
@@ -0,0 +1,15 @@
+var rows = args.rows ? args.rows : 30;
+var query = (args.query.indexOf('|') > 0) ? args.query : args.query + ' | head ' + rows;
+
+var res = executeCommand('search', {'using-brand': 'splunk', query: query});
+var table = {
+ Type: 1,
+ ContentsFormat: 'table',
+ Contents: []
+};
+
+for (var i=0; i 0) {
+ return res;
+}
+return 'No files found';
diff --git a/Scripts/strings.py b/Scripts/strings.py
new file mode 100644
index 000000000000..199e3bd05f0a
--- /dev/null
+++ b/Scripts/strings.py
@@ -0,0 +1,45 @@
+import re
+import string
+
+# Optional arguments and default values
+chars = 4
+if 'chars' in demisto.args():
+ chars = int(demisto.args()['chars'])
+
+size = 1024
+if 'size' in demisto.args():
+ size = int(demisto.args()['size'])
+
+regex = None
+if 'filter' in demisto.args():
+ regex = re.compile(demisto.args()['filter'], re.I)
+
+fEntry = demisto.executeCommand('getFilePath', {'id': demisto.args()['entry']})[0]
+if not isError(fEntry):
+ matches = []
+ with open(demisto.get(fEntry, 'Contents.path'), 'rb', 1024 * 1024) as f:
+ buff = ''
+ c = f.read(1)
+ while c != '':
+ if c in string.printable:
+ buff += c
+ if len(buff) >= 32 * 1024:
+ matches.append('File is a regular text file')
+ break
+ else:
+ if len(buff) >= chars:
+ if regex:
+ if regex.match(buff):
+ matches.append(buff)
+ else:
+ matches.append(buff)
+ if len(matches) >= size:
+ break
+ buff = ''
+ c = f.read(1)
+ if matches:
+ demisto.results('\n'.join(matches))
+ else:
+ demisto.results('No strings were found.')
+else:
+ demisto.results(fEntry)
diff --git a/Scripts/taniumAskQuestion.js b/Scripts/taniumAskQuestion.js
new file mode 100644
index 000000000000..fbab2126664a
--- /dev/null
+++ b/Scripts/taniumAskQuestion.js
@@ -0,0 +1,95 @@
+// An example script for asking Tanium a question
+
+/* name - The name of the Saved-Question in Tanium server (mandatory if no 'id' was specified)
+For example:
+ Computer Name
+ User Information
+ Adobe Acrobat Versions
+ BIOS Information
+ CPU Utilization Over 75%
+ Installed Applications
+ ...
+*/
+// id - The ID of the Saved-Question in Tanium server (mandatory if no 'name' was specified)
+// timeout - seconds to wait on each iteration while waiting for the question's result
+var taniumArgs = {
+ 'name': args.name,
+ 'id': args.id,
+ 'timeout': args.timeout
+ };
+
+var table = {
+ Type: 1,
+ ContentsFormat: 'table',
+ Contents: []
+};
+
+var res = executeCommand('tn-result-info', taniumArgs);
+if (res[0].Type !== entryTypes.error) {
+ if (!res[0].Contents.result_infos) {
+ return res[0].Contents.Envelope.Body.return.command;
+ }
+
+ // Need to compare 'mr_passed' and 'estimated_total' values,
+ // to confirm that all machines have answered
+ var answers = res[0].Contents.result_infos.result_info.mr_passed;
+ var expectedAnswers = res[0].Contents.result_infos.result_info.estimated_total;
+
+ // Check the status 10 times, and wait 'timeout' seconds between each iteration
+ var iterToWait = 10;
+ var sec = 1;
+ if (taniumArgs.timeout) {
+ sec = parseInt(taniumArgs.timeout) || 1;
+ }
+ while (answers !== expectedAnswers && iterToWait-- > 0) {
+ wait(sec);
+ res = executeCommand('tn-result-info', taniumArgs);
+ if (res[0].Type === entryTypes.error) {
+ return res[0];
+ }
+ answers = res[0].Contents.result_infos.result_info.mr_passed;
+ expectedAnswers = res[0].Contents.result_infos.result_info.estimated_total;
+ }
+
+ // Get question data (i.e. question result)
+ var qDataRes = executeCommand('tn-result-data', taniumArgs);
+ if (!qDataRes[0].Contents.result_sets) {
+ return qDataRes[0].Contents.Envelope.Body.return.command;
+ }
+
+ // Extract the relevant fields from the data
+ itemCount = parseInt(qDataRes[0].Contents.result_sets.result_set.item_count);
+ if (itemCount === 0) {
+ return 'No results';
+ }
+
+ var output = "";
+ var cs = qDataRes[0].Contents.result_sets.result_set.cs;
+ var rs = qDataRes[0].Contents.result_sets.result_set.rs;
+
+ // When a single item is returned, rs.r is a single object. Otherwise, it is an array...
+ if (itemCount === 1) {
+ var row = {};
+ for (var i=0; i < cs.c.length; i++) {
+ //output += cs.c[i].dn + ': ' + rs.r.c[i].v + '\n';
+ row[cs.c[i].dn] = rs.r.c[i].v;
+ }
+ table.Contents.push(row);
+ } else {
+ for (var item=0; item < itemCount; item++) {
+ var row = {};
+ for (var j=0; j < cs.c.length; j++) {
+ // output += cs.c[j].dn + ': ' + rs.r[item].c[j].v + '\n';
+ row[cs.c[j].dn] = rs.r[item].c[j].v;
+ }
+ table.Contents.push(row);
+ }
+ }
+
+ if (table.Contents.length === 0) {
+ return 'No Results';
+ }
+ return table;
+}
+
+return res;
diff --git a/Scripts/taniumAskQuestionComplex.js b/Scripts/taniumAskQuestionComplex.js
new file mode 100644
index 000000000000..e5ddb59edcb4
--- /dev/null
+++ b/Scripts/taniumAskQuestionComplex.js
@@ -0,0 +1,159 @@
+// An example script for asking a complex question with filter and select-filter
+
+/* This script represents the following question:
+ "Get Computer Name and IP Address starts with 172 from machines where Operating System contains 2012"
+ Explanation:
+ There are two selects in this question: Computer Name and IP Address, but IP Address has what is called a select filter on it.
+ When a Tanium Client evaluates these selects, it will provide its computer name in response to the Computer Name select,
+ and it will respond with any IP Addresses that it has that start with the substring "172".
+ So, if a machine doesn't have any IP addresses that start with "172", it will actually return its computer name and "[no results]".
+ If it did have a valid IP address, it would return its computer name and the valid IP address(es).
+
+ The latter half of the question, "where Operating System contains 2012", is called a filter, which limits which machines respond.
+ For instance, when Tanium Clients see this Question, they will only evaluate the selects of the Question if their calculated Operating System value contains the substring "2012".
+ If you have no 2012 machines, then no machines will respond to your Question. This filter is similar to the select filter described in the previous paragraph,
+ but the is a very important distinction:
+ If a Tanium Client doesn't meet a filter, it doesn't respond.
+ If a Tanium Client doesn't meet a select filter, it will respond with the string "[no results]".
+*/
+
+var table = {
+ Type: 1,
+ ContentsFormat: 'table',
+ Contents: []
+};
+
+// "Get Computer Name and IP Address starts with 172"
+var selects = '\
+\
+ \
+ \
+';
+
+// "where Operating System contains 2012"
+var group = '\
+\
+ 1\
+ 0\
+ \
+ \
+ 1\
+ 0\
+ \
+ \
+ \
+ Operating System\
+ \
+ RegexMatch\
+ String\
+ .*2012.*\
+ 0\
+ 1\
+ \
+ \
+ \
+ 0\
+ \
+ \
+';
+
+var taniumArgs = {
+ 'selects': selects,
+ 'group': group,
+ 'timeout': args.timeout
+ };
+
+var res = executeCommand('tn-add-question-complex', taniumArgs);
+if (res[0].Type !== entryTypes.error) {
+ var result = res[0].Contents.Envelope.Body.return;
+ if (result.command !== 'AddObject') {
+ return result.command;
+ }
+ if (result.result_object.question.id) {
+ var qid = result.result_object.question.id;
+
+ // check the status of the new question, based on the returned ID
+ var infoRes = executeCommand('tn-result-info', {'id': qid, 'question-type': 'question'});
+ if (infoRes[0].Type !== entryTypes.error) {
+ if (!infoRes[0].Contents.result_infos) {
+ return infoRes[0].Contents.Envelope.Body.return.command;
+ }
+
+ // Need to compare 'mr_passed' and 'estimated_total' values,
+ // to confirm that all machines have answered
+ var answers = infoRes[0].Contents.result_infos.result_info.mr_passed;
+ var expectedAnswers = infoRes[0].Contents.result_infos.result_info.estimated_total;
+
+ // Check the status 10 times, and wait 'timeout' seconds between each iteration
+ var iterToWait = 10;
+ var sec = 1;
+ if (taniumArgs.timeout) {
+ sec = parseInt(taniumArgs.timeout) || 1;
+ }
+ while (answers !== expectedAnswers && iterToWait-- > 0) {
+ wait(sec);
+ infoRes = executeCommand('tn-result-info', {'id': qid, 'question-type': 'question'});
+ if (infoRes[0].Type === entryTypes.error) {
+ return infoRes[0];
+ }
+ answers = infoRes[0].Contents.result_infos.result_info.mr_passed;
+ expectedAnswers = infoRes[0].Contents.result_infos.result_info.estimated_total;
+ }
+
+ // Get question data (i.e. question result)
+ var qDataRes = executeCommand('tn-result-data', {'id': qid, 'question-type': 'question'});
+ if (!qDataRes[0].Contents.result_sets) {
+ return qDataRes[0].Contents.Envelope.Body.return.command;
+ }
+
+ // Extract the relevant fields from the data
+ itemCount = parseInt(qDataRes[0].Contents.result_sets.result_set.item_count);
+ if (itemCount === 0) {
+ return 'No results';
+ }
+
+ var output = "";
+ var cs = qDataRes[0].Contents.result_sets.result_set.cs;
+ var rs = qDataRes[0].Contents.result_sets.result_set.rs;
+
+ // When a single item is returned, rs.r is a single object. Otherwise, it is an array...
+ if (itemCount === 1) {
+ var row = {};
+ for (var i=0; i < cs.c.length; i++) {
+ //output += cs.c[i].dn + ': ' + rs.r.c[i].v + '\n';
+ row[cs.c[i].dn] = rs.r.c[i].v;
+ }
+ table.Contents.push(row);
+ } else {
+ for (var item=0; item < itemCount; item++) {
+ var row = {};
+ for (var j=0; j < cs.c.length; j++) {
+ // output += cs.c[j].dn + ': ' + rs.r[item].c[j].v + '\n';
+ row[cs.c[j].dn] = rs.r[item].c[j].v;
+ }
+ table.Contents.push(row);
+ }
+ }
+
+ if (table.Contents.length === 0) {
+ return 'No Results';
+ }
+ return table;
+ }
+ }
+}
+return res
diff --git a/Scripts/taniumDeployAction.js b/Scripts/taniumDeployAction.js
new file mode 100644
index 000000000000..a822b4df240a
--- /dev/null
+++ b/Scripts/taniumDeployAction.js
@@ -0,0 +1,44 @@
+// An example script for deploying Tanium action with filter
+
+if (!args.packageName && !args.packageID) {
+ return 'Missing parameter: packageName or packageID';
+}
+
+var packageName = args.packageName || '';
+var packageID = args.packageID || '';
+var packageParams = args.parameters || '';
+var comment = args.comment || '';
+
+// targetGroup is the properties of the systems on which the requested package should run
+var targetGroup = '\
+\
+ 1\
+ 0\
+ \
+ \
+ \
+ IP Address\
+ \
+ 0\
+ 1\
+ 0\
+ RegexMatch\
+ 172.*\
+ String\
+ \
+ \
+ 0\
+ \
+ Online is \"True\"\
+';
+
+var taniumArgs = {
+ 'package-name': packageName,
+ 'package-id': packageID,
+ 'parameters': packageParams,
+ 'comment': comment,
+ 'target-group': targetGroup,
+ };
+
+var res = executeCommand('tn-deploy-package', taniumArgs);
+return res
diff --git a/Scripts/triagephishing.js b/Scripts/triagephishing.js
new file mode 100644
index 000000000000..a625db0a745a
--- /dev/null
+++ b/Scripts/triagephishing.js
@@ -0,0 +1,85 @@
+var results = []; // Collect all the results
+var malicious = false;
+
+var body = incidents[0].details;
+var fromRE = /From:.*href="mailto:(.*?)"/ig;
+var from = fromRE.exec(body);
+var toRE = /To:.*href="mailto:(.*?)"/ig;
+var to = toRE.exec(body);
+var sentRE = /Sent:<\/b> (.*?)
/ig;
+var sent = sentRE.exec(body);
+var subjectRE = /Subject:<\/b> (.*?)/ig;
+var subject = subjectRE.exec(body);
+
+var fromStr = '';
+var toStr = '';
+var subjectStr = '';
+var sentStr = '';
+if (from && from.length > 1) {
+ fromStr = from[1];
+}
+if (to && to.length > 1) {
+ toStr = to[1];
+}
+if (sent && sent.length > 1) {
+ sentStr = sent[1];
+}
+if (subject && subject.length > 1) {
+ subjectStr = subject[1];
+}
+results.push({Contents: {From: fromStr, To: toStr, Sent: sentStr, Subject: subjectStr}, ContentsFormat: formats.table, Type: entryTypes.note}); // Add the raw email details
+
+// Check the URLs via reputation services
+var urlPattern = /href=".*"/ig;
+var urls = body.match(urlPattern);
+var cleanUrls = [];
+if (urls) {
+ for (var i=0; i= 0) continue;
+ cleanUrls.push(u);
+ var rep = executeCommand('url', {url: u});
+ if (rep && Array.isArray(rep)) {
+ for (var r = 0; r < rep.length; r++) {
+ if (positiveUrl(rep[r])) {
+ results.push(shortUrl(rep[r]));
+ malicious = true;
+ }
+ }
+ }
+ }
+}
+if (!malicious) {
+ results.push({Contents: 'Only clean URLs found: ' + cleanUrls.join(', '), Type: entryTypes.note, ContentsFormat: formats.text});
+}
+
+// Check the attachments via reputation services
+var entries = executeCommand('getEntries', {});
+for (var i=0; i -1) && (pidindex > -1)) {
+ for (var j = 0; j < jsonout.rows.length; j++) {
+ var obj = {'ip':jsonout.rows[j][ipindex].split(':')[0], 'port':jsonout.rows[j][ipindex].split(':')[1], 'pid':jsonout.rows[j][pidindex]}
+ ipaddrarr.push(obj);
+ }
+ }
+ }
+ }
+ }
+}
+
+for (var i = 0; i < ipaddrarr.length; i++) {
+ var repscript = 'DataIPReputation';
+ if (args.repscript){
+ repscript = args.repscript;
+ }
+ var iprep = executeCommand(repscript, {input: ipaddrarr[i].ip});
+ if (iprep[0].Type !== entryTypes.error) {
+ if (iprep[0].Contents <= reputationthreshold) {
+ var result = {};
+ result['PID'] = ipaddrarr[i].pid;
+ result['IP'] = ipaddrarr[i].ip;
+ result['PORT'] = ipaddrarr[i].port;
+ result['REPUTATION'] = iprep[0].Contents;
+ resultarr.push(result);
+ }
+ }
+
+}
+var result = {};
+result.Contents = resultarr;
+result.ContentsFormat = formats.table;
+result.Type = entryTypes.note;
+
+return result;
diff --git a/Scripts/volimageinfo.js b/Scripts/volimageinfo.js
new file mode 100644
index 000000000000..60fd1ac2d824
--- /dev/null
+++ b/Scripts/volimageinfo.js
@@ -0,0 +1,3 @@
+var cmdline = 'imageinfo';
+var out = executeCommand('Volatility', {memdump:args.memdump, profile:args.profile, system: args.system, cmd:cmdline});
+return out;
diff --git a/Scripts/voljson.js b/Scripts/voljson.js
new file mode 100644
index 000000000000..42fb07f462e7
--- /dev/null
+++ b/Scripts/voljson.js
@@ -0,0 +1 @@
+packOutput('vol.py --output=json -f ' + args.file + ' ' + args.cmd);
diff --git a/Scripts/volldrmodules.js b/Scripts/volldrmodules.js
new file mode 100644
index 000000000000..abeef6444477
--- /dev/null
+++ b/Scripts/volldrmodules.js
@@ -0,0 +1,3 @@
+var cmdline = 'ldrmodules';
+var out = executeCommand('Volatility', {memdump:args.memdump, profile:args.profile, system: args.system, cmd:cmdline});
+return out;
diff --git a/Scripts/volmalfind.js b/Scripts/volmalfind.js
new file mode 100644
index 000000000000..5dedc0505301
--- /dev/null
+++ b/Scripts/volmalfind.js
@@ -0,0 +1,3 @@
+var cmdline = 'malfind -p ' + args.pid;
+var out = executeCommand('Volatility', {memdump:args.memdump, profile:args.profile, system: args.system, cmd:cmdline});
+return out;
diff --git a/Scripts/volmalfinddumpagent.js b/Scripts/volmalfinddumpagent.js
new file mode 100644
index 000000000000..eadcd24d4d5b
--- /dev/null
+++ b/Scripts/volmalfinddumpagent.js
@@ -0,0 +1,27 @@
+var cmdline = 'vol.py -f ' + args.memdump + ' ' + 'malfind';
+if (args.pid) {
+ cmdline = cmdline + ' -p ' + args.pid;
+}
+var dumpdir = args.dumpdir;
+
+cmdline = cmdline + ' -D ' + dumpdir;
+var timeout = 600 ;
+if (args.timeout) {
+ timeout = args.timeout ;
+}
+
+mkdir(dumpdir);
+var output = execute(cmdline,timeout);
+if (output.Success) {
+ // we need to take all the files and move them over.
+ pack(output);
+ var results = files(dumpdir);
+ for (var i = 0; i < results.length; i++) {
+ if (results[i].Type !== "Folder") {
+ var fileName = results[i].Path;
+ pack_file(fileName);
+ del(fileName);
+ }
+ }
+}
+rmdir(dumpdir);
\ No newline at end of file
diff --git a/Scripts/volnetworkconnections.js b/Scripts/volnetworkconnections.js
new file mode 100644
index 000000000000..132a6955d7f3
--- /dev/null
+++ b/Scripts/volnetworkconnections.js
@@ -0,0 +1,90 @@
+var cmds = [];
+
+switch (args.profile) {
+ case 'VistaSP0x64' :
+ case 'VistaSP0x86' :
+ case 'VistaSP1x64' :
+ case 'VistaSP1x86' :
+ case 'VistaSP2x64' :
+ case 'VistaSP2x86' :
+ case 'Win2008R2SP0x64' :
+ case 'Win2008R2SP1x64' :
+ case 'Win2008SP1x64' :
+ case 'Win2008SP1x86' :
+ case 'Win2008SP2x64' :
+ case 'Win2008SP2x86' :
+ case 'Win7SP0x64' :
+ case 'Win7SP0x86' :
+ case 'Win7SP1x64' :
+ case 'Win7SP1x86' :
+ case 'Win81U1x64' :
+ case 'Win81U1x86' :
+ case 'Win8SP0x64' :
+ case 'Win8SP0x86' :
+ case 'Win8SP1x64' :
+ case 'Win8SP1x86' :
+ case 'Win10x64' :
+ case 'Win10x86' :
+ case 'Win2012R2x64' :
+ case 'Win2012x64' :
+ cmds.push('netscan');
+ break;
+ case 'Win2003SP0x86' :
+ case 'Win2003SP1x64' :
+ case 'Win2003SP1x86' :
+ case 'Win2003SP2x64' :
+ case 'Win2003SP2x86' :
+ case 'WinXPSP1x64' :
+ case 'WinXPSP2x64' :
+ case 'WinXPSP2x86' :
+ case 'WinXPSP3x86' :
+ cmds.push('connections');
+ cmds.push('connscan');
+ cmds.push('sockets');
+ cmds.push('sockscan');
+ break;
+ default:
+ break;
+}
+
+var resultarr = [];
+
+for (var i = 0; i < cmds.length; i++) {
+ var cmdline = cmds[i];
+ if (args.profile) {
+ cmdline = cmdline + ' --profile=' + args.profile;
+ }
+ var out = executeCommand('VolJson', {file:args.memdump, system: args.system, cmd:cmdline});
+ if (out) {
+ var mapper = function(columns) {
+ return function(val) {
+ return val.reduce(function(prev, curr, i) {
+ prev[columns[i]] = '' + curr;
+ return prev;
+ }, {});
+ };
+ };
+ for (var r = 0; r < out.length; r++) {
+ if (out[r].Type !== entryTypes.error) {
+ var jsonout = JSON.parse(out[r].Contents);
+ result = {};
+ result.Contents = jsonout.rows.map(mapper(jsonout.columns));
+ result.ContentsFormat = formats.table;
+ result.Type = entryTypes.note;
+ resultarr.push(result);
+ }
+ else
+ {
+ result = {};
+ var errstring = out[r].Contents;
+ result.Contents = errstring.split('Stderr:')[1];
+ result.ContentsFormat = formats.text;
+ result.Type = entryTypes.error;
+ resultarr.push(result);
+ }
+ }
+ }
+
+}
+
+return resultarr;
diff --git a/Scripts/volpslist.js b/Scripts/volpslist.js
new file mode 100644
index 000000000000..240cd177b5c7
--- /dev/null
+++ b/Scripts/volpslist.js
@@ -0,0 +1,3 @@
+var cmdline = 'pslist';
+var out = executeCommand('Volatility', {memdump:args.memdump, profile:args.profile, system: args.system, cmd:cmdline});
+return out;
diff --git a/Scripts/volruncmds.js b/Scripts/volruncmds.js
new file mode 100644
index 000000000000..9923445acf38
--- /dev/null
+++ b/Scripts/volruncmds.js
@@ -0,0 +1,43 @@
+var cmds = args.cmds.split(',');
+
+var resultarr = [];
+
+for (var i = 0; i < cmds.length; i++) {
+ var cmdline = cmds[i];
+ if (args.profile) {
+ cmdline = cmdline + ' --profile=' + args.profile;
+ }
+ var out = executeCommand('VolJson', {file:args.memdump, system: args.system, cmd:cmdline});
+ if (out) {
+ var mapper = function(columns) {
+ return function(val) {
+ return val.reduce(function(prev, curr, i) {
+ prev[columns[i]] = '' + curr;
+ return prev;
+ }, {});
+ };
+ };
+ for (var r = 0; r < out.length; r++) {
+ if (out[r].Type !== entryTypes.error) {
+ var jsonout = JSON.parse(out[r].Contents);
+ result = {};
+ result.Contents = jsonout.rows.map(mapper(jsonout.columns));
+ result.ContentsFormat = formats.table;
+ result.Type = entryTypes.note;
+ resultarr.push(result);
+ }
+ else
+ {
+ result = {};
+ var errstring = out[r].Contents;
+ result.Contents = errstring.split('Stderr:')[1];
+ result.ContentsFormat = formats.text;
+ result.Type = entryTypes.error;
+ resultarr.push(result);
+ }
+ }
+ }
+
+}
+
+return resultarr;
diff --git a/Scripts/whois.js b/Scripts/whois.js
new file mode 100644
index 000000000000..5b69af9b9706
--- /dev/null
+++ b/Scripts/whois.js
@@ -0,0 +1 @@
+return executeCommand('whois', {'query':args.query});
diff --git a/Scripts/whoissummary.js b/Scripts/whoissummary.js
new file mode 100644
index 000000000000..cff20646a49a
--- /dev/null
+++ b/Scripts/whoissummary.js
@@ -0,0 +1,37 @@
+// A simple example script for parsing WHOIS data
+
+// Regexp for matching ": " pattern
+var re = /^([a-zA-Z\s]+):\s(.+)$/;
+
+function stringStartsWith (string, prefix) {
+ return string.slice(0, prefix.length) == prefix;
+}
+
+var res = executeCommand('whois', {'using-brand': 'whois', 'query':args.query});
+var lines = res[0].Contents.split('\n');
+
+var out = '';
+for (var i=0; i < lines.length; ++i) {
+ var str = lines[i];
+ str.trim();
+ if (str.length === 0) {
+ continue;
+ }
+
+ // Skip '%' lines
+ if (stringStartsWith(str, '%')) {
+ continue;
+ }
+
+ // Stop when reaching the "last update" line
+ if (stringStartsWith(str, '>>> Last update')) {
+ break;
+ }
+
+ // See if the line matches the regex
+ if (re.test(str)) {
+ out += str + '\n';
+ }
+}
+
+return out;
diff --git a/Scripts/wildfirereport.js b/Scripts/wildfirereport.js
new file mode 100644
index 000000000000..e7be44deb67a
--- /dev/null
+++ b/Scripts/wildfirereport.js
@@ -0,0 +1,22 @@
+var raw
+if (args.md5) {
+ raw = executeCommand('wildfire-report', {md5: args.md5});
+} else if (args.hash) {
+ raw = executeCommand('wildfire-report', {hash: args.hash});
+} else {
+ return 'no';
+}
+
+if (!raw[0] || !raw[0].Contents || !raw[0].Contents.wildfire || !raw[0].Contents.wildfire.file_info) {
+ return "failed";
+}
+
+var item = raw[0].Contents.wildfire.file_info;
+return {Contents: {
+ Type: item.filetype,
+ Malware: item.malware,
+ MD5: item.md5,
+ SHA256: item.sha256,
+ Size: item.size},
+ ContentsFormat: formats.table,
+ Type: entryTypes.note};
\ No newline at end of file
diff --git a/Scripts/wildfireupload.js b/Scripts/wildfireupload.js
new file mode 100644
index 000000000000..9bee2ee843d3
--- /dev/null
+++ b/Scripts/wildfireupload.js
@@ -0,0 +1,15 @@
+var raw = executeCommand('wildfire-upload', {upload: args.upload});
+
+if (!raw[0] || !raw[0].Contents || !raw[0].Contents.wildfire || !raw[0].Contents.wildfire['upload-file-info']) {
+ return "failed";
+}
+
+var item = raw[0].Contents.wildfire['upload-file-info'];
+return {Contents: {
+ Type: item.filetype,
+ MD5: item.md5,
+ SHA256: item.sha256,
+ Size: item.size,
+ URL: item.url},
+ ContentsFormat: formats.table,
+ Type: entryTypes.note};
\ No newline at end of file
diff --git a/Scripts/xbinfo.py b/Scripts/xbinfo.py
new file mode 100644
index 000000000000..be43926a418f
--- /dev/null
+++ b/Scripts/xbinfo.py
@@ -0,0 +1,19 @@
+res = '## Exabeam global info'
+entry = demisto.executeCommand('xb-users', {})[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ res += '\n### Users:'
+ res += '\n- High Risk: ' + str(demisto.get(entry, 'Contents.highRisk'))
+ res += '\n- Recent: ' + str(demisto.get(entry, 'Contents.recent'))
+ res += '\n- Total: ' + str(demisto.get(entry, 'Contents.total'))
+entry = demisto.executeCommand('xb-assets', {})[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ res += '\n### Assets:'
+ res += '\n- High Risk: ' + str(demisto.get(entry, 'Contents.highRisk'))
+ res += '\n- Recent: ' + str(demisto.get(entry, 'Contents.recent'))
+ res += '\n- Total: ' + str(demisto.get(entry, 'Contents.total'))
+entry = demisto.executeCommand('xb-events', {})[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ res += '\n### Events:'
+ res += '\n- Recent: ' + str(int(demisto.get(entry, 'Contents.recent')))
+ res += '\n- Total: ' + str(int(demisto.get(entry, 'Contents.total')))
+demisto.results({'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': res})
diff --git a/Scripts/xblockouts.py b/Scripts/xblockouts.py
new file mode 100644
index 000000000000..e6bd7916653e
--- /dev/null
+++ b/Scripts/xblockouts.py
@@ -0,0 +1,41 @@
+import time
+
+rows = '100'
+if demisto.args().has_key('rows'):
+ rows = demisto.args()['rows']
+days = '1'
+if demisto.args().has_key('days'):
+ days = demisto.args()['days']
+
+res = '## Exabeam Lockouts'
+entry = demisto.executeCommand('xb-lockouts', {'numberOfResults': rows, 'num': days})[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ lockouts = demisto.get(entry, 'Contents.lockouts')
+ if lockouts:
+ res += '\n### Total in the last ' + days + ' days: ' + str(len(lockouts))
+ for l in lockouts:
+ info = demisto.get(l, 'user.info')
+ if info:
+ res += '\n\n\n![photo](data:image/png;base64,' + demisto.get(info, 'photo').replace('\n', '') + ')'
+ res += '\n|Account|Email|Name|Title|Department|Devision|DN|Location|Manager|Office|Cell|Group|First Seen|Last Seen|'
+ res += '\n|-------|-----|----|-----|----------|--------|--|--------|-------|------|----|-----|----------|---------|'
+ res += '\n| ' + demisto.get(info, 'accountId') + ' | ' + demisto.get(info, 'email') + ' | ' + demisto.get(info, 'fullName') + ' | ' + \
+ demisto.get(info, 'title') + ' | ' + demisto.get(info, 'department') + ' | ' + demisto.get(info, 'division') + ' | ' + \
+ demisto.get(info, 'dn') + ' | ' + demisto.get(info, 'location') + ', ' + demisto.get(info, 'country') + ' | ' + \
+ demisto.get(info, 'manager') + ' | ' + demisto.get(info, 'phoneOffice') + ' | ' + demisto.get(info, 'phoneCell') + ' | ' + \
+ demisto.get(info, 'group') + ' | ' + time.ctime(demisto.get(l, 'user.firstSeen') / 1000.0) + ' | ' + time.ctime(demisto.get(l, 'user.lastSeen') / 1000.0) + ' |'
+ res += '\nScore: **' + str(demisto.get(l, 'user.riskScore')) + '**'
+ res += '\n|ID|Accounts|Start|End|Label|LoginHost|Zones|InitialRisk|Risk|Last Activity Type|Last Activity Time|Account Changes|Account Lockouts|Assets|Events|Failed Logins|Reasons|'
+ res += '\n|--|--------|-----|---|-----|---------|-----|-----------|----|------------------|------------------|---------------|----------------|------|------|-------------|-------|'
+ lInfo = demisto.get(l, 'lockoutInfo')
+ if lInfo:
+ res += '\n| ' + demisto.get(lInfo, 'lockoutId') + ' | ' + ','.join(demisto.get(lInfo, 'accounts')) + ' | ' + time.ctime(demisto.get(lInfo, 'startTime') / 1000.0) + ' | ' + \
+ time.ctime(demisto.get(lInfo, 'endTime') / 1000.0) + ' | ' + demisto.get(lInfo, 'label') + ' | ' + demisto.get(lInfo, 'loginHost') + ' | ' + \
+ ','.join(demisto.get(lInfo, 'zones')) + ' | ' + str(demisto.get(lInfo, 'initialRiskScore')) + ' | ' + str(demisto.get(lInfo, 'riskScore')) + ' | ' + \
+ str(demisto.get(lInfo, 'lastActivityType')) + ' | ' + time.ctime(demisto.get(lInfo, 'lastActivityTime') / 1000.0) + ' | ' + \
+ str(demisto.get(lInfo, 'numOfAccountChanges')) + ' | ' + str(demisto.get(lInfo, 'numOfAccountLockouts')) + ' | ' + \
+ str(demisto.get(lInfo, 'numOfAssets')) + ' | ' + str(demisto.get(lInfo, 'numOfEvents')) + ' | ' + str(demisto.get(lInfo, 'numOfFailedLogins')) + ' | ' + \
+ str(demisto.get(lInfo, 'numOfReasons')) + ' |'
+ demisto.results({'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': res})
+else:
+ demisto.results(entry)
diff --git a/Scripts/xbnotable.py b/Scripts/xbnotable.py
new file mode 100644
index 000000000000..56ec8ccfc4a1
--- /dev/null
+++ b/Scripts/xbnotable.py
@@ -0,0 +1,40 @@
+import time
+
+rows = '100'
+if demisto.args().has_key('rows'):
+ rows = demisto.args()['rows']
+days = '1'
+if demisto.args().has_key('days'):
+ days = demisto.args()['days']
+
+res = '## Exabeam notable users'
+entry = demisto.executeCommand('xb-notable', {'numberOfResults': rows, 'num': days})[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ users = demisto.get(entry, 'Contents.users')
+ if users:
+ res += '\n### Total in the last ' + days + ' days: ' + str(len(users))
+ for user in users:
+ info = demisto.get(user, 'user.info')
+ if info:
+ res += '\n\n\n![photo](data:image/png;base64,' + demisto.get(info, 'photo').replace('\n', '') + ')'
+ res += '\n|Account|Email|Name|Title|Department|Devision|DN|Location|Manager|Office|Cell|Group|First Seen|Last Seen|'
+ res += '\n|-------|-----|----|-----|----------|--------|--|--------|-------|------|----|-----|----------|---------|'
+ res += '\n| ' + demisto.get(info, 'accountId') + ' | ' + demisto.get(info, 'email') + ' | ' + demisto.get(info, 'fullName') + ' | ' + \
+ demisto.get(info, 'title') + ' | ' + demisto.get(info, 'department') + ' | ' + demisto.get(info, 'division') + ' | ' + \
+ demisto.get(info, 'dn') + ' | ' + demisto.get(info, 'location') + ', ' + demisto.get(info, 'country') + ' | ' + \
+ demisto.get(info, 'manager') + ' | ' + demisto.get(info, 'phoneOffice') + ' | ' + demisto.get(info, 'phoneCell') + ' | ' + \
+ demisto.get(info, 'group') + ' | ' + time.ctime(demisto.get(user, 'user.firstSeen') / 1000.0) + ' | ' + time.ctime(demisto.get(user, 'user.lastSeen') / 1000.0) + ' |'
+ res += '\nScore: **' + str(demisto.get(user, 'highestRiskScore')) + '**'
+ res += '\nHighest Risk Session:'
+ res += '\n|ID|Accounts|Start|End|Label|LoginHost|Zones|InitialRisk|Risk|Assets|Events|Reasons|SecurityEvents|'
+ res += '\n|--|--------|-----|---|-----|---------|-----|-----------|----|------|------|-------|--------------|'
+ session = demisto.get(user, 'highestRiskSession')
+ if session:
+ res += '\n| ' + demisto.get(session, 'sessionId') + ' | ' + ','.join(demisto.get(session, 'accounts')) + ' | ' + time.ctime(demisto.get(session, 'startTime') / 1000.0) + ' | ' + \
+ time.ctime(demisto.get(session, 'endTime') / 1000.0) + ' | ' + demisto.get(session, 'label') + ' | ' + demisto.get(session, 'loginHost') + ' | ' + \
+ ','.join(demisto.get(session, 'zones')) + ' | ' + str(demisto.get(session, 'initialRiskScore')) + ' | ' + str(demisto.get(session, 'riskScore')) + ' | ' + \
+ str(demisto.get(session, 'numOfAssets')) + ' | ' + str(demisto.get(session, 'numOfEvents')) + ' | ' + str(demisto.get(session, 'numOfReasons')) + ' | ' + \
+ str(demisto.get(session, 'numOfSecurityEvents')) + ' |'
+ demisto.results({'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': res})
+else:
+ demisto.results(entry)
diff --git a/Scripts/xbtimeline.py b/Scripts/xbtimeline.py
new file mode 100644
index 000000000000..15fa3fc30ff4
--- /dev/null
+++ b/Scripts/xbtimeline.py
@@ -0,0 +1,13 @@
+import time
+
+res = []
+entry = demisto.executeCommand('xb-timeline', {'username': demisto.args()['username']})[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ entities = demisto.get(entry, 'Contents.entities')
+ if entities:
+ for e in entities:
+ res.append({'1.ID': demisto.get(e, 'id'), '2.Type': demisto.get(e, 'tp'), '3.Start': time.ctime(demisto.get(e, 'st') / 1000.0), '4.End': time.ctime(demisto.get(e, 'et') / 1000.0), \
+ '5.Initial Risk': str(demisto.get(e, 'irs')), '6.Risk': str(demisto.get(e, 'rs')), '7.OS': str(demisto.get(e, 'os')), '8.OE': str(demisto.get(e, 'oe'))})
+ demisto.results({'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': res})
+else:
+ demisto.results(entry)
diff --git a/Scripts/xbtriggeredrules.py b/Scripts/xbtriggeredrules.py
new file mode 100644
index 000000000000..c41203c8a80a
--- /dev/null
+++ b/Scripts/xbtriggeredrules.py
@@ -0,0 +1,12 @@
+res = []
+entry = demisto.executeCommand('xb-triggered-rules', {'containerId': demisto.args()['session']})[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ model = demisto.get(entry, 'Contents.modelDefs')
+ if model:
+ for key in model:
+ m = model[key]['attributes']
+ if m:
+ res.append({'1.ID': key, '2.Description': demisto.get(m, 'description'), '3.Type': demisto.get(m, 'modelType'), '4.Template': demisto.get(m, 'modelTemplate')})
+ demisto.results({'ContentsFormat': formats['table'], 'Type': entryTypes['note'], 'Contents': res})
+else:
+ demisto.results(entry)
diff --git a/Scripts/xbuser.py b/Scripts/xbuser.py
new file mode 100644
index 000000000000..773af46fd03d
--- /dev/null
+++ b/Scripts/xbuser.py
@@ -0,0 +1,31 @@
+import time
+
+res = '## Exabeam User Info'
+entry = demisto.executeCommand('xb-userinfo', {'username': demisto.args()['username']})[0]
+if entry['Type'] != entryTypes['error'] and entry['ContentsFormat'] == formats['json']:
+ info = demisto.get(entry, 'Contents.userInfo.info')
+ if info:
+ res += '\n\n\n![photo](data:image/png;base64,' + demisto.get(info, 'photo').replace('\n', '') + ')'
+ res += '\n|Account|Email|Name|Title|Department|Devision|DN|Location|Manager|Office|Cell|Group|First Seen|Last Seen|'
+ res += '\n|-------|-----|----|-----|----------|--------|--|--------|-------|------|----|-----|----------|---------|'
+ res += '\n| ' + demisto.get(info, 'accountId') + ' | ' + demisto.get(info, 'email') + ' | ' + demisto.get(info, 'fullName') + ' | ' + \
+ demisto.get(info, 'title') + ' | ' + demisto.get(info, 'department') + ' | ' + demisto.get(info, 'division') + ' | ' + \
+ demisto.get(info, 'dn') + ' | ' + demisto.get(info, 'location') + ', ' + demisto.get(info, 'country') + ' | ' + \
+ demisto.get(info, 'manager') + ' | ' + demisto.get(info, 'phoneOffice') + ' | ' + demisto.get(info, 'phoneCell') + ' | ' + \
+ demisto.get(info, 'group') + ' | ' + time.ctime(demisto.get(entry, 'Contents.userInfo.firstSeen') / 1000.0) + ' | ' + time.ctime(demisto.get(entry, 'Contents.userInfo.lastSeen') / 1000.0) + ' |'
+ res += '\nScore: **' + str(demisto.get(entry, 'Contents.userInfo.riskScore')) + '**, On watch: ' + str(demisto.get(entry, 'Contents.isOnWatchlist'))
+ res += '\n\n### Manager Info'
+ info = demisto.get(entry, 'Contents.managerInfo.info')
+ if info:
+ res += '\n\n\n![photo](data:image/png;base64,' + demisto.get(info, 'photo').replace('\n', '') + ')'
+ res += '\n|Account|Email|Name|Title|Department|Devision|DN|Location|Manager|Office|Cell|Group|First Seen|Last Seen|'
+ res += '\n|-------|-----|----|-----|----------|--------|--|--------|-------|------|----|-----|----------|---------|'
+ res += '\n| ' + demisto.get(info, 'accountId') + ' | ' + demisto.get(info, 'email') + ' | ' + demisto.get(info, 'fullName') + ' | ' + \
+ demisto.get(info, 'title') + ' | ' + demisto.get(info, 'department') + ' | ' + demisto.get(info, 'division') + ' | ' + \
+ demisto.get(info, 'dn') + ' | ' + demisto.get(info, 'location') + ', ' + demisto.get(info, 'country') + ' | ' + \
+ demisto.get(info, 'manager') + ' | ' + demisto.get(info, 'phoneOffice') + ' | ' + demisto.get(info, 'phoneCell') + ' | ' + \
+ demisto.get(info, 'group') + ' | ' + time.ctime(demisto.get(entry, 'Contents.managerInfo.firstSeen') / 1000.0) + ' | ' + time.ctime(demisto.get(entry, 'Contents.managerInfo.lastSeen') / 1000.0) + ' |'
+ res += '\nScore: **' + str(demisto.get(entry, 'Contents.managerInfo.riskScore')) + '**'
+ demisto.results({'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': res})
+else:
+ demisto.results(entry)