Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions detections/application/zoom_high_video_latency.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Zoom High Video Latency
id: 6ad6b548-adfa-452c-aa77-9ff94877e832
version: 1
date: '2025-06-02'
author: Marissa Bower, Raven Tait
status: experimental
type: Anomaly
description: Detects particularly high latency from Zoom logs. Latency observed from threat actors
performing Remote Employment Fraud (REF) is typically well above what’s normal for the majority of employees.
data_source: []
search: '`zoom_index`
| spath "payload.object.participant.qos{}.type"
| search "payload.object.participant.qos{}.type"=video_input
| rename payload.object.participant.qos{}.details.avg_latency as avg_latency "payload.object.participant.qos{}.details.latency" as latency payload.object.participant.email as email
| rex field=avg_latency "(?<average_latency>\d+) ms"
| rex field=latency "(?<overall_latency>\d+) ms"
| search email="*"
| table email overall_latency latency avg_latency average_latency _raw
| stats latest(overall_latency) as overall_latency by email _raw
| where overall_latency>300 | `zoom_high_video_latency_filter`'
how_to_implement: The analytic leverages Zoom logs to be ingested using
Splunk Connect for Zoom (https://splunkbase.splunk.com/app/4961)
known_false_positives: While latency could simply indicate a slow network connection, when combined
with other indicators, it can help build a more complete picture. Tune the threshold as needed for
your environment baseline.
drilldown_searches:
- name: View the detection results for - "$email$"
search: '%original_detection_search% | search payload.object.participant.email = "$email$"'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
- name: View risk events for the last 7 days for - "$email$"
search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$email$")
starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime
values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories)
as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic)
as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
rba:
message: Suspicious latency from $email$ in Zoom activity.
risk_objects:
- field: email
type: user
score: 39
threat_objects: []
tags:
analytic_story:
- Remote Employment Fraud
asset_type: Identity
mitre_attack_id:
- T1078
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: identity
29 changes: 29 additions & 0 deletions detections/application/zoom_rare_audio_devices.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Zoom Rare Audio Devices
id: 9fdbf709-4c46-4819-9fb6-98b2d72059ed
version: 1
date: '2025-06-02'
author: Marissa Bower, Raven Tait
status: experimental
type: Hunting
description: Detects rare audio devices from Zoom logs. Actors performing Remote Employment
Fraud (REF) typically use unusual device information compared to a majority of employees.
Detecting this activity requires careful analysis, regular review, and a thorough
understanding of the audio and video devices commonly used within your environment.
data_source: []
search: '`zoom_index` speaker=* NOT (camera=*iPhone* OR camera="*FaceTime*"
OR speaker="*AirPods*" OR camera="*MacBook*" OR microphone="*MacBook Pro Microphone*")
| rare speaker limit=50 | `zoom_rare_audio_devices_filter`'
how_to_implement: The analytic leverages Zoom logs to be ingested using
Splunk Connect for Zoom (https://splunkbase.splunk.com/app/4961)
known_false_positives: This is a hunting query meant to identify rare audio devices.
tags:
analytic_story:
- Remote Employment Fraud
asset_type: Identity
mitre_attack_id:
- T1123
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: identity
29 changes: 29 additions & 0 deletions detections/application/zoom_rare_input_devices.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Zoom Rare Input Devices
id: d290eeef-d05e-49a8-b598-72296023b87b
version: 1
date: '2025-06-02'
author: Marissa Bower, Raven Tait
status: experimental
type: Hunting
description: Detects rare input devices from Zoom logs. Actors performing Remote Employment
Fraud (REF) typically use unusual device information compared to a majority of employees.
Detecting this activity requires careful analysis, regular review, and a thorough
understanding of the audio and video devices commonly used within your environment.
data_source: []
search: '`zoom_index` microphone=* NOT (camera=*iPhone* OR camera="*FaceTime*"
OR speaker="*AirPods*" OR camera="*MacBook*" OR microphone="*MacBook Pro Microphone*")
| rare microphone limit=50 | `zoom_rare_input_devices_filter`'
how_to_implement: The analytic leverages Zoom logs to be ingested using
Splunk Connect for Zoom (https://splunkbase.splunk.com/app/4961)
known_false_positives: This is a hunting query meant to identify rare microphone devices.
tags:
analytic_story:
- Remote Employment Fraud
asset_type: Identity
mitre_attack_id:
- T1123
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: identity
29 changes: 29 additions & 0 deletions detections/application/zoom_rare_video_devices.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Zoom Rare Video Devices
id: 9b2b819d-c76b-4dc6-bd3d-148edb8de83e
version: 1
date: '2025-06-02'
author: Marissa Bower, Raven Tait
status: experimental
type: Hunting
description: Detects rare video devices from Zoom logs. Actors performing Remote Employment
Fraud (REF) typically use unusual device information compared to a majority of employees.
Detecting this activity requires careful analysis, regular review, and a thorough
understanding of the audio and video devices commonly used within your environment.
data_source: []
search: '`zoom_index` camera=* NOT (camera=*iPhone* OR camera="*FaceTime*"
OR speaker="*AirPods*" OR camera="*MacBook*" OR microphone="*MacBook Pro Microphone*")
| rare camera limit=50 | `zoom_rare_video_devices_filter`'
how_to_implement: The analytic leverages Zoom logs to be ingested using
Splunk Connect for Zoom (https://splunkbase.splunk.com/app/4961)
known_false_positives: This is a hunting query meant to identify rare video devices.
tags:
analytic_story:
- Remote Employment Fraud
asset_type: Identity
mitre_attack_id:
- T1123
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: identity
113 changes: 113 additions & 0 deletions detections/cloud/geographic_improbable_location.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: Geographic Improbable Location
id: 64f91df1-49ec-46aa-81bd-2282d3cea765
version: 1
date: '2025-06-03'
author: Marissa Bower, Raven Tait
status: experimental
type: Anomaly
description: Geolocation data can be inaccurate or easily spoofed by Remote Employment Fraud (REF) workers.
REF actors sometimes slip up and reveal their true location, creating what we call 'improbable travel'
scenarios — logins from opposite sides of the world within minutes. This identifies situations where these
travel scenarios occur.
data_source:
- Okta
search: '| tstats summariesonly=true values(Authentication.app) as app from datamodel=Authentication.Authentication
where (`okta` OR (index="firewall" AND sourcetype="pan:globalprotect"))
AND Authentication.action="success" AND Authentication.app IN ("Workday", "Slack", "*GlobalProtect", "Jira*",
"Atlassian Cloud", "Zoom") AND NOT Authentication.user="unknown" by _time index sourcetype host Authentication.user
Authentication.src span=1s
| `drop_dm_object_name("Authentication")`
| fields user,src,app,_time,count,host
| eval user=lower(replace(user, "((^.*\\\)|(@.*$))", ""))
| join type=outer user
[| inputlookup identity_lookup_expanded where user_status=active
| rex field=email "^(?<user>[a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$"
| rename email as user_email bunit as user_bunit priority as user_priority work_country as user_work_country work_city as user_work_city
| fields user user_email user_bunit user_priority user_work_country user_work_city]
| eventstats dc(src) as src_count by user
| eventstats dc(user) as user_count by src
| sort 0 + _time
| iplocation src
| lookup local=true asn_lookup_by_cidr ip as src OUTPUT ip asn description
| eval session_lat=if(isnull(src_lat), lat, src_lat), session_lon=if(isnull(src_long), lon, src_long),
session_city=if(isnull(src_city), City, src_city), session_country=if(isnull(src_country), Country, src_country),
session_region=if(isnull(src_region), Region, src_region)
| eval session_city=if(isnull(session_city) OR match(session_city,"^\s+|^$"), null(), session_city),
session_country=if(isnull(session_country) OR match(session_country,"^\s+|^$"), null(), session_country),
session_region=if(isnull(session_region) OR match(session_region,"^\s+|^$"), null(), session_region)
| where isnotnull(session_lat) and isnotnull(session_lon)
| eval session_city=if(isnull(session_city),"-",session_city), session_country=if(isnull(session_country),"-",session_country),
session_region=if(isnull(session_region),"-",session_region)
| streamstats current=t window=2 earliest(session_region) as prev_region,earliest(session_lat) as prev_lat,
earliest(session_lon) as prev_lon, earliest(session_city) as prev_city, earliest(session_country) as prev_country,
earliest(_time) as prev_time, earliest(src) as prev_src, latest(user_bunit) as user_bunit,
earliest(app) as prev_app values(user_work_country) as user_work_country by user
| where (src!=prev_src) AND !(prev_city=session_city AND prev_country=session_country) AND ((isnotnull(prev_city)
AND isnotnull(session_city)) OR prev_country!=session_country)
| `globedistance(session_lat,session_lon,prev_lat,prev_lon,"m")`
| eval time_diff=if((_time-prev_time)==0, 1, _time - prev_time)
| eval speed = round(distance*3600/time_diff,2)
| eval distance= round(distance,2)
| eval user_work_country=case(user_work_country="usa","United States", user_work_country="cze","Czechia",
user_work_country="pol","Poland", user_work_country="ind","India", user_work_country="fra","France",
user_work_country="can","Canada", user_work_country="mys","Malaysia", user_work_country="kor","South Korea",
user_work_country="aus","Australia", user_work_country="bel","Belgium", user_work_country="dnk","Denmark",
user_work_country="bra","Brazil", user_work_country="deu","Germany", user_work_country="jpn","Japan",
user_work_country="che","Switzerland", user_work_country="swe","Sweden", user_work_country="zaf","South Africa",
user_work_country="irl","Ireland", user_work_country="ita","Italy", user_work_country="nor","Norway",
user_work_country="gbr","United Kingdom", user_work_country="hkg","Hong Kong", user_work_country="chn","China",
user_work_country="esp","Spain", user_work_country="nld", "Netherlands", user_work_country="twn","Taiwan",
user_work_country="est","Estonia", user_work_country="sgp","Singapore", user_work_country="are","United Arab Emirates", 1=1,"N/A")
| lookup local=true asn_lookup_by_cidr ip as prev_src OUTPUT ip as prev_ip asn as prev_asn description as prev_description
| eval suspect=if(!user_work_country==session_country,"Sketchy","Normal")
| search (speed>500 AND distance>750)
| table _time,prev_time,user,host,src,prev_src,app,prev_app,distance,speed,suspect,session_city,session_region,
session_country,prev_city,prev_region,prev_country,user_priority,user_work_*,prev_ip,ip,asn,prev_asn,prev_description,description
| rename _time as event_time
| convert ctime(event_time) timeformat="%Y-%m-%d %H:%M:%S"
| convert ctime(prev_time) timeformat="%Y-%m-%d %H:%M:%S"
| eval problem=if(!session_country==prev_country AND (!session_country==user_work_country),"Yes","Nope")
| search NOT (prev_city="-" OR session_city="-") AND NOT
[inputlookup known_devices_public_ip_filter.csv
| fields ip
| rename ip as src]
| dedup user host prev_src src
| fillnull value="N/A"
| search problem="Yes"| `geographic_improbable_location_filter`'
how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the
Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). This also utilizes
Splunk Enterprise Security Suite for several macros and lookups. The known_devices_public_ip_filter
lookup is a placeholder for known public edge devices in your network.
known_false_positives: Legitimate usage of some VPNs may cause false positives. Tune as needed.
drilldown_searches:
- name: View the detection results for - "$user$"
search: '%original_detection_search% | search Authentication.user = "$user$"'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
- name: View risk events for the last 7 days for - "$user$"
search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$")
starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime
values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories)
as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic)
as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
rba:
message: Improbable travel speed between locations observed for $user$.
risk_objects:
- field: user
type: user
score: 50
threat_objects: []
tags:
analytic_story:
- Remote Employment Fraud
asset_type: Identity
mitre_attack_id:
- T1078
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: identity
61 changes: 61 additions & 0 deletions detections/cloud/okta_non_standard_vpn_usage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Okta Non-Standard VPN Usage
id: 58eb9f80-896c-42f8-86c6-27ab59026c9c
version: 1
date: '2025-06-03'
author: Marissa Bower, Raven Tait
status: experimental
type: TTP
description: Remote Employment Fraud (REF) actors will often use virtual private networks (VPNs) to conceal their
true physical location. Threat actors mask their originating IP address and instead appear to be situated in
any location where the VPN service has a node.
data_source:
- Okta
search: '`okta` debugContext.debugData.tunnels IN (*Astrill*,*Azire*,*CyberGhost*,*Express*VPN,*HideMe*,
*IPVanish*,*Mullvad*,*Nord*VPN*,*OVPN*,*PIA*VPN*,*Proton*VPN*,*Pure*VPN*,*Slick*VPN*,*Surf*Easy*,
*SurfShark*,*Star*VPN*,*TorGuard*,*TorProxy*,*Tiger*VPN*,*TunnelBear*,*Unblock*VPN*,*Warp*VPN*,*WarpSpeed*,
*VPNReactor*,*VPN*Shield*,*VPN*Super*VPN*,*ZenMate*) ```listing of commonly used known VPN providers. Add or remove whatever is appropriate for your environment```
| eval user=coalesce(''actor.alternateId'',user), user=mvindex(split(user, "@"), 0)
| rename targetUserAlternateId as user client.* as * request.* as * ipChain{}.* as * geographicalContext.* as * debugContext.* as * debugData.* as *
| eval status=case(match(_raw, "FAILURE"), "failure", !match(_raw, "FAILURE"), "success")
| stats count values(status) as status max(published) as UTC min(_time) as firsttime max(_time) as lasttime values(target_data) as target_data values(displayMessage) as displayMessage values(eventType) as eventType values(city) as city values(country) as country values(action) as action values(src_ip) as src_ip values(outcome.*) as * values(user) as user by tunnels,_time,host sourcetype index
| fillnull value="N/A"
| convert ctime(*ttime)
| `okta_non_standard_vpn_usage_filter`'
how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the
Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553).
known_false_positives: Limited to no expected false positives once a baseline of common VPN software has been completed.
drilldown_searches:
- name: View the detection results for - "$user$"
search: '%original_detection_search% | search actor.alternateId = "$user$"'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
- name: View risk events for the last 7 days for - "$user$"
search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$")
starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime
values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories)
as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic)
as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
rba:
message: Uncommon VPN software used by $user$ to connect to Okta.
risk_objects:
- field: user
type: user
score: 50
threat_objects: []
tags:
analytic_story:
- Remote Employment Fraud
- Suspicious Okta Activity
asset_type: Identity
mitre_attack_id:
- T1078
- T1572
- T1090
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: identity
4 changes: 4 additions & 0 deletions macros/zoom_index.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
definition: index=zoom
description: customer specific splunk configurations(eg- index, source, sourcetype).
Replace the macro definition with configurations for your Splunk Environment.
name: zoom_index
23 changes: 23 additions & 0 deletions stories/remote_employment_fraud.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Remote Employment Fraud
id: 81a785e2-1046-44ea-80d7-badf381aa49a
version: 1
status: production
date: '2025-06-02'
author: Raven Tait
description: Fortify your insider threat monitoring with searches that monitor for and help you
investigate possible remote employment fraud.
narrative: Remote employment fraud involves threat actors posing as job seekers or employers to
gain unauthorized access to organizations, often using fake or stolen identities. This can result
in insider threats, data breaches, financial loss, and reputational damage, as attackers exploit
remote onboarding processes to infiltrate systems or harvest sensitive information. Strong
identity verification, background checks, and ongoing monitoring are critical to mitigating
these risks.
references: []
tags:
category:
- Adversary Tactics
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
usecase: Insider Threat