Skip to content
Draft
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
114 changes: 114 additions & 0 deletions content/exchange/artifacts/Generic.Client.ADStatus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: Generic.Client.ADStatus
author: Andreas Misje – @misje
description: |
Get Active Directory join status from a computer. The information returned
depends on the operating system, but DomainJoined (boolean) and Domain is
returned for all. DomainJoined is always set. Domain is always uppercase.

On Linux, realm/sssd is used to query AD status/configuration. The following
columns are returned:

- _RealmType (e.g. "kerberos")
- _RealmName (e.g. "AD.EXAMPLE.ORG")
- Domain (e.g. "AD.EXAMPLE.ORG)
- AllowedUsers (e.g. ["bob"])
- AllowedGroups (e.g. ["it"])
- DomainJoined (boolean)

On Windows, `dsregcmd` is used to return AD status/configuration. Only a small
part of its output is returned:

- AzureAdJoined (boolean)
- EnterpriseJoined (boolean)
- DomainJoined (boolean)
- NetBIOS (e.g. "EXAMPLE")
- Domain (e.g. "AD.EXAMPLE.ORG)
- DeviceName (e.g. "DESKTOP-FOO")

On macOS, `dsconfigad` is used to return AD status/configuration:

- Domain (e.g. "AD.EXAMPLE.ORG")
- DeviceName (e.g. "DESKTOP-FOO")
- AdminGroups (e.g. ["it"])
- DomainJoined (boolean)

type: CLIENT

sources:
- query: |
LET Info <= SELECT OS
FROM info()

LET SplitString(String) = filter(regex='.+',
list=split(string=String, sep=', ?'))

LET LinuxStatus = SELECT
parse_string_with_regex(
string=Stdout,
regex=(''' +type: +(?P<_RealmType>.*)''', ''' +realm-name: +(?P<_RealmName>.*)''', ''' +domain-name: +(?P<Domain>.*)''', ''' +permitted-logins: +(?P<AllowedUsers>.*)''', ''' +permitted-groups: +(?P<AllowedGroups>.*)''', )) AS ADStatus
FROM execve(
argv=('realm', 'list'))

LET WindowsStatus = SELECT
parse_string_with_regex(
string=Stdout,
regex=('''(?s)Device State.+AzureAdJoined *: *(?P<AzureAdJoined>\S+)''', '''(?s)Device State.+EnterpriseJoined *: *(?P<EnterpriseJoined>\S+)''', '''(?s)Device State.+DomainJoined *: *(?P<DomainJoined>\S+)''', '''(?s)Device State.+DomainName *: *(?P<NetBIOS>\S+)''', '''(?s)Device State.+Device Name *: *(?P<DeviceName>\S+)''', )) AS ADStatus
FROM execve(
argv=('dsregcmd', '/status'))

LET DarwinStatus = SELECT
parse_string_with_regex(
string=Stdout,
regex=('''Active Directory Domain += +(?P<Domain>\S+)''', '''Computer Account += +(?P<DeviceName>\S+)\$''', ''' +Allowed admin groups += +(?P<AdminGroups>.+)''', )) AS ADStatus
FROM execve(
argv=('dsconfigad', '-show'))

LET ADDict = SELECT
ADStatus + dict(Domain=upcase(string=ADStatus.Domain)) AS ADStatus
FROM switch(
linux={
SELECT *
FROM if(
condition=Info[0].OS = 'linux',
then={
SELECT ADStatus + dict(
DomainJoined=ADStatus != dict(),
AllowedUsers=SplitString(String=ADStatus.AllowedUsers),
AllowedGroups=SplitString(String=ADStatus.AllowedGroups)) AS ADStatus
FROM LinuxStatus
})
},
windows={
SELECT
*
FROM if(
condition=Info[0].OS = 'windows',
then={
SELECT
ADStatus + dict(
AzureAdJoined=ADStatus.AzureAdJoined = 'YES',
EnterpriseJoined=ADStatus.EnterpriseJoined = 'YES',
DomainJoined=ADStatus.DomainJoined = 'YES') +
parse_string_with_regex(
string=ADStatus.DeviceName,
regex='''^(?P<DeviceName>[^.]+)\.(?P<Domain>\S+)$''') AS ADStatus
FROM WindowsStatus
})
},
darwin={
SELECT
*
FROM if(
condition=Info[0].OS = 'darwin',
then={
SELECT
ADStatus + dict(
DomainJoined=ADStatus != dict(),
AdminGroups=SplitString(
String=ADStatus.AdminGroups)) AS ADStatus
FROM DarwinStatus
})
})

SELECT *
FROM foreach(row=ADDict, column='ADStatus')
150 changes: 150 additions & 0 deletions content/exchange/artifacts/Generic.Client.Defender.Health.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
name: Generic.Client.Defender.Health
author: Andreas Misje – @misje
description: |
Get MDATP health

Microsoft Defender for Endpoint Advanced Threat Protection is an EDR solution
for Windows, Darwin and Linux. This artifact retrieves all available information
about the agents's status and configuration. The artifact will fail if MDATP is
not installed on the endpoint.

The output from the MDATP status commands vary significantly between Windows
and Linux/Darwin. The output on Linux and Darwin are almost identical, with a
few exceptions:

Linux has these additional fields:

- behaviorMonitoring
- supplementaryEventsSubsystem

Darwin has these additional fields:

- deviceControlEnforcementLevel
- ecsConfigurationIds
- fullDiskAccessEnabled
- networkEventsSubsystem
- tamperProtection
- troubleshootingMode

The notebook suggestion "Common MDATP health information" provides a nice
summary of information of fields present in at least Windows and one of Linux
and Darwin.

type: CLIENT

implied_permissions:
- EXECVE

sources:
- query: |
LET Info <= SELECT OS
FROM info()

SELECT *
FROM if(
condition=Info[0].OS = 'linux',
then={
SELECT parse_json(data=Stdout) AS MDATPHealth
FROM execve(argv=['/usr/bin/mdatp', 'health', '--output', 'json'])
},
else=if(
condition=Info[0].OS = 'darwin',
then={
SELECT parse_json(data=Stdout) AS MDATPHealth
FROM execve(argv=['/usr/local/bin/mdatp', 'health', '--output', 'json'])
},
else=if(
condition=Info[0].OS = 'windows',
then={
SELECT
to_dict(item={
SELECT _key,
_value
FROM parse_records_with_regex(
file=Stdout,
accessor='data',
regex='''^\s*(?P<_key>\S+)\s+:\s+(?P<_value>[^\r\n]+)''')
}) + dict(
edrMachineId=read_file(
accessor='reg',
filename='''HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Advanced Threat Protection\senseId''')) AS MDATPHealth
FROM Artifact.Windows.System.PowerShell(
Command='Get-MpComputerStatus')
})))

notebook:
- name: Common MDATP health information
type: vql_suggestion
template: |
/*
# Common MDATP health information
*/
LET ColumnTypes <= dict(`ClientId`='client')

LET S = scope()

LET Result <= SELECT
ClientId,
S.Fqdn || client_info(client_id=ClientId).os_info.fqdn AS Fqdn,
S.MDATPHealth || dict() AS D
FROM source()

SELECT *
FROM foreach(
row=Result,
query={
SELECT *
FROM if(
condition='AMEngineVersion' IN D,
then={
SELECT
ClientId,
Fqdn,
D.edrMachineId AS edrMachineId,
D.AMProductVersion AS appVersion,
D.AMEngineVersion AS engineVersion,
D.AntivirusSignatureVersion AS definitionsVersion,
timestamp(string=D.AntivirusSignatureLastUpdated) AS definitionsUpdated,
if(condition=D.DefenderSignaturesOutOfDate = NULL,
then=NULL,
else=D.DefenderSignaturesOutOfDate != 'False') AS definitionsUpToDate,
if(condition=D.BehaviorMonitorEnabled = NULL,
then=NULL,
else=D.BehaviorMonitorEnabled = 'True') AS behaviorMonitoringEnabled,
if(condition=D.RealTimeProtectionEnabled = NULL,
then=NULL,
else=D.RealTimeProtectionEnabled = 'True') AS realTimeProtectionEnabled,
if(
condition=D.IsTamperProtected = NULL,
then=NULL,
else=D.IsTamperProtected = 'True') AS tamperProtectionEnabled,
if(
condition=D.TroubleShootingMode = NULL,
then=NULL,
else=D.TroubleShootingMode != 'Disabled') AS troubleshootingModeEnabled
FROM scope()
},
else={
SELECT
ClientId,
Fqdn,
D.edrMachineId AS edrMachineId,
D.appVersion AS appVersion,
D.engineVersion AS engineVersion,
D.definitionsVersion AS definitionsVersion,
timestamp(
epoch=D.definitionsUpdated) AS definitionsUpdated,
if(
condition=D.definitionsStatus.`$type` = NULL,
then=NULL,
else=D.definitionsStatus.`$type` = 'upToDate') AS definitionsUpToDate,
// Not available in Darwin:
D.behaviorMonitoring.displayValue AS behaviorMonitoringEnabled,
D.realTimeProtectionEnabled.value AS realTimeProtectionEnabled,
// Not available in Linux:
D.tamperProtection.displayValue AS tamperProtectionEnabled,
// Not available in Linux:
D.troubleshootingMode AS troubleshootingModeEnabled
FROM scope()
})
})
Loading
Loading