Repository navigation
Add Linux Auditd PAM Configuration File Modification detection - #4381
vahidmalekk wants to merge 2 commits into
Conversation
Detects PAM auth-stack tampering (T1556.003) under /etc/pam.d/ and /etc/security/ via Linux Auditd, correlating the PATH record for the PAM file with the SYSCALL record to surface the editing process. Covers both passwordless (pam_permit.so) and magic-password PAM backdoors.
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c8dbc431df
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ( | ||
| (type=PATH name IN ("/etc/pam.d/*", "/etc/security/*") nametype IN ("CREATE","NORMAL","DELETE")) | ||
| OR | ||
| (type=SYSCALL syscall IN ("rename","renameat","renameat2","open","openat","openat2","write","truncate","ftruncate","unlinkat","unlink","linkat","symlinkat")) |
There was a problem hiding this comment.
Match syscall names against the interpreted field
With the enriched event shape documented for the declared Linux Auditd Syscall source, lowercase syscall is numeric (syscall=59) while the interpreted name is uppercase SYSCALL=execve (data_sources/linux_auditd_syscall.yml:70). Comparing the lowercase field to names such as openat therefore selects no SYSCALL companion record in that supported configuration; process_name remains null and the line-23 guard drops every matching PATH event. Match SYSCALL, mapped syscall numbers, or an explicitly normalized field instead.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 189e045. You're right that matching the lowercase interpreted syscall name against the enriched data source is fragile. Rather than special-casing the field, I reworked the analytic to follow the house pattern used in linux_auditd_possible_access_or_modification_of_sshd_config_file: it no longer filters SYSCALL by name at all. It correlates type=PATH + type=CWD + type=SYSCALL by audit event id, so the PATH record is what identifies the PAM file and the SYSCALL record is only joined to recover the process and success. That removes the field-case dependency entirely.
| search: |- | ||
| `linux_auditd` | ||
| ( | ||
| (type=PATH name IN ("/etc/pam.d/*", "/etc/security/*") nametype IN ("CREATE","NORMAL","DELETE")) |
There was a problem hiding this comment.
Reconstruct relative audit paths before filtering
When a process first changes into a PAM directory and opens a relative filename (for example, cd /etc/pam.d && sed -i ... common-auth), auditd records the filename in PATH name and the directory separately in its CWD record. The absolute-prefix predicate here rejects that PATH before correlation, so this common edit never reaches the finding; existing path analytics such as detections/endpoint/linux_auditd_possible_access_or_modification_of_sshd_config_file.yml:21-37 retain CWD and reconstruct relative names. Collect and reconstruct CWD before applying the PAM path filter.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 189e045 using the exact CWD-reconstruction pattern from linux_auditd_possible_access_or_modification_of_sshd_config_file: the search now includes type=CWD in the correlation and reconstructs the absolute path with mvmap(names, if(match(names,"^/"), names, current_working_directory."/".names)) before applying the /etc/pam.d and /etc/security filter. So the cd /etc/pam.d && sed -i ... common-auth case is now caught. data_source and how_to_implement updated to require the type=CWD record.
| | rex "msg=audit\([^)]*:(?<audit_id>\d+)\)" | ||
| | stats values(name) as names values(nametype) as nametype values(comm) as process_name values(exe) as process values(syscall) as syscall values(auid) as auid values(uid) as uid min(_time) as firstTime max(_time) as lastTime BY audit_id host | ||
| | eval pam_file=mvfilter(match(names, "^/etc/pam\.d/.+$") OR match(names, "^/etc/security/.+$")) | ||
| | where mvcount(pam_file) > 0 AND isnotnull(process_name) |
There was a problem hiding this comment.
Require a successful syscall before reporting modification
When the suggested -p wa watch captures a write-like syscall that fails (for example, an unprivileged openat denied with success=no), auditd still supplies PATH and SYSCALL records. Because this correlation neither preserves nor checks success, this final guard emits a risk event saying the PAM file 'was modified' even though no filesystem change occurred, creating false positives from denied attempts. Require a successful companion syscall or report failed attempts separately.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 189e045. The correlation now collects success from the companion SYSCALL record and the guard is where match_count>0 AND success="yes" AND isnotnull(process_name), so a denied/failed write (e.g. an unprivileged openat returning success=no) no longer produces a "was modified" risk event. Validated against live auditd data - the sed/cp/tee write events (success=yes) surface while nothing else does.
… syscall-name filter (address review)
Summary
Adds a new anomaly analytic, Linux Auditd PAM Configuration File Modification, detecting tampering with the PAM authentication stack (T1556.003) under
/etc/pam.d/and/etc/security/via Linux Auditd.Coverage gap
There is currently no ESCU detection for PAM modification / PAM backdoors (T1556.003). The only rule referencing a PAM path,
linux_suspicious_staging_of_alternate_system_files, is for CVE-2025-32463 (fake-rootdir staging, T1036) and explicitly excludes/etc/*, so it never fires on edits to the real PAM files. This analytic closes that gap.How it works
PAM backdoors work by writing a PAM config file - e.g. adding
auth sufficient pam_permit.so(passwordless for any account) or a pam_exec/custom module granting access on a hardcoded password for any existing user. The search correlates the auditdPATHrecord for a/etc/pam.dor/etc/securityfile (nametypeCREATE for an editor rewrite, NORMAL for an in-place write) with theSYSCALLrecord from the same audit event id, surfacing the editing process (comm/exe) and user - context the existingLinux Auditd Pathrules (e.g. doas.conf creation) don't provide.Data source
Linux Auditd Path+Linux Auditd Syscall.Testing
True-positive dataset captured from a reversible PAM-backdoor sim on an authorized lab host, covering both install methods (
sedrewrite → CREATE,tee -aappend → NORMAL). Paired attack data: splunk/attack_data#1259;tests.attack_datapoints at it, so the unit test passes once #1259 merges.MITRE ATT&CK
T1556.003, T1556
Modeled as Anomaly with a filter macro because package upgrades / pam-auth-update legitimately touch these files (documented in known_false_positives). Happy to adjust the syscall list, paths, or FP filtering to STRT conventions.