# ───────────────────────────────────────────────────────────────────────────── # MCProof Device Security Check — Windows (PowerShell) # # A passive, one-time audit of this PC's security posture, mapped to CIS/NIST # controls. Reads settings only — changes nothing, installs nothing, sends # nothing off the device. Produces a plain-English HTML report on your Desktop. # # Run (right-click > Run with PowerShell, or): # powershell -ExecutionPolicy Bypass -File .\mcproof-device-check.ps1 # A few checks (BitLocker) read more when run as Administrator; without it they # degrade to "review manually" rather than failing. # # NOT antivirus. This assesses hygiene (encryption, firewall, updates, screen # lock, Defender status) — it is not real-time protection. Windows Defender is # the built-in protection; keep it on. # ───────────────────────────────────────────────────────────────────────────── $ErrorActionPreference = 'SilentlyContinue' $checks = New-Object System.Collections.Generic.List[object] function Add-Check($name, $status, $detail, $fix, $control) { $checks.Add([pscustomobject]@{ Name = $name; Status = $status; Detail = $detail; Fix = $fix; Control = $control }) $sym = switch ($status) { 'pass' {'PASS '} 'warn' {'WARN '} 'fail' {'FAIL '} default {'CHECK'} } $col = switch ($status) { 'pass' {'Green'} 'warn' {'Yellow'} 'fail' {'Red'} default {'Gray'} } Write-Host ("{0} {1}" -f $sym, $name) -ForegroundColor $col if ($detail) { Write-Host (" " + $detail) -ForegroundColor DarkGray } } function HtmlEsc($s) { if ($null -eq $s) { return '' } ($s -replace '&','&' -replace '<','<' -replace '>','>') } # 1. Disk encryption — BitLocker try { $bl = Get-BitLockerVolume -MountPoint $env:SystemDrive -ErrorAction Stop if ($bl.ProtectionStatus -eq 'On') { Add-Check 'Disk encryption (BitLocker)' 'pass' 'The system drive is encrypted.' '' 'NIST SC-28 / CIS 3.11' } else { Add-Check 'Disk encryption (BitLocker)' 'fail' "The system drive is NOT encrypted (status: $($bl.ProtectionStatus)). A lost/stolen PC exposes all data." 'Turn on BitLocker: Settings > Privacy & security > Device encryption (or Control Panel > BitLocker).' 'NIST SC-28 / CIS 3.11' } } catch { Add-Check 'Disk encryption (BitLocker)' 'unknown' 'Could not read BitLocker (needs Administrator, or not available on this edition).' 'Run as Administrator, or enable Device Encryption in Settings.' 'NIST SC-28' } # 2. Firewall try { $fw = Get-NetFirewallProfile -ErrorAction Stop $off = @($fw | Where-Object { -not $_.Enabled }) if ($off.Count -eq 0) { Add-Check 'Firewall' 'pass' 'Windows Firewall is on for all profiles.' '' 'NIST SC-7 / CIS 4' } else { Add-Check 'Firewall' 'fail' ("Firewall is OFF for: " + (($off | ForEach-Object { $_.Name }) -join ', ') + '.') 'Turn on Windows Defender Firewall for all network profiles.' 'NIST SC-7 / CIS 4' } } catch { Add-Check 'Firewall' 'unknown' 'Could not read firewall state.' '' 'NIST SC-7' } # 3. Antivirus — Microsoft Defender (or third-party) try { $mp = Get-MpComputerStatus -ErrorAction Stop if ($mp.AntivirusEnabled -and $mp.RealTimeProtectionEnabled) { $age = [int]$mp.AntivirusSignatureAge if ($age -le 7) { Add-Check 'Antivirus (Defender)' 'pass' "Real-time protection is on; virus definitions are $age day(s) old." '' 'NIST SI-3 / CIS 10' } else { Add-Check 'Antivirus (Defender)' 'warn' "Real-time protection is on, but definitions are $age days old." 'Update definitions: Windows Security > Virus & threat protection > Check for updates.' 'NIST SI-3 / CIS 10' } } else { Add-Check 'Antivirus (Defender)' 'fail' 'Antivirus or real-time protection is DISABLED.' 'Turn on Windows Security real-time protection, or confirm your third-party AV is active and up to date.' 'NIST SI-3 / CIS 10' } } catch { Add-Check 'Antivirus' 'unknown' 'Could not read Defender status (a third-party AV may be managing protection).' 'Confirm your antivirus shows "protected" in its dashboard.' 'NIST SI-3' } # 4. Tamper Protection (integrity of the security controls) try { $mp2 = Get-MpComputerStatus -ErrorAction Stop if ($mp2.IsTamperProtected) { Add-Check 'Tamper Protection' 'pass' 'Tamper Protection is on — malware cannot silently disable Defender.' '' 'NIST SI-7 / CIS 10' } else { Add-Check 'Tamper Protection' 'warn' 'Tamper Protection is off.' 'Turn it on: Windows Security > Virus & threat protection > Manage settings > Tamper Protection.' 'NIST SI-7 / CIS 10' } } catch { Add-Check 'Tamper Protection' 'unknown' 'Could not read Tamper Protection.' '' 'NIST SI-7' } # 5. OS updates — most recent patch try { $hf = Get-HotFix -ErrorAction Stop | Where-Object { $_.InstalledOn } | Sort-Object InstalledOn -Descending | Select-Object -First 1 if ($hf -and $hf.InstalledOn) { $days = [int]((New-TimeSpan -Start $hf.InstalledOn -End (Get-Date)).TotalDays) if ($days -le 45) { Add-Check 'OS updates' 'pass' "Last update installed $days day(s) ago." '' 'NIST SI-2 / CIS 7' } else { Add-Check 'OS updates' 'warn' "The last update was $days days ago." 'Run Windows Update: Settings > Windows Update > Check for updates.' 'NIST SI-2 / CIS 7' } } else { Add-Check 'OS updates' 'unknown' 'Could not determine the last update date.' 'Run Windows Update.' 'NIST SI-2' } } catch { Add-Check 'OS updates' 'unknown' 'Could not read update history.' 'Run Windows Update.' 'NIST SI-2' } # 6. Screen lock (password-protected, timely) try { $d = Get-ItemProperty 'HKCU:\Control Panel\Desktop' -ErrorAction Stop $secure = "$($d.ScreenSaverIsSecure)"; $timeout = [int]("$($d.ScreenSaveTimeOut)" -replace '\D','') if ($secure -eq '1' -and $timeout -gt 0 -and $timeout -le 900) { Add-Check 'Screen lock' 'pass' ("Locks after {0} min and requires a password." -f [int]($timeout/60)) '' 'NIST AC-11 / CIS 4' } else { Add-Check 'Screen lock' 'warn' 'A prompt password on wake / short lock timeout may not be set.' 'Settings > Accounts > Sign-in options > "If you have been away, when should Windows require sign-in" = short; set a screensaver lock.' 'NIST AC-11 / CIS 4' } } catch { Add-Check 'Screen lock' 'unknown' 'Could not read screen-lock settings.' '' 'NIST AC-11' } # 7. Least privilege — is this session an administrator? try { $admin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if ($admin) { Add-Check 'Least privilege' 'warn' 'This account has administrator rights; working day-to-day as an admin widens the blast radius of any compromise.' 'Use a standard account for daily work; keep an admin account only for installs.' 'NIST AC-6 / CIS 5' } else { Add-Check 'Least privilege' 'pass' 'Running as a standard (non-admin) user.' '' 'NIST AC-6 / CIS 5' } } catch { Add-Check 'Least privilege' 'unknown' 'Could not determine account privilege.' '' 'NIST AC-6' } # 8. Guest account try { $g = Get-LocalUser -Name 'Guest' -ErrorAction Stop if ($g.Enabled) { Add-Check 'Guest account' 'warn' 'The Guest account is enabled.' 'Disable it: run "net user guest /active:no" as Administrator.' 'NIST AC-2 / CIS 5' } else { Add-Check 'Guest account' 'pass' 'The Guest account is disabled.' '' 'NIST AC-2 / CIS 5' } } catch { Add-Check 'Guest account' 'unknown' 'Could not read the Guest account state.' '' 'NIST AC-2' } # 9. Remote Desktop (RDP) try { $rdp = (Get-ItemProperty 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -ErrorAction Stop).fDenyTSConnections if ($rdp -eq 0) { Add-Check 'Remote Desktop (RDP)' 'warn' 'Remote Desktop is ENABLED — ensure it is needed and access is restricted (VPN / Network Level Authentication).' 'If unused: Settings > System > Remote Desktop > off.' 'NIST AC-17 / CIS 4' } else { Add-Check 'Remote Desktop (RDP)' 'pass' 'Remote Desktop is disabled.' '' 'NIST AC-17 / CIS 4' } } catch { Add-Check 'Remote Desktop (RDP)' 'unknown' 'Could not read Remote Desktop state.' '' 'NIST AC-17' } # 10. User Account Control (UAC) try { $uac = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -ErrorAction Stop).EnableLUA if ($uac -eq 1) { Add-Check 'User Account Control (UAC)' 'pass' 'UAC is enabled.' '' 'NIST AC-6 / CIS 5' } else { Add-Check 'User Account Control (UAC)' 'fail' 'UAC is DISABLED — apps can gain admin rights without a prompt.' 'Turn UAC on: search "UAC" > set to at least "Notify me when apps try to make changes".' 'NIST AC-6 / CIS 5' } } catch { Add-Check 'User Account Control (UAC)' 'unknown' 'Could not read UAC state.' '' 'NIST AC-6' } # 11. SmartScreen try { $ss = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer' -ErrorAction Stop).SmartScreenEnabled if ($ss -and $ss -ne 'Off') { Add-Check 'SmartScreen' 'pass' "SmartScreen is on ($ss) — warns on malicious downloads and sites." '' 'NIST SI-3 / CIS 10' } else { Add-Check 'SmartScreen' 'warn' 'SmartScreen appears to be off.' 'Turn it on: Windows Security > App & browser control > Reputation-based protection.' 'NIST SI-3 / CIS 10' } } catch { Add-Check 'SmartScreen' 'unknown' 'Could not read SmartScreen state.' '' 'NIST SI-3' } # 12. Windows Update service try { $wu = Get-Service -Name wuauserv -ErrorAction Stop if ($wu.StartType -ne 'Disabled') { Add-Check 'Windows Update service' 'pass' "The Windows Update service is available (status: $($wu.Status))." '' 'NIST SI-2 / CIS 7' } else { Add-Check 'Windows Update service' 'fail' 'The Windows Update service is DISABLED — the PC will stop receiving security fixes.' 'Set the "Windows Update" service to Manual (or Automatic) and run updates.' 'NIST SI-2 / CIS 7' } } catch { Add-Check 'Windows Update service' 'unknown' 'Could not read the Windows Update service.' '' 'NIST SI-2' } # 13. Automatic login try { $wl = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -ErrorAction Stop if ("$($wl.AutoAdminLogon)" -eq '1') { Add-Check 'Automatic login' 'fail' 'Automatic login is ON — anyone who powers on the PC is signed in (the password may be stored in the registry).' 'Turn it off: run "netplwiz" and re-check "Users must enter a user name and password".' 'NIST AC-3 / CIS 5' } else { Add-Check 'Automatic login' 'pass' 'Automatic login is off — a password is required at startup.' '' 'NIST AC-3 / CIS 5' } } catch { Add-Check 'Automatic login' 'unknown' 'Could not read the auto-login setting.' '' 'NIST AC-3' } # 14. Legacy SMBv1 protocol try { $smb = Get-SmbServerConfiguration -ErrorAction Stop if ($smb.EnableSMB1Protocol) { Add-Check 'Legacy SMBv1' 'fail' 'SMBv1 is ENABLED — an obsolete protocol exploited by WannaCry / NotPetya.' 'Disable it (as Admin): Set-SmbServerConfiguration -EnableSMB1Protocol $false — or Turn Windows Features off > SMB 1.0.' 'NIST CM-7 / CIS 4' } else { Add-Check 'Legacy SMBv1' 'pass' 'SMBv1 is disabled.' '' 'NIST CM-7 / CIS 4' } } catch { Add-Check 'Legacy SMBv1' 'unknown' 'Could not read SMB server configuration.' '' 'NIST CM-7' } # 15. Ransomware protection (Controlled Folder Access) try { $cfa = (Get-MpPreference -ErrorAction Stop).EnableControlledFolderAccess if ($cfa -eq 1) { Add-Check 'Ransomware protection' 'pass' 'Controlled Folder Access is on — blocks unauthorized apps from changing your files.' '' 'NIST SI-3 / CIS 10' } else { Add-Check 'Ransomware protection' 'warn' 'Controlled Folder Access (ransomware protection) is off.' 'Turn it on: Windows Security > Virus & threat protection > Ransomware protection.' 'NIST SI-3 / CIS 10' } } catch { Add-Check 'Ransomware protection' 'unknown' 'Could not read ransomware protection.' '' 'NIST SI-3' } # ── grade + report ──────────────────────────────────────────────────────────── $pass = @($checks | Where-Object Status -eq 'pass').Count $warn = @($checks | Where-Object Status -eq 'warn').Count $fail = @($checks | Where-Object Status -eq 'fail').Count $unk = @($checks | Where-Object { $_.Status -notin 'pass','warn','fail' }).Count if ($fail -gt 0) { $grade='At risk'; $gcolor='#e02a4d' } elseif ($warn -gt 0) { $grade='Needs attention'; $gcolor='#d4af37' } else { $grade='Solid'; $gcolor='#4cc47f' } Write-Host "" Write-Host ("Device posture: {0} ({1} pass, {2} warn, {3} fail, {4} review)" -f $grade,$pass,$warn,$fail,$unk) -ForegroundColor Cyan $rowsHtml = ($checks | ForEach-Object { $fixHtml = if ($_.Fix) { "
Fix: " + (HtmlEsc $_.Fix) + "
" } else { "" } "$(HtmlEsc $_.Name)$($_.Status)$(HtmlEsc $_.Detail)$fixHtml$(HtmlEsc $_.Control)" }) -join "`n" $host_name = $env:COMPUTERNAME $osver = (Get-CimInstance Win32_OperatingSystem).Caption $now = Get-Date -Format 'yyyy-MM-dd HH:mm' $report = Join-Path ([Environment]::GetFolderPath('Desktop')) 'mcproof-device-report.html' $html = @" MCProof Device Security Check — $host_name
MCProof

Device Security Check

$host_name · $osver · $now
$grade$pass pass · $warn warn · $fail fail · $unk review
$rowsHtml
CheckStatusWhat it meansControl
MCProof Device Security Check reads settings only — it changes nothing and sends nothing off this device. This is an automated assessment of security hygiene, not antivirus and not legal advice; a qualified reviewer should confirm before any compliance representation. Controls referenced: NIST SP 800-53 / CIS Controls v8. — Millennials Creatives LLC
"@ $html | Out-File -FilePath $report -Encoding UTF8 Write-Host ("Report saved: {0}" -f $report) Start-Process $report | Out-Null