Home

Awesome

ADTimeline

Table of contents:

  1. The ADTimeline PowerShell script
    1. Description
    2. Prerequisites
    3. Usage
    4. Files generated
    5. Custom groups
  2. The ADTimeline App for Splunk
    1. Description
    2. Sourcetypes
    3. AD General information dashboards
    4. AD threat hunting dashboards
    5. Enhance your traditional event logs threat hunting with ADTimeline

The ADTimeline PowerShell script: <a name="thescript"></a>

Description: <a name="description"></a>

The ADTimeline script generates a timeline based on Active Directory replication metadata for objects considered of interest.
Replication metadata gives you the time at which each replicated attribute for a given object was last changed. As a result the timeline of modifications is partial. For each modification of a replicated attribute a version number is incremented.
ADTimeline was first presented at the CoRI&IN 2019 (Conférence sur la réponse aux incidents et l’investigation numérique). Slides of the presentation, in french language, are available here. It was also presented at the Amsterdam 2019 FIRST Technical Colloquium, slides in english are available here.

Objects considered of interest retrieved by the script include:

Prerequisites: <a name="prerequisites"></a>

Usage: <a name="usage"></a>

In online mode no argument is mandatory and the closest global catalog is used for processing. If no global catalog is found run the script with the server argument :

PS> .\AD-timeline.ps1 -server <GLOBAL CATALOG FQDN>

In offline mode: Replay if necessary transaction logs of the NTDS database, mount it on your analysis machine (ADLDS + RSAT-AD-Tools installed) and use 3266 as LDAP port.

C:\Windows\System32> dsamain.exe -dbpath <NTDS.DIT path> -ldapport 3266 -allownonadminaccess

If necessary use the allowupgrade switch.

Launch the script targetting localhost on port 3266:

PS> .\AD-timeline.ps1 -server "127.0.0.1:3266"

If you encounter performance issues when running against a large MSExchange organization with forwarders massively used, use the nofwdSMTP parameter:

PS>.\ADTimeline -nofwdSMTPaltRecipient

Files generated <a name="files"></a>

Output files are generated in the current directory:

To import files for analysis with powershell.

PS> import-csv timeline_%DOMAINFQDN%.csv -delimiter ";"
PS> import-clixml ADobjects_%DOMAINFQDN%.xml
PS> import-clixml gcADobjects_%DOMAINFQDN%.xml

The analysis with the ADTimeline for Splunk is a better solution.

Custom groups <a name="groups"></a>

If you want to include custom AD groups in the timeline (for example virtualization admin groups, network admins, VIP groups...) use the Customgroups parameter.

Customgroups parameter can be a string with multiple group comma separated (no space):

PS>./ADTimeline -customgroups "VIP-group1,ESX-Admins,Tier1-admins"

Customgroups parameter can also be an array, in case you import the list from a file (one group per line):

PS>$customgroups = get-content customgroups.txt
PS>./ADTimeline -customgroups $customgroups

If you do not want to use a parameter you can also uncomment and edit the following array at the begining of the script:

$groupscustom = ("VIP-group1","ESX-Admis","Tier1-admins")

The ADTimeline App for Splunk: <a name="theapp"></a>

Description: <a name="descriptionsplk"></a>

The ADTimeline application for Splunk processes and analyses the Active Directory data collected by the ADTimeline PowerShell script. The app was presented at the 32nd annual FIRST Conference, a recording of the presentation is available here.

The app's "Getting started" page will give you the instructions for the import process.

Once indexed the dashboards provided by the app will help the DFIR analyst to spot some Acitve Directory persistence mechanisms, misconfigurations, security audit logging bypass, mail exfiltration, brute force attacks ...

The app is also packaged and available on Splunkbase. It has no prerequisite and will work with a free Splunk license.

Splunkapp

Sourcetypes: <a name="sourcetype"></a>

After processing the ADTimeline script you should have two or three files to import in Splunk (%DOMAINFQDN% is the Active Directory fully qualified domain name):

The adtimeline sourcetype:

The adtimeline sourcetype is the data from the timeline_%DOMAINFQDN%.csv file, which is the Active Directory timeline built with replication metadata for objects considered of interest.

The timestamp value is the ftimeLastOriginatingChange value of the replication metadata, which is the time the attribute was last changed, time is UTC.

The extracted fields are:

The adobjects sourcetype:

The adobjects sourcetype is the data from the ADobjects_%DOMAINFQDN%.xml file, which is an export of the Active Directory objects considered of interested and retrieved via the LDAP protocol.

The timestamp value is the createTimeStamp attribute value, time zone is specified in the attribute value.

The extracted fields are:

The gcobjects sourcetype:

The gcobjects sourcetype is the data from the gcADobjects_%DOMAINFQDN%.xml file, which is an export of the Active Directory objects within the forest but outside the current domain and considered of interested, those objects are retrieved via the Global Catalog protocol.

The timestamp value is the WhenCreated attribute value, time zone is UTC.

The extracted fields are:

AD General information dashboards: <a name="infradashboards"></a>

The Active Directory Infrastructure dashboard:

This dashboard analyses Adtimeline data in order to create some panels giving you information on the Windows domain infrastructure.

The different panels are:

The sensitive accounts dashboard:

This dashboard provides an inventory of the privileged accounts in the domain and accounts prone to common attack scenarios due to their configuration.

The different panels are:

AD threat hunting dashboards: <a name="threathuntdashboards"></a>

The investigate timeframe dashboard:

Use this dashboard to investigate a particular timeframe.

The different panels are:

The track suspicious activity dashboard

This dashboard analyses the Active Directory timeline and highlights some modifications which can be a sign of a suspicious activity, the modifications spoted can also be legitimate and need a triage analysis.

The different panels are:

The track suspicious Exchange activity dashboard

This dashboard analyses your Exchange onprem Active Directory objects and highlights some modifications which can be a sign of a suspicious activity, the modifications spotted can also be legitimate and need a triage analysis.

The different panels are:

Enhance your traditional event logs threat hunting with ADTimeline: <a name="threathuntevtx"></a>

The adobjects sourcetype is a set of data which can be used to uncover suspicious activity by performing Active Directory educated queries on the Windows event logs. We assume the sourcetype used for event logs is called winevent and its EventData part has the correct field extraction applied, for example EventID 4624 has among other fields TargetUserName and TargetUserSid extracted:

EVtx

You can perfrom similar queries with the Splunk App for Windows Infrastructure and the Splunk Supporting Add-on for Active Directory. Here are some queries using ADTimeline data and Windows event logs which can help with your threat hunt.

index="*" sourcetype="winevent" EventID="4624" Channel="Security" 
[ search index="*" sourcetype=adobjects  ((ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND adminCount=1) earliest = 1 latest = now() | rename SID as TargetUserSid | fields TargetUserSid ] 
| strcat TargetDomainName "\\" TargetUserName TargetFullName | stats values(TargetFullName) as TargetFullName , values(LogonType) as logonTypes, values(IpAddress) as IpAdresses, values(Computer) as Computers, dc(Computer) as CountComputers, dc(IpAddress) as CountIpAdresses by TargetUserSid
index="*" sourcetype="winevent" EventID="4688" Channel="Security" 
[search index="*" sourcetype=adobjects  ((ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND adminCount=1) earliest = 1 latest = now() | rename SamAccountName as TargetUserName | fields TargetUserName] 
OR [search index="*" sourcetype=adobjects  ((ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND adminCount=1) earliest = 1 latest = now() | rename SID as SubjectUserSid | fields SubjectUserSid] 
|  stats values(CommandLine) by Computer, TargetUserName,SubjectUserName
index="*" sourcetype="winevent" Channel="*PowerShell*"  
[search index="*" sourcetype=adobjects  ((ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND adminCount=1) earliest = 1 latest = now() | dedup SamAccountName | return 1000 $SamAccountName]
index="*" sourcetype="winevent" Channel="Security" EventID="4769" 
[search index="*" sourcetype=adobjects (ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND (SPNs=* AND NOT Name=krbtgt) earliest = 1 latest = now() | rename SID as ServiceSid | fields ServiceSid] 
| strcat TargetDomainName "\\" TargetUserName TargetFullName  
|  eval time=strftime(_time,"%Y-%m-%d %H:%M:%S") 
|  stats list(ServiceName) as Services, dc(ServiceName) as nbServices, list(time) as time by IpAddress, TicketEncryptionType 
| sort -nbServices
index="*" sourcetype="winevent" Channel="Security" EventID="4688" 
[search index="*" sourcetype=adobjects  (ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND (SPNs=* AND NOT Name=krbtgt)  earliest = 1 latest = now()   | rename SamAccountName as TargetUserName | fields TargetUserName ] 
OR  [search index="*" sourcetype=adobjects  (ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND (SPNs=* AND NOT Name=krbtgt)  earliest = 1 latest = now()   | rename SID as SubjectUserSid | fields SubjectUserSid ] 
|  stats values(CommandLine) as cmdlines, values(TargetUserName) as TargetSubjectNames, values(SubjectUserName) as SubjectUserNames by Computer
index="*" sourcetype="winevent" Channel="Security" EventID="4624" 
[search index="*" sourcetype=adobjects   (ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND (SPNs=* AND NOT Name=krbtgt)  earliest = 1 latest = now() | rename SID as TargetUserSid  | fields TargetUserSid] 
| strcat TargetDomainName "\\" TargetUserName TargetFullName | stats values(TargetFullName) as TargetFullNames, values(IpAddress) as IpAddresses by LogonType, Computer
index="*" sourcetype="winevent" Channel="Security" EventID="4624" 
[search index="*" sourcetype=adobjects (ObjectClass = "user" OR ObjectClass = "inetOrgPerson") earliest = 1 latest = now() |  eval eval_asrep_bit = floor(userAccountControl / pow(2, 22)) %2 | search eval_asrep_bit = 1 |  rename SID as TargetUserSid  | fields TargetUserSid]  
| strcat TargetDomainName "\\" TargetUserName TargetFullName | stats values(TargetFullName) as TargetFullNames, values(IpAddress) as IpAddresses by LogonType, Computer
index="*" sourcetype="winevent" EventID="4624" Channel="Security" 
[search index="*" sourcetype=adobjects ObjectClass = "Computer" earliest = 1 latest = now() | eval eval_deleg_bit = floor(userAccountControl / pow(2, 19)) %2  | search  eval_deleg_bit = 1 | eval eval_dc_bit = floor(userAccountControl / pow(2, 13)) %2 |  eval eval_rodc_bit = floor(userAccountControl / pow(2, 26)) %2 | search eval_dc_bit = 0 AND eval_rodc_bit = 0 |   rename dNSHostName as Computer | fields Computer] 
|  search *   [search index="*" sourcetype=adobjects  ((ObjectClass = "user" OR ObjectClass = "inetOrgPerson") AND adminCount=1) earliest = 1 latest = now() | eval canbedelagated = round(((userAccountControl / pow(2, 20)) %2), 0) | search canbedelagated = 0 | rename SID as TargetUserSid  | fields TargetUserSid ] 
|  strcat TargetDomainName "\\" TargetUserName TargetFullName 
| table  _time, Computer, TargetFullName, IpAddress, LogonType, LogonProcessName
index="*" sourcetype="winevent" EventID="4624" Channel="Security" TargetUserName = "*$" NOT TargetUserSid="S-1-5-18"
[search index="*" sourcetype=adobjects ObjectClass = "Computer"  earliest = 1 latest = now() |  eval eval_deleg_bit = floor(userAccountControl / pow(2, 19)) %2  | search  eval_deleg_bit = 1 | eval eval_dc_bit = floor(userAccountControl / pow(2, 13)) %2 |  eval eval_rodc_bit = floor(userAccountControl / pow(2, 26)) %2 | search eval_dc_bit = 0 AND eval_rodc_bit = 0  |  rename dNSHostName as Computer | fields Computer]
| strcat TargetDomainName "\\" TargetUserName TargetFullName | stats values(LogonType), values(IpAddress), values(LogonProcessName) count by Computer, TargetFullName