Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ROAD2timeline Plugin #42

Closed
wants to merge 10 commits into from
Closed

Conversation

rcobb-scwx
Copy link
Contributor

@rcobb-scwx rcobb-scwx commented Jul 7, 2022

Hi Dirk-jan,

Here is a PR for a new ROADtools plugin called ROAD2timeline.

ROAD2timeline was inspired by forensic tools like plaso/log2timeline that can generate a forensic timeline of events based on the various timestamps found in evidence (usually file system MACB times, etc).

The database created by ROADtools contains many timestamps across its tables. Here is a dump of the DATETIME columns based on the database schema:

AppRoleAssignments
|_ deletionTimestamp
|_ creationTimestamp

Applications
|_ deletionTimestamp

Contacts
|_ deletionTimestamp
|_ lastDirSyncTime

Devices
|_ deletionTimestamp
|_ approximateLastLogonTimestamp
|_ complianceExpiryTime
|_ lastDirSyncTime

DirectoryRoles
|_ deletionTimestamp

ExtensionPropertys
|_ deletionTimestamp

Groups
|_ deletionTimestamp
|_ createdDateTime
|_ expirationDateTime
|_ lastDirSyncTime
|_ renewedDateTime

OAuth2PermissionGrants
|_ expiryTime
|_ startTime

Policys
|_ deletionTimestamp

RoleDefinitions
|_ deletionTimestamp

ServicePrincipals
|_ deletionTimestamp
|_ preferredTokenSigningKeyEndDateTime

TenantDetails
|_ deletionTimestamp
|_ companyLastDirSyncTime
|_ createdDateTime

Users
|_ deletionTimestamp
|_ acceptedOn
|_ createdDateTime
|_ employeeHireDate
|_ invitedOn
|_ lastDirSyncTime
|_ lastPasswordChangeDateTime
|_ onPremisesPasswordChangeTimestamp
|_ refreshTokensValidFromDateTime
|_ userStateChangedOn

ROAD2timeline generates a timeline of all the Azure AD object timestamps in the ROADtools database. For example, if there are 10 rows and 4 datetime columns (ie, timestamps) in the Devices table, ROAD2timeline will create a timeline containing 40 entries in chronologically-sorted order.

To make it easier for humans to review the timeline, I also included simple templates that are populated from the column values in a given row. These are defined and can be customized in a YAML file. For example:

Devices:
  lastDirSyncTime: 'Directory sync for {deviceOSType} device {displayName} (SID: {onPremisesSecurityIdentifier}, Device ID: {deviceId}, Object ID: {objectId})'
  approximateLastLogonTimestamp: 'Approximate last logon for {deviceOSType} device {displayName} (Device ID: {deviceId})'

The plugin will generate a timeline based on all the datetime columns in the database schema, even if a corresponding template is not defined. In this PR, I provide templates for about half of the columns in the schema. It would be helpful to get community feedback on the most useful way to represent these timeline entries. Users can also provide their own template file with a CLI option.

Example Usage

roadrecon plugin road2timeline --help
usage: roadrecon plugin road2timeline [-h] [-d DATABASE] [-t TEMPLATE_FILE] [-f OUTPUT_FILE]

Timeline analysis of Azure AD objects

optional arguments:
  -h, --help            show this help message and exit
  -d DATABASE, --database DATABASE
                        Database file. Can be the local database name for SQLite, or an SQLAlchemy compatible URL such as postgresql+psycopg2:https://dirkjan@/roadtools
  -t TEMPLATE_FILE, --template-file TEMPLATE_FILE
                        File containing string templates to translate Azure AD objects into a timeline entry. Defaults to `road2timeline.yaml` in the current working directory.
  -f OUTPUT_FILE, --output-file OUTPUT_FILE
                        File to save timeline outputs. Output format determined by file extension. Supported extensions include: [csv, pickle, jsonl, parquet]
roadrecon plugin road2timeline -d roadrecon.db -f timeline.parquet
Timeline entry template file road2timeline.yaml not found, defaulting to built-in templates
Loading timeline entry templates from <redacted>/ROADtools/roadrecon/roadtools/roadrecon/plugins/road2timeline.yaml
Timeline saved to file <redacted>/timeline.parquet

Users can analyze the timeline with their tool of choice. Here is an example of how I often parse these timelines in pandas:

import pandas as pd
timeline_df = pd.read_parquet("timeline.parquet")
timeline_df.groupby([
    pd.Grouper(freq="10T"),
    "_table_name",
    "_timestamp_column",
    "_message",
])._object_id.nunique().to_frame()

image

Known Issues/Caveats/Questions

  • This plugin requires the pandas module (and its numpy dependency). If the users wants to export the timeline as a parquet file, then pyarrow is also required. I am not sure what would be the best way to make these optionally installable or otherwise not break functionality for folks who want to run ROADtools without this plugin.
  • I am unfamiliar with the convention for the module add_args function. I mostly copied how other plugins were structured.
  • We need to ensure that the YAML file of template strings is included in the built Python package. I added this to the setup.py, but it could use testing.
  • As mentioned above, only about half of the datetime columns have a corresponding template in the YAML file. I figured you and/or the community would have input on the best way to represent these timeline entries.
  • Timestamps contained in nested objects (ie, inside JSON arrays) are currently not supported/parsed. I am generating the timeline based on the database schema, not by searching for timestamps inside the row values.
  • It works fine in my test tenants, but these are relatively small. It would be helpful to run the plugin against a variety of tenants to see if there are performance issues/parsing problems, etc.
  • It doesn't seem like there is a convention for logging with plugins, so we are printing to console.
  • It would be really neat to have this render in the web UI somehow. I included JSONL output so this might be easier in the future.
  • Currently, the timeline output column names are prefixed with an underscore (ie, _timestamp_column) and do not match the naming convention for log2timeline. This would be an easy change if literal interoperability between ROADtools and log2timeline is desired.
  • If this is merged, someone should add some usage instructions to the wiki.

I think that about sums it up! I hope others find this useful. Please let me know if you have any questions/issues and I will address them.

Thanks for all your work on ROADtools!! It rocks.

Ryan Marcotte Cobb

@dirkjanm
Copy link
Owner

thanks, appreciated. This is some great work! I've made some slight changes to the PR to account for the requirements being optional, unfortunately because of the typed functions this also means putting most definitions in an if statement, which is a bit hacky but it works for now.

@dirkjanm dirkjanm closed this Nov 15, 2022
@dirkjanm
Copy link
Owner

Note that the PR was merged but GitHub doesn't recognize it because I did some stuff manually

@rcobb-scwx
Copy link
Contributor Author

Thanks @dirkjanm -- if you are interested, I wrote a blog post on how I came to write this plugin: https://blog.detect.dev/posts/road2timeline.html

Much appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants