Detect and automatically respond to Metasploit SSH login scanner fingerprinting activity on Linux hosts.
When Metasploit's auxiliary/scanner/ssh/ssh_login module successfully authenticates via SSH, it immediately executes a deterministic command sequence to identify the target platform:
id # Always — check if we're on a POSIX system
uname -a # If "id=" found — get kernel/OS info
grep unifi.version /tmp/system.cfg # Always on Linux — probe for Ubiquiti device
This behavior is defined in lib/metasploit/framework/ssh/platform.rb (get_platform_info method).
The grep unifi.version /tmp/system.cfg command is executed on every Linux target regardless of whether it is a Ubiquiti device. On non-Ubiquiti hosts (the vast majority), this command has zero legitimate use — making it an extremely high-fidelity indicator of Metasploit post-auth fingerprinting.
SSH brute-force attacks are inherently noisy and well-defended — fail2ban, rate limiting, and account lockout policies handle them effectively. But the fingerprinting occurs after successful authentication, meaning the attacker already has valid credentials (obtained through credential stuffing, phishing, password reuse, or a prior breach). At that point, traditional brute-force defenses have already been bypassed.
The fingerprint sequence is the earliest observable indicator that a compromised credential is being used by Metasploit — and it occurs before the attacker can pivot, escalate privileges, or exfiltrate data. Detecting it enables an automated response (session kill + account lock) within ~1 second of login, turning a successful compromise into a failed one. Without fingerprint detection, the attacker's session proceeds silently with no alert beyond a normal successful login in auth.log.
The SSH post-login platform fingerprinting behavior has evolved over several Metasploit releases:
| Version | Date | Change |
|---|---|---|
| ~4.x (2011) | 2011-10-13 | Basic platform detection added to ssh_login.rb — runs id + uname -a and matches output against OS patterns (Linux, Darwin, Windows, etc.). Commit a0c34d1c. |
| 4.16.46 | 2018-03-17 | Platform fingerprinting code refactored from inline ssh_login.rb into shared library lib/metasploit/framework/login_scanner/ssh.rb. Commit dcb514e5. |
| 5.0.11 | 2019-03-12 | grep unifi.version /tmp/system.cfg introduced. Ubiquiti UniFi device detection added to the SSH login scanner's gather_proof method. This is the key detectable indicator. Commit a0b1ca17. |
| 5.0.36 | 2019-06-27 | GatherProof advanced option added to ssh_login.rb, allowing users to disable the fingerprinting with set GatherProof false. Enabled by default. Commit dc81adb4. Note: This only controlled the login scanner's gather_proof call. From 6.1.21 onward, GatherProof false no longer prevents the fingerprint — see the 6.1.21 entry. |
| 6.1.21 | 2021-10-22 | Fingerprinting code consolidated into a standalone lib/metasploit/framework/ssh/platform.rb module. A second, independent call was added in SshCommandShellBind#bootstrap to auto-detect the session platform (needed for Unix vs. Windows shell escaping). This call runs unconditionally during session setup, regardless of GatherProof. With both defaults (GatherProof true + CreateSession true), the fingerprint sequence now executes twice. Commit 726c5f26. |
| 6.4.124+ | Current | No changes to the fingerprint sequence. grep unifi.version /tmp/system.cfg is still executed on every Linux target. |
Summary: All Metasploit versions from 5.0.11 (March 2019) through the current release (6.4.124+) execute grep unifi.version /tmp/system.cfg on every Linux host after a successful SSH login. Since 6.1.21 (October 2021), the fingerprint runs during session bootstrap regardless of the GatherProof setting — the only way to suppress it is set CreateSession false (credential-validation-only mode). Versions prior to 5.0.11 still run id and uname -a but lack the distinctive Ubiquiti probe.
- auditd rules monitor
execvesyscalls forid,uname, andgrep(taggedmsf_ssh_fp) - A lightweight Python daemon tails the audit log looking for the Metasploit fingerprint pattern
- On detection, the daemon:
- Kills the SSH session (finds and terminates the
sshdprocess) - Locks the compromised user account (via
passwd -l) - Optionally blocks the attacker's IP (via
iptables) - Logs the incident to syslog and stderr
- Kills the SSH session (finds and terminates the
Detection latency: 200-500ms from the trigger command.
Matches all three commands within a 10-second window from the same audit session:
idunamegrepwith argument containingunifi.versionorsystem.cfg
False positive rate: effectively zero on non-Ubiquiti hosts.
Triggers on grep unifi.version /tmp/system.cfg alone without requiring the full sequence. Lower latency but same false positive profile.
- Linux with auditd installed and running
- Python 3.8+
- Root privileges (for killing sessions and locking accounts)
sudo ./install.shThis will:
- Install audit rules to
/etc/audit/rules.d/ssh-probe-guard.rules - Install the daemon to
/usr/local/bin/ssh-probe-guard - Install and enable a systemd service
sudo systemctl start ssh-probe-guard # start
sudo systemctl status ssh-probe-guard # check status
journalctl -u ssh-probe-guard -f # follow logs# Detect and log only, no response actions
sudo python3 ssh_probe_guard.py --dry-run
# Full response: kill session + lock account
sudo python3 ssh_probe_guard.py
# Also block attacker IP via iptables
sudo python3 ssh_probe_guard.py --block-ip
# Respond to single strong indicator (faster)
sudo python3 ssh_probe_guard.py --strong-indicator-only| Flag | Description |
|---|---|
--dry-run |
Log detections only, take no response actions |
--no-kill |
Lock accounts but don't kill SSH sessions |
--no-lock-account |
Kill session but don't lock the user account |
--block-ip |
Also add an iptables DROP rule for the attacker IP |
--strong-indicator-only |
Trigger on grep unifi.version alone |
--audit-log PATH |
Custom audit log path |
When Metasploit's SSH login scanner authenticates and fingerprints a host, ssh-probe-guard detects the sequence, kills the session, and locks the account — all within ~1 second:
$ journalctl -u ssh-probe-guard -f
Apr 02 23:47:45 ubuntu ssh-probe-guard[21474]: ssh-probe-guard v1.1.0 starting - monitoring /var/log/audit/audit.log (lock_account=True, block_ip=False, dry_run=False)
Apr 02 23:47:55 ubuntu ssh-probe-guard[21474]: METASPLOIT SSH FINGERPRINT DETECTED - User: test, PID: 21527, PPID: 21526, Session: 33, Reason: Full fingerprint sequence (id -> uname -a -> grep unifi.version)
Apr 02 23:47:55 ubuntu ssh-probe-guard[21474]: Killing SSH session - sshd PID: 21477 (user: test, ses: 33)
Apr 02 23:47:56 ubuntu ssh-probe-guard[21474]: SSH session killed successfully (sshd PID: 21477)
Apr 02 23:47:56 ubuntu ssh-probe-guard[21474]: Locking user account: test
Apr 02 23:47:56 ubuntu usermod[21542]: lock user 'test' password
Apr 02 23:47:56 ubuntu ssh-probe-guard[21474]: Account locked via usermod: test
sudo systemctl disable --now ssh-probe-guard
sudo rm /usr/local/bin/ssh-probe-guard \
/etc/audit/rules.d/ssh-probe-guard.rules \
/etc/systemd/system/ssh-probe-guard.service
sudo augenrules --loadCreateSession false: The fingerprint sequence runs in two independent code paths: once in the login scanner'sgather_proof(controlled byGatherProof), and again during session bootstrap inSshCommandShellBind#bootstrap(unconditional — needed to choose between Unix/Windows shell escaping). SettingGatherProof falseonly skips the first call. The fingerprinting still executes during session setup. The only way to fully suppress it isset CreateSession false, which also prevents getting a session — making the scan credential-validation-only.- Custom login scanners: This specifically detects Metasploit's
get_platform_infosequence. Other tools with different fingerprinting logic would not be detected by these exact rules (but the approach is extensible). - Ubiquiti devices: On actual Ubiquiti hardware,
grep unifi.version /tmp/system.cfgis not anomalous. Use the full sequence detection mode (default) on Ubiquiti devices, or exclude them.
| Approach | Pros | Cons |
|---|---|---|
| auditd + daemon (this tool) | Works on all Linux, kernel-level visibility, simple to deploy | ~200-500ms latency |
| PAM module | Hooks session start | Can't monitor post-login commands |
| eBPF | <50ms latency, lowest overhead | Requires kernel 4.18+, complex development |
| SSH ForceCommand | Simple | Breaks normal SSH usage |
| inotify on /tmp/system.cfg | Fast for file access | Only catches file access, not the full command pattern |
The auditd approach was chosen for maximum compatibility, reliability, and maintainability.