Skip to content

Commit 04a5a23

Browse files
authored
Merge branch 'develop' into panos/revert-noisy-rule
2 parents 7309a5e + 3ced396 commit 04a5a23

7 files changed

+1328
-435
lines changed

Pipfile

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ policyuniverse = "==1.5.1.20230817"
2121
requests = "==2.31.0"
2222
panther-analysis-tool = "~=0.54.0"
2323
panther-detection-helpers = "==0.4.0"
24+
pycountry = "==24.6.1"
2425

2526
[requires]
2627
python_version = "3.11"

Pipfile.lock

+440-429
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

data_models/onelogin_data_model.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33

44
def get_event_type(event):
55
# currently, only tracking a handful of event types
6-
if event.get("event_type_id") == 72 and event.get("privilege_name") == "Super user":
6+
event_type_id = str(event.get("event_type_id"))
7+
if event_type_id == "72" and event.get("privilege_name") == "Super user":
78
return event_type.ADMIN_ROLE_ASSIGNED
8-
if event.get("event_type_id") == 6:
9+
if event_type_id == "6":
910
return event_type.FAILED_LOGIN
10-
if event.get("event_type_id") == 5:
11+
if event_type_id == "5":
1112
return event_type.SUCCESSFUL_LOGIN
12-
if event.get("event_type_id") == 13:
13+
if event_type_id == "13":
1314
return event_type.USER_ACCOUNT_CREATED
1415
return None

packs/azure_signin.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ PackDefinition:
99
# Globals used in these detections
1010
- global_filter_azuresignin
1111
- panther_azuresignin_helpers
12-
13-
12+
- panther_base_helpers
1413
- panther_event_type_helpers
1514
# Data Models
1615
- Standard.Azure.Audit.SignIn

packs/standard_ruleset.yml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ PackDefinition:
2626
- Standard.MFADisabled
2727
- Standard.NewAWSAccountCreated
2828
- Standard.NewUserAccountCreated
29+
- Standard.SignInFromRogueState
2930
# Global Helpers
3031
- panther_base_helpers
3132
- panther_aws_helpers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import panther_event_type_helpers as event_type
2+
import pycountry
3+
4+
# Configuration Required:
5+
# Configure the below list of rogue states according to your needs/experience
6+
# Refer to the link below to find the alpha-2 code corresponding to your country
7+
# https://www.iban.com/country-codes
8+
ROGUE_STATES = {"CN", "IR", "RU"}
9+
10+
11+
def rule(event):
12+
# Only evaluate successful logins
13+
if event.udm("event_type") != event_type.SUCCESSFUL_LOGIN:
14+
return False
15+
16+
# Ignore events with no IP data
17+
if not event.udm("source_ip"):
18+
return False
19+
20+
# Get contry of request origin and compare to identified rogue state list
21+
return bool(is_rogue_state(get_country(event).alpha_2))
22+
23+
24+
def title(event):
25+
log_type = event.get("p_log_type")
26+
country = get_country(event)
27+
account_name = get_account_name(event)
28+
return f"{log_type}: Sign-In for account {account_name} from Rogue State '{country.name}'"
29+
30+
31+
def alert_context(event):
32+
return {
33+
"source_ip": event.udm("source_ip"),
34+
"country": get_country(event).name,
35+
"account_name": get_account_name(event),
36+
}
37+
38+
39+
def get_country(event) -> str:
40+
"""Returns the country code from an event's IPinfo data."""
41+
location_data = event.deep_get("p_enrichment", "ipinfo_location", event.udm_path("source_ip"))
42+
if not location_data:
43+
return "" # Ignore event if we have no enrichment to analyze
44+
return pycountry.countries.get(alpha_2=location_data.get("country").upper())
45+
46+
47+
def get_account_name(event) -> str:
48+
"""Returns the account name."""
49+
if account_name := event.deep_get("p_udm", "user", "email"):
50+
return account_name
51+
if account_name := event.deep_get("p_udm", "user", "name"):
52+
return account_name
53+
if account_name := event.udm("actor_user"):
54+
return account_name
55+
return "UNKNWON ACCOUNT"
56+
57+
58+
def is_rogue_state(country_code: str) -> bool:
59+
"""Returns whether the country code provided belongs to an identified rogue state."""
60+
# This function makes it easy for us to use unit test mocks to ensure altering the ROGUE_STATES
61+
# dict doesn't break our test suite.
62+
return country_code in ROGUE_STATES

0 commit comments

Comments
 (0)