forensicnomicon 0.3.1

The ForensicNomicon — comprehensive DFIR artifact catalog: UserAssist, Shimcache, Amcache, Prefetch, $MFT, ShellBags, EVTX, NTDS.dit, SAM, SRUM, LNK, Jump Lists + KAPE/Velociraptor/Sigma/MITRE. Zero deps.
Documentation
use crate::threat_intel::{
    profile::{MalwareClass, MalwareProfile, ProfileSignal, WeightedExclusion},
    signals::*,
};

pub static MEDUSA: MalwareProfile = MalwareProfile {
    id: "medusa",
    family: "Medusa",
    aliases: &["medusa-linux", "libmed"],
    description: "LD_PRELOAD rootkit emphasising network hiding and PAM credential harvesting. \
                  Hooks recvmsg/recvfrom to hide connections, hooks pam_authenticate for credential \
                  theft. Network-heavy hook set distinguishes from Father/Azazel. No LKM component.",
    malware_class: MalwareClass::LdPreloadNetworkHider,
    mitre_techniques: &["T1574.006", "T1014", "T1556.003", "T1205.001"],
    signals: &[
        ProfileSignal { id: ELF_HOOKS_NETWORK_HIDING,        weight: 30, required: true  },
        ProfileSignal { id: ELF_HOOKS_PAM_CREDENTIAL,        weight: 30, required: true  },
        ProfileSignal { id: ELF_HOOKS_PROCESS_HIDING,        weight: 20, required: false },
        ProfileSignal { id: ELF_GLOBALLY_LOADED,             weight: 20, required: false },
        ProfileSignal { id: ELF_NOT_IN_PKG_DB,               weight: 15, required: false },
        ProfileSignal { id: ARTIFACT_LD_PRELOAD_FOREIGN,     weight: 20, required: false },
        ProfileSignal { id: TEMPORAL_LDPRELOAD_SSHD_RESTART, weight: 25, required: false },
        ProfileSignal { id: ELF_LIBC_SHADOW_EXPORTS,         weight: 15, required: false },
    ],
    exclusions: &[
        WeightedExclusion { id: SYSTEM_KERNEL_TAINT_OOT,    penalty: 30 },
        WeightedExclusion { id: ARTIFACT_PAM_STAGING_FATHER, penalty: 20 },
        WeightedExclusion { id: ELF_STRING_FATHER_FORMAT,    penalty: 15 },
    ],
    class_threshold:     60,
    probable_threshold:  95,
    confirmed_threshold: 125,
};

#[cfg(test)]
mod tests {
    use super::MEDUSA;
    use crate::threat_intel::{
        engine::{score_against_profile, DetectedSignal},
        profile::Classification,
        signals::*,
    };

    fn sigs(ids: &[&'static str]) -> Vec<DetectedSignal> {
        ids.iter()
            .map(|&id| DetectedSignal {
                id,
                confidence: 1.0,
                evidence: String::new(),
            })
            .collect()
    }

    #[test]
    fn medusa_required_signals_reach_class_threshold() {
        let s = sigs(&[ELF_HOOKS_NETWORK_HIDING, ELF_HOOKS_PAM_CREDENTIAL]);
        let m = score_against_profile(&s, &MEDUSA);
        assert!(
            m.score >= MEDUSA.class_threshold,
            "score {} < class_threshold {}",
            m.score,
            MEDUSA.class_threshold
        );
        assert!(m.classification >= Classification::ClassMatch);
    }

    #[test]
    fn medusa_with_sshd_restart_and_foreign_reaches_probable() {
        let s = sigs(&[
            ELF_HOOKS_NETWORK_HIDING,
            ELF_HOOKS_PAM_CREDENTIAL,
            ARTIFACT_LD_PRELOAD_FOREIGN,
            TEMPORAL_LDPRELOAD_SSHD_RESTART,
            ELF_GLOBALLY_LOADED,
        ]);
        let m = score_against_profile(&s, &MEDUSA);
        assert!(
            m.score >= MEDUSA.probable_threshold,
            "score {} < probable_threshold {}",
            m.score,
            MEDUSA.probable_threshold
        );
        assert!(m.classification >= Classification::Probable);
    }

    #[test]
    fn medusa_full_signals_reach_confirmed() {
        let s = sigs(&[
            ELF_HOOKS_NETWORK_HIDING,
            ELF_HOOKS_PAM_CREDENTIAL,
            ELF_HOOKS_PROCESS_HIDING,
            ELF_GLOBALLY_LOADED,
            ELF_NOT_IN_PKG_DB,
            ARTIFACT_LD_PRELOAD_FOREIGN,
            TEMPORAL_LDPRELOAD_SSHD_RESTART,
            ELF_LIBC_SHADOW_EXPORTS,
        ]);
        let m = score_against_profile(&s, &MEDUSA);
        assert_eq!(m.classification, Classification::Confirmed);
    }

    #[test]
    fn medusa_network_hiding_only_is_no_match() {
        let s = sigs(&[ELF_HOOKS_NETWORK_HIDING]);
        let m = score_against_profile(&s, &MEDUSA);
        assert_eq!(m.classification, Classification::NoMatch);
    }

    #[test]
    fn medusa_kernel_taint_reduces_score() {
        let base = sigs(&[
            ELF_HOOKS_NETWORK_HIDING,
            ELF_HOOKS_PAM_CREDENTIAL,
            ARTIFACT_LD_PRELOAD_FOREIGN,
            TEMPORAL_LDPRELOAD_SSHD_RESTART,
        ]);
        let with_lkm = sigs(&[
            ELF_HOOKS_NETWORK_HIDING,
            ELF_HOOKS_PAM_CREDENTIAL,
            ARTIFACT_LD_PRELOAD_FOREIGN,
            TEMPORAL_LDPRELOAD_SSHD_RESTART,
            SYSTEM_KERNEL_TAINT_OOT,
        ]);
        let m_base = score_against_profile(&base, &MEDUSA);
        let m_lkm = score_against_profile(&with_lkm, &MEDUSA);
        assert!(
            m_lkm.score < m_base.score,
            "kernel taint should penalise Medusa (no LKM)"
        );
    }
}