Skip to content
Please update to the latest release 0.77.3 to address Multiple CVEs.
Custom.MacOS.Detection.DylibHijacking

Custom.MacOS.Detection.DylibHijacking

This artifact is intended to alert on executables susceptible to dylib injection and highjacking. This includes checking the executable for library validation issues such as com.apple.security.cs.disable-library-validation and com.apple.security.cs.allow-dyld-environment-variables, multiple (relative) @executable_path’s and LC_LOAD_WEAK_DYLIB entries. The artifact TargetPath looks at the default installation path for MacOS binaries (MachO). To run the artifact against a binary in a non-standard location, add it to the csv TargetPath parameter. Results will be displayed in two parts, dylibloads and libraryvalidation. Only duplicate entries will be displayed for dylibloads.

NOTE: This artifact runs best against targeted endpoints rather than an entire network. It is also recommended to increase the default Timeout period to 6000 seconds or more as performance is quite slow.


name: Custom.MacOS.Detection.DylibHijacking
author: Blake McDermott & Ford Larman
description: |
   This artifact is intended to alert on executables susceptible to dylib injection and highjacking. This includes checking the executable for library validation issues such as `com.apple.security.cs.disable-library-validation` and `com.apple.security.cs.allow-dyld-environment-variables`, multiple (relative) `@executable_path`'s and `LC_LOAD_WEAK_DYLIB` entries. The artifact `TargetPath` looks at the default installation path for MacOS binaries (MachO). To run the artifact against a binary in a non-standard location, add it to the csv `TargetPath` parameter. Results will be displayed in two parts, `dylibloads` and `libraryvalidation`. Only duplicate entries will be displayed for `dylibloads`.
   
   NOTE: This artifact runs best against targeted endpoints rather than an entire network. It is also recommended to increase the default `Timeout` period to 6000 seconds or more as performance is quite slow.

# Can be CLIENT, CLIENT_EVENT, SERVER, SERVER_EVENT or NOTEBOOK
type: CLIENT

required_permissions:
  - EXECVE
  
parameters:
   - name: TargetPath
     type: csv
     default: |
        Glob
        /Applications/*/Contents/MacOS/*
        
   - name: ExcludePath
     type: regex
     description: Binaries to exclude incase of excessive permission issue errors.
     default: ''
        
   - name: YaraRule
     type: yara
     description: https://github.com/airbnb/binaryalert/blob/master/rules/public/MachO.yara
     default: |
        rule MachO {
            meta:
                description = "Mach-O binaries"
            condition:
                uint32(0) == 0xfeedface or uint32(0) == 0xcefaedfe or uint32(0) == 0xfeedfacf or uint32(0) == 0xcffaedfe or uint32(0) == 0xcafebabe or uint32(0) == 0xbebafeca
        }
  
export: |
    LET x64_Profile = '''[
        ["Header", 32,[
            ["Magic", 0, "Enumeration", {
                "type": "uint32",
                "choices": {
                    "0": UNKNOWN,
                    "0xFEEDFACE": "MH_MAGIC",
                    "0xCEFAEDFE": "MH_CIGAM",
                    "0xFEEDFACF": "MH_MAGIC_64",
                    "0xCFFAEDFE": "MH_CIGAM_64",
                    "0xCAFEBABE": "FAT_MAGIC",
                    "0xBEBAFECA": "FAT_CIGAM"
                }
            }],
            ["CPU_Type", 4, "Enumeration", {
                "type": "int32",
                "choices": {
                    "-1": "ANY",
                    "7": "X86",
                    "16777223": "X86_64",
                    "8": "MIPS",
                    "10": "MC98000",
                    "12": "ARM",
                    "16777234": "ARM64",
                    "14": "SPARC",
                    "18": "POWERPC",
                    "16777228": "POWERPC_64"
                }
            }],
            ["CPU_Subtype", 8, "uint32"],
            ["Filetype", 12, "Enumeration", {
                "type": "uint32",
                "choices": {
                    "1": "MH_OBJECT",
                    "2": "MH_EXECUTE",
                    "3": "MH_FVMLIB",
                    "4": "MH_CORE",
                    "5": "MH_PRELOAD",
                    "6": "MH_DYLIB",
                    "7": "MH_DYLINKER",
                    "8": "MH_BUNDLE"
                }
            }],
            ["NumberCommands", 16, "uint32"],
            ["SizeOfCommands", 20, "uint32"],
            ["Flagss", 24, "uint32"],
            ["Reserved", 28, "uint32"],
            ["LoadCommands", 32, "Array", {
                "type": "LoadCommand",
                "count": "x=>x.NumberCommands"
            }]
        ]],
        ["LoadCommand", "x=>x.cmdsize", [
            ["cmd", 0, "Enumeration", {
                "type": "uint32",
                "choices": {
                    "1": 'LC_SEGMENT',
                    "2": 'LC_SYMTAB',
                    "3": 'LC_SYMSEG',
                    "4": 'LC_THREAD',
                    "5": 'LC_UNIXTHREAD',
                    "6": 'LC_LOADFVMLIB',
                    "7": 'LC_IDFVMLIB',
                    "8": 'LC_IDENT',
                    "9": 'LC_FVMFILE',
                    "10": 'LC_PREPAGE',
                    "11": 'LC_DYSYMTAB',
                    "12": 'LC_LOAD_DYLIB',
                    "13": 'LC_ID_DYLIB',
                    "14": 'LC_LOAD_DYLINKER',
                    "15": 'LC_ID_DYLINKER',
                    "16": 'LC_PREBOUND_DYLIB',
                    "17": 'LC_ROUTINES',
                    "18": 'LC_SUB_FRAMEWORK',
                    "19": 'LC_SUB_UMBRELLA',
                    "20": 'LC_SUB_CLIENT',
                    "21": 'LC_SUB_LIBRARY',
                    "22": 'LC_TWOLEVEL_HINTS',
                    "23": 'LC_PREBIND_CKSUM',
                    "2147483672": 'LC_LOAD_WEAK_DYLIB',
                    "25": 'LC_SEGMENT_64',
                    "26": 'LC_ROUTINES_64',
                    "27": 'LC_UUID',
                    "2147483676": 'LC_RPATH',
                    "29": 'LC_CODE_SIGNATURE',
                    "30": 'LC_SEGMENT_SPLIT_INFO',
                    "2147483679": 'LC_REEXPORT_DYLIB',
                    "32": 'LC_LAZY_LOAD_DYLIB',
                    "33": 'LC_ENCRYPTION_INFO',
                    "34": 'LC_DYLD_INFO',
                    "2147483682": 'LC_DYLD_INFO_ONLY',
                }
            }],
            ["cmdsize", 4, "uint32"],
            ["timestamp", 8, "uint32"],
            ["current_version", 12, "uint32"],
            ["compatibility_version", 16, "uint32"],
            ["Name", 24, "String", {
                "length": "x=>x.cmdsize"
            }]
        ]]
        ]'''
        
        LET FAT_Profile = '''[
        ["Header", 0, [
            ["Magic", 0, "Enumeration", {
                "type": "uint32",
                "choices": {
                    "0": UNKNOWN,
                    "0xFEEDFACE": "MH_MAGIC",
                    "0xCEFAEDFE": "MH_CIGAM",
                    "0xFEEDFACF": "MH_MAGIC_64",
                    "0xCFFAEDFE": "MH_CIGAM_64",
                    "0xCAFEBABE": "FAT_MAGIC",
                    "0xBEBAFECA": "FAT_CIGAM"
                }
            }],
            ["NFAT_Arch", 4, "uint32b"],
            ["FAT_Archs", 8, "Array", {
                "type": "FAT_Arch",
                "count": "x=>x.NFAT_Arch"
            }]
        ]],
        ["FAT_Arch", 20, [
            ["CPU_Type", 0, "Enumeration", {
                "type": "int32b",
                "choices": {
                    "-1": "ANY",
                    "7": "X86",
                    "16777223": "X86_64",
                    "8": "MIPS",
                    "10": "MC98000",
                    "12": "ARM",
                    "16777234": "ARM64",
                    "14": "SPARC",
                    "18": "POWERPC",
                    "16777228": "POWERPC_64"
                }
            }],
            ["CPU_Subtype", 4, "uint32b"],
            ["Offset", 8, "uint32b"],
            ["Size", 12, "uint32b"],
            ["Align", 16, "uint32b"]
        ]]
        ]'''
        
        LET yara_rule = YaraRule
        
        // Parse FAT_Profile for Architecture details
        LET FatHeader = SELECT OSPath, parse_binary(filename=OSPath, profile=FAT_Profile, struct='Header').FAT_Archs AS Header FROM foreach(
            row={SELECT * FROM glob(globs=TargetPath.Glob) WHERE NOT OSPath =~ ExcludePath},
            query={SELECT OSPath, log(message="OS Path: %v, Exclude Path: %v", args=[OSPath, ExcludePath]) FROM yara(rules=yara_rule, files=OSPath)}
            ) 
        
        LET Archs = SELECT * FROM foreach(
            row=FatHeader,
            query={SELECT OSPath, * FROM foreach(row=Header)}
        ) WHERE CPU_Type =~ "X86_64" OR CPU_Type =~ "POWERPC_64"
        
        // Parse x64_Profile for dylib details
        LET Headers = SELECT * FROM foreach(
            row=Archs,
            query={SELECT OSPath, CPU_Type, parse_binary(filename=OSPath, offset=Offset, profile=x64_Profile, struct='Header').LoadCommands AS LoadCommands FROM scope()}
        ) 
        
        
        LET LC = SELECT cmd AS LoadCommand, Name AS FilePath, CPU_Type AS Architecture, OSPath FROM foreach(
            row=Headers,
            query={SELECT *, CPU_Type, OSPath FROM foreach(row=LoadCommands)}
        ) WHERE LoadCommand = "LC_LOAD_DYLIB" OR LoadCommand = "LC_LOAD_WEAK_DYLIB" OR LoadCommand = "LC_RPATH"

sources:
  - precondition:
      SELECT OS From info() where OS = 'darwin'
  - name: dylib loads
    query:
        
        LET PathDepth = SELECT len(list=split(string=FilePath, sep="\\.\\.")[:-1]) AS Hops, split(string=FilePath, sep="\\.\\.")[-1] AS Dirs, OSPath FROM LC WHERE LoadCommand = "LC_RPATH"
        
        
        LET ExecPaths = SELECT FilePath[6:] AS Executable_Path, * FROM LC WHERE (LoadCommand = "LC_LOAD_DYLIB" OR LoadCommand = "LC_LOAD_WEAK_DYLIB") AND FilePath =~ "@rpath"
        
        
        LET AllPaths = SELECT * FROM foreach(
            row={SELECT *, path_join(components=path_split(path=OSPath)[:(0-(Hops + 1))]) AS BinPath, OSPath AS TempPath FROM PathDepth WHERE Hops >= 1},
            query={SELECT BinPath+Dirs+Executable_Path AS RPathGlobs, BinPath, Executable_Path, Dirs, Hops, TempPath FROM ExecPaths
            }
        ) 
        GROUP BY RPathGlobs
        
        LET DylibGlobs = SELECT * FROM foreach(
            row=AllPaths,
            query={SELECT *, TempPath FROM glob(globs=RPathGlobs)}
        ) 
        
        // Display results for duplicate .dylib loads per application - Duplicate folders will also be reported if detected.
        SELECT * FROM foreach(
            row={
                SELECT Name AS NameCount, count() AS Count
                FROM DylibGlobs 
                GROUP BY Name, TempPath
            },
            query={
                SELECT if(condition=Name =~".dylib", then="Dylib", else="Folder") AS Type, Name, OSPath, Count, TempPath, hash(path=OSPath) AS Hash,
                authenticode(filename=OSPath) AS Authenticode, if(condition=Name[-6:] =~ ".dylib", then="Duplicate .dylib file names! This is an indication of potential highjacking. Validate the file hashes against known examples.", else="Duplicate directory paths!") AS Result
                FROM DylibGlobs
                WHERE Name = NameCount AND Count >= 2
            }
        )
        

# Execute codesign on target executable and check for values.
  - name: library validation
    query:
        
      LET LibraryInfo = SELECT OSPath AS AppName, Entitlements["plist"]["dict"]["key"] AS keys FROM foreach(
                        row = {SELECT OSPath FROM Archs GROUP BY OSPath},
                        query = {SELECT parse_xml(file=Stdout, accessor="data") AS Entitlements, OSPath, Stdout FROM execve(argv=["codesign", "-d", "--entitlements", ":-", OSPath])}
                    )
                    
        // Check for potential library validation issues from LibraryInfo
        SELECT *, if(condition="com.apple.security.cs.allow-dyld-environment-variables" IN keys OR "com.apple.security.cs.disable-library-validation" IN keys, then="Potential dylib injection - 'com.apple.security.cs.allow-dyld-environment-variables' or 'com.apple.security.cs.disable-library-validation' present!", else="No library validation issues found.") AS Result FROM LibraryInfo