MacOS.Search.FileFinder

Find files on the filesystem using the filename or content.

Performance Note

This artifact can be quite expensive, especially if we search file content. It will require opening each file and reading its entire content. To minimize the impact on the endpoint we recommend this artifact is collected with a rate limited way (about 20-50 ops per second).

This artifact is useful in the following scenarios:

  • We need to locate all the places on our network where customer data has been copied.

  • We’ve identified malware in a data breach, named using short random strings in specific folders and need to search for other instances across the network.

  • We believe our user account credentials have been dumped and need to locate them.

  • We need to search for exposed credit card data to satisfy PCI requirements.

  • We have a sample of data that has been disclosed and need to locate other similar files


name: MacOS.Search.FileFinder
description: |
  Find files on the filesystem using the filename or content.


  ## Performance Note

  This artifact can be quite expensive, especially if we search file
  content. It will require opening each file and reading its entire
  content. To minimize the impact on the endpoint we recommend this
  artifact is collected with a rate limited way (about 20-50 ops per
  second).

  This artifact is useful in the following scenarios:

    * We need to locate all the places on our network where customer
      data has been copied.

    * We’ve identified malware in a data breach, named using short
      random strings in specific folders and need to search for other
      instances across the network.

    * We believe our user account credentials have been dumped and
      need to locate them.

    * We need to search for exposed credit card data to satisfy PCI
      requirements.

    * We have a sample of data that has been disclosed and need to
      locate other similar files


precondition:
  SELECT * FROM info() where OS = 'darwin'

parameters:
  - name: SearchFilesGlob
    default: /Users/*
    description: Use a glob to define the files that will be searched (Use ** for recursive).

  - name: SearchFilesGlobTable
    type: csv
    default: |
      Glob
      /Users/someuser/*
    description: Alternative specify multiple globs in a table

  - name: YaraRule
    type: yara
    default:
    description: A yara rule to search for matching files.

  - name: Upload_File
    default: N
    type: bool

  - name: Calculate_Hash
    default: N
    type: bool

  - name: MoreRecentThan
    default: ""
    type: timestamp

  - name: ModifiedBefore
    default: ""
    type: timestamp

  - name: DoNotFollowSymlinks
    type: bool
    default: Y
    description: If specified we are allowed to follow symlinks while globbing

sources:
- query: |
    LET file_search = SELECT OSPath,
               Sys.mft as Inode,
               Mode.String AS Mode, Size,
               Mtime AS MTime,
               Atime AS ATime,
               Ctime AS CTime,
               IsDir, Mode
        FROM glob(globs=SearchFilesGlobTable.Glob + SearchFilesGlob,
                  accessor="file", nosymlink=DoNotFollowSymlinks)

    LET more_recent = SELECT * FROM if(
        condition=MoreRecentThan,
        then={
          SELECT * FROM file_search
          WHERE MTime > MoreRecentThan
        },
        else={ SELECT * FROM file_search})

    LET modified_before = SELECT * FROM if(
        condition=ModifiedBefore,
        then={
          SELECT * FROM more_recent
          WHERE MTime < ModifiedBefore
           AND  MTime > MoreRecentThan
        },
        else={SELECT * FROM more_recent})

    LET keyword_search = SELECT * FROM if(
        condition=YaraRule,
        then={
          SELECT * FROM foreach(
            row={
               SELECT * FROM modified_before
               WHERE Mode.IsRegular
            },
            query={
               SELECT OSPath, Inode, Mode,
                      Size, ATime, MTime, CTime,
                      str(str=String.Data) As Keywords

               FROM yara(files=OSPath,
                         key="A",
                         rules=YaraRule,
                         accessor="file")
            })
        },
        else={SELECT * FROM modified_before})

    SELECT OSPath, Inode, Mode, Size, ATime,
             MTime, CTime, get(field='Keywords') AS Keywords,
               if(condition=Upload_File and Mode.IsRegular,
                  then=upload(file=OSPath,
                              accessor="file")) AS Upload,
               if(condition=Calculate_Hash and Mode.IsRegular,
                  then=hash(path=OSPath,
                            accessor="file")) AS Hash
    FROM keyword_search

column_types:
  - name: ATime
    type: timestamp
  - name: MTime
    type: timestamp
  - name: CTime
    type: timestamp
  - name: Upload
    type: preview_upload