# How witr Detects LD_PRELOAD and DYLD_* Library Injection in Linux and macOS Processes

> witr detects and warns about LD_PRELOAD and DYLD_* library injection attacks on Linux and macOS by scanning process environment variables against defined rules.

- Repository: [Pranshu Parmar/witr](https://github.com/pranshuparmar/witr)
- Tags: internals
- Published: 2026-08-10

---

**`witr` scans process environment variables against hardcoded rules to flag `LD_PRELOAD` and any `DYLD_*` variables as potential library injection attacks.**

The `witr` CLI security tool monitors running processes for suspicious environment configurations that attackers commonly exploit to preload malicious shared libraries. According to the `pranshuparmar/witr` source code, the detection engine inspects every process in the ancestry chain using pattern-matching rules defined in the core detection module.

## Detection Rules for Environment Variable Injection

The detection logic centers on a slice of `envSuspiciousRule` structs declared in [`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go). Two specific rules target library injection vectors across Unix-like systems:

### LD_PRELOAD Detection on Linux

The first rule matches the exact environment variable name `LD_PRELOAD`:

```go
// internal/source/detect.go (lines 40-44)
{
    name:    "LD_PRELOAD",
    pattern: regexp.MustCompile(`^LD_PRELOAD$`),
    warning: "Process sets LD_PRELOAD (potential library injection)",
    // includeKeys: false (default)
}

```

When this rule fires, `witr` emits a fixed warning string without listing additional variable names.

### DYLD_* Detection on macOS

The second rule catches any environment variable starting with `DYLD_` using a prefix pattern:

```go
// internal/source/detect.go (lines 45-50)
{
    name:        "DYLD_*",
    pattern:     regexp.MustCompile(`^DYLD_.*`),
    warning:     "Process sets DYLD_* variables (potential library injection)",
    includeKeys: true,
}

```

The `includeKeys: true` flag causes the detector to collect and display which specific `DYLD_*` variables were found—critical for macOS where multiple dynamic linker variables exist (`DYLD_INSERT_LIBRARIES`, `DYLD_LIBRARY_PATH`, `DYLD_FRAMEWORK_PATH`, etc.).

## Scanning Implementation: envSuspiciousWarnings

The function `envSuspiciousWarnings(env []string) []string` executes the actual scanning logic. Located in [`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go) (lines 93-108), this function iterates over all rules and environment entries:

```go
func envSuspiciousWarnings(env []string) []string {
    var warnings []string
    
    for _, rule := range envSuspiciousRules {
        var matches []string
        
        for _, e := range env {
            // Parse KEY=VALUE format
            parts := strings.SplitN(e, "=", 2)
            key := parts[0]
            
            if rule.pattern.MatchString(key) {
                matches = append(matches, key)
            }
        }
        
        if len(matches) > 0 {
            warning := rule.warning
            if rule.includeKeys {
                warning += ": " + strings.Join(matches, ", ")
            }
            warnings = append(warnings, warning)
        }
    }
    
    return warnings
}

```

Key behaviors of this implementation:

- **Pattern matching** uses compiled regular expressions for flexibility
- **Key collection** only occurs when `includeKeys` is enabled (DYLD rule)
- **Duplicate elimination** happens naturally via iteration order
- **Empty value handling** processes variables regardless of assigned value

## Integration with Process Detection

The `Detect` function orchestrates full process analysis in [`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go). It invokes multiple detection subsystems including `detectShell`, `detectContainer`, and the environment scanner. When `envSuspiciousWarnings` returns non-empty results, those warnings populate the `model.Source` description structure that drives console output.

Typical runtime output appears as:

```text
[WARN] Process sets LD_PRELOAD (potential library injection)
[WARN] Process sets DYLD_* variables (potential library injection): DYLD_INSERT_LIBRARIES, DYLD_LIBRARY_PATH

```

## Practical Verification with Test Cases

The [`internal/source/detect_test.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect_test.go) file validates both rules against known-good and known-bad inputs. You can verify detection behavior programmatically:

```go
package main

import (
    "fmt"
    "github.com/pranshuparmar/witr/internal/source"
)

func main() {
    // Simulate attacker-controlled environment
    maliciousEnv := []string{
        "PATH=/usr/bin:/bin",
        "LD_PRELOAD=/tmp/libc_hook.so",
        "DYLD_INSERT_LIBRARIES=/tmp/dylib_runner.dylib",
        "DYLD_LIBRARY_PATH=/tmp/fake_libs",
        "HOME=/root",
    }
    
    warnings := source.EnvSuspiciousWarnings(maliciousEnv)
    
    for _, w := range warnings {
        fmt.Println("[WARN]", w)
    }
    // Output:
    // [WARN] Process sets LD_PRELOAD (potential library injection)
    // [WARN] Process sets DYLD_* variables (potential library injection): DYLD_INSERT_LIBRARIES, DYLD_LIBRARY_PATH
}

```

## Security Relevance: Why These Variables Matter

Library injection via environment variables remains a persistent attack vector:

- **`LD_PRELOAD`** forces the Linux dynamic linker to load specified shared libraries before all others, enabling function hooking and privilege escalation
- **`DYLD_INSERT_LIBRARIES`** provides equivalent functionality on macOS, loading additional libraries into the target process
- **`DYLD_LIBRARY_PATH`** redirects library resolution to attacker-controlled directories

`witr` flags these conditions regardless of whether the values appear malicious, adopting a detect-then-investigate posture suitable for security monitoring pipelines.

## Summary

- **[`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go)** defines injection detection rules in the `envSuspiciousRules` slice
- **`LD_PRELOAD`** triggers exact-match detection with a fixed warning message
- **`DYLD_*`** triggers prefix-match detection with dynamic key enumeration via `includeKeys`
- **`envSuspiciousWarnings()`** implements the core scanning algorithm iterating over rules and environment variables
- **Detection coverage** includes Linux and macOS library injection vectors through unified Go implementation
- **Integration point** is the `Detect` function which surfaces warnings through `model.Source` descriptions

## Frequently Asked Questions

### How does witr distinguish between legitimate and malicious LD_PRELOAD usage?

`witr` does not attempt semantic analysis of library paths—it flags all `LD_PRELOAD` presence as suspicious. This design reflects the tool's purpose as a security auditor rather than a policy enforcement engine. Operators must investigate flagged processes manually to determine if preloaded libraries are authorized system components or attack artifacts.

### Does witr detection work inside containerized environments?

Yes. The `detectContainer` function in [`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go) runs alongside environment scanning. `witr` reads procfs for container metadata and applies the same `envSuspiciousWarnings` logic to processes regardless of namespace isolation. Container detection and library injection detection operate as independent checks that both contribute to process risk scoring.

### What macOS versions does the DYLD_* detection cover?

The prefix pattern `^DYLD_.*` matches all DYLD-prefixed variables without version-specific logic. This approach remains compatible across macOS releases because the dynamic linker environment variable namespace has remained stable. Apple's System Integrity Protection (SIP) restricts DYLD manipulation for system processes, but `witr` still detects attempts in user-space processes and post-exploitation scenarios where SIP is disabled.

### Can the detection rules be customized or extended?

The current implementation in `pranshuparmar/witr` uses a hardcoded `envSuspiciousRules` slice. Adding custom patterns requires modifying [`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go) and recompiling. The struct-based definition (`envSuspiciousRule` with `name`, `pattern`, `warning`, and `includeKeys` fields) provides a clear extension point for contributors seeking to add detection for additional environment-based attack vectors.