Users Online

· Guests Online: 14

· Members Online: 0

· Total Members: 229
· Newest Member: Zarfdrilhor

Forum Threads

Newest Threads
No Threads created
Hottest Threads
No Threads created

Latest Articles

Disabling USB Ports for Data Copying

Disabling USB Ports for Data Copying

 

 

This is a common enterprise Data Loss Prevention (DLP) control. Here are methods for both platforms:


Windows

Method 1: Group Policy (GPO) — Recommended for Enterprises

Computer Configuration →
  Administrative Templates →
    System →
      Removable Storage Access

Set these policies to Enabled/Deny:

  • Removable Disks: Deny read access
  • Removable Disks: Deny write access ← blocks copying to USB
  • All Removable Storage classes: Deny all access ← blocks everything

Apply via GPO to push across all domain-joined machines.


Method 2: Registry Edit

reg

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR]
"Start"=dword:00000004

Value 

Meaning

3

USB storage enabled

4

USB storage disabled

Via Command Prompt (Admin):

cmd

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR" /v Start /t REG_DWORD /d 4 /f

Re-enable:

cmd

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR" /v Start /t REG_DWORD /d 3 /f

Method 3: Device Manager (Per Machine)

  1. Open Device Manager
  2. Expand Universal Serial Bus controllers
  3. Right-click each USB Mass Storage Device → Disable device

⚠️ This disables the device but doesn't prevent re-enabling by users with admin rights.


Method 4: PowerShell (Bulk Deployment)

powershell

# Disable USB storage
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\USBSTOR" `
  -Name "Start" -Value 4
 
# Verify
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\USBSTOR" -Name "Start"

Method 5: Read-Only (Allow read, block write)

reg

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\StorageDevicePolicies]
"WriteProtect"=dword:00000001

cmd

reg add "HKLM\SYSTEM\CurrentControlSet\Control\StorageDevicePolicies" /v WriteProtect /t REG_DWORD /d 1 /f

This allows USB devices to be read but prevents any data from being copied TO the USB.


Linux

Method 1: Blacklist USB Storage Module

bash

# Create a blacklist config
echo "blacklist usb_storage" | sudo tee /etc/modprobe.d/disable-usb-storage.conf
echo "blacklist uas" | sudo tee -a /etc/modprobe.d/disable-usb-storage.conf
 
# Update initramfs
sudo update-initramfs -u   # Debian/Ubuntu
sudo dracut --force        # RHEL/CentOS/Fedora
 
# Reboot to apply
sudo reboot

Method 2: Unload Module Immediately (No Reboot)

bash

# Remove the module from running kernel
sudo modprobe -r usb_storage
sudo modprobe -r uas
 
# Verify it's unloaded
lsmod | grep usb_storage   # Should return nothing

⚠️ Temporary — reloads on reboot unless blacklisted.


Method 3: udev Rules (Block Specific or All USB Storage)

bash

sudo nano /etc/udev/rules.d/99-block-usb.rules

Block all USB storage:

udev

ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", RUN{program}="/bin/sh -c 'echo 0 > /sys/\$devpath/authorized'"

Block write access only (read-only USB):

udev

ACTION=="add", KERNEL=="sd*", SUBSYSTEMS=="usb", RUN{program}="/sbin/blockdev --setro /dev/%k"

Reload rules:

bash

sudo udevadm control --reload-rules
sudo udevadm trigger

Method 4: USBGuard (Enterprise-Grade)

USBGuard is purpose-built for enterprise USB control:

bash

# Install
sudo apt install usbguard          # Debian/Ubuntu
sudo dnf install usbguard          # RHEL/Fedora
 
# Generate policy from currently connected devices
sudo usbguard generate-policy > /etc/usbguard/rules.conf
 
# Block all USB devices by default
echo 'block' | sudo tee /etc/usbguard/rules.conf
 
# Allow specific device by ID
echo 'allow id 1234:5678' | sudo tee -a /etc/usbguard/rules.conf
 
# Start and enable
sudo systemctl enable --now usbguard

Method 5: File System Permissions (Prevent Mounting)

bash

# Restrict who can mount USB
sudo chmod 700 /media
sudo chmod 700 /mnt
 
# Remove user ability to mount
sudo nano /etc/fstab
# Add: noauto,noexec options for removable media

Enterprise-Level Solutions

For large-scale enforcement, consider dedicated DLP tools:

Tool

Platform

Feature

Microsoft Intune

Windows/macOS

MDM-based USB control

Symantec DLP

Cross-platform

Content-aware blocking

CrowdStrike Falcon

Cross-platform

USB device control policies

 

 

 

 

Forcepoint DLP

Cross-platform

Block by device class/ID

USBGuard

Linux

Whitelist/blacklist USB

McAfee ePO

Windows

Centralized USB policy


Best Practice Summary

Goal

Windows

Linux

Block all USB storage

GPO or USBSTOR registry

Blacklist usb_storage

Block write only

WriteProtect registry key

udev read-only rule

Allow specific USBs

GPO device ID whitelist

USBGuard whitelist

Enterprise-wide

Microsoft Intune / GPO

USBGuard + Ansible

Audit USB usage

Windows Event Log (ID 2003)

udevadm monitor / auditd


Would you like help with auditing USB usage logs, whitelisting specific devices, or deploying via Ansible/GPO at scale?

 

 

Auditing USB Usage Logs — Windows & Linux


Windows USB Audit Logs

Step 1: Enable Auditing via Group Policy

Computer Configuration →
  Windows Settings →
    Security Settings →
      Advanced Audit Policy Configuration →
        Object Access →
          Audit Removable Storage → Enable (Success & Failure)

Or via command line:

cmd

auditpol /set /subcategory:"Removable Storage" /success:enable /failure:enable

Step 2: Key Event IDs to Monitor

Event ID

Meaning

6416

New external device recognized

4663

File copied to/from removable storage

4656

Handle requested on USB object

4657

Registry value modified (USBSTOR)

2003

USB device policy change


Step 3: Query Logs via PowerShell

Find all USB device connections:

powershell

Get-WinEvent -LogName Security | Where-Object {$_.Id -eq 6416} |
  Select-Object TimeCreated, Message |
  Format-List

Find file copy attempts to USB:

powershell

Get-WinEvent -LogName Security | Where-Object {$_.Id -eq 4663} |
  Where-Object {$_.Message -like "*RemovableStorage*"} |
  Select-Object TimeCreated, Message |
  Export-Csv "C:\USB_Audit_Log.csv" -NoTypeInformation

Check USB history from registry:

powershell

# Lists all USB devices ever connected
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Enum\USBSTOR\*\*" |
  Select-Object FriendlyName, Mfg, @{N="LastConnected";E={$_.PSChildName}} |
  Format-Table -AutoSize

Export last 7 days of USB events:

powershell

$startDate = (Get-Date).AddDays(-7)
 
Get-WinEvent -FilterHashtable @{
  LogName   = 'Security'
  Id        = 6416, 4663
  StartTime = $startDate
} | Select-Object TimeCreated, Id, Message |
  Export-Csv "C:\USB_7Day_Report.csv" -NoTypeInformation
 
Write-Host "Report saved to C:\USB_7Day_Report.csv"

Step 4: Event Viewer (GUI)

Event Viewer →
  Windows Logs →
    Security →
      Filter Current Log →
        Event ID: 6416

Step 5: Audit via SIEM (Splunk Example)

spl

index=wineventlog EventCode=6416 OR EventCode=4663
| eval action=case(EventCode==6416,"Device Connected", EventCode==4663,"File Access")
| table _time, host, user, action, Message
| sort -_time


Linux USB Audit Logs

Method 1: syslog / journald (Built-in)

Check recent USB events:

bash

# systemd journal
journalctl -k | grep -i usb
 
# Kernel messages
dmesg | grep -i usb | grep -i "storage\|mass\|sd[a-z]"
 
# syslog
grep -i usb /var/log/syslog          # Debian/Ubuntu
grep -i usb /var/log/messages        # RHEL/CentOS

Watch USB events in real time:

bash

journalctl -kf | grep -i usb

Method 2: udevadm Monitor (Real-Time)

bash

# Monitor all USB add/remove events live
sudo udevadm monitor --subsystem-match=usb --property
 
# Compact view
sudo udevadm monitor --subsystem-match=block | grep -i usb

Output example:

UDEV [12345.678] add /devices/pci0000:00/usb1/1-1 (usb)
ACTION=add
ID_VENDOR=SanDisk
ID_MODEL=Ultra_USB_3.0

Method 3: auditd — Enterprise-Grade USB Auditing

Install auditd:

bash

sudo apt install auditd audispd-plugins    # Debian/Ubuntu
sudo dnf install audit                     # RHEL/Fedora
sudo systemctl enable --now auditd

Add USB audit rules:

bash

sudo nano /etc/audit/rules.d/usb.rules

bash

# Watch USB storage mounting
-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=4294967295 -k usb_mount
 
# Watch file writes to USB mount points
-w /media -p rwxa -k usb_media_access
-w /mnt -p rwxa -k usb_mnt_access
 
# Watch USB device node creation
-a always,exit -F arch=b64 -S open -F dir=/dev -F success=1 -k usb_dev_open

Reload rules:

bash

sudo augenrules --load
sudo systemctl restart auditd

Method 4: Query auditd Logs

Search all USB mount events:

bash

sudo ausearch -k usb_mount | aureport -f -i

Search by user:

bash

sudo ausearch -k usb_media_access -ua john | aureport -f -i

Generate summary report:

bash

# USB access summary for last 24 hours
sudo aureport --start today --end now -f | grep -i "media\|mnt"

Full USB audit report:

bash

sudo ausearch -k usb_mount -k usb_media_access \
  --start $(date -d '7 days ago' '+%m/%d/%Y') \
  --end $(date '+%m/%d/%Y') \
  | aureport -f -i \
  | tee /var/log/usb_weekly_report.txt
 
echo "Report saved: /var/log/usb_weekly_report.txt"

Method 5: USBGuard Audit Log

bash

# View USBGuard decision log
sudo journalctl -u usbguard | grep -E "allow|block|reject"
 
# Detailed USB policy decisions
sudo usbguard list-devices
 
# Devices that were blocked
sudo journalctl -u usbguard | grep "block"

Method 6: Inotify — Watch for File Copies to USB

bash

# Install inotify-tools
sudo apt install inotify-tools
 
# Monitor /media for any file writes (data exfiltration detection)
sudo inotifywait -m -r -e create,moved_to,close_write /media \
  --format '%T %w %f %e' --timefmt '%Y-%m-%d %H:%M:%S' \
  | tee /var/log/usb_file_watch.log

Comparison Summary

Capability

Windows

Linux

Device connection log

Event ID 6416

journalctl -k, dmesg

File copy detection

Event ID 4663

auditd + inotify

Real-time monitoring

Event Viewer / PowerShell

udevadm monitor

Historical USB devices

Registry (USBSTOR)

journalctl, auditd logs

Enterprise SIEM feed

Windows Event Forwarding

auditd → syslog → SIEM

Policy enforcement log

GPO audit logs

USBGuard journal


Centralizing Logs to a SIEM

Windows → SIEM:

powershell

# Forward events using Windows Event Forwarding (WEF)
wecutil qc /quiet   # Configure as event collector

Linux → SIEM (rsyslog):

bash

# Forward auditd logs to central SIEM
echo "*.* @your-siem-server:514" | sudo tee -a /etc/rsyslog.conf
sudo systemctl restart rsyslog

 

 

Automated USB Audit Report Scripts


Windows — PowerShell Script

Save as USB-Audit-Report.ps1:

powershell

# ============================================

# USB Audit Report Script — Windows

# Generates HTML + CSV report of USB activity

# ============================================

 

param(

    [int]$DaysBack = 7,

    [string]$ReportPath = "C:\Reports\USB_Audit",

    [string]$SMTPServer = "smtp.company.com",

    [string]$EmailTo = "security@company.com",

    [string]$EmailFrom = "usb-audit@company.com",

    [switch]$SendEmail

)

 

# --- Setup ---

$date = Get-Date -Format "yyyy-MM-dd_HH-mm"

$startDate = (Get-Date).AddDays(-$DaysBack)

if (-not (Test-Path $ReportPath)) { New-Item -ItemType Directory -Path $ReportPath | Out-Null }

$csvFile  = "$ReportPath\USB_Report_$date.csv"

$htmlFile = "$ReportPath\USB_Report_$date.html"

 

Write-Host "`n[*] Collecting USB events from last $DaysBack days..." -ForegroundColor Cyan

 

# --- Collect Events ---

$events = @()

 

# Event 6416 - New device recognized

$dev = Get-WinEvent -FilterHashtable @{

    LogName   = 'Security'

    Id        = 6416

    StartTime = $startDate

} -ErrorAction SilentlyContinue

 

foreach ($e in $dev) {

    $xml = [xml]$e.ToXml()

    $data = $xml.Event.EventData.Data

    $events += [PSCustomObject]@{

        Time       = $e.TimeCreated

        EventID    = $e.Id

        EventType  = "Device Connected"

        User       = ($data | Where-Object { $_.Name -eq "SubjectUserName" }).'#text'

        Computer   = $env:COMPUTERNAME

        DeviceName = ($data | Where-Object { $_.Name -eq "DeviceDescription" }).'#text'

        DeviceID   = ($data | Where-Object { $_.Name -eq "DeviceInstanceId" }).'#text'

        FilePath   = "N/A"

    }

}

 

# Event 4663 - File access on removable storage

$files = Get-WinEvent -FilterHashtable @{

    LogName   = 'Security'

    Id        = 4663

    StartTime = $startDate

} -ErrorAction SilentlyContinue |

    Where-Object { $_.Message -like "*Removable*" }

 

foreach ($e in $files) {

    $xml = [xml]$e.ToXml()

    $data = $xml.Event.EventData.Data

    $events += [PSCustomObject]@{

        Time       = $e.TimeCreated

        EventID    = $e.Id

        EventType  = "File Access"

        User       = ($data | Where-Object { $_.Name -eq "SubjectUserName" }).'#text'

        Computer   = $env:COMPUTERNAME

        DeviceName = "N/A"

        DeviceID   = "N/A"

        FilePath   = ($data | Where-Object { $_.Name -eq "ObjectName" }).'#text'

    }

}

 

# --- USB Device History from Registry ---

Write-Host "[*] Reading USB device history from registry..." -ForegroundColor Cyan

$usbHistory = @()

$usbPath = "HKLM:\SYSTEM\CurrentControlSet\Enum\USBSTOR"

if (Test-Path $usbPath) {

    Get-ChildItem $usbPath | ForEach-Object {

        $deviceType = $_.PSChildName

        Get-ChildItem $_.PSPath | ForEach-Object {

            $props = Get-ItemProperty $_.PSPath

            $usbHistory += [PSCustomObject]@{

                DeviceType    = $deviceType

                FriendlyName  = $props.FriendlyName

                Manufacturer  = $props.Mfg

                SerialNumber  = $_.PSChildName

            }

        }

    }

}

 

# --- Export CSV ---

$events | Export-Csv $csvFile -NoTypeInformation

Write-Host "[+] CSV saved: $csvFile" -ForegroundColor Green

 

# --- Generate HTML Report ---

$totalEvents   = $events.Count

$deviceEvents  = ($events | Where-Object { $_.EventType -eq "Device Connected" }).Count

$fileEvents    = ($events | Where-Object { $_.EventType -eq "File Access" }).Count

$uniqueUsers   = ($events | Select-Object -Unique User).Count

 

$tableRows = $events | Sort-Object Time -Descending | ForEach-Object {

    $rowColor = if ($_.EventType -eq "File Access") { "#fff3cd" } else { "#d4edda" }

    "<tr style='background:$rowColor'>

        <td>$($_.Time)</td>

        <td>$($_.EventType)</td>

        <td>$($_.User)</td>

        <td>$($_.Computer)</td>

        <td>$($_.DeviceName)</td>

        <td>$($_.FilePath)</td>

    </tr>"

}

 

$historyRows = $usbHistory | ForEach-Object {

    "<tr>

        <td>$($_.FriendlyName)</td>

        <td>$($_.Manufacturer)</td>

        <td>$($_.SerialNumber)</td>

        <td>$($_.DeviceType)</td>

    </tr>"

}

 

$html = @"

<!DOCTYPE html>

<html>

<head>

<meta charset='UTF-8'>

<title>USB Audit Report</title>

<style>

  body { font-family: Segoe UI, sans-serif; background: #f4f6f9; padding: 30px; }

  h1   { color: #c0392b; }

  h2   { color: #2c3e50; border-bottom: 2px solid #e74c3c; padding-bottom: 5px; }

  .summary { display: flex; gap: 20px; margin-bottom: 30px; }

  .card { background: white; border-radius: 8px; padding: 20px 30px;

          box-shadow: 0 2px 6px rgba(0,0,0,0.1); text-align: center; }

  .card h3 { margin: 0; font-size: 2em; color: #e74c3c; }

  .card p  { margin: 5px 0 0; color: #666; }

  table  { width: 100%; border-collapse: collapse; background: white;

           border-radius: 8px; overflow: hidden;

           box-shadow: 0 2px 6px rgba(0,0,0,0.1); margin-bottom: 30px; }

  th     { background: #2c3e50; color: white; padding: 12px; text-align: left; }

  td     { padding: 10px 12px; border-bottom: 1px solid #eee; font-size: 0.9em; }

  tr:hover td { background: #f0f4ff; }

  .footer { color: #999; font-size: 0.8em; margin-top: 20px; }

</style>

</head>

<body>

<h1>🔌 USB Audit Report</h1>

<p>Period: <strong>$(Get-Date $startDate -Format 'yyyy-MM-dd')</strong> to <strong>$(Get-Date -Format 'yyyy-MM-dd')</strong> | Host: <strong>$env:COMPUTERNAME</strong></p>

 

<div class='summary'>

  <div class='card'><h3>$totalEvents</h3><p>Total Events</p></div>

  <div class='card'><h3>$deviceEvents</h3><p>Devices Connected</p></div>

  <div class='card'><h3>$fileEvents</h3><p>File Access Events</p></div>

  <div class='card'><h3>$uniqueUsers</h3><p>Unique Users</p></div>

</div>

 

<h2>Event Log</h2>

<table>

  <tr><th>Time</th><th>Event Type</th><th>User</th><th>Computer</th><th>Device</th><th>File Path</th></tr>

  $($tableRows -join "`n")

</table>

 

<h2>USB Device History (Registry)</h2>

<table>

  <tr><th>Friendly Name</th><th>Manufacturer</th><th>Serial Number</th><th>Device Type</th></tr>

  $($historyRows -join "`n")

</table>

 

<div class='footer'>Generated: $(Get-Date) | USB Audit Script v1.0</div>

</body>

</html>

"@

 

$html | Out-File $htmlFile -Encoding UTF8

Write-Host "[+] HTML report saved: $htmlFile" -ForegroundColor Green

 

# --- Optional Email ---

if ($SendEmail) {

    Send-MailMessage `

        -To $EmailTo -From $EmailFrom `

        -Subject "USB Audit Report - $env:COMPUTERNAME - $(Get-Date -Format 'yyyy-MM-dd')" `

        -Body "Please find the USB audit report attached." `

        -Attachments $csvFile, $htmlFile `

        -SmtpServer $SMTPServer

    Write-Host "[+] Email sent to $EmailTo" -ForegroundColor Green

}

 

Write-Host "`n[✔] Report complete! Events found: $totalEvents`n" -ForegroundColor Green

Run it:

powershell

# Basic run (last 7 days)

.\USB-Audit-Report.ps1

 

# Last 30 days + send email

.\USB-Audit-Report.ps1 -DaysBack 30 -SendEmail

 

# Schedule via Task Scheduler (weekly)

schtasks /create /tn "USB Audit Report" /tr "powershell -File C:\Scripts\USB-Audit-Report.ps1" /sc weekly /d MON /st 08:00 /ru SYSTEM



Linux — Bash Script

Save as usb_audit_report.sh:

bash

#!/bin/bash

# ============================================

# USB Audit Report Script — Linux

# Generates HTML + CSV report of USB activity

# ============================================

 

# --- Config ---

DAYS_BACK=${1:-7}

REPORT_DIR="/var/log/usb-audit-reports"

DATE=$(date +"%Y-%m-%d_%H-%M")

CSV_FILE="$REPORT_DIR/usb_report_$DATE.csv"

HTML_FILE="$REPORT_DIR/usb_report_$DATE.html"

EMAIL_TO="security@company.com"

SEND_EMAIL=false   # Set to true to enable email

HOSTNAME=$(hostname)

 

mkdir -p "$REPORT_DIR"

 

echo "[*] Collecting USB events from last $DAYS_BACK days..."

 

# --- Collect from journald ---

SINCE=$(date -d "$DAYS_BACK days ago" "+%Y-%m-%d %H:%M:%S")

 

echo "Timestamp,Source,Event,Device,User,Details" > "$CSV_FILE"

 

# USB device connections from kernel log

journalctl -k --since="$SINCE" 2>/dev/null | \

grep -iE "usb|scsi|sd[a-z]|mass.storage" | \

while IFS= read -r line; do

    ts=$(echo "$line" | awk '{print $1, $2, $3}')

    echo "$ts,kernel,Device Event,USB Device,system,$line" >> "$CSV_FILE"

done

 

# USB mount events from syslog

grep -iE "usb|mount|sd[a-z]" /var/log/syslog 2>/dev/null | \

awk -v since="$SINCE" '$0 >= since' | \

while IFS= read -r line; do

    ts=$(echo "$line" | awk '{print $1, $2, $3}')

    user=$(echo "$line" | grep -oP 'user=\K\S+' || echo "system")

    echo "$ts,syslog,Mount Event,USB Device,$user,$line" >> "$CSV_FILE"

done

 

# auditd USB events (if available)

if command -v ausearch &>/dev/null; then

    ausearch -k usb_mount -k usb_media_access \

        --start "$(date -d "$DAYS_BACK days ago" '+%m/%d/%Y')" \

        --end "$(date '+%m/%d/%Y')" 2>/dev/null | \

    grep -E "^time|uid|cmd|name" | \

    paste - - - - | \

    while IFS= read -r line; do

        echo "$(date),auditd,USB Access,Removable Media,$(echo $line | grep -oP 'uid=\K\d+'),$(echo $line)" >> "$CSV_FILE"

    done

fi

 

# --- Collect connected USB devices now ---

echo "[*] Scanning current USB devices..."

CURRENT_DEVICES=$(lsusb 2>/dev/null)

 

# --- Count stats ---

TOTAL=$(tail -n +2 "$CSV_FILE" | wc -l)

MOUNT_EVENTS=$(grep -c "Mount Event" "$CSV_FILE" 2>/dev/null || echo 0)

DEVICE_EVENTS=$(grep -c "Device Event" "$CSV_FILE" 2>/dev/null || echo 0)

AUDIT_EVENTS=$(grep -c "auditd" "$CSV_FILE" 2>/dev/null || echo 0)

 

# --- Build HTML Report ---

echo "[*] Generating HTML report..."

 

TABLE_ROWS=""

while IFS=',' read -r ts source event device user details; do

    [[ "$ts" == "Timestamp" ]] && continue

    if [[ "$event" == *"Mount"* ]]; then

        ROW_COLOR="#fff3cd"

    elif [[ "$source" == "auditd" ]]; then

        ROW_COLOR="#f8d7da"

    else

        ROW_COLOR="#d4edda"

    fi

    TABLE_ROWS+="<tr style='background:$ROW_COLOR'>

        <td>$ts</td><td>$source</td><td>$event</td>

        <td>$device</td><td>$user</td>

        <td style='font-size:0.8em;word-break:break-all'>${details:0:120}...</td>

    </tr>"

done < "$CSV_FILE"

 

DEVICE_ROWS=""

while IFS= read -r line; do

    BUS=$(echo "$line" | grep -oP 'Bus \K\d+')

    DEV=$(echo "$line" | grep -oP 'Device \K\d+')

    ID=$(echo "$line" | grep -oP 'ID \K[\w:]+')

    NAME=$(echo "$line" | sed 's/.*ID [^ ]* //')

    DEVICE_ROWS+="<tr><td>Bus $BUS</td><td>Device $DEV</td><td>$ID</td><td>$NAME</td></tr>"

done <<< "$CURRENT_DEVICES"

 

cat > "$HTML_FILE" <<EOF

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>USB Audit Report - $HOSTNAME</title>

<style>

  body  { font-family: Ubuntu, sans-serif; background: #f4f6f9; padding: 30px; }

  h1    { color: #c0392b; }

  h2    { color: #2c3e50; border-bottom: 2px solid #e74c3c; padding-bottom: 5px; }

  .summary { display: flex; gap: 20px; margin-bottom: 30px; flex-wrap: wrap; }

  .card { background: white; border-radius: 8px; padding: 20px 30px;

          box-shadow: 0 2px 6px rgba(0,0,0,0.1); text-align: center; min-width: 130px; }

  .card h3 { margin: 0; font-size: 2em; color: #e74c3c; }

  .card p  { margin: 5px 0 0; color: #666; font-size: 0.9em; }

  table  { width: 100%; border-collapse: collapse; background: white;

           border-radius: 8px; overflow: hidden;

           box-shadow: 0 2px 6px rgba(0,0,0,0.1); margin-bottom: 30px; }

  th     { background: #2c3e50; color: white; padding: 12px; text-align: left; }

  td     { padding: 9px 12px; border-bottom: 1px solid #eee; font-size: 0.85em; }

  tr:hover td { background: #f0f4ff; }

  .footer { color: #999; font-size: 0.8em; margin-top: 20px; }

</style>

</head>

<body>

<h1>🔌 USB Audit Report</h1>

<p>Host: <strong>$HOSTNAME</strong> | Period: <strong>Last $DAYS_BACK days</strong> | Generated: <strong>$(date)</strong></p>

 

<div class="summary">

  <div class="card"><h3>$TOTAL</h3><p>Total Events</p></div>

  <div class="card"><h3>$DEVICE_EVENTS</h3><p>Device Events</p></div>

  <div class="card"><h3>$MOUNT_EVENTS</h3><p>Mount Events</p></div>

  <div class="card"><h3>$AUDIT_EVENTS</h3><p>Audit Events</p></div>

</div>

 

<h2>Currently Connected USB Devices</h2>

<table>

  <tr><th>Bus</th><th>Device</th><th>ID</th><th>Description</th></tr>

  $DEVICE_ROWS

</table>

 

<h2>USB Event Log</h2>

<table>

  <tr><th>Timestamp</th><th>Source</th><th>Event</th><th>Device</th><th>User</th><th>Details</th></tr>

  $TABLE_ROWS

</table>

 

<div class="footer">

  🟢 Device Connected &nbsp;|&nbsp;

  🟡 Mount Event &nbsp;|&nbsp;

  🔴 Audit Alert &nbsp;|&nbsp;

  USB Audit Script v1.0

</div>

</body>

</html>

EOF

 

echo "[+] CSV saved : $CSV_FILE"

echo "[+] HTML saved: $HTML_FILE"

 

# --- Optional Email via sendmail/mailx ---

if [ "$SEND_EMAIL" = true ]; then

    if command -v mailx &>/dev/null; then

        echo "USB Audit Report for $HOSTNAME — $(date)" | \

        mailx -s "USB Audit Report - $HOSTNAME - $(date +%Y-%m-%d)" \

              -a "$HTML_FILE" -a "$CSV_FILE" \

              "$EMAIL_TO"

        echo "[+] Email sent to $EMAIL_TO"

    else

        echo "[!] mailx not found — install with: apt install mailutils"

    fi

fi

 

echo ""

echo "[✔] Report complete! Total events: $TOTAL"

echo "    HTML → $HTML_FILE"

echo "    CSV  → $CSV_FILE"

Deploy it:

bash

# Make executable

chmod +x usb_audit_report.sh

 

# Run manually (last 7 days)

sudo ./usb_audit_report.sh

 

# Run for last 30 days

sudo ./usb_audit_report.sh 30

 

# Schedule via cron (every Monday at 8 AM)

(crontab -l; echo "0 8 * * MON root /opt/scripts/usb_audit_report.sh 7") | sudo crontab -

 

# Enable email

sed -i 's/SEND_EMAIL=false/SEND_EMAIL=true/' usb_audit_report.sh


Output Sample (Both Platforms)

Both scripts produce:

/Reports/USB_Audit/

├── usb_report_2026-05-30_08-00.html   ← Visual dashboard

└── usb_report_2026-05-30_08-00.csv    ← Raw data for SIEM/Excel

The HTML report includes:

  • Summary cards — total events, device connections, file access, mount events
  • Currently connected devices table
  • Full event log — color-coded by event type
  • Registry/journal history of all USB devices ever connected

Deployment at Scale

Method

Windows

Linux

Push script

GPO Startup Script

Ansible playbook

Schedule

Task Scheduler

cron / systemd timer

Collect reports

Network share / SFTP

rsync to central server

Central dashboard

SIEM (Splunk/Sentinel)

ELK Stack


Would you like a combined dashboard that aggregates reports from multiple machines, or help setting up real-time email alerts when a USB is plugged in?

Here's a full solution — a central server that collects USB audit data from all machines, plus a live web dashboard.


Architecture Overview

Windows Machines  ──┐
                    ├──► Central Server (Linux) ──► Web Dashboard
Linux Machines    ──┘       /var/usb-central/

Part 1: Central Collection Server (Linux)

Setup collector script

Save as /opt/usb-central/collect.sh:

bash

#!/bin/bash
# Central USB report collector
# Pulls CSV reports from all agents via SSH/SFTP
 
CENTRAL_DIR="/var/usb-central/reports"
MACHINES_FILE="/opt/usb-central/machines.txt"  # one host per line
SSH_KEY="/opt/usb-central/.ssh/usb_audit_key"
REPORT_GLOB="/var/log/usb-audit-reports/*.csv"
 
mkdir -p "$CENTRAL_DIR"
 
while IFS='|' read -r host os label; do
    [[ "$host" =~ ^#.*$ || -z "$host" ]] && continue
    echo "[*] Pulling from $label ($host) [$os]..."
 
    DEST="$CENTRAL_DIR/$label"
    mkdir -p "$DEST"
 
    if [[ "$os" == "linux" ]]; then
        rsync -az -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \
            "root@$host:$REPORT_GLOB" "$DEST/" 2>/dev/null \
            && echo "[+] $label OK" || echo "[!] $label FAILED"
 
    elif [[ "$os" == "windows" ]]; then
        # Windows agents push via SFTP or SMB share
        # Mount SMB share and copy CSVs
        SHARE="\\\\$host\\USB_Reports"
        smbclient "$SHARE" -A /opt/usb-central/smb.auth \
            -c "lcd $DEST; mask *.csv; mget *" 2>/dev/null \
            && echo "[+] $label OK" || echo "[!] $label FAILED (check SMB share)"
    fi
done < "$MACHINES_FILE"
 
echo "[✔] Collection complete: $(date)"

machines.txt format:

# host|os|label
192.168.1.10|linux|web-server-01
192.168.1.11|linux|db-server-01
192.168.1.20|windows|workstation-win-01
192.168.1.21|windows|finance-pc-01

Part 2: Dashboard Generator

Save as /opt/usb-central/generate_dashboard.py:

python

#!/usr/bin/env python3
"""
USB Audit Central Dashboard Generator
Aggregates CSV reports from all machines → single HTML dashboard
"""
 
import os, csv, json, glob
from datetime import datetime
from collections import defaultdict
 
CENTRAL_DIR  = "/var/usb-central/reports"
DASHBOARD    = "/var/www/html/usb-dashboard/index.html"
os.makedirs(os.path.dirname(DASHBOARD), exist_ok=True)
 
# ── Aggregate all CSVs ──────────────────────────────────────────────
all_events   = []
machine_stats = defaultdict(lambda: {"total": 0, "mounts": 0, "files": 0, "last_seen": "N/A"})
 
for machine_dir in sorted(glob.glob(f"{CENTRAL_DIR}/*")):
    machine = os.path.basename(machine_dir)
    for csv_file in sorted(glob.glob(f"{machine_dir}/*.csv"), reverse=True):
        with open(csv_file, newline="", encoding="utf-8", errors="ignore") as f:
            reader = csv.DictReader(f)
            for row in reader:
                row["Machine"] = machine
                all_events.append(row)
                machine_stats[machine]["total"] += 1
                machine_stats[machine]["last_seen"] = row.get("Timestamp", "N/A")
                evt = row.get("Event", row.get("EventType", ""))
                if "Mount" in evt:   machine_stats[machine]["mounts"] += 1
                if "File"  in evt:   machine_stats[machine]["files"]  += 1
 
all_events.sort(key=lambda x: x.get("Timestamp", ""), reverse=True)
 
# ── Summary stats ───────────────────────────────────────────────────
total_events   = len(all_events)
total_machines = len(machine_stats)
total_mounts   = sum(v["mounts"] for v in machine_stats.values())
total_files    = sum(v["files"]  for v in machine_stats.values())
 
# ── Build JSON for charts ────────────────────────────────────────────
machine_labels  = json.dumps(list(machine_stats.keys()))
machine_counts  = json.dumps([v["total"] for v in machine_stats.values()])
 
# Events per day (last 14 days)
day_counts = defaultdict(int)
for e in all_events:
    ts = e.get("Timestamp", "")
    day = ts[:10] if ts else "unknown"
    day_counts[day] += 1
sorted_days    = sorted(day_counts.keys())[-14:]
day_labels     = json.dumps(sorted_days)
day_values     = json.dumps([day_counts[d] for d in sorted_days])
 
# Event type breakdown
type_counts = defaultdict(int)
for e in all_events:
    type_counts[e.get("Event", e.get("EventType", "Unknown"))] += 1
type_labels = json.dumps(list(type_counts.keys())[:6])
type_values = json.dumps(list(type_counts.values())[:6])
 
# ── Table rows ───────────────────────────────────────────────────────
def event_color(evt):
    if "File"   in evt: return "#fff3cd"
    if "Mount"  in evt: return "#f8d7da"
    return "#d4edda"
 
table_rows = ""
for e in all_events[:500]:  # cap at 500 rows
    evt   = e.get("Event", e.get("EventType", ""))
    color = event_color(evt)
    table_rows += f"""<tr style='background:{color}'>
        <td>{e.get('Timestamp','')[:19]}</td>
        <td><span class='badge'>{e.get('Machine','')}</span></td>
        <td>{e.get('Source','')}</td>
        <td>{evt}</td>
        <td>{e.get('User','')}</td>
        <td class='detail'>{str(e.get('Details',''))[:100]}</td>
    </tr>"""
 
machine_rows = ""
for m, s in machine_stats.items():
    health = "🟢" if s["total"] < 50 else "🟡" if s["total"] < 200 else "🔴"
    machine_rows += f"""<tr>
        <td>{health} {m}</td>
        <td>{s['total']}</td>
        <td>{s['mounts']}</td>
        <td>{s['files']}</td>
        <td>{str(s['last_seen'])[:19]}</td>
    </tr>"""
 
# ── HTML Dashboard ───────────────────────────────────────────────────
html = f"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="300">
<title>USB Audit Dashboard</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js"></script>
<style>
  *    {{ box-sizing: border-box; margin: 0; padding: 0; }}
  body {{ font-family: Segoe UI, Ubuntu, sans-serif; background: #0f1117; color: #e0e0e0; }}
  header {{ background: #1a1d27; padding: 20px 30px; border-bottom: 2px solid #e74c3c;
            display: flex; align-items: center; justify-content: space-between; }}
  header h1 {{ color: #e74c3c; font-size: 1.5em; }}
  header span {{ color: #666; font-size: 0.85em; }}
  .main {{ padding: 25px 30px; }}
  .summary {{ display: grid; grid-template-columns: repeat(4,1fr); gap: 16px; margin-bottom: 25px; }}
  .card {{ background: #1a1d27; border-radius: 10px; padding: 20px 24px;
           border-left: 4px solid #e74c3c; }}
  .card h3 {{ font-size: 2.2em; color: #e74c3c; }}
  .card p  {{ color: #888; font-size: 0.85em; margin-top: 4px; }}
  .charts {{ display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 25px; }}
  .chart-box {{ background: #1a1d27; border-radius: 10px; padding: 20px; }}
  .chart-box h2 {{ font-size: 0.95em; color: #aaa; margin-bottom: 15px; }}
  .section {{ background: #1a1d27; border-radius: 10px; padding: 20px; margin-bottom: 20px; }}
  .section h2 {{ font-size: 1em; color: #e74c3c; margin-bottom: 15px; border-bottom: 1px solid #2c2f3e; padding-bottom: 8px; }}
  table  {{ width: 100%; border-collapse: collapse; font-size: 0.85em; }}
  th     {{ background: #2c2f3e; color: #aaa; padding: 10px 12px; text-align: left; font-weight: 500; }}
  td     {{ padding: 9px 12px; border-bottom: 1px solid #1e2130; }}
  tr:hover td {{ background: #1e2130; }}
  .badge {{ background: #2c3e50; color: #7fb3d3; padding: 2px 8px;
            border-radius: 4px; font-size: 0.8em; }}
  .detail {{ color: #666; font-size: 0.8em; max-width: 300px;
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }}
  input[type=text] {{ background: #2c2f3e; border: 1px solid #444; color: #eee;
                      padding: 7px 12px; border-radius: 6px; width: 280px; margin-bottom: 12px; }}
  @media(max-width:900px) {{
    .summary {{ grid-template-columns: repeat(2,1fr); }}
    .charts  {{ grid-template-columns: 1fr; }}
  }}
</style>
</head>
<body>
<header>
  <h1>🔌 USB Audit Central Dashboard</h1>
  <span>Auto-refreshes every 5 min &nbsp;|&nbsp; Last update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</span>
</header>
 
<div class="main">
 
  <!-- Summary Cards -->
  <div class="summary">
    <div class="card"><h3>{total_events}</h3><p>Total Events</p></div>
    <div class="card"><h3>{total_machines}</h3><p>Machines Monitored</p></div>
    <div class="card"><h3>{total_mounts}</h3><p>USB Mounts</p></div>
    <div class="card"><h3>{total_files}</h3><p>File Access Events</p></div>
  </div>
 
  <!-- Charts -->
  <div class="charts">
    <div class="chart-box">
      <h2>📅 Events Per Day (Last 14 Days)</h2>
      <canvas id="dayChart" height="180"></canvas>
    </div>
    <div class="chart-box">
      <h2>🖥️ Events by Machine</h2>
      <canvas id="machineChart" height="180"></canvas>
    </div>
    <div class="chart-box">
      <h2>📊 Event Type Breakdown</h2>
      <canvas id="typeChart" height="180"></canvas>
    </div>
  </div>
 
  <!-- Machine Summary -->
  <div class="section">
    <h2>🖥️ Machine Summary</h2>
    <table>
      <tr><th>Machine</th><th>Total Events</th><th>USB Mounts</th><th>File Access</th><th>Last Event</th></tr>
      {machine_rows}
    </table>
  </div>
 
  <!-- Event Log -->
  <div class="section">
    <h2>📋 Event Log (Latest 500)</h2>
    <input type="text" id="searchBox" placeholder="🔍 Filter by machine, user, event..." onkeyup="filterTable()">
    <table id="eventTable">
      <tr><th>Timestamp</th><th>Machine</th><th>Source</th><th>Event</th><th>User</th><th>Details</th></tr>
      {table_rows}
    </table>
  </div>
 
</div>
 
<script>
// Charts
const chartOpts = (color) => ({{
  responsive: true,
  plugins: {{ legend: {{ display: false }} }},
  scales: {{
    x: {{ ticks: {{ color:'#666' }}, grid: {{ color:'#1e2130' }} }},
    y: {{ ticks: {{ color:'#666' }}, grid: {{ color:'#1e2130' }} }}
  }}
}});
 
new Chart(document.getElementById('dayChart'), {{
  type: 'bar',
  data: {{ labels: {day_labels}, datasets: [{{
    data: {day_values}, backgroundColor: '#e74c3c', borderRadius: 4
  }}]}},
  options: chartOpts('#e74c3c')
}});
 
new Chart(document.getElementById('machineChart'), {{
  type: 'bar',
  data: {{ labels: {machine_labels}, datasets: [{{
    data: {machine_counts}, backgroundColor: '#3498db', borderRadius: 4
  }}]}},
  options: chartOpts('#3498db')
}});
 
new Chart(document.getElementById('typeChart'), {{
  type: 'doughnut',
  data: {{ labels: {type_labels}, datasets: [{{
    data: {type_values},
    backgroundColor: ['#e74c3c','#f39c12','#2ecc71','#3498db','#9b59b6','#1abc9c']
  }}]}},
  options: {{ responsive: true, plugins: {{ legend: {{ labels: {{ color:'#aaa' }} }} }} }}
}});
 
// Filter table
function filterTable() {{
  const q = document.getElementById('searchBox').value.toLowerCase();
  document.querySelectorAll('#eventTable tr:not(:first-child)').forEach(row => {{
    row.style.display = row.innerText.toLowerCase().includes(q) ? '' : 'none';
  }});
}}
</script>
</body>
</html>"""
 
with open(DASHBOARD, "w") as f:
    f.write(html)
 
print(f"[✔] Dashboard generated: {DASHBOARD}")
print(f"    Machines : {total_machines}")
print(f"    Events   : {total_events}")
print(f"    Generated: {datetime.now()}")

Part 3: Deploy & Automate

bash

# Install dependencies on central server
sudo apt install python3 nginx rsync samba-client -y
 
# Setup nginx to serve dashboard
sudo mkdir -p /var/www/html/usb-dashboard
sudo chmod 755 /var/www/html/usb-dashboard
 
# Make scripts executable
chmod +x /opt/usb-central/collect.sh
chmod +x /opt/usb-central/generate_dashboard.py
 
# Schedule: collect every 30 min, regenerate dashboard immediately after
crontab -e

cron

# Collect reports from all machines every 30 minutes
*/30 * * * * /opt/usb-central/collect.sh >> /var/log/usb-central.log 2>&1
 
# Regenerate dashboard after collection
*/30 * * * * sleep 60 && python3 /opt/usb-central/generate_dashboard.py >> /var/log/usb-central.log 2>&1
 
# Full daily report at 7 AM
0 7 * * * /opt/usb-central/collect.sh && python3 /opt/usb-central/generate_dashboard.py

bash

# Run manually right now
sudo /opt/usb-central/collect.sh
sudo python3 /opt/usb-central/generate_dashboard.py
 
# Access dashboard at:
# http://your-central-server/usb-dashboard/

Part 4: Windows Agent — Auto-Push Reports

Add this to your Windows USB-Audit-Report.ps1 to push CSVs to the central server:

powershell

# Add at end of existing Windows script
$CentralServer = "central-server.company.com"
$SharePath     = "\\$CentralServer\USB_Reports\$env:COMPUTERNAME"
 
# Create machine folder on share if missing
if (-not (Test-Path $SharePath)) {
    New-Item -ItemType Directory -Path $SharePath -Force | Out-Null
}
 
# Push latest CSV to central server
Copy-Item $csvFile -Destination $SharePath -Force
Write-Host "[+] Report pushed to central server: $SharePath" -ForegroundColor Green

Dashboard Preview

┌─────────────────────────────────────────────────────┐
│  🔌 USB Audit Central Dashboard        [auto-refresh]│
├──────────┬──────────────┬─────────────┬─────────────┤
│  1,243   │      12      │    89       │    204      │
│  Events  │  Machines    │  USB Mounts │ File Access │
├──────────┴──────────────┴─────────────┴─────────────┤
│ [Bar: Events/Day] [Bar: By Machine] [Pie: EventType]│
├─────────────────────────────────────────────────────┤
│ Machine Summary Table                               │
🟢 web-server-01  │ 12  │ 3  │ 0  │ 2026-05-30    │
🟡 db-server-01   │ 67  │ 12 │ 5  │ 2026-05-30    │
🔴 finance-pc-01  │ 231 │ 45 │ 31 │ 2026-05-30    │
├─────────────────────────────────────────────────────┤
🔍 [Filter box]   Event Log (latest 500)            │
└─────────────────────────────────────────────────────┘

Summary

Component

Location

Purpose

collect.sh

Central server

Pulls CSVs from all agents

generate_dashboard.py

Central server

Builds live HTML dashboard

machines.txt

Central server

List of all monitored hosts

USB-Audit-Report.ps1

Each Windows host

Generates + pushes CSV

usb_audit_report.sh

Each Linux host

Generates CSV locally

nginx

Central server

Serves the dashboard

cron

Central server

Automates collection + rebuild


Would you like to add real-time email/Slack alerts, user-based risk scoring, or role-based access to the dashboard?

 

 

 

Q: Which feature would you like to add? A: All three

Q: Which platform? A: Both Windows & Linux

10:42 pm

Here's the complete implementation for all three features across both platforms.


Part 1: Real-Time Email & Slack Alerts

Linux — USB Event Watcher + Alerter

Save as /opt/usb-central/usb_alerter.py:

python

#!/usr/bin/env python3
"""
Real-time USB Alert System — Linux
Watches udev events and sends Email + Slack alerts instantly
"""
 
import subprocess, smtplib, json, time, socket, requests
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import datetime
 
# ── Config ──────────────────────────────────────────────────────────
SMTP_SERVER   = "smtp.company.com"
SMTP_PORT     = 587
SMTP_USER     = "usb-alerts@company.com"
SMTP_PASS     = "your_smtp_password"
EMAIL_TO      = ["security@company.com", "itadmin@company.com"]
 
SLACK_WEBHOOK = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
 
HOSTNAME      = socket.gethostname()
LOG_FILE      = "/var/log/usb-realtime-alerts.log"
 
# Risk rules — devices matching these trigger HIGH alerts
HIGH_RISK_VENDORS = ["Unknown", "Generic", "Cheap"]
HIGH_RISK_KEYWORDS = ["sandisk", "kingston", "verbatim", "usb drive"]
 
# ── Helpers ──────────────────────────────────────────────────────────
def log(msg):
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"[{ts}] {msg}"
    print(line)
    with open(LOG_FILE, "a") as f:
        f.write(line + "\n")
 
def get_risk_level(vendor, model, user):
    """Simple risk scoring for alerts"""
    score = 0
    vendor_l = (vendor or "").lower()
    model_l  = (model  or "").lower()
    if any(k in vendor_l for k in HIGH_RISK_KEYWORDS): score += 2
    if any(k in model_l  for k in HIGH_RISK_KEYWORDS): score += 2
    if vendor_l in ["unknown", ""]:                    score += 3
    if user in ["root", "Administrator"]:              score += 2
    if score >= 5: return "🔴 CRITICAL"
    if score >= 3: return "🟡 HIGH"
    return "🟢 LOW"
 
def send_email(subject, html_body):
    try:
        msg = MIMEMultipart("alternative")
        msg["Subject"] = subject
        msg["From"]    = SMTP_USER
        msg["To"]      = ", ".join(EMAIL_TO)
        msg.attach(MIMEText(html_body, "html"))
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as s:
            s.starttls()
            s.login(SMTP_USER, SMTP_PASS)
            s.sendmail(SMTP_USER, EMAIL_TO, msg.as_string())
        log(f"[EMAIL] Sent: {subject}")
    except Exception as e:
        log(f"[EMAIL ERROR] {e}")
 
def send_slack(message, color, fields):
    try:
        payload = {
            "attachments": [{
                "color":  color,
                "title":  "🔌 USB Alert",
                "text":   message,
                "fields": fields,
                "footer": f"USB Monitor | {HOSTNAME}",
                "ts":     int(time.time())
            }]
        }
        requests.post(SLACK_WEBHOOK, json=payload, timeout=5)
        log("[SLACK] Alert sent")
    except Exception as e:
        log(f"[SLACK ERROR] {e}")
 
def alert(action, vendor, model, serial, user="unknown"):
    ts       = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    risk     = get_risk_level(vendor, model, user)
    color    = {"🔴 CRITICAL": "danger", "🟡 HIGH": "warning"}.get(risk, "good")
    emoji    = "🔴" if "CRITICAL" in risk else "🟡" if "HIGH" in risk else "🟢"
 
    subject  = f"{emoji} USB {action} — {HOSTNAME} — {risk}"
 
    # ── HTML Email ──
    html = f"""
    <div style="font-family:sans-serif;max-width:600px;margin:auto">
      <div style="background:#c0392b;color:white;padding:15px 20px;border-radius:8px 8px 0 0">
        <h2 style="margin:0">🔌 USB {action} Detected</h2>
      </div>
      <div style="background:#f9f9f9;padding:20px;border:1px solid #ddd">
        <table style="width:100%;border-collapse:collapse">
          <tr><td style="padding:8px;color:#666;width:140px">Risk Level</td>
              <td style="padding:8px;font-weight:bold">{risk}</td></tr>
          <tr style="background:#fff"><td style="padding:8px;color:#666">Host</td>
              <td style="padding:8px">{HOSTNAME}</td></tr>
          <tr><td style="padding:8px;color:#666">Time</td>
              <td style="padding:8px">{ts}</td></tr>
          <tr style="background:#fff"><td style="padding:8px;color:#666">Vendor</td>
              <td style="padding:8px">{vendor}</td></tr>
          <tr><td style="padding:8px;color:#666">Model</td>
              <td style="padding:8px">{model}</td></tr>
          <tr style="background:#fff"><td style="padding:8px;color:#666">Serial</td>
              <td style="padding:8px">{serial}</td></tr>
          <tr><td style="padding:8px;color:#666">User</td>
              <td style="padding:8px">{user}</td></tr>
          <tr style="background:#fff"><td style="padding:8px;color:#666">Action</td>
              <td style="padding:8px">{action}</td></tr>
        </table>
      </div>
      <div style="background:#eee;padding:10px 20px;font-size:0.8em;color:#999;
                  border-radius:0 0 8px 8px">
        USB Monitor v1.0 | {HOSTNAME} | Auto-generated alert
      </div>
    </div>"""
 
    # ── Slack Fields ──
    fields = [
        {"title": "Host",    "value": HOSTNAME, "short": True},
        {"title": "Risk",    "value": risk,     "short": True},
        {"title": "Vendor",  "value": vendor,   "short": True},
        {"title": "Model",   "value": model,    "short": True},
        {"title": "Serial",  "value": serial,   "short": True},
        {"title": "User",    "value": user,     "short": True},
    ]
 
    send_email(subject, html)
    send_slack(f"USB *{action}* on `{HOSTNAME}` by `{user}`", color, fields)
    log(f"[ALERT] {action} | {vendor} {model} | {serial} | {user} | {risk}")
 
# ── udev Monitor Loop ────────────────────────────────────────────────
def get_current_user():
    try:
        result = subprocess.run(["who"], capture_output=True, text=True)
        users = [line.split()[0] for line in result.stdout.strip().splitlines() if line]
        return users[0] if users else "unknown"
    except:
        return "unknown"
 
def watch_usb():
    log("[*] USB Real-time Monitor started")
    proc = subprocess.Popen(
        ["udevadm", "monitor", "--subsystem-match=usb", "--property"],
        stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True
    )
 
    props = {}
    for line in proc.stdout:
        line = line.strip()
        if not line:
            # End of event block — process it
            action = props.get("ACTION", "")
            if action in ("add", "remove"):
                vendor  = props.get("ID_VENDOR",       "Unknown")
                model   = props.get("ID_MODEL",        "Unknown")
                serial  = props.get("ID_SERIAL_SHORT", "N/A")
                devtype = props.get("DEVTYPE",         "")
                if devtype == "usb_device":
                    user = get_current_user()
                    alert(
                        action="Connected" if action == "add" else "Removed",
                        vendor=vendor, model=model,
                        serial=serial, user=user
                    )
            props = {}
        elif "=" in line:
            k, _, v = line.partition("=")
            props[k.strip()] = v.strip()
 
if __name__ == "__main__":
    watch_usb()

Deploy as a systemd service:

bash

sudo tee /etc/systemd/system/usb-alerter.service <<EOF
[Unit]
Description=USB Real-Time Alert Monitor
After=network.target
 
[Service]
ExecStart=/usr/bin/python3 /opt/usb-central/usb_alerter.py
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
 
[Install]
WantedBy=multi-user.target
EOF
 
sudo systemctl daemon-reload
sudo systemctl enable --now usb-alerter
sudo systemctl status usb-alerter

Windows — PowerShell USB Watcher + Alerter

Save as USB-RealTime-Alert.ps1:

powershell

# ============================================
# USB Real-Time Alert — Windows
# Monitors WMI for USB events → Email + Slack
# ============================================
 
param(
    [string]$SMTPServer   = "smtp.company.com",
    [string]$EmailFrom    = "usb-alerts@company.com",
    [string[]]$EmailTo    = @("security@company.com","itadmin@company.com"),
    [string]$SlackWebhook = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
    [string]$LogFile      = "C:\Logs\USB-Alerts.log"
)
 
if (-not (Test-Path (Split-Path $LogFile))) {
    New-Item -ItemType Directory -Path (Split-Path $LogFile) -Force | Out-Null
}
 
function Write-Log($msg) {
    $ts = Get-Date -Format "yyyy-MM-dd HH:MM:ss"
    "$ts $msg" | Tee-Object -FilePath $LogFile -Append
}
 
function Get-RiskLevel($deviceName, $user) {
    $score = 0
    $dn = $deviceName.ToLower()
    if ($dn -match "unknown|generic")      { $score += 3 }
    if ($dn -match "sandisk|kingston|usb") { $score += 2 }
    if ($user -match "admin|administrator"){ $score += 2 }
    if ($score -ge 5) { return "CRITICAL" }
    if ($score -ge 3) { return "HIGH" }
    return "LOW"
}
 
function Send-SlackAlert($action, $device, $user, $risk) {
    $color = switch ($risk) {
        "CRITICAL" { "danger"  }
        "HIGH"     { "warning" }
        default    { "good"    }
    }
    $body = @{
        attachments = @(@{
            color   = $color
            title   = "USB $action Detected"
            text    = "USB *$action* on ``$env:COMPUTERNAME`` by ``$user``"
            fields  = @(
                @{ title="Host";   value=$env:COMPUTERNAME; short=$true },
                @{ title="Risk";   value=$risk;             short=$true },
                @{ title="Device"; value=$device;           short=$true },
                @{ title="User";   value=$user;             short=$true },
                @{ title="Time";   value=(Get-Date -Format "yyyy-MM-dd HH:mm:ss"); short=$true }
            )
            footer  = "USB Monitor | $env:COMPUTERNAME"
        })
    } | ConvertTo-Json -Depth 5
 
    try {
        Invoke-RestMethod -Uri $SlackWebhook -Method POST -Body $body `
            -ContentType "application/json" | Out-Null
        Write-Log "[SLACK] Alert sent for $device"
    } catch {
        Write-Log "[SLACK ERROR] $_"
    }
}
 
function Send-EmailAlert($action, $device, $user, $risk) {
    $emoji   = if ($risk -eq "CRITICAL") {"🔴"} elseif ($risk -eq "HIGH") {"🟡"} else {"🟢"}
    $subject = "$emoji USB $action — $env:COMPUTERNAME — $risk"
    $html = @"
<div style='font-family:sans-serif;max-width:600px;margin:auto'>
  <div style='background:#c0392b;color:white;padding:15px 20px;border-radius:8px 8px 0 0'>
    <h2 style='margin:0'>🔌 USB $action Detected</h2>
  </div>
  <div style='background:#f9f9f9;padding:20px;border:1px solid #ddd'>
    <table style='width:100%;border-collapse:collapse'>
      <tr><td style='padding:8px;color:#666;width:120px'>Risk</td>
          <td style='padding:8px;font-weight:bold'>$risk</td></tr>
      <tr style='background:#fff'><td style='padding:8px;color:#666'>Host</td>
          <td style='padding:8px'>$env:COMPUTERNAME</td></tr>
      <tr><td style='padding:8px;color:#666'>Time</td>
          <td style='padding:8px'>$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')</td></tr>
      <tr style='background:#fff'><td style='padding:8px;color:#666'>Device</td>
          <td style='padding:8px'>$device</td></tr>
      <tr><td style='padding:8px;color:#666'>User</td>
          <td style='padding:8px'>$user</td></tr>
    </table>
  </div>
</div>
"@
    try {
        Send-MailMessage -To $EmailTo -From $EmailFrom -Subject $subject `
            -Body $html -BodyAsHtml -SmtpServer $SMTPServer
        Write-Log "[EMAIL] Sent: $subject"
    } catch {
        Write-Log "[EMAIL ERROR] $_"
    }
}
 
# ── WMI Event Watcher ──────────────────────────────────────────────
Write-Log "[*] USB Real-Time Monitor started on $env:COMPUTERNAME"
 
$query = "SELECT * FROM __InstanceCreationEvent WITHIN 2
          WHERE TargetInstance ISA 'Win32_USBHub'"
 
Register-WmiEvent -Query $query -SourceIdentifier "USBConnected" -Action {
    $device = $event.SourceEventArgs.NewEvent.TargetInstance.Description
    $user   = (Get-WmiObject Win32_ComputerSystem).UserName
    $risk   = Get-RiskLevel -deviceName $device -user $user
 
    Write-Log "[ALERT] USB Connected: $device | User: $user | Risk: $risk"
    Send-EmailAlert -action "Connected" -device $device -user $user -risk $risk
    Send-SlackAlert -action "Connected" -device $device -user $user -risk $risk
}
 
Write-Log "[*] Watching for USB events... Press Ctrl+C to stop"
while ($true) { Start-Sleep -Seconds 5 }

Run as a Windows Service:

powershell

# Install NSSM (Non-Sucking Service Manager)
choco install nssm -y
 
# Create service
nssm install USBAlertMonitor powershell.exe "-ExecutionPolicy Bypass -File C:\Scripts\USB-RealTime-Alert.ps1"
nssm set USBAlertMonitor Start SERVICE_AUTO_START
nssm start USBAlertMonitor


Part 2: User-Based Risk Scoring

Save as /opt/usb-central/risk_engine.py:

python

#!/usr/bin/env python3
"""
USB User Risk Scoring Engine
Tracks per-user USB behavior and generates risk scores
"""
 
import json, os, csv, glob
from datetime import datetime, timedelta
from collections import defaultdict
 
CENTRAL_DIR  = "/var/usb-central/reports"
RISK_DB      = "/var/usb-central/risk_scores.json"
RISK_REPORT  = "/var/www/html/usb-dashboard/risk.html"
 
# ── Risk Rules ───────────────────────────────────────────────────────
RULES = {
    "usb_connect_per_day"    : {"threshold": 5,   "points": 10, "label": "Excessive USB connections/day"},
    "file_access_per_day"    : {"threshold": 20,  "points": 15, "label": "Excessive file access/day"},
    "after_hours_activity"   : {"threshold": 0,   "points": 20, "label": "USB activity outside business hours"},
    "multiple_machines"      : {"threshold": 2,   "points": 25, "label": "USB used on multiple machines same day"},
    "unknown_device"         : {"threshold": 0,   "points": 30, "label": "Unknown/unregistered USB device"},
    "repeated_connect_remove": {"threshold": 10,  "points": 15, "label": "Rapid connect/remove cycles"},
}
 
RISK_LEVELS = {
    (0,  29):  ("LOW",      "#2ecc71"),
    (30, 59):  ("MEDIUM",   "#f39c12"),
    (60, 89):  ("HIGH",     "#e67e22"),
    (90, 999): ("CRITICAL", "#e74c3c"),
}
 
def get_risk_level(score):
    for (lo, hi), (label, color) in RISK_LEVELS.items():
        if lo <= score <= hi:
            return label, color
    return "UNKNOWN", "#999"
 
# ── Load & Analyze Events ────────────────────────────────────────────
def load_events(days=30):
    events = []
    since  = datetime.now() - timedelta(days=days)
    for csv_file in glob.glob(f"{CENTRAL_DIR}/**/*.csv", recursive=True):
        machine = os.path.basename(os.path.dirname(csv_file))
        with open(csv_file, newline="", encoding="utf-8", errors="ignore") as f:
            for row in csv.DictReader(f):
                try:
                    ts = datetime.strptime(row.get("Timestamp","")[:19], "%Y-%m-%d %H:%M:%S")
                    if ts >= since:
                        row["machine"] = machine
                        row["_ts"]     = ts
                        events.append(row)
                except:
                    pass
    return events
 
def score_users(events):
    user_data = defaultdict(lambda: {
        "events": [], "machines": set(), "devices": set(),
        "score": 0, "violations": [], "last_seen": None
    })
 
    for e in events:
        user = e.get("User", e.get("user", "unknown")) or "unknown"
        user_data[user]["events"].append(e)
        user_data[user]["machines"].add(e.get("machine",""))
        user_data[user]["devices"].add(e.get("DeviceID", e.get("Details",""))[:30])
        user_data[user]["last_seen"] = e.get("Timestamp","")
 
    scores = {}
    for user, data in user_data.items():
        score = 0
        violations = []
        evts  = data["events"]
 
        # Group by day
        by_day = defaultdict(list)
        for e in evts:
            day = str(e["_ts"].date())
            by_day[day].append(e)
 
        connects   = sum(1 for e in evts if "Connect" in e.get("Event", e.get("EventType","")))
        file_accesses = sum(1 for e in evts if "File" in e.get("Event", e.get("EventType","")))
 
        # Rule: Excessive connections/day
        for day, day_evts in by_day.items():
            day_connects = sum(1 for e in day_evts if "Connect" in e.get("Event",""))
            if day_connects > RULES["usb_connect_per_day"]["threshold"]:
                score += RULES["usb_connect_per_day"]["points"]
                violations.append(f"Excessive connections on {day} ({day_connects})")
 
        # Rule: Excessive file access/day
        for day, day_evts in by_day.items():
            day_files = sum(1 for e in day_evts if "File" in e.get("Event",""))
            if day_files > RULES["file_access_per_day"]["threshold"]:
                score += RULES["file_access_per_day"]["points"]
                violations.append(f"Excessive file access on {day} ({day_files})")
 
        # Rule: After-hours activity (before 7am or after 8pm)
        after_hours = [e for e in evts if e["_ts"].hour < 7 or e["_ts"].hour >= 20]
        if after_hours:
            score += RULES["after_hours_activity"]["points"] * len(after_hours)
            violations.append(f"After-hours USB activity ({len(after_hours)} events)")
 
        # Rule: Multiple machines same day
        for day, day_evts in by_day.items():
            machines_today = set(e.get("machine","") for e in day_evts)
            if len(machines_today) > RULES["multiple_machines"]["threshold"]:
                score += RULES["multiple_machines"]["points"]
                violations.append(f"USB on {len(machines_today)} machines on {day}")
 
        level, color = get_risk_level(min(score, 999))
        scores[user] = {
            "score":      min(score, 999),
            "level":      level,
            "color":      color,
            "violations": violations[:10],
            "total_events": len(evts),
            "connects":   connects,
            "file_access":file_accesses,
            "machines":   list(data["machines"]),
            "last_seen":  data["last_seen"],
        }
 
    return dict(sorted(scores.items(), key=lambda x: x[1]["score"], reverse=True))
 
# ── Generate Risk HTML Page ──────────────────────────────────────────
def generate_risk_page(scores):
    rows = ""
    for user, s in scores.items():
        violations_html = "".join(f"<li>{v}</li>" for v in s["violations"]) or "<li>None</li>"
        bar_w = min(s["score"], 100)
        rows += f"""
        <tr>
          <td><strong>{user}</strong></td>
          <td>
            <div style='background:#2c2f3e;border-radius:4px;height:12px;width:150px'>
              <div style='background:{s["color"]};width:{bar_w}%;height:12px;border-radius:4px'></div>
            </div>
            <span style='font-size:0.8em;color:#aaa'>{s["score"]} pts</span>
          </td>
          <td><span style='background:{s["color"]};color:white;padding:3px 10px;
              border-radius:12px;font-size:0.8em'>{s["level"]}</span></td>
          <td>{s["total_events"]}</td>
          <td>{s["connects"]}</td>
          <td>{s["file_access"]}</td>
          <td>{", ".join(s["machines"][:3])}</td>
          <td>{str(s["last_seen"])[:16]}</td>
          <td><ul style='margin:0;padding-left:16px;font-size:0.8em;color:#aaa'>{violations_html}</ul></td>
        </tr>"""
 
    critical = sum(1 for s in scores.values() if s["level"] == "CRITICAL")
    high     = sum(1 for s in scores.values() if s["level"] == "HIGH")
 
    html = f"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="300">
<title>USB Risk Scores</title>
<style>
  body  {{ font-family:Segoe UI,Ubuntu,sans-serif; background:#0f1117; color:#e0e0e0; padding:30px; }}
  h1    {{ color:#e74c3c; }}
  .summary {{ display:flex; gap:16px; margin:20px 0; flex-wrap:wrap; }}
  .card {{ background:#1a1d27; border-radius:10px; padding:18px 24px;
           border-left:4px solid #e74c3c; min-width:130px; text-align:center; }}
  .card h3 {{ font-size:2em; color:#e74c3c; margin:0; }}
  .card p  {{ color:#888; font-size:0.85em; margin:4px 0 0; }}
  table  {{ width:100%; border-collapse:collapse; background:#1a1d27;
            border-radius:10px; overflow:hidden; margin-top:20px; }}
  th {{ background:#2c2f3e; color:#aaa; padding:11px 13px; text-align:left; font-size:0.85em; }}
  td {{ padding:10px 13px; border-bottom:1px solid #1e2130; font-size:0.85em; vertical-align:top; }}
  tr:hover td {{ background:#1e2130; }}
</style>
</head>
<body>
<h1>👤 USB User Risk Scores</h1>
<p style='color:#666'>Last 30 days | Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<div class="summary">
  <div class="card"><h3>{len(scores)}</h3><p>Users Tracked</p></div>
  <div class="card"><h3>{critical}</h3><p>Critical Risk</p></div>
  <div class="card"><h3>{high}</h3><p>High Risk</p></div>
</div>
<table>
  <tr>
    <th>User</th><th>Risk Score</th><th>Level</th><th>Events</th>
    <th>Connects</th><th>File Access</th><th>Machines</th>
    <th>Last Seen</th><th>Violations</th>
  </tr>
  {rows}
</table>
</body>
</html>"""
 
    with open(RISK_REPORT, "w") as f:
        f.write(html)
 
    with open(RISK_DB, "w") as f:
        json.dump({u: {k: v for k, v in s.items() if k != "_ts"}
                   for u, s in scores.items()}, f, indent=2, default=str)
 
    print(f"[✔] Risk report: {RISK_REPORT}")
    print(f"[✔] Risk DB    : {RISK_DB}")
    return scores
 
if __name__ == "__main__":
    print("[*] Loading events...")
    events = load_events(days=30)
    print(f"[*] Scoring {len(set(e.get('User','?') for e in events))} users...")
    scores = score_users(events)
    generate_risk_page(scores)
    print(f"\nTop Risk Users:")
    for user, s in list(scores.items())[:5]:
        print(f"  {s['level']:8} {s['score']:4} pts  {user}")


Part 3: Role-Based Dashboard Access (nginx + htpasswd)

Setup RBAC with nginx

bash

# Install tools
sudo apt install nginx apache2-utils -y
 
# Create user DB
sudo mkdir -p /etc/nginx/usb-auth
 
# Add roles
# Admin — sees everything
sudo htpasswd -c /etc/nginx/usb-auth/admins.htpasswd admin
sudo htpasswd    /etc/nginx/usb-auth/admins.htpasswd soc_analyst
 
# Auditors — read-only, no risk page
sudo htpasswd -c /etc/nginx/usb-auth/auditors.htpasswd auditor1
sudo htpasswd    /etc/nginx/usb-auth/auditors.htpasswd auditor2
 
# Managers — summary only
sudo htpasswd -c /etc/nginx/usb-auth/managers.htpasswd manager1

nginx config — save as /etc/nginx/sites-available/usb-dashboard:

nginx

server {
    listen 443 ssl;
    server_name usb-monitor.company.com;
 
    ssl_certificate     /etc/ssl/certs/usb-monitor.crt;
    ssl_certificate_key /etc/ssl/private/usb-monitor.key;
 
    root /var/www/html/usb-dashboard;
    index index.html;
 
    # ── Admin: full access ──────────────────────────
    location / {
        auth_basic           "USB Dashboard — Admin";
        auth_basic_user_file /etc/nginx/usb-auth/admins.htpasswd;
        try_files $uri $uri/ =404;
    }
 
    # ── Auditors: dashboard + reports, no risk page ─
    location ~ ^/(index\.html|reports/)$ {
        auth_basic           "USB Dashboard — Auditor";
        auth_basic_user_file /etc/nginx/usb-auth/auditors.htpasswd;
        try_files $uri $uri/ =404;
    }
 
    # ── Managers: summary only ──────────────────────
    location /summary.html {
        auth_basic           "USB Dashboard — Manager";
        auth_basic_user_file /etc/nginx/usb-auth/managers.htpasswd;
        try_files $uri =404;
    }
 
    # ── Risk page: admins only ──────────────────────
    location /risk.html {
        auth_basic           "USB Risk Report — Admin Only";
        auth_basic_user_file /etc/nginx/usb-auth/admins.htpasswd;
        try_files $uri =404;
    }
 
    # Block direct CSV access for non-admins
    location ~* \.csv$ {
        auth_basic           "USB Reports — Admin Only";
        auth_basic_user_file /etc/nginx/usb-auth/admins.htpasswd;
        try_files $uri =404;
    }
 
    # Security headers
    add_header X-Frame-Options        SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection       "1; mode=block";
    add_header Strict-Transport-Security "max-age=31536000";
}
 
# Redirect HTTP → HTTPS
server {
    listen 80;
    server_name usb-monitor.company.com;
    return 301 https://$host$request_uri;
}

bash

# Enable site
sudo ln -s /etc/nginx/sites-available/usb-dashboard /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Full Automation — Single Crontab

cron

# Collect agent reports every 30 min
*/30 * * * * /opt/usb-central/collect.sh
 
# Regenerate main dashboard
*/30 * * * * sleep 90 && python3 /opt/usb-central/generate_dashboard.py
 
# Regenerate risk scores every hour
0 * * * * python3 /opt/usb-central/risk_engine.py
 
# Daily full run at 7 AM + email summary
0 7 * * * /opt/usb-central/collect.sh && \
          python3 /opt/usb-central/generate_dashboard.py && \
          python3 /opt/usb-central/risk_engine.py

Complete System Overview

┌─────────────────────────────────────────────────────────────┐
│                  USB Security Platform                       │
├────────────┬────────────────────┬───────────────────────────┤
│  Agents    │   Central Server   │      Dashboard            │
│            │                    │                           │
│ Windows    │  collect.sh        │  /index.html  ← Admins   │
│  WMI Watch │  (every 30 min)    │  /risk.html   ← Admins   │
│  → CSV     │        ↓           │  /reports/    ← Auditors │
│  → Push    │  generate_         │  /summary.html← Managers │
│            │  dashboard.py      │                           │
│ Linux      │        ↓           │  nginx RBAC              │
│  udevadm   │  risk_engine.py    │  + SSL/TLS               │
│  → CSV     │        ↓           │  + Auth headers          │
│  → rsync   │  usb_alerter.py    │                           │
│            │  (real-time)       │  Slack  ← Instant alerts │
│            │        ↓           │  Email  ← Instant alerts │
└────────────┴────────────────────┴───────────────────────────┘

Role Access Matrix

Page 

Admin

SOC Analyst

Auditor

Manager

/index.html — Full dashboard

/risk.html — Risk scores

/reports/ — Raw CSVs

/summary.html — Summary only

Real-time alerts (Slack/Email)


Comments

No Comments have been Posted.

Post Comment

Please Login to Post a Comment.

Ratings

Rating is available to Members only.

Please login or register to vote.

No Ratings have been Posted.
Render time: 1.71 seconds
29,313,596 unique visits