A lightweight Flask dashboard for discovering connected USB devices, flagging vendor IDs on a local watchlist, recording device activity, and controlling USB storage access on Windows and Linux.
Warning
This project can disable USB devices or unload the Linux usb_storage kernel module when it starts. Review the security and safety notes before running it on a primary machine.
USBGuard provides a browser-based view of USB devices connected to the host computer. The application:
- Discovers USB devices with PowerShell on Windows and
lsusbon Linux. - Extracts vendor IDs from the operating-system output.
- Compares those IDs with a configurable watchlist.
- Records device scans and watchlist matches in
usb.log. - Encrypts the activity log with AES-256 through GnuPG after every ten logged events.
- Provides an administrator unlock page that attempts to re-enable USB access.
This repository is a learning and prototype project. It is not related to, or a replacement for, the upstream Linux USBGuard daemon.
Screenshot coming soon. Recommended path: docs/images/usbguard-dashboard.png.
| Platform | Discovery method | Control method |
|---|---|---|
| Windows | Get-WmiObject and Get-PnpDevice |
Enable-PnpDevice / Disable-PnpDevice |
| Linux | lsusb |
Load or unload the usb_storage kernel module with modprobe |
The Windows scanner filters common internal components such as root hubs, host controllers, composite devices, and USB input devices. The Linux scanner reads the standard lsusb output.
The app extracts the four-character USB vendor ID and compares it with MALICIOUS_VENDORS in app.py. Matching devices appear in the dashboard response and activity log.
Important
A vendor ID identifies a manufacturer, not whether an individual device is malicious. A match is a signal for manual review, not proof of compromise.
Opening the dashboard records:
- The scan timestamp.
- The raw operating-system device output.
- Any matching vendor IDs and device descriptions.
After ten events, encrypt.py uses python-gnupg to create usb.log.gpg with AES-256 symmetric encryption. The plaintext log is removed only when encryption succeeds.
The /unlock page checks the submitted password and attempts to enable USB access:
- Windows enables the selected PnP devices.
- Linux loads the
usb_storagemodule.
Browser request
|
v
Flask route in app.py
|
+--> Query connected devices
| |
| +--> Windows PowerShell
| `--> Linux lsusb
|
+--> Extract and compare vendor IDs
|
+--> Append the scan to usb.log
| |
| `--> Encrypt every 10 events
|
`--> Render the dashboard
USBGuard/
├── app.py # Flask app, USB discovery, controls, routes, and logging
├── encrypt.py # GnuPG setup and symmetric log encryption
├── requirements.txt # Pinned Python dependencies
├── LICENSE # MIT license
├── usb.log # Plaintext runtime activity log
└── usb.log.gpg # Encrypted log output
Flask also expects the following templates at runtime:
templates/
├── index.html # Main device dashboard
└── unlock.html # Administrator password form
If these template files are not present in your checkout, Flask will raise TemplateNotFound when you open the corresponding page.
- Python 3.9 or newer.
- GnuPG available as
gpg. - Permission to inspect and manage USB devices.
- Windows PowerShell.
- The
Get-PnpDevice,Enable-PnpDevice, andDisable-PnpDevicecommands. - An elevated terminal for device state changes.
usbutilsforlsusb.kmodformodprobe.sudoaccess for loading and unloadingusb_storage.
On Ubuntu or Debian:
sudo apt update
sudo apt install python3 python3-venv gnupg usbutils kmod-
Clone the repository:
git clone https://github.com/Pickachu19/USBGuard.git cd USBGuard -
Create and activate a virtual environment:
Linux or macOS
python3 -m venv .venv source .venv/bin/activateWindows PowerShell
py -m venv .venv .\.venv\Scripts\Activate.ps1 -
Install the dependencies:
python -m pip install --upgrade pip python -m pip install -r requirements.txt
The current prototype keeps its settings near the top of app.py.
Replace the default before running the app:
ADMIN_PASSWORD = "use-a-long-unique-password"The repository default is usbdetector. Do not expose the server to another machine while using that password.
Add or remove lowercase, four-character USB vendor IDs:
MALICIOUS_VENDORS = {
"0c45",
"1d34",
# Add reviewed vendor IDs here.
}encrypt.py currently uses a hardcoded passphrase. Replace it with a strong, unique value before storing real device data. For a production-quality design, load it from a protected environment variable or secret store instead of committing it to source control.
Caution
Starting app.py immediately calls disable_usb_ports(). On Linux, that runs sudo modprobe -r usb_storage. On Windows, it attempts to disable filtered USB PnP devices. Disconnect unneeded storage, save open work, and use a test machine.
Start the Flask development server:
python app.pyThen open:
http://127.0.0.1:5000
The development server binds to 0.0.0.0:5000 and runs with debug mode enabled in the current source. Use host firewall rules and a trusted network.
- Open the dashboard to scan connected USB devices.
- Review any entries listed as malicious or suspicious.
- Check
usb.logfor timestamped scan details. - Visit
/unlockand enter the administrator password when USB access should be restored. - Refresh device data through
/update_devices.
| Route | Methods | Purpose |
|---|---|---|
/ |
GET | Scan devices, log the event, and render the dashboard |
/unlock |
GET, POST | Display the password form and attempt to enable USB access |
/update_devices |
GET | Return the latest scan as JSON |
Example response from /update_devices:
{
"devices": "raw operating-system output",
"malicious": [],
"timestamp": "2026-07-24 12:00:00",
"device_count": 0,
"malicious_count": 0
}Before using USBGuard outside a lab, account for these limitations:
- The Flask development server and debug mode are not suitable for production.
- The app binds to every network interface by default.
- The administrator password and encryption passphrase are hardcoded.
- Password verification has no rate limiting, session management, or CSRF protection.
- Vendor-ID matching can produce both false positives and false negatives.
- USB device control requires elevated operating-system privileges.
- Disabling a driver can interrupt mounted storage and cause data loss.
- Runtime logs may contain device names and hardware identifiers.
- The repository currently contains generated log files; review their contents before sharing a fork.
For real deployment, move secrets to environment variables, disable Flask debug mode, bind to localhost, add authentication protections, avoid running the web process as an administrator, and delegate privileged operations to a narrowly scoped system service.
The Flask templates are missing. Add templates/index.html and templates/unlock.html using the structure shown above.
Install usbutils:
sudo apt install usbutilsOpen an elevated PowerShell window and verify:
Get-PnpDevice | Where-Object { $_.Class -eq 'USB' }The application intentionally filters several internal and input-device categories.
Run from an account with the required sudo permission. Avoid granting unrestricted passwordless administrator access to the Flask process.
Confirm that GnuPG is installed and visible:
gpg --versionAlso verify that the process can read usb.log and write to the project directory.
The current endpoint estimates the count from newline formatting in the raw command output. Windows PowerShell versions and device types may format results differently.
There is no automated test suite in the current repository. Before submitting changes:
- Run the app on the platform you changed.
- Verify
/,/unlock, and/update_devices. - Test with no external devices and with multiple devices.
- Confirm that log encryption succeeds without deleting plaintext on failure.
- Check that internal USB controllers and input devices are not disabled.
Issues and pull requests are welcome. Keep changes focused, explain platform-specific behavior, and include manual verification steps for Windows and Linux when applicable.
USBGuard is available under the MIT License.