diff --git a/docs/sources/Vectra/index.md b/docs/sources/Vectra/index.md new file mode 100644 index 0000000..9e87e07 --- /dev/null +++ b/docs/sources/Vectra/index.md @@ -0,0 +1,65 @@ +# Vendor - Vectra + +## Product - Cognito + +| Ref | Link | +|----------------|---------------------------------------------------------------------------------------------------------| +| Technology Add-On for Vectra Cognito | https://splunkbase.splunk.com/app/4408/ | + + +### Sourcetypes + +| sourcetype | notes | +|----------------|---------------------------------------------------------------------------------------------------------| +|vectra:cognito:detect || +|vectra:cognito:accountdetect || +|vectra:cognito:accountscoring || +|vectra:cognito:audit || +|vectra:cognito:campaigns || +|vectra:cognito:health || +|vectra:cognito:hostscoring || +|vectra:cognito:accountlockdown || + + +### Index Configuration + +| key | sourcetype | index | notes | +|----------------|----------------|----------------|----------------| +|Vectra Networks_X Series|vectra:cognito:detect |main| +|Vectra Networks_X Series_accountdetect|vectra:cognito:accountdetect |main| +|Vectra Networks_X Series_asc|vectra:cognito:accountscoring |main| +|Vectra Networks_X Series_audit|vectra:cognito:audit |main| +|Vectra Networks_X Series_campaigns|vectra:cognito:campaigns |main| +|Vectra Networks_X Series_health|vectra:cognito:health |main| +|Vectra Networks_X Series_hsc|vectra:cognito:hostscoring |main| +|Vectra Networks_X Series_lockdown|vectra:cognito:accountlockdown |main| + + +### Filter type + +MSG Parse: This filter parses message content + +### Options + +Note: listed for reference; processing utilizes the Microsoft ArcSight log path as this format is a subtype of CEF + +| Variable | default | description | +|----------------|----------------|----------------| +| SC4S_LISTEN_CEF_TCP_PORT | empty string | Enable a TCP port for this specific vendor product using a comma-separated list of port numbers | +| SC4S_LISTEN_CEF_UDP_PORT | empty string | Enable a UDP port for this specific vendor product using a comma-separated list of port numbers | +| SC4S_ARCHIVE_CEF | no | Enable archive to disk for this specific source | +| SC4S_DEST_CEF_HEC | no | When Splunk HEC is disabled globally set to yes to enable this specific source | + +* NOTE: Set only _one_ set of CEF variables for the entire SC4S deployment, regardless of how +many ports are in use by this CEF source (or any others). See the "Common Event Format" source +documentation for more information. + +### Verification + +An active site will generate frequent events use the following search to check for new events + +Verify timestamp, and host values match as expected + +``` +index= (sourcetype="deepsecurity*") +``` diff --git a/mkdocs.yml b/mkdocs.yml index 7b37c90..310b09d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -41,6 +41,7 @@ nav: - Symantec: sources/Symantec/index.md - Trend: sources/Trend/index.md - Ubiquiti: sources/Ubiquiti/index.md + - Vectra: sources/Vectra/index.md - VMware: sources/VMWare/index.md - Zscaler: sources/Zscaler/index.md - Performance: "performance.md" diff --git a/package/etc/conf.d/filters/common_event_format/cef.conf b/package/etc/conf.d/filters/common_event_format/cef.conf index 0369c79..26b9fa5 100644 --- a/package/etc/conf.d/filters/common_event_format/cef.conf +++ b/package/etc/conf.d/filters/common_event_format/cef.conf @@ -1,3 +1,4 @@ filter f_cef { - program("CEF"); + program("CEF") or + message('CEF\:\d\|'); }; \ No newline at end of file diff --git a/package/etc/conf.d/log_paths/lp-common_event_format.conf.tmpl b/package/etc/conf.d/log_paths/lp-common_event_format.conf.tmpl index 04b3925..a7f362a 100644 --- a/package/etc/conf.d/log_paths/lp-common_event_format.conf.tmpl +++ b/package/etc/conf.d/log_paths/lp-common_event_format.conf.tmpl @@ -79,6 +79,22 @@ log { }; parser (p_cef_header); + if { + filter{ + match('^CEF:(\d*)' value('fields.cef_version') flags(store-matches)) + }; + rewrite { + set("$1", value("fields.cef_version")); + }; + } elif { + filter{ + match('(.*)CEF:(\d*)' value('fields.cef_version') flags(store-matches)) + }; + rewrite { + set("$1", value("fields.cef_vendor_header")); + set("$2", value("fields.cef_version")); + }; + } else {}; rewrite { set("${fields.cef_device_vendor}_${fields.cef_device_product}", value("fields.sc4s_vendor_product")); @@ -88,6 +104,13 @@ log { # If we have an rt or end field that is best we use the If trick here so if this parser fails # We don't get sent to fallback. if { + # 12 digit epoch timestamps are non-standard; when used they often indicate the fields are misused + # Non-standard strptime formats also choke the syslog-ng date parser, which outputs wildy random timestamps + # Simply filter and ignore + filter{ + match('^\d{12}', value('.cef.start')) or match('^\d{12}', value('.cef.end')) or match('^\d{12}', value('.cef.rt')); + }; + } elif { filter{ match('^.', value('.cef.rt')) }; @@ -160,6 +183,18 @@ log { set("app control" value("fields.cef_device_event_class")); }; }; + } elif { + filter{ + match("Vectra Networks_X Series" value("fields.sc4s_vendor_product")); + }; + if { + filter{ + match("vectra_cef_account_detection" value("fields.cef_vendor_header")); + }; + rewrite { + set("accountdetect" value("fields.cef_device_event_class")); + }; + }; }; parser(p_cef_class); diff --git a/package/etc/context_templates/splunk_metadata.csv.example b/package/etc/context_templates/splunk_metadata.csv.example index 5e7c7f1..0253251 100644 --- a/package/etc/context_templates/splunk_metadata.csv.example +++ b/package/etc/context_templates/splunk_metadata.csv.example @@ -130,6 +130,14 @@ ubiquiti_unifi,index,netops unknown,index,main unknown,source,SC4S:unknown unknown,sourcetype,SC4S:unknown +Vectra Networks_X Series,sourcetype,vectra:cognito:detect +Vectra Networks_X Series_accountdetect,sourcetype,vectra:cognito:accountdetect +Vectra Networks_X Series_asc,sourcetype,vectra:cognito:accountscoring +Vectra Networks_X Series_audit,sourcetype,vectra:cognito:audit +Vectra Networks_X Series_campaigns,sourcetype,vectra:cognito:campaigns +Vectra Networks_X Series_health,sourcetype,vectra:cognito:health +Vectra Networks_X Series_hsc,sourcetype,vectra:cognito:hostscoring +Vectra Networks_X Series_lockdown,sourcetype,vectra:cognito:accountlockdown vmware_esx,index,main vmware_horizon,index,main vmware_nsx,index,main @@ -140,4 +148,4 @@ zscaler_fw,index,netfw zscaler_lss,index,netproxy zscaler_web,index,netproxy zscaler_zia_audit,index,netops -zscaler_zia_sandbox,index,main \ No newline at end of file +zscaler_zia_sandbox,index,main diff --git a/tests/test_vectra_ai.py b/tests/test_vectra_ai.py new file mode 100644 index 0000000..05e9887 --- /dev/null +++ b/tests/test_vectra_ai.py @@ -0,0 +1,261 @@ +# Copyright 2019 Splunk, Inc. +# +# Use of this source code is governed by a BSD-2-clause-style +# license that can be found in the LICENSE-BSD2 file or at +# https://opensource.org/licenses/BSD-2-Clause +import random + +from jinja2 import Environment + +from .sendmessage import * +from .splunkutils import * +from .timeutils import * + +env = Environment() + +# <13>Aug 21 09:24:00 S180356X5A19242 vectra_cef -: CEF:0|Vectra Networks|X Series|5.8|hsc|Host Score Change|3|externalId=2765220 cat=HOST SCORING dvc=10.34.252.35 dvchost=10.34.252.35 shost=snavpxdevdi2468.corp.firstam.com src=10.32.137.135 dst=10.32.137.135 flexNumber1Label=threat flexNumber1=22 flexNumber2Label=certainty flexNumber2=51 flexNumber3Label=privilege flexNumber3=1 cs3Label=scoreDecreases cs3=False cs4Label=Vectra Event URL cs4=https://10.34.252.35/hosts/2765220 start=1598027040563 end=1598027040563 cs1Label=sourceKeyAsset cs1=False cs2Label=destKeyAsset cs2=False + + +def test_vectra_ai_hsc(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string( + "{{ bsd }} {{ host }} vectra_cef -: CEF:0|Vectra Networks|X Series|5.8|hsc|Host Score Change|3|externalId=2765220 cat=HOST SCORING dvc=10.111.111.35 dvchost=10.111.111.35 shost=snavpxdevdi2468.corp.firstam.com src=10.111.11.135 dst=10.11.11.135 flexNumber1Label=threat flexNumber1=22 flexNumber2Label=certainty flexNumber2=51 flexNumber3Label=privilege flexNumber3=1 cs3Label=scoreDecreases cs3=False cs4Label=Vectra Event URL cs4=https://10.34.252.35/hosts/2765220 start={{ epoch }} end={{ epoch }} cs1Label=sourceKeyAsset cs1=False cs2Label=destKeyAsset cs2=False\n" + ) + message = mt.render(mark="<111>", bsd=bsd, host=host) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string( + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="vectra:cognito:hostscoring"' + ) + search = st.render(epoch=epoch, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + + +def test_vectra_ai_asc(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string( + "{{ bsd }} {{ host }} vectra_cef -: CEF:0|Vectra Networks|X Series|$version|asc|Account Score Change|3|externalId=$account_id cat=$category dvc=$headend_addr flexNumber1Label=threat flexNumber1=$threat flexNumber2Label=certainty flexNumber2=$certainty cs1Label=Vectra Event URL cs1=$href start=$UTCTimeStartCEF end={{ epoch }}\n" + ) + message = mt.render(mark="<111>", bsd=bsd, host=host) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string( + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="vectra:cognito:accountscoring"' + ) + search = st.render(epoch=epoch, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + + +# <13>Aug 21 09:26:06 xxxxxxx vectra_cef -: CEF:0|Vectra Networks|X Series|5.8|smb_brute_force|SMB Brute-Force|7|externalId=110076 cat=LATERAL MOVEMENT dvc=10.34.11.35 dvchost=10.34.111.11 shost=snavpfaxrfax001.corp.firstam.com src=172.17.111.111 flexNumber1Label=threat flexNumber1=70 flexNumber2Label=certainty flexNumber2=95 cs4Label=Vectra Event URL cs4=https://10.34.252.35/detections/110076?detail_id\=25428794 cs5Label=triaged cs5=False dst=172.17.111.111 dhost= proto= dpt=445 out=None in=None start=1570653042000 end=1598027100000 +def test_vectra_ai_host_detect( + record_property, setup_wordlist, setup_splunk, setup_sc4s +): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string( + "{{ bsd }} {{ host }} vectra_cef -: CEF:0|Vectra Networks|X Series|5.8|smb_brute_force|SMB Brute-Force|7|externalId=110076 cat=LATERAL MOVEMENT dvc=10.34.11.35 dvchost=10.34.111.11 shost=snavpfaxrfax001.corp.firstam.com src=172.17.111.111 flexNumber1Label=threat flexNumber1=70 flexNumber2Label=certainty flexNumber2=95 cs4Label=Vectra Event URL cs4=https://10.34.252.35/detections/110076?detail_id\=25428794 cs5Label=triaged cs5=False dst=172.17.111.111 dhost= proto= dpt=445 out=None in=None start=1570653042000 end={{ epoch }}\n" + ) + message = mt.render(mark="<111>", bsd=bsd, host=host) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string( + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="vectra:cognito:detect"' + ) + search = st.render(epoch=epoch, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + + +def test_vectra_ai_accountdetect( + record_property, setup_wordlist, setup_splunk, setup_sc4s +): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string( + "{{ bsd }} {{ host }} vectra_cef_account_detection -: CEF:0|Vectra Networks|X Series|$version|$d_type|$d_type_vname|$severity|externalId=$detection_id cat=$category dvc=$headend_addr account=$accountflexNumber1Label=threat flexNumber1=$threat flexNumber2Label=certainty flexNumber2=$certainty cs4Label=Vectra Event URL cs4=$href cs5Label=triaged cs5=$triaged dst=$dd_dst_ip dhost=$dd_dst_dns dpt=$dd_dst_port out=$dd_bytes_sent in=$dd_bytes_rcvd start=$UTCTimeStartCEF end={{ epoch }}\n" + ) + message = mt.render(mark="<111>", bsd=bsd, host=host) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string( + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="vectra:cognito:accountdetect"' + ) + search = st.render(epoch=epoch, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + + +def test_vectra_ai_lockdown(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string( + "{{ bsd }} {{ host }} vectra_cef -: CEF:0|Vectra Networks|X Series|$version|lockdown|Account Lockdown|3|externalId=$account_idcat=$categorydvc=$headend_addrsuser=$useraccount=$account_namecs1Label=action cs1=$actioncs2Label=success cs2=$successcs4Label=Vectra Event URL cs4=$hrefstart=$UTCTimeStartend=$UTCTimeEnd\n" + ) + message = mt.render(mark="<111>", bsd=bsd, host=host) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string( + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="vectra:cognito:accountlockdown"' + ) + search = st.render(epoch=epoch, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + + +def test_vectra_ai_campaign(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string( + "{{ bsd }} {{ host }} vectra_cef -: CEF:0|Vectra Networks|X Series|$version|campaigns|$campaign_name|2| externalId=$campaign_id cat=CAMPAIGNS act=$action dvc=$headend_addr dvchost=$dvchost shost=$src_name src=$src_ip suid=$src_hid cs4Label=VectraEventURL cs4=$campaign_link dhost=$dest_name dst=$dest_ip duid=$dest_id rt=$timestamp reason=$reason cs6Label=VectraDetectionIDcs6=$det_id\n" + ) + message = mt.render(mark="<111>", bsd=bsd, host=host) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string( + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="vectra:cognito:campaigns"' + ) + search = st.render(epoch=epoch, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + + +def test_vectra_ai_audit(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string( + "{{ bsd }} {{ host }} vectra_cef_audit -: CEF:0|Vectra Networks|X Series|5.8|audit|user_action|0|dvc=10.111.11.35 dvchost=10.11.111.35 suser=anagarajan spriv=Security Analyst src=None deviceFacility=13 cat=user_action outcome=True msg=session timeout with length 8:07:13\n" + ) + message = mt.render(mark="<111>", bsd=bsd, host=host) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string( + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="vectra:cognito:audit"' + ) + search = st.render(epoch=epoch, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + + +def test_vectra_ai_health(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string( + "{{ bsd }} {{ host }} CEF:0|Vectra Networks|X Series|$version|health|$type|0|dvc=$headend_addr dvchost=$dvchost deviceFacility=14 outcome=$result msg=$message\n" + ) + message = mt.render(mark="<111>", bsd=bsd, host=host) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string( + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="vectra:cognito:health"' + ) + search = st.render(epoch=epoch, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1