108 lines
2.1 KiB
Go
108 lines
2.1 KiB
Go
/*
|
|
©AngelaMos | 2026
|
|
pam.go
|
|
|
|
Scans PAM configuration for authentication backdoor indicators
|
|
|
|
Checks /etc/pam.d/ for pam_exec.so entries that run custom scripts
|
|
and for references to non-standard PAM modules that may accept any
|
|
credential.
|
|
|
|
MITRE ATT&CK:
|
|
T1556.003 - Modify Authentication Process: Pluggable Authentication Modules
|
|
*/
|
|
|
|
package scanner
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/CarterPerez-dev/sentinel/pkg/types"
|
|
)
|
|
|
|
const (
|
|
pamScannerName = "pam"
|
|
mitrePAM = "T1556.003"
|
|
)
|
|
|
|
func init() {
|
|
Register(&PAMScanner{})
|
|
}
|
|
|
|
type PAMScanner struct{}
|
|
|
|
func (p *PAMScanner) Name() string {
|
|
return pamScannerName
|
|
}
|
|
|
|
func (p *PAMScanner) Scan(
|
|
root string,
|
|
) []types.Finding {
|
|
var findings []types.Finding
|
|
|
|
pamD := ResolveRoot(root, "/etc/pam.d")
|
|
for _, path := range ListFiles(pamD) {
|
|
findings = append(
|
|
findings,
|
|
p.scanPamConfig(path)...,
|
|
)
|
|
}
|
|
|
|
return findings
|
|
}
|
|
|
|
func (p *PAMScanner) scanPamConfig(
|
|
path string,
|
|
) []types.Finding {
|
|
lines := ReadLines(path)
|
|
if lines == nil {
|
|
return nil
|
|
}
|
|
|
|
var findings []types.Finding
|
|
for _, line := range lines {
|
|
trimmed := strings.TrimSpace(line)
|
|
if IsCommentOrEmpty(trimmed) {
|
|
continue
|
|
}
|
|
|
|
if strings.Contains(trimmed, "pam_exec.so") {
|
|
sev := types.SeverityMedium
|
|
if NetworkToolPattern.MatchString(trimmed) ||
|
|
TempDirPattern.MatchString(trimmed) {
|
|
sev = types.SeverityHigh
|
|
}
|
|
|
|
findings = append(findings, types.Finding{
|
|
Scanner: pamScannerName,
|
|
Severity: sev,
|
|
Title: "pam_exec.so runs external command",
|
|
Path: path,
|
|
Evidence: trimmed,
|
|
MITRE: mitrePAM,
|
|
})
|
|
}
|
|
|
|
if strings.Contains(trimmed, "pam_permit.so") &&
|
|
containsAuthContext(trimmed) {
|
|
findings = append(findings, types.Finding{
|
|
Scanner: pamScannerName,
|
|
Severity: types.SeverityHigh,
|
|
Title: "pam_permit.so in auth context (accepts any credential)",
|
|
Path: path,
|
|
Evidence: trimmed,
|
|
MITRE: mitrePAM,
|
|
})
|
|
}
|
|
}
|
|
return findings
|
|
}
|
|
|
|
func containsAuthContext(line string) bool {
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 1 {
|
|
return false
|
|
}
|
|
return fields[0] == "auth"
|
|
}
|