Skip to content

Commit

Permalink
V1.3 (#9)
Browse files Browse the repository at this point in the history
* adding limacharlie output

* refinement

* Added clean skip if secret or password is missing for the input processor

* Added ADX batchsize configurability and ElasticCloud querying

* updated template to demonstrate ADX batching

* typo fix to ADX schema

* added path creation and date-variable to csv

* added options for 2x date-variable to csv

* ignore report folder in git

* added markdown table output per action

* updated gitignore

* updated README to add supported files

* update to Markdown output

* module updates

* updates to docs

* huge speed boost by altering write process

* year update

* sample report update

* version bump

* added msgraphapi SDK support and initial actions

* improvement to array output in fields

* adding dynamic groups

* refined error reporting

* additional stats event

* disable by default due to req and speed

* change to array to accommodate maintenance

* adding auth device id

* add log based MFA updates and new edges

* default off

* version bumps etc

* cypher refinement to win performance

* improvement to path parsing for config

* more MFA based rules

* update to gitignore

* update to gitignore

* added mfa reports

* added mfa queries and add them to paths

* app consent and roles

* refined queries to make BHCE compatible

* new actions

* added max path lenght to improve performance

* added support for user and azuser

* added multi tenant support

* removed experimental reference

* small kql optimization
  • Loading branch information
olafhartong authored Mar 9, 2024
1 parent abaa95f commit 44e4fd6
Show file tree
Hide file tree
Showing 59 changed files with 1,590 additions and 489 deletions.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ output/*.csv
bin/*
configs/*
.vscode/*
actionsTEST/*
client_actions/*/*
falconhound
dist/*
.goreleaser.yaml
cache.db
.idea/*
.idea/*
report/*/*
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BSD 3-Clause License

Copyright (c) 2023, FalconForce
Copyright (c) 2024, FalconForce

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Expand Down
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ Currently, FalconHound supports the following data sources and or targets:
- Neo4j
- MS Graph API (early stage)
- CSV files
- Azure Data Explorer (ADX) - beta
- LogScale
- BloodHound CE and BHE (early stage)
- MarkDown files
- Elastic (early stage)

Additional data sources and targets are planned for the future.

Expand Down Expand Up @@ -177,12 +182,40 @@ Each target has several options that can be configured. Depending on the target,
All targets have the `Name` and `Enabled` fields. The `Name` field is used to identify the target. The `Enabled` field is used to enable or disable the target. If this is set to false, the target will be ignored.

#### CSV

CSV supports the {{date}} variable, which will be replaced by the current date in the format `YYYY-MM-DD`. This can be used to create daily reports.
This can be used in a folder or file name (e.g. `path/to/filename-{{date}}.csv`) or in the foldername itself.

```yaml
- Name: CSV
Enabled: true
Path: path/to/filename.csv
```

#### Markdown

Markdown supports the {{date}} variable, which will be replaced by the current date in the format `YYYY-MM-DD`. This can be used to create daily reports.
This can be used in a folder or file name (e.g. `path/to/filename-{{date}}.md`) or in the foldername itself.

```yaml
- Name: Markdown
Enabled: true
Path: path/to/filename.md
```
Example output:
```markdown
# Results for query: N4J_REPORT_DomainAdmins
## Get a list of Domain Admins
Description: Get a list of Domain Admins.
Date: 2024-02-19
| Name | ObjectID |
| --- | --- |
| [email protected] | S-1-5-21-1122334455-112233445-1112223334-11223344 |
```

#### Neo4j

The Neo4j target will write the results of the query to a Neo4j database. This output is per line and therefore it requires some additional configuration.
Expand Down
8 changes: 8 additions & 0 deletions actions-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,12 @@ Targets: # Targets are the platforms that this action will pu
DisplayName: MDE Exploitable Machines
SearchKey: DeviceName
Overwrite: true # Overwrite the watchlist with the query results, when false it will append the results to the watchlist
- Name: ADX
Enabled: true
Table: FalconHound
BatchSize: 1000 # Number of records to push to ADX in one batch, these will show up in the ADX table as 1 row with an array of values
- Name: Markdown
Enabled: true
Path: reports/{{date}}/get_sessions_mde.md


2 changes: 1 addition & 1 deletion actions/01-Sentinel/sen_get_az_role_assignments.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Query: |
let timeframe = 15m;
AuditLogs
| where ingestion_time() >= ago(timeframe)
| where OperationName startswith "Add member to role"
| where OperationName =~ "Add member to role"
| extend TargetResources=parse_json(TargetResources)
| extend ObjectId = TargetResources.[0].id, userPrincipalName=TargetResources.[0].userPrincipalName, modifiedProperties=TargetResources.[0].modifiedProperties
| extend RoleObjectId = tostring(modifiedProperties[0].newValue),TenantId=AADTenantId
Expand Down
53 changes: 53 additions & 0 deletions actions/01-Sentinel/sen_get_user_mfa_updates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Name: User MFA setting updates
ID: SEN_AZ_MFA_Updates
Description: Gets all additive and updates to MFA settings, deletions are not captured
Author: FalconForce
Version: '1.0'
Info: |-
Active: true # Enable to run this action
Debug: false # Enable to see query results in the console
SourcePlatform: Sentinel # Sentinel, Watchlist, Neo4j, CSV, MDE, Graph, Splunk
Query: |
let timeframe = 15m;
AuditLogs
| where Result == "success"
| where OperationName == "Update user"
| extend UserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| extend modifiedProperties = parse_json(TargetResources[0].modifiedProperties)
| mv-expand modifiedProperty = modifiedProperties
| extend displayName = tostring(modifiedProperty.displayName),
oldValue = modifiedProperty.oldValue,
newValue = modifiedProperty.newValue
| project-away modifiedProperties, modifiedProperty
| where displayName startswith "Strong"
| mv-expand newValue
| extend newValues = parse_json(tostring(newValue))[0]
| extend MfaAuthenticatorDeviceName = newValues.DeviceName
| extend MfaDeviceId = newValues.DeviceId
| extend MfaPhoneNumber = newValues.PhoneNumber
| extend AuthenticatorFlavor = newValues.AuthenticatorFlavor
| extend MfaEmailAddress = newValues.Email
| extend DeviceTag = newValues.DeviceTag
| where isnotnull(AuthenticatorFlavor) or isnotnull( MfaPhoneNumber)
| extend MfaDeviceId=case(MfaDeviceId == "00000000-0000-0000-0000-000000000000","",MfaDeviceId)
| extend MfaAuthMethods=case((AuthenticatorFlavor =="Authenticator" and DeviceTag =~ "SoftwareTokenActivated"),"SoftwareOath",
(AuthenticatorFlavor =="Authenticator" and DeviceTag !~ "SoftwareTokenActivated"),"MicrosoftAuthenticator",
(isnotempty(MfaPhoneNumber)),"Phone",
(isnotempty(MfaEmailAddress)),"Email",
"Unknown" )
| project UserPrincipalName=toupper(UserPrincipalName), UserId=toupper(TargetResources[0].id), MfaAuthenticatorDeviceName,MfaPhoneNumber, MfaAuthMethods, MfaDeviceId=toupper(MfaDeviceId), MfaEmailAddress=toupper(MfaEmailAddress)
Targets: # Targets are the platforms that this action will push to (CSV, Neo4j, Sentinel, Wachlist, Slack, Teams, Splunk, Markdown)
- Name: Neo4j
Enabled: true
Query: |
WITH toUpper($objectid) as objectid, $MfaAuthMethods as MfaAuthMethods, $MfaPhoneNumber as MfaPhoneNumber,$MfaAuthenticatorDeviceName as MfaAuthenticatorDeviceName, toUpper($MfaDeviceId) as MfaDeviceId, $MfaEmailAddress as MfaEmailAddress
MATCH (t:AZUser {objectid: objectid})
SET t.MfaAuthMethods = (CASE WHEN NOT MfaAuthMethods IN coalesce(t.MfaAuthMethods, []) THEN coalesce(t.MfaAuthMethods, []) + [MfaAuthMethods] ELSE t.MfaAuthMethods END)
SET t.MfaPhoneNumber = MfaPhoneNumber, t.MfaAuthenticatorDeviceName = MfaAuthenticatorDeviceName, t.MfaDeviceId = MfaDeviceId, t.MfaEmailAddress = MfaEmailAddress
Parameters:
objectid: UserId
MfaAuthMethods: MfaAuthMethods
MfaPhoneNumber: MfaPhoneNumber
MfaEmailAddress: MfaEmailAddress
MfaAuthenticatorDeviceName: MfaAuthenticatorDeviceName
MfaDeviceId: MfaDeviceId
4 changes: 3 additions & 1 deletion actions/02-MDE/mde_exploitable_hosts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ Targets:
Overwrite: true
- Name: ADX
Enabled: false
Table: FalconHound
Table: FalconHound
- Name: LimaCharlie
Enabled: false
8 changes: 2 additions & 6 deletions actions/02-MDE/mde_new_sessions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Query: |
| where LogonType !in ("Network","Service")
| where isnotempty(AccountSid)
| where not(AccountSid matches regex excludeSid)
| extend ComputerName = toupper(DeviceName), DomainName = strcat(tostring(split(DeviceName, '.')[-2]), '.', tostring(split(DeviceName, '.')[-1]))
| extend DeviceName = toupper(DeviceName), DomainName = strcat(tostring(split(DeviceName, '.')[-2]), '.', tostring(split(DeviceName, '.')[-1]))
| summarize Timestamp=min(Timestamp) by DeviceName,AccountSid,AccountName,AccountDomain, LogonType
# Targets are the platforms that this action will push to (CSV, Neo4j, Sentinel, Wachlist, Slack, Teams, Splunk, Markdown)
Targets:
Expand All @@ -35,8 +35,4 @@ Targets:
WatchlistName: FH_MDE_Sessions
DisplayName: MDE Sessions
SearchKey: AccountName
Overwrite: true
- Name: BHSession
Enabled: false # Experimental
BatchSize: 100

Overwrite: true
2 changes: 1 addition & 1 deletion actions/04-MSgraph/graph_eligible_role_assignments.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Author: FalconForce
Version: '1.0'
Info: |-
Active: true # Enable to run this action
Debug: true # Enable to see query results in the console
Debug: false # Enable to see query results in the console
SourcePlatform: MSGraph # Sentinel, Watchlist, Neo4j, MDE, Graph, Splunk
Query: |
/beta/roleManagement/directory/roleEligibilitySchedules
Expand Down
37 changes: 37 additions & 0 deletions actions/04-MSgraph/msgraph_dynamicgroups.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Name: Get Dynamic Groups
ID: GRAPH_DynamicGroups
Description: Get Dynamic Groups and their rules. Requires Directory.Read.All permissions
Author: FalconForce
Version: '1.0'
Info: |-
Active: true # Enable to run this action
Debug: true # Enable to see query results in the console
SourcePlatform: MSGraphApi # Sentinel, Watchlist, Neo4j, MDE, Graph, Splunk
Query: |
GetDynamicGroups
Targets: # Targets are the platforms that this action will push to (CSV, Neo4j, Sentinel, Wachlist, Slack, Teams, Splunk, Markdown)
- Name: Neo4j
Enabled: true
Query: |
WITH toUpper($objectid) as objectid, toUpper($displayname) as name, $membershiprule as membershiprule, $membershiprulestate as membershiprulestate, $grouptype as grouptype, $displayname as displayname, $tenantid as tenantid
MERGE (x:AZBase {objectid:objectid})
SET x:AZGroup, x+={
name: name,
tenantid: tenantid,
objectid: objectid,
displayname: displayname,
falconhound:True,
memebershiprule: membershiprule,
membershiprulestate: membershiprulestate,
grouptype: grouptype
}
Parameters:
objectid: ObjectId
grouptype: GroupType
membershiprule: MembershipRule
membershiprulestate: MembershipRuleProcessingState
displayname: DisplayName
tenantid: TenantId
- Name: Markdown
Enabled: false
Path: report/TEST/oauth.md
33 changes: 33 additions & 0 deletions actions/04-MSgraph/msgraph_mfa.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Name: Get MFA Status
ID: GRAPH_MFA_Status
Description: Get per user MFA settings. WARNING, this will be slow. Requires User.Read.All and UserAuthenticationMethod.Read.All permissions
Author: FalconForce
Version: '1.0'
Info: |-
Active: false # disabled by default due to the long processing time, enable only when needed, updates can be gathered from logs
Debug: false # Enable to see query results in the console
SourcePlatform: MSGraphApi # Sentinel, Watchlist, Neo4j, MDE, Graph, Splunk
Query: |
GetMFA
Targets: # Targets are the platforms that this action will push to (CSV, Neo4j, Sentinel, Wachlist, Slack, Teams, Splunk, Markdown)
- Name: Neo4j
Enabled: true
Query: |
WITH toUpper($objectid) as objectid, $MfaAuthMethods as MfaAuthMethods, $MfaPhoneNumber as MfaPhoneNumber, $MfaSmsMethod as MfaSmsMethod, $MfaSignInPreference as MfaSignInPreference, toUpper($MfaEmailAddress) as MfaEmailAddress, $MfaHelloDevice as MfaHelloDevice, $MfaFidoDeviceName as MfaFidoDeviceName, $MfaFidoModel as MfaFidoModel, $MfaAuthenticatorDeviceName as MfaAuthenticatorDeviceName, $MfaAuthenticatorDeviceId as MfaAuthenticatorDeviceId
MATCH (t {objectid: objectid})
SET t.MfaAuthMethods = MfaAuthMethods, t.MfaPhoneNumber = MfaPhoneNumber, t.MfaSmsMethod = MfaSmsMethod, t.MfaSignInPreference = MfaSignInPreference, t.MfaEmailAddress = MfaEmailAddress, t.MfaHelloDevice = MfaHelloDevice, t.MfaFidoDeviceName = MfaFidoDeviceName, t.MfaFidoModel = MfaFidoModel, t.MfaAuthenticatorDeviceName = MfaAuthenticatorDeviceName, t.MfaAuthenticatorDeviceId = MfaAuthenticatorDeviceId
Parameters:
objectid: ObjectId
MfaAuthMethods: MfaAuthMethods
MfaPhoneNumber: PhoneNumber
MfaSmsMethod: SmsSignInState
MfaSignInPreference: SignInPreference
MfaEmailAddress: MfaEmailAddress
MfaHelloDevice: HelloDevice
MfaFidoDeviceName: FidoDeviceName
MfaFidoModel: FidoModel
MfaAuthenticatorDeviceName: AuthenticatorDeviceName
MfaAuthenticatorDeviceId: AuthenticatorDeviceId
- Name: Markdown
Enabled: false
Path: report/TEST/MFA.md
29 changes: 29 additions & 0 deletions actions/04-MSgraph/msgraph_oauthconsent.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Name: Get OAuth Consent
ID: GRAPH_OAuthConsent
Description: Get OAuth Consent. Requires Directory.Read.All permissions
Author: FalconForce
Version: '1.0'
Info: |-
Active: true # Enable to run this action
Debug: false # Enable to see query results in the console
SourcePlatform: MSGraphApi # Sentinel, Watchlist, Neo4j, MDE, Graph, Splunk
Query: |
GetOAuthConsent
Targets: # Targets are the platforms that this action will push to (CSV, Neo4j, Sentinel, Wachlist, Slack, Teams, Splunk, Markdown)
- Name: Neo4j
Enabled: true
Query: |
WITH toUpper($objectid) as objectid, $ConsentType as ConsentType, $StartTime as StartTime, $ExpiryTime as ExpiryTime, toUpper($ResourceId) as ResourceId, $Scope as Scope
MATCH (s {objectid: objectid}) MATCH (t {objectid: ResourceId})
MERGE (s)-[r:HasConsent]->(t)
SET r.ConsentType = ConsentType, r.StartTime = StartTime, r.ExpiryTime = ExpiryTime, r.Scope = Scope
Parameters:
objectid: ClientId
ConsentType: ConsentType
StartTime: StartTime
ExpiryTime: ExpiryTime
ResourceId: ResourceId
Scope: Scope
- Name: Markdown
Enabled: false
Path: report/TEST/oauth.md
26 changes: 26 additions & 0 deletions actions/06-Elastic/elk_new_sessions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Name: Get new logon sessions
ID: ELK_New_Sessions
Description: Gets all logon events from Elastic Cloud and sends them to Neo4j
Author: FalconForce
Version: '1.0'
Info: |
Gets all logon events from Elastic Cloud, filters out non-user logons, and creates a relationship between the computer and the user in Neo4j,
with the timestamp of the first logon event.
Active: true # Enable to run this action
Debug: false # Enable to see query results in the console
SourcePlatform: Elastic # Sentinel, Watchlist, Neo4j, CSV, MDE, Graph, Splunk
Query: | # Splunk index can be hardcoded or a variable set in the config.yml file
@timestamp:[now-15h TO now]
AND winlog.event_id: 4624
AND winlog.event_data.TargetUserSid: *S-1-5-21-*
AND winlog.event_data.LogonType: (2 OR 10 OR 11 OR 12 OR 13 OR 14 OR 15)
Targets: # Targets are the platforms that this action will push to (CSV, Neo4j, Sentinel, Wachlist, Slack, Teams, Splunk, Markdown)
- Name: Neo4j
Enabled: true
Query: |
WITH toUpper($Computer) as Computer, toUpper($TargetUserSid) as TargetUserSid, $Timestamp as Timestamp
MATCH (x:Computer {name:Computer}) MATCH (y:User {objectid:TargetUserSid}) MERGE (x)-[r:HasSession]->(y) SET r.since=Timestamp SET r.source='falconhound'
Parameters:
Computer: winlog.computer_name
TargetUserSid: winlog.event_data.TargetUserSid
Timestamp: "@timestamp"
15 changes: 15 additions & 0 deletions actions/10-Neo4j/n4j_0_cln_remove_mfa_device_sharing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Name: CLEANUP - Remove the MFA Device Sharing edge
ID: N4J_CLN_Remove_MFA_Device_Sharing
Description: Removes the MFA Device Sharing edge from the graph if the nodes do not share the same device
Author: FalconForce
Version: '1.0'
Info: |-
Active: true # Enable to run this action
Debug: false # Enable to see query results in the console
SourcePlatform: Neo4j
Query: |
MATCH (u1:AZUser)-[r:MfaDeviceSharing]-(u2:AZUser)
WHERE u1.MfaAuthenticatorDeviceId IS NOT NULL AND u2.MfaAuthenticatorDeviceId IS NOT NULL
AND u1.MfaAuthenticatorDeviceId <> u2.MfaAuthenticatorDeviceId
DELETE r
Targets: []
15 changes: 15 additions & 0 deletions actions/10-Neo4j/n4j_0_cln_remove_mfa_email_sharing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Name: CLEANUP - Remove the MFA Email Sharing edge
ID: N4J_CLN_Remove_MFA_EMAIL_Sharing
Description: Removes the MFA Email edge from the graph if the nodes do not share the same address
Author: FalconForce
Version: '1.0'
Info: |-
Active: true # Enable to run this action
Debug: false # Enable to see query results in the console
SourcePlatform: Neo4j
Query: |
MATCH (u1:AZUser)-[r:MfaEmailSharing]-(u2:AZUser)
WHERE u1.MfaEmailAddress IS NOT NULL AND u2.MfaEmailAddress IS NOT NULL
AND u1.MfaEmailAddress <> u2.MfaEmailAddress
DELETE r
Targets: []
15 changes: 15 additions & 0 deletions actions/10-Neo4j/n4j_0_cln_remove_mfa_phone_sharing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Name: CLEANUP - Remove the MFA Phone Sharing edge
ID: N4J_CLN_Remove_MFAPhoneSharing
Description: Removes the MFA Phone Sharing edge from the graph if the nodes do not share the same phone number
Author: FalconForce
Version: '1.0'
Info: |-
Active: true # Enable to run this action
Debug: false # Enable to see query results in the console
SourcePlatform: Neo4j
Query: |
MATCH (u1:AZUser)-[r:MfaPhoneSharing]-(u2:AZUser)
WHERE u1.MfaPhoneNumber IS NOT NULL AND u2.MfaPhoneNumber IS NOT NULL
AND u1.MfaPhoneNumber <> u2.MfaPhoneNumber
DELETE r
Targets: []
20 changes: 20 additions & 0 deletions actions/10-Neo4j/n4j_1_edge_add_mfa_authdevice_sharing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Name: EDGE - Add MFA Authenticator Device Sharing edges
ID: N4J_EDGE_ADD_MFAAUTHDEVICE_SHARING
Description: Checks for accounts with the same MFA Authenticator device and adds a MfaDeviceSharing edge.
Author: FalconForce
Version: '1.0'
Info: |-
Active: true # Enable to run this action
Debug: false # Enable to see query results in the console
SourcePlatform: Neo4j
Query: |
MATCH (user1:AZUser)
WHERE user1.MfaAuthenticatorDeviceId IS NOT NULL AND user1.MfaAuthenticatorDeviceId <> ""
WITH user1.MfaAuthenticatorDeviceId AS device, COLLECT(user1) AS users
UNWIND users AS u1
UNWIND users AS u2
WITH u1, u2
WHERE id(u1) < id(u2)
MERGE (u1)-[r:MfaDeviceSharing]-(u2)
SET r.enforced = false
Targets: []
Loading

0 comments on commit 44e4fd6

Please sign in to comment.