Linux.Persistence.LdPreload

Parses the GNU glibc LD_PRELOAD mechanism. Any path listed in /etc/ld.so.preload or assigned to LD_PRELOAD is force‑loaded into every dynamically linked executable. A single rogue entry therefore grants system‑wide code execution and persistence at process start‑up.

The artifact has two scopes:

• Parses /etc/ld.so.preload
• Checks /proc/<pid>/environ for an LD_PRELOAD= entry.


name: Linux.Persistence.LdPreload
author: Matt Green - @mgreen27
description: |
  Parses the GNU glibc **LD_PRELOAD** mechanism. Any path listed in 
  `/etc/ld.so.preload` or assigned to `LD_PRELOAD` is force‑loaded into every 
  dynamically linked executable. A single 
  rogue entry therefore grants system‑wide code execution and persistence at
  process start‑up.
  
  The artifact has two scopes:

  • Parses `/etc/ld.so.preload`   
  • Checks `/proc/<pid>/environ` for an `LD_PRELOAD=` entry.

references:
    - https://attack.mitre.org/techniques/T1574/006/

type: CLIENT

parameters:
  - name: TargetGlob
    default: /etc/ld.so.preload
  - name: ContentRegex
    default: .
    description: Regex to target suspicious content strings.
    
sources:
  - precondition:
      SELECT OS From info() where OS = 'linux' 
    query: |
       SELECT OSPath,
              Mtime,
              Atime,
              Ctime,
              Btime,
              Size,
              read_file(filename=OSPath)[0:5000] AS Content
       FROM glob(globs=TargetGlob)
       WHERE Content =~ ContentRegex

  - name: Environment Variable
    query: |
       LET target_proc = SELECT OSPath,
                                parse_string_with_regex(
                                  regex='''LD_PRELOAD=(?<LD_PRELOAD>[^\x00]+)''',
                                  string=read_file(filename=OSPath + '/environ')).LD_PRELOAD AS LD_PRELOAD
         FROM glob(
           globs='/proc/[0-9]*')
         WHERE LD_PRELOAD
       
       LET parsed_proc = SELECT
           *, int(int=OSPath[1]) AS Pid,
           to_dict(item={
                SELECT * FROM parse_csv(
                   filename=OSPath + '/status', 
                   separator=':', 
                   columns=['_key','_value']) 
            }) AS Status
       FROM target_proc
       
       SELECT
           Pid,
           Status.PPid AS PPid,
           Status.Name AS Name,
           stat(filename=OSPath + '/exe').Data.Link AS Exe,
           read_file(filename=OSPath + '/cmdline') AS Cmdline,
           LD_PRELOAD,
           stat(filename=OSPath + '/cwd').Data.Link AS WorkingDir,
           split(string=read_file(filename=OSPath + '/environ'), sep='''\x00''') AS Environ
       FROM parsed_proc
       WHERE LD_PRELOAD =~ ContentRegex