Detecting Developer Credential Harvesting: Monitoring .npmrc, .pypirc, and Cloud Config Files

Detecting Developer Credential Harvesting: Monitoring .npmrc, .pypirc, and Cloud Config Files

Problem

Developer workstations and CI runners accumulate credentials in predictable locations. Package managers, cloud CLIs, and version control systems all persist authentication material to well-known paths under the user’s home directory. The tooling expects these files to exist; it reads them automatically on every invocation. That predictability — fixed paths, fixed formats — is exactly what post-exploitation toolkits exploit.

Two active threats as of May 2026 demonstrate the pattern:

PamDOORa (sold on the Rehub forum since late April 2026) installs a malicious PAM module that executes on every authentication event — every ssh login, every sudo prompt, every su invocation. On each auth event, it reads the authenticating user’s credential files and exfiltrates them to attacker-controlled infrastructure. Because PAM authentication is a high-frequency event on developer workstations and CI runners, the exfiltration window is measured in seconds from initial compromise. The PAM module runs as root, so file permissions provide no barrier.

Quasar Linux RAT takes a different approach. It targets developers specifically by reading all known credential file paths in the first five seconds of execution — before any C2 beaconing or persistence installation that might trigger other detection mechanisms. It is distributed as a compromised build dependency: a typosquatted or hijacked npm or pip package whose postinstall or install hook executes the RAT. The package executes during the developer’s normal workflow, in the context of the developer’s user account, with full access to all credential files owned by that user.

A third category — malicious postinstall scripts embedded directly in npm or pip packages — represents the same credential read pattern without a persistent RAT component. The script reads .npmrc, .pypirc, and cloud credential files and exfiltrates them via an outbound HTTP request before the developer notices the install output.

What all three share: they access credential files using the standard openat() syscall family, from processes that have no legitimate reason to read those files. The kernel observes every openat() call. eBPF can match those calls against known credential file paths in microseconds, before any data leaves the host. Detection is possible at the kernel level with either Tetragon or Falco, and on systems without eBPF support, inotifywait provides a userspace fallback.

Target systems: Linux developer workstations, CI runners (GitHub Actions self-hosted, GitLab Runner, Jenkins agents), any Linux host with developer tooling installed. Kernel 5.8+ for eBPF-based detection; any Linux kernel for inotifywait.

Threat Model

Three concrete attacker scenarios drive the detection design.

1. PamDOORa-style PAM backdoor (root-level, triggered on every auth event). The attacker has already achieved code execution with sufficient privilege to install a PAM module — typically through a supply chain compromise of a system package, or through an initial access vector that yielded a root shell. The PAM module registers itself in /etc/pam.d/sshd and /etc/pam.d/sudo. On every subsequent authentication event, the module opens the authenticating user’s home directory credential files and writes their contents to an exfiltration endpoint. Because the module runs in the PAM stack at root privilege, it reads files regardless of their permissions. Detection window: the openat() calls from the PAM module process (sshd or sudo) against user home directory credential paths are anomalous — those processes have no legitimate reason to read .npmrc or ~/.aws/credentials.

2. Quasar Linux RAT executing as a compromised build dependency. The attacker has published or hijacked an npm or pip package that developers install during normal dependency management. The package’s install hook spawns a subprocess that reads all credential files from the installing user’s home directory within the first few seconds of execution. It then exfiltrates them over HTTPS to an attacker endpoint before the rest of the installation completes. The RAT executable may persist to disk or operate entirely from the subprocess — either way, the credential reads happen early. Detection window: a subprocess spawned by npm or pip (or node, python) that reads files in ~/.aws/, ~/.kube/, ~/.config/gcloud/ has no legitimate reason to do so — those are cloud credential stores, not package management artifacts.

3. Malicious postinstall script reading .npmrc and sending token to C2. This is the simplest vector: a package’s scripts.postinstall field in package.json contains a one-liner that reads .npmrc and sends its contents to a webhook. The script executes as a child of npm, in the developer’s user context, during npm install. Detection window: a process reading .npmrc whose parent is npm or node and that makes an outbound network connection immediately after the read is anomalous; npm reads .npmrc at startup, not via a subprocess, and it does not subsequently open outbound connections to arbitrary endpoints.

The common thread across all three scenarios: credential files are read by the wrong process. The kernel can observe which process calls openat() on which path. That is the detection primitive this article builds on.

Configuration / Implementation

Credential File Target Paths

The complete list of files to monitor. These paths use ~ to represent the user’s home directory; in practice, monitoring tools need to expand this to the actual path or use glob patterns against /home/* and /root.

~/.npmrc                                           # npm automation token
~/.pypirc                                          # PyPI username + password
~/.git-credentials                                 # Git HTTPS credential store (plaintext)
~/.netrc                                           # Generic credential store (FTP, curl, etc.)
~/.aws/credentials                                 # AWS access key ID + secret
~/.aws/config                                      # AWS profile configuration (may contain credentials)
~/.config/gcloud/credentials.db                    # GCP OAuth2 tokens
~/.config/gcloud/application_default_credentials.json  # GCP ADC credentials
~/.kube/config                                     # Kubernetes cluster credentials + tokens
~/.docker/config.json                              # Docker registry tokens (base64-encoded)
~/.ssh/id_rsa                                      # RSA private key
~/.ssh/id_ed25519                                  # Ed25519 private key
~/.ssh/id_ecdsa                                    # ECDSA private key
~/.ssh/id_dsa                                      # DSA private key (legacy)

SSH private keys are included because they are functionally equivalent to credentials: an attacker with a copy of id_ed25519 can authenticate to any system that trusts the corresponding public key. The absence of a passphrase on developer workstation keys (common for convenience) makes them immediately usable.

Tetragon TracingPolicy: Kernel-Level File Access Monitoring

Tetragon attaches eBPF programs via kprobes. The sys_openat hook fires for every file open operation, before the kernel returns the file descriptor to userspace. The TracingPolicy below matches opens of credential file paths from processes that are not the expected legitimate owner application.

# tetragon-credential-file-monitoring.yaml
# Detects anomalous reads of developer credential files.
# Deploy as a TracingPolicy on developer workstations or CI nodes.
# Requires Tetragon 1.2+ and kernel 5.8+.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: developer-credential-harvesting
  namespace: kube-system
spec:
  kprobes:
    - call: "sys_openat"
      syscall: true
      args:
        - index: 0
          type: "int"          # dirfd
        - index: 1
          type: "string"       # pathname
        - index: 2
          type: "int"          # flags
      selectors:
        # Match opens of credential file paths.
        # The suffix match covers /home/alice/.npmrc, /home/bob/.aws/credentials, etc.
        - matchArgs:
            - index: 1
              operator: "Postfix"
              values:
                - "/.npmrc"
                - "/.pypirc"
                - "/.git-credentials"
                - "/.netrc"
                - "/.aws/credentials"
                - "/.aws/config"
                - "/.kube/config"
                - "/.docker/config.json"
                - "/gcloud/credentials.db"
                - "/gcloud/application_default_credentials.json"
                - "/.ssh/id_rsa"
                - "/.ssh/id_ed25519"
                - "/.ssh/id_ecdsa"
                - "/.ssh/id_dsa"
          # Exclude legitimate processes that have good reason to read these files.
          # Process name matching reduces false positives from developer tools.
          matchBinaries:
            - operator: "NotIn"
              values:
                - "/usr/bin/npm"
                - "/usr/local/bin/npm"
                - "/usr/bin/git"
                - "/usr/local/bin/git"
                - "/usr/bin/pip"
                - "/usr/bin/pip3"
                - "/usr/local/bin/pip"
                - "/usr/local/bin/pip3"
                - "/usr/bin/python3"
                - "/usr/bin/aws"
                - "/usr/local/bin/aws"
                - "/usr/lib/google-cloud-sdk/bin/gcloud"
                - "/usr/bin/kubectl"
                - "/usr/local/bin/kubectl"
                - "/usr/bin/docker"
                - "/usr/bin/ssh"
                - "/usr/bin/ssh-agent"
                - "/usr/bin/scp"
          matchActions:
            - action: Post
              rateLimit: "10/minute"
              ratelimitScope: "process"
            # To enforce (kill the reading process) rather than only alert,
            # uncomment the following line:
            # - action: Sigkill

A few design choices in this policy deserve explanation:

The Postfix operator matches the trailing portion of the pathname string. This handles paths under /home/alice/, /home/bob/, /root/, and any other home directory location without enumerating individual usernames. It does not require Tetragon to know which users exist on the system.

The matchBinaries exclusion list uses NotIn with full binary paths. This means a process at /tmp/npm (a common RAT disguise) is not excluded — only the known-good paths for legitimate tools are excluded. The exclusion list is conservative by design: a false negative (missing a legitimate read) is preferable to a false positive (alerting on git reading .git-credentials) during initial deployment, but the list must be reviewed for each environment.

The Sigkill action is commented out. On a workstation, killing a process that reads .aws/credentials will break aws-cli invocations. Enable enforcement only after the exclusion list is validated against the local developer workflow. On CI runners with a fixed tool set, the exclusion list is smaller and enforcement is more tractable.

Falco Rule: Syscall-Level Detection

Falco 0.38+ with the modern rule syntax provides an alternative detection layer. Falco operates on the syscall event stream from its eBPF driver and does not require Tetragon to be deployed. The rule below covers both read and write events — detecting writes catches cases where a process is creating a credential file that another process will then harvest.

# falco-developer-credential-rules.yaml
# Falco rules for developer credential file access monitoring.
# Compatible with Falco 0.38+ modern rule syntax.

# Macro: credential file paths matched against fd.name.
# fd.name is the resolved absolute path of the opened file descriptor.
- macro: developer_credential_file
  condition: >
    fd.name pmatch (
      "*/.npmrc",
      "*/.pypirc",
      "*/.git-credentials",
      "*/.netrc",
      "*/.aws/credentials",
      "*/.aws/config",
      "*/.kube/config",
      "*/.docker/config.json",
      "*/.config/gcloud/credentials.db",
      "*/.config/gcloud/application_default_credentials.json",
      "*/.ssh/id_rsa",
      "*/.ssh/id_ed25519",
      "*/.ssh/id_ecdsa",
      "*/.ssh/id_dsa"
    )

# Macro: processes with legitimate reasons to read credential files.
# proc.exepath is the resolved path of the executable, bypassing argv[0] spoofing.
- macro: legitimate_credential_reader
  condition: >
    proc.exepath in (
      "/usr/bin/npm",
      "/usr/local/bin/npm",
      "/usr/bin/git",
      "/usr/local/bin/git",
      "/usr/bin/pip",
      "/usr/bin/pip3",
      "/usr/local/bin/pip",
      "/usr/local/bin/pip3",
      "/usr/bin/python3",
      "/usr/bin/aws",
      "/usr/local/bin/aws",
      "/usr/lib/google-cloud-sdk/bin/gcloud",
      "/usr/bin/kubectl",
      "/usr/local/bin/kubectl",
      "/usr/bin/docker",
      "/usr/bin/ssh",
      "/usr/bin/scp",
      "/usr/bin/bash",
      "/usr/bin/sh"
    )
    or proc.pexepath in (
      "/usr/bin/npm",
      "/usr/local/bin/npm",
      "/usr/bin/git",
      "/usr/local/bin/git"
    )

# Rule 1: Anomalous read of a developer credential file.
# Triggers when a process not in the legitimate_credential_reader macro opens a
# credential file for reading.
- rule: Anomalous Developer Credential File Read
  desc: >
    A process that is not a known developer tool has opened a developer credential
    file for reading. This matches PamDOORa (PAM module reading user credential files
    on auth events), Quasar Linux RAT (reading all credential files on first execution),
    and malicious postinstall scripts.
  condition: >
    open_read
    and developer_credential_file
    and not legitimate_credential_reader
  output: >
    Developer credential file read by unexpected process
    (user=%user.name uid=%user.uid
     proc=%proc.name exepath=%proc.exepath pid=%proc.pid
     parent=%proc.pname pexepath=%proc.pexepath ppid=%proc.ppid
     file=%fd.name
     cmdline=%proc.cmdline
     container=%container.name image=%container.image.repository)
  priority: CRITICAL
  tags: [credential-harvesting, post-exploitation, T1552.001]

# Rule 2: Credential file written by an unexpected process.
# Detects when a malicious process creates or overwrites a credential file,
# potentially to replace it with a backdoored version or to capture it after
# the legitimate tool writes it.
- rule: Anomalous Developer Credential File Write
  desc: >
    A process not in the known-good list has opened a developer credential file
    for writing. Legitimate tools (npm, pip, aws configure) write these files,
    but writes from unexpected processes may indicate credential manipulation.
  condition: >
    open_write
    and developer_credential_file
    and not legitimate_credential_reader
  output: >
    Developer credential file written by unexpected process
    (user=%user.name uid=%user.uid
     proc=%proc.name exepath=%proc.exepath pid=%proc.pid
     parent=%proc.pname ppid=%proc.ppid
     file=%fd.name
     cmdline=%proc.cmdline)
  priority: WARNING
  tags: [credential-harvesting, T1552.001]

# Rule 3: PAM-context process reading credential files.
# sshd and sudo have no legitimate reason to open developer credential files.
# This specifically targets PamDOORa-style PAM backdoors.
- rule: PAM Process Reading Developer Credential Files
  desc: >
    sshd, sudo, or su has opened a developer credential file. This is the
    behavioural signature of a PamDOORa-style PAM backdoor that reads credential
    files during authentication events.
  condition: >
    open_read
    and developer_credential_file
    and proc.name in ("sshd", "sudo", "su", "login", "gdm-session-wor")
  output: >
    PAM-context process reading developer credential file — possible PAM backdoor
    (proc=%proc.name pid=%proc.pid uid=%user.uid
     file=%fd.name
     cmdline=%proc.cmdline)
  priority: CRITICAL
  tags: [credential-harvesting, pam-backdoor, pamdoora, T1556.003]

inotifywait: Lightweight Workstation Alternative

On developer workstations where eBPF is not available (older kernels, restricted environments, macOS via Linux VM without eBPF support), inotifywait from the inotify-tools package provides userspace file monitoring. It is easier to bypass than eBPF monitoring — a process can copy a file without triggering an open event on the copy target — but it catches the common case.

#!/usr/bin/env bash
# credential-watch.sh
# Monitor developer credential files with inotifywait.
# Run as a systemd user service or in a tmux session.
# Requires: inotify-tools package

set -euo pipefail

CRED_DIRS=(
  "$HOME"
  "$HOME/.aws"
  "$HOME/.kube"
  "$HOME/.config/gcloud"
  "$HOME/.ssh"
  "$HOME/.docker"
)

# Build the list of existing directories to watch.
WATCH_DIRS=()
for dir in "${CRED_DIRS[@]}"; do
  [[ -d "$dir" ]] && WATCH_DIRS+=("$dir")
done

CRED_FILES_PATTERN='\.npmrc$|\.pypirc$|\.git-credentials$|\.netrc$|credentials$|credentials\.db$|application_default_credentials\.json$|config\.json$|id_rsa$|id_ed25519$|id_ecdsa$|id_dsa$'

# Watch for access (open for read) and close_write events.
# -r: recursive; -m: monitor continuously; -e: events to watch.
inotifywait -r -m -e access -e close_write \
  --format '%T %w%f %e %p' \
  --timefmt '%Y-%m-%dT%H:%M:%S' \
  "${WATCH_DIRS[@]}" 2>/dev/null \
  | grep -E "$CRED_FILES_PATTERN" \
  | while read -r timestamp filepath event pid; do
      # Resolve the reading process from the PID if available.
      proc_name="unknown"
      if [[ -n "$pid" && -d "/proc/$pid" ]]; then
        proc_name=$(cat "/proc/$pid/comm" 2>/dev/null || echo "unknown")
        proc_exe=$(readlink "/proc/$pid/exe" 2>/dev/null || echo "unknown")
      fi
      echo "[CREDENTIAL-ACCESS] timestamp=$timestamp file=$filepath event=$event pid=$pid proc=$proc_name exe=$proc_exe" \
        | tee -a /var/log/credential-access.log \
        | logger -t credential-watch -p security.warning
    done

Note that inotifywait receives the PID only when using --format '%p', which requires inotify-tools 3.20.11+. On older versions, omit %p and lose process attribution. Redirect output to syslog via logger so alerts reach your log aggregation pipeline.

CI Runner Hardening: Avoiding Credential Files on Disk

The most effective mitigation for CI environments is eliminating credential files entirely. Most cloud providers and package registries support authentication methods that do not write files to disk.

AWS: Use OIDC federation instead of static credentials. GitHub Actions and GitLab CI both support OIDC tokens that the runner exchanges for temporary AWS credentials via sts:AssumeRoleWithWebIdentity. No ~/.aws/credentials file is written.

# .github/workflows/deploy.yml (excerpt)
# OIDC-based AWS authentication — no credential files written to disk.
- name: Configure AWS credentials via OIDC
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
    aws-region: us-east-1
    # Credentials are set as environment variables, not written to ~/.aws/credentials.

npm: Pass the npm token as an environment variable rather than writing .npmrc.

# In CI, set NPM_TOKEN as a masked secret environment variable.
# Then configure npm to read it at install time without writing ~/.npmrc.
npm config set //registry.npmjs.org/:_authToken "${NPM_TOKEN}"
# Or use .npmrc in the project root (not home directory) with variable interpolation:
# //registry.npmjs.org/:_authToken=${NPM_TOKEN}

Detection rule for CI credential file creation: If a CI job writes a credential file to the home directory and another process reads it within the same job, that sequence is worth alerting on — it may indicate a compromised build step that planted credentials for a harvesting step to pick up.

# Falco rule: detect credential file write followed by unexpected read in CI context.
- rule: CI Credential File Written Then Read by Different Process
  desc: >
    A credential file was written by one process and subsequently read by a process
    that is not the same binary. In a CI runner, this pattern may indicate a
    compromised build step writing credentials for a harvesting subprocess.
  condition: >
    open_read
    and developer_credential_file
    and container.id != ""
    and not legitimate_credential_reader
  output: >
    Credential file read by non-owner process in container
    (container=%container.name image=%container.image.repository
     proc=%proc.name exepath=%proc.exepath
     file=%fd.name cmdline=%proc.cmdline)
  priority: HIGH
  tags: [credential-harvesting, ci-security, T1552.001]

Credential File Permissions Hardening

Correct permissions do not stop root-level attackers (PamDOORa runs as root), but they block lateral movement by other user accounts and provide a clear audit baseline.

# Audit and harden credential file permissions.
# Run as the user whose files to audit (not as root).

# Find credential files with permissions more permissive than 600.
find "$HOME" \
  \( -name ".npmrc" \
     -o -name ".pypirc" \
     -o -name ".git-credentials" \
     -o -name ".netrc" \
  \) \
  -not -perm 600 \
  -exec ls -la {} \;

find "$HOME/.aws" -type f -not -perm 600 -exec ls -la {} \; 2>/dev/null
find "$HOME/.kube" -type f -name "config" -not -perm 600 -exec ls -la {} \; 2>/dev/null
find "$HOME/.ssh" -type f -name "id_*" -not -perm 600 -exec ls -la {} \; 2>/dev/null

# Apply correct permissions.
chmod 600 \
  "$HOME/.npmrc" \
  "$HOME/.pypirc" \
  "$HOME/.git-credentials" \
  "$HOME/.netrc" \
  2>/dev/null

chmod 700 "$HOME/.ssh" 2>/dev/null
chmod 600 "$HOME/.ssh/id_"* 2>/dev/null
chmod 700 "$HOME/.aws" "$HOME/.kube" 2>/dev/null
chmod 600 "$HOME/.aws/credentials" "$HOME/.aws/config" 2>/dev/null
chmod 600 "$HOME/.kube/config" 2>/dev/null

Replacing File-Based Credentials

The durable fix is eliminating credential files where possible.

Git HTTPS tokens: Replace ~/.git-credentials with a credential helper that retrieves tokens from the system keychain (macOS Keychain, GNOME Keyring, or a GPG-encrypted store) rather than storing them in plaintext.

# Linux with GNOME Keyring: use the libsecret credential helper.
# Install: apt install git-credential-libsecret (Debian/Ubuntu)
#          dnf install git-credential-libsecret (Fedora)
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret

# Remove the plaintext store after configuring the helper.
rm -f "$HOME/.git-credentials"

AWS on servers/CI: Use OIDC workload identity (described above) or EC2 instance profiles / EKS IRSA. No static credential file required.

Kubernetes: Use short-lived tokens via exec credential plugins (e.g., aws eks get-token) rather than long-lived tokens stored in ~/.kube/config.

Expected Behaviour

The following table describes the expected detection outcome for each access scenario across both Tetragon and Falco.

Access Scenario Process File Accessed Tetragon Decision Falco Decision Alert Generated
npm reads .npmrc at startup /usr/bin/npm ~/.npmrc Allow (excluded binary) No rule match (excluded) No
git reads .git-credentials for HTTPS push /usr/bin/git ~/.git-credentials Allow (excluded binary) No rule match (excluded) No
aws-cli reads ~/.aws/credentials /usr/bin/aws ~/.aws/credentials Allow (excluded binary) No rule match (excluded) No
kubectl reads ~/.kube/config /usr/bin/kubectl ~/.kube/config Allow (excluded binary) No rule match (excluded) No
Unknown process reads ~/.aws/credentials /tmp/worker ~/.aws/credentials Alert + optional Sigkill CRITICAL alert Yes — both systems
PamDOORa (via sshd) reads all credential files sshd (with PAM hook) ~/.npmrc, ~/.aws/credentials, etc. Alert (sshd not excluded) CRITICAL alert (PAM rule) Yes — both systems
Quasar RAT reads all credential files on first run /tmp/.q or disguised binary All credential paths Alert (not excluded) CRITICAL alert Yes — both systems
Malicious postinstall script reads .npmrc subprocess of npm ~/.npmrc Alert (subprocess binary path not excluded) CRITICAL alert Yes — Falco (parent process check); Tetragon depends on binary path
python3 script (IDE tooling) reads .pypirc /usr/bin/python3 ~/.pypirc Allow (excluded binary) Allow (excluded binary) No — false negative risk

The last row highlights a gap: /usr/bin/python3 is in the exclusion list because some legitimate tools (pip, poetry, twine) invoke it to read .pypirc. A Quasar RAT that disguises itself as a Python script and is invoked via the system Python binary will not trigger an alert. Mitigating this requires adding parent process checks: python3 reading .pypirc is expected only when its parent is a known package management tool. In Falco, add proc.pname in ("pip", "pip3", "twine", "poetry") to the exclusion condition for .pypirc reads by python3.

Trade-offs

Approach Bypass Difficulty Portability False Positive Rate Maintenance Burden Notes
Tetragon TracingPolicy (kprobe on sys_openat) High — attacker must avoid openat() entirely or operate before Tetragon loads Requires eBPF + kernel 5.8+ Low with tuned exclusion list Medium — exclusion list must track tool paths across distros Supports enforcement (Sigkill) in addition to alerting
Falco rule (open_read / open_write) High — same syscall interception as Tetragon Requires Falco eBPF driver or kernel module Low-Medium — proc.exepath is more reliable than proc.name for exclusions Medium — same exclusion list problem Detection-only; no enforcement capability
inotifywait (userspace) Low — sendfile(), mmap(), or copy_file_range() bypass open events Any Linux kernel with inotify (2.6.13+) Medium — no process context; generates alerts for all accesses Low — shell script, no policy files No process attribution without PID parsing; cannot distinguish legitimate from malicious access at inotifywait level
Process exclusion list (binary path matching) Medium — attacker renames binary to match exclusion path N/A (component of Tetragon/Falco approach) Lower with full-path matching (proc.exepath) vs. name matching High — list must be updated when tools upgrade or relocate Use proc.exepath (resolved symlink) not proc.name (argv[0]) to prevent spoofing
Credential file elimination (OIDC, keychain helpers) N/A — removes the target Requires cloud provider support or keychain infrastructure N/A — prevention, not detection Low once configured Most effective long-term mitigation; reduces attack surface rather than detecting exploitation

Failure Modes

Failure Mode Impact Detection Gap Mitigation
Tetragon not running on workstation No kernel-level detection All credential file reads go undetected Use Falco as a fallback; deploy inotifywait as a systemd user service; enforce Tetragon installation via configuration management
Exclusion list too broad (e.g., excluding all python3) Malicious process disguised as or invoked via Python evades detection Any credential read from /usr/bin/python3 missed Narrow exclusion to specific parent-child process combinations; use proc.pexepath in Falco conditions
Credential file moved to non-standard path (e.g., $PYPIRC=/opt/creds/.pypirc) Rule does not match the non-standard path Credential file read at non-standard path missed Monitor for environment variable setting that redirects credential file paths; alert on PYPIRC, NPMRC, KUBECONFIG, AWS_SHARED_CREDENTIALS_FILE env vars set to non-standard values
eBPF disabled on hardened kernel (e.g., kernel.unprivileged_bpf_disabled=1 + no Tetragon DaemonSet with root) No kprobe attachment Tetragon cannot load TracingPolicy Use Falco with kernel module driver instead of eBPF driver; fall back to inotifywait
Attacker reads file via sendfile() or mmap() instead of openat() Falco open_read rule does not trigger Read via alternative syscall missed Add Tetragon kprobes on __x64_sys_mmap and sys_sendfile for credential file paths; inotifywait -e access catches mmap reads
PAM backdoor reads files from memory-mapped pages (no syscall) No syscall-level detection Kernel-resident attacker may bypass all detection Verify PAM module integrity via /articles/cross-cutting/pam-module-integrity-verification/; use aide or tripwire to detect PAM module changes
Rate limiting on Tetragon Post action suppresses alerts during burst harvest Alerts generated at rate limit but not for all files Some individual file reads not alerted during burst Set ratelimitScope: process not global; increase rate limit for credential file rules specifically
CI runner re-uses home directory across jobs (persistent runner) Credential files from one job available to subsequent jobs Cross-job credential access not detected as anomalous Enforce ephemeral home directories per job; detect writes to home directory at end of job and alert if credential files present