Initial commit - 611 cybersecurity skills across all subdomains

This commit is contained in:
mukul975
2026-02-25 10:47:44 +01:00
commit 22a7ab1462
1765 changed files with 280648 additions and 0 deletions
@@ -0,0 +1,212 @@
---
name: None
description: Authenticated (credentialed) vulnerability scanning uses valid system credentials to log into target hosts and perform deep inspection of installed software, patches, configurations, and security sett
domain: cybersecurity
subdomain: vulnerability-management
tags: [vulnerability-management, cve, authenticated-scanning, credentials, nessus, qualys, risk]
version: "1.0"
author: mahipal
license: MIT
---
# Performing Authenticated Vulnerability Scan
## Overview
Authenticated (credentialed) vulnerability scanning uses valid system credentials to log into target hosts and perform deep inspection of installed software, patches, configurations, and security settings. Compared to unauthenticated scanning, credentialed scans detect 45-60% more vulnerabilities with significantly fewer false positives because they can directly query installed packages, registry keys, and file system contents.
## Prerequisites
- Vulnerability scanner (Nessus, Qualys, OpenVAS, Rapid7 InsightVM)
- Service accounts with appropriate privileges on target systems
- Secure credential storage (vault integration preferred)
- Network access from scanner to target management ports
- Written authorization from system owners
## Core Concepts
### Why Authenticated Scanning
Unauthenticated scanning can only assess externally visible services and banners, often leading to:
- Missed vulnerabilities in locally installed software
- Inaccurate version detection from banner changes
- Inability to check patch levels, configurations, or local policies
- Higher false positive rates due to inference-based detection
Authenticated scanning resolves these by directly querying the target OS.
### Credential Types by Platform
#### Linux/Unix Systems
- **SSH Key Authentication**: RSA/Ed25519 key pairs (recommended)
- **SSH Username/Password**: Fallback for systems without key-based auth
- **Sudo/Su Elevation**: Non-root user with sudo privileges
- **Certificate-based SSH**: X.509 certificates for enterprise environments
#### Windows Systems
- **SMB (Windows)**: Domain or local admin credentials
- **WMI**: Windows Management Instrumentation queries
- **WinRM**: Windows Remote Management (HTTPS preferred)
- **Kerberos**: Domain authentication with service tickets
#### Network Devices
- **SNMP v3**: USM with authentication and privacy (AES-256)
- **SSH**: For Cisco IOS, Juniper JunOS, Palo Alto PAN-OS
- **API Tokens**: REST API for modern network platforms
#### Databases
- **Oracle**: SYS/SYSDBA credentials or TNS connection
- **Microsoft SQL Server**: Windows auth or SQL auth
- **PostgreSQL**: Role-based authentication
- **MySQL**: User/password with SELECT privileges
## Implementation Steps
### Step 1: Create Dedicated Service Accounts
```bash
# Linux: Create scan service account
sudo useradd -m -s /bin/bash -c "Vulnerability Scanner Service Account" nessus_svc
sudo usermod -aG sudo nessus_svc
# Configure sudo for passwordless specific commands
echo 'nessus_svc ALL=(ALL) NOPASSWD: /usr/bin/dpkg -l, /usr/bin/rpm -qa, \
/bin/cat /etc/shadow, /usr/sbin/dmidecode, /usr/bin/find' | sudo tee /etc/sudoers.d/nessus_svc
# Generate SSH key pair
sudo -u nessus_svc ssh-keygen -t ed25519 -f /home/nessus_svc/.ssh/id_ed25519 -N ""
# Distribute public key to targets
for host in $(cat target_hosts.txt); do
ssh-copy-id -i /home/nessus_svc/.ssh/id_ed25519.pub nessus_svc@$host
done
```
```powershell
# Windows: Create scan service account via PowerShell
New-ADUser -Name "SVC_VulnScan" `
-SamAccountName "SVC_VulnScan" `
-UserPrincipalName "SVC_VulnScan@domain.local" `
-Description "Vulnerability Scanner Service Account" `
-PasswordNeverExpires $true `
-CannotChangePassword $true `
-Enabled $true `
-AccountPassword (Read-Host -AsSecureString "Enter Password")
# Add to local Administrators group on targets via GPO or:
Add-ADGroupMember -Identity "Domain Admins" -Members "SVC_VulnScan"
# For least privilege, use a dedicated GPO for local admin rights instead
# Enable WinRM on targets
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Service\AllowRemote -Value $true
winrm set winrm/config/service '@{AllowUnencrypted="false"}'
```
### Step 2: Configure Scanner Credentials
#### Nessus Configuration
```json
{
"credentials": {
"add": {
"Host": {
"SSH": [{
"auth_method": "public key",
"username": "nessus_svc",
"private_key": "/path/to/id_ed25519",
"elevate_privileges_with": "sudo",
"escalation_account": "root"
}],
"Windows": [{
"auth_method": "Password",
"username": "DOMAIN\\SVC_VulnScan",
"password": "stored_in_vault",
"domain": "domain.local"
}],
"SNMPv3": [{
"username": "nessus_snmpv3",
"security_level": "authPriv",
"auth_algorithm": "SHA-256",
"auth_password": "stored_in_vault",
"priv_algorithm": "AES-256",
"priv_password": "stored_in_vault"
}]
}
}
}
}
```
### Step 3: Validate Credential Access
```bash
# Test SSH connectivity
ssh -i /path/to/key -o ConnectTimeout=10 nessus_svc@target_host "uname -a && sudo dpkg -l | head -5"
# Test WinRM connectivity
python3 -c "
import winrm
s = winrm.Session('target_host', auth=('DOMAIN\\\\SVC_VulnScan', 'password'), transport='ntlm')
r = s.run_cmd('systeminfo')
print(r.std_out.decode())
"
# Test SNMP v3 connectivity
snmpwalk -v3 -u nessus_snmpv3 -l authPriv -a SHA-256 -A authpass -x AES-256 -X privpass target_host sysDescr.0
```
### Step 4: Run Authenticated Scan
Configure and launch the scan using the Nessus API:
```bash
# Create scan with credentials
curl -k -X POST https://nessus:8834/scans \
-H "X-Cookie: token=$TOKEN" \
-H "Content-Type: application/json" \
-d '{
"uuid": "'$TEMPLATE_UUID'",
"settings": {
"name": "Authenticated Scan - Production",
"text_targets": "192.168.1.0/24",
"launch": "ON_DEMAND"
},
"credentials": {
"add": {
"Host": {
"SSH": [{"auth_method": "public key", "username": "nessus_svc", "private_key": "/keys/id_ed25519"}],
"Windows": [{"auth_method": "Password", "username": "DOMAIN\\SVC_VulnScan", "password": "vault_ref"}]
}
}
}
}'
```
### Step 5: Verify Credential Success
After scan completion, check credential verification results:
- **Plugin 19506** (Nessus Scan Information): Shows credential status
- **Plugin 21745** (OS Security Patch Assessment): Confirms local checks
- **Plugin 117887** (Local Security Checks): Credential verification
- **Plugin 110385** (Nessus Credentialed Check): Target-level auth status
## Credential Security Best Practices
1. **Use a secrets vault** (HashiCorp Vault, CyberArk, AWS Secrets Manager) for credential storage
2. **Rotate credentials** every 90 days or after personnel changes
3. **Principle of least privilege** - only grant minimum required access
4. **Audit credential usage** - monitor service account login events
5. **Encrypt in transit** - use SSH keys over passwords, WinRM over HTTPS
6. **Separate accounts** per scanner - never share credentials across tools
7. **Disable interactive login** for scan service accounts where possible
8. **Log all authentication** events for scan accounts in SIEM
## Common Pitfalls
- Using domain admin accounts instead of least-privilege service accounts
- Storing credentials in plaintext scan configurations
- Not testing credentials before scan launch (leads to wasted scan windows)
- Forgetting to configure sudo/elevation for Linux targets
- Windows UAC blocking remote credentialed checks
- Firewall rules blocking WMI/WinRM/SSH between scanner and targets
- Credential lockout from multiple failed authentication attempts
## Related Skills
- scanning-infrastructure-with-nessus
- performing-network-vulnerability-assessment
- implementing-continuous-vulnerability-monitoring
@@ -0,0 +1,38 @@
# Authenticated Vulnerability Scan Report Template
## Scan Configuration
| Field | Value |
|-------|-------|
| Scan Date | [YYYY-MM-DD] |
| Scanner | [Nessus/Qualys/OpenVAS] |
| Scan Type | Authenticated (Credentialed) |
| Targets | [TARGET_RANGE] |
| Credential Types | SSH / WinRM / SMB / SNMPv3 |
## Credential Success Summary
| Protocol | Targets | Success | Failed | Rate |
|----------|---------|---------|--------|------|
| SSH | [N] | [N] | [N] | [%] |
| WinRM | [N] | [N] | [N] | [%] |
| SMB | [N] | [N] | [N] | [%] |
| SNMPv3 | [N] | [N] | [N] | [%] |
| **Total** | **[N]** | **[N]** | **[N]** | **[%]** |
## Findings Summary (Authenticated vs Unauthenticated Comparison)
| Severity | Auth Scan | Unauth Scan | Delta |
|----------|-----------|-------------|-------|
| Critical | [N] | [N] | [+N] |
| High | [N] | [N] | [+N] |
| Medium | [N] | [N] | [+N] |
| Low | [N] | [N] | [+N] |
## Authentication Failures
| Host | Protocol | Failure Reason | Remediation |
|------|----------|---------------|-------------|
| [IP] | [SSH/WinRM] | [reason] | [action] |
## Recommendations
1. **Credential Coverage**: [Current %] - Target: >95%
2. **Failed Hosts**: Investigate [N] authentication failures
3. **Privilege Gaps**: [N] hosts missing sudo/admin elevation
4. **Credential Rotation**: Next rotation due [DATE]
@@ -0,0 +1,39 @@
# Standards and References - Authenticated Vulnerability Scanning
## Industry Standards
- **NIST SP 800-115**: Technical Guide to Information Security Testing and Assessment
- **NIST SP 800-53 RA-5**: Vulnerability Scanning (requires credentialed scanning for compliance)
- **CIS Controls v8 Control 7.5**: Perform automated vulnerability scans of internal assets on a quarterly basis using authenticated scanning
- **PCI DSS v4.0 Req 11.3.1**: Internal vulnerability scans must use authenticated scanning
- **DISA STIG**: Requires credentialed scanning for compliance validation
## Credential Management Standards
- **NIST SP 800-63B**: Digital Identity Guidelines - Authentication and Lifecycle Management
- **CIS Controls v8 Control 5**: Account Management
- **OWASP Credential Storage Cheat Sheet**: Secure credential handling best practices
## Scanner Documentation
- Nessus Credentialed Checks: https://docs.tenable.com/nessus/Content/CredentialedChecks.htm
- Qualys Authenticated Scanning: https://www.qualys.com/docs/qualys-scanning-best-practices.pdf
- OpenVAS Credential Management: https://docs.greenbone.net/
- Rapid7 InsightVM Credentials: https://docs.rapid7.com/insightvm/managing-shared-credentials/
## Verification Plugins (Nessus)
| Plugin ID | Name | Purpose |
|-----------|------|---------|
| 19506 | Nessus Scan Information | Shows scan metadata and credential status |
| 21745 | OS Security Patch Assessment | Confirms local security checks enabled |
| 117887 | Local Security Checks Enabled | Per-host credential verification |
| 110385 | Nessus Credentialed Check | Detailed credential success/failure |
| 10394 | Microsoft Windows SMB Log In Possible | Windows SMB auth verification |
| 10180 | Ping the Remote Host | Host reachability confirmation |
## Minimum Privileges Required
| Platform | Minimum Privilege | Notes |
|----------|------------------|-------|
| Linux | Root or sudo user | Sudo with NOPASSWD for specific commands |
| Windows | Local Administrator | Or domain account with local admin GPO |
| Cisco IOS | Privilege 15 | Enable mode access required |
| SNMP | Read-only (v3 authPriv) | SNMPv3 with encryption |
| Oracle DB | SELECT ANY DICTIONARY | Minimum for audit queries |
| PostgreSQL | pg_read_all_settings | Read-only role sufficient |
@@ -0,0 +1,61 @@
# Workflows - Authenticated Vulnerability Scanning
## Workflow 1: Credential Preparation and Validation
```
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Create Service │────>│ Configure Least │────>│ Test Credentials │
│ Accounts │ │ Privilege Access │ │ on Sample Hosts │
└──────────────────┘ └──────────────────┘ └──────────────────┘
┌────────────────────────────────────────────────┘
v
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Store in Secrets │────>│ Configure Scanner│────>│ Validate Auth │
│ Vault │ │ Credentials │ │ Success Rate │
└──────────────────┘ └──────────────────┘ └──────────────────┘
```
## Workflow 2: Authenticated Scan Execution
1. **Pre-scan**: Verify credentials, check network connectivity, confirm scan window
2. **Discovery**: Host enumeration to identify live targets
3. **Authentication**: Scanner authenticates to each target host
4. **Local Enumeration**: Query installed packages, patches, configurations
5. **Vulnerability Assessment**: Match local data against vulnerability database
6. **Report Generation**: Compile findings with credential success metrics
7. **Post-scan**: Verify no service disruption, archive results
## Workflow 3: Credential Success Monitoring
```
Scan Completion
├──> Check Plugin 117887 (Local Security Checks)
│ │
│ ├──> SUCCESS: Proceed to analyze findings
│ └──> FAILURE: Investigate cause
│ │
│ ├──> Network connectivity issue
│ ├──> Credential expired or changed
│ ├──> Firewall blocking management ports
│ ├──> Account locked out
│ └──> Insufficient privileges
└──> Calculate Credential Success Rate
├──> Target: >95% authenticated hosts
├──> Alert if <90% success rate
└──> Document exceptions for failed hosts
```
## Workflow 4: Credential Lifecycle Management
| Phase | Action | Frequency |
|-------|--------|-----------|
| Provisioning | Create accounts with least privilege | One-time |
| Distribution | Deploy keys/passwords to scanner | One-time |
| Validation | Test connectivity before scans | Per scan |
| Rotation | Change passwords, rotate keys | 90 days |
| Monitoring | Audit login events in SIEM | Continuous |
| Deprovisioning | Remove accounts when scanner retired | As needed |
@@ -0,0 +1,390 @@
#!/usr/bin/env python3
"""
Authenticated Vulnerability Scan Credential Validator and Manager
Pre-validates scanner credentials against target hosts before launching
vulnerability scans to ensure maximum authenticated coverage.
Requirements:
pip install paramiko pywinrm pysnmp pandas
Usage:
python process.py validate --targets targets.txt --creds creds.json
python process.py report --nessus-file scan_results.nessus
"""
import argparse
import json
import socket
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
from pathlib import Path
import pandas as pd
try:
import paramiko
except ImportError:
paramiko = None
try:
import winrm
except ImportError:
winrm = None
class CredentialValidator:
"""Validate scanner credentials against target hosts."""
def __init__(self, timeout: int = 10):
self.timeout = timeout
self.results = []
def check_port(self, host: str, port: int) -> bool:
"""Check if a TCP port is open on the target host."""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(self.timeout)
result = sock.connect_ex((host, port))
sock.close()
return result == 0
except (socket.error, OSError):
return False
def validate_ssh(self, host: str, username: str, password: str = None,
key_file: str = None, port: int = 22) -> dict:
"""Validate SSH credentials against a target host."""
result = {
"host": host, "protocol": "SSH", "port": port,
"username": username, "status": "UNKNOWN", "details": ""
}
if not paramiko:
result["status"] = "SKIP"
result["details"] = "paramiko not installed"
return result
if not self.check_port(host, port):
result["status"] = "FAIL"
result["details"] = f"Port {port} not reachable"
return result
try:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
connect_kwargs = {
"hostname": host, "port": port, "username": username,
"timeout": self.timeout, "allow_agent": False, "look_for_keys": False
}
if key_file:
connect_kwargs["key_filename"] = key_file
elif password:
connect_kwargs["password"] = password
else:
result["status"] = "FAIL"
result["details"] = "No password or key file provided"
return result
client.connect(**connect_kwargs)
# Test command execution
_, stdout, stderr = client.exec_command("id && uname -a", timeout=10)
output = stdout.read().decode().strip()
error = stderr.read().decode().strip()
# Test sudo access
_, stdout_sudo, stderr_sudo = client.exec_command(
"sudo -n id 2>&1", timeout=10
)
sudo_output = stdout_sudo.read().decode().strip()
has_sudo = "uid=0" in sudo_output
client.close()
result["status"] = "SUCCESS"
result["details"] = f"Auth OK. Sudo: {'Yes' if has_sudo else 'No'}. {output[:100]}"
result["has_sudo"] = has_sudo
except paramiko.AuthenticationException:
result["status"] = "FAIL"
result["details"] = "Authentication failed - invalid credentials"
except paramiko.SSHException as e:
result["status"] = "FAIL"
result["details"] = f"SSH error: {str(e)[:100]}"
except Exception as e:
result["status"] = "FAIL"
result["details"] = f"Connection error: {str(e)[:100]}"
return result
def validate_winrm(self, host: str, username: str, password: str,
port: int = 5985, use_ssl: bool = False) -> dict:
"""Validate WinRM credentials against a Windows target."""
result = {
"host": host, "protocol": "WinRM", "port": port,
"username": username, "status": "UNKNOWN", "details": ""
}
if not winrm:
result["status"] = "SKIP"
result["details"] = "pywinrm not installed"
return result
check_port = 5986 if use_ssl else port
if not self.check_port(host, check_port):
result["status"] = "FAIL"
result["details"] = f"Port {check_port} not reachable"
return result
try:
scheme = "https" if use_ssl else "http"
session = winrm.Session(
f"{scheme}://{host}:{check_port}/wsman",
auth=(username, password),
transport="ntlm",
server_cert_validation="ignore" if use_ssl else "validate"
)
r = session.run_cmd("whoami")
output = r.std_out.decode().strip()
# Check admin privileges
r_admin = session.run_cmd("net", ["localgroup", "Administrators"])
admin_output = r_admin.std_out.decode()
is_admin = username.split("\\")[-1].lower() in admin_output.lower()
result["status"] = "SUCCESS"
result["details"] = f"Auth OK as {output}. Admin: {'Yes' if is_admin else 'No'}"
result["is_admin"] = is_admin
except Exception as e:
result["status"] = "FAIL"
result["details"] = f"WinRM error: {str(e)[:150]}"
return result
def validate_smb(self, host: str, username: str, password: str,
domain: str = "", port: int = 445) -> dict:
"""Validate SMB credentials against a Windows target."""
result = {
"host": host, "protocol": "SMB", "port": port,
"username": username, "status": "UNKNOWN", "details": ""
}
if not self.check_port(host, port):
result["status"] = "FAIL"
result["details"] = f"Port {port} not reachable"
return result
try:
from impacket.smbconnection import SMBConnection
conn = SMBConnection(host, host, sess_port=port)
conn.login(username, password, domain)
shares = conn.listShares()
share_names = [s["shi1_netname"].rstrip("\x00") for s in shares]
conn.logoff()
result["status"] = "SUCCESS"
result["details"] = f"Auth OK. Shares: {', '.join(share_names[:5])}"
except ImportError:
result["status"] = "SKIP"
result["details"] = "impacket not installed"
except Exception as e:
result["status"] = "FAIL"
result["details"] = f"SMB error: {str(e)[:150]}"
return result
def validate_snmpv3(self, host: str, username: str, auth_password: str,
priv_password: str, port: int = 161) -> dict:
"""Validate SNMPv3 credentials against a target."""
result = {
"host": host, "protocol": "SNMPv3", "port": port,
"username": username, "status": "UNKNOWN", "details": ""
}
try:
from pysnmp.hlapi import (
SnmpEngine, UsmUserData, UdpTransportTarget,
ContextData, ObjectType, ObjectIdentity, getCmd,
usmHMACSHAAuthProtocol, usmAesCfb128Protocol
)
iterator = getCmd(
SnmpEngine(),
UsmUserData(username, auth_password, priv_password,
authProtocol=usmHMACSHAAuthProtocol,
privProtocol=usmAesCfb128Protocol),
UdpTransportTarget((host, port), timeout=self.timeout),
ContextData(),
ObjectType(ObjectIdentity("SNMPv2-MIB", "sysDescr", 0))
)
errorIndication, errorStatus, errorIndex, varBinds = next(iterator)
if errorIndication:
result["status"] = "FAIL"
result["details"] = f"SNMP error: {errorIndication}"
elif errorStatus:
result["status"] = "FAIL"
result["details"] = f"SNMP status: {errorStatus.prettyPrint()}"
else:
for varBind in varBinds:
result["status"] = "SUCCESS"
result["details"] = f"Auth OK. sysDescr: {str(varBind[1])[:100]}"
except ImportError:
result["status"] = "SKIP"
result["details"] = "pysnmp not installed"
except Exception as e:
result["status"] = "FAIL"
result["details"] = f"SNMP error: {str(e)[:150]}"
return result
def validate_all(self, targets: list, credentials: dict, max_workers: int = 20) -> list:
"""Validate credentials against all targets in parallel."""
self.results = []
tasks = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
for target in targets:
host = target.strip()
if not host:
continue
# SSH validation
if "ssh" in credentials:
cred = credentials["ssh"]
tasks.append(executor.submit(
self.validate_ssh, host, cred["username"],
cred.get("password"), cred.get("key_file"),
cred.get("port", 22)
))
# WinRM validation
if "winrm" in credentials:
cred = credentials["winrm"]
tasks.append(executor.submit(
self.validate_winrm, host, cred["username"],
cred["password"], cred.get("port", 5985),
cred.get("use_ssl", False)
))
# SMB validation
if "smb" in credentials:
cred = credentials["smb"]
tasks.append(executor.submit(
self.validate_smb, host, cred["username"],
cred["password"], cred.get("domain", ""),
cred.get("port", 445)
))
# SNMPv3 validation
if "snmpv3" in credentials:
cred = credentials["snmpv3"]
tasks.append(executor.submit(
self.validate_snmpv3, host, cred["username"],
cred["auth_password"], cred["priv_password"],
cred.get("port", 161)
))
for future in as_completed(tasks):
try:
result = future.result()
self.results.append(result)
status_icon = "[+]" if result["status"] == "SUCCESS" else "[-]"
print(f" {status_icon} {result['host']}:{result['port']} "
f"({result['protocol']}) - {result['status']}: {result['details'][:80]}")
except Exception as e:
print(f" [!] Validation error: {e}")
return self.results
def generate_report(self, output_path: str = None) -> pd.DataFrame:
"""Generate validation report."""
df = pd.DataFrame(self.results)
if df.empty:
print("[-] No validation results to report")
return df
print("\n" + "=" * 70)
print("CREDENTIAL VALIDATION SUMMARY")
print("=" * 70)
total = len(df)
success = len(df[df["status"] == "SUCCESS"])
fail = len(df[df["status"] == "FAIL"])
skip = len(df[df["status"] == "SKIP"])
print(f"Total Checks: {total}")
print(f" SUCCESS: {success} ({success/total*100:.1f}%)")
print(f" FAIL: {fail} ({fail/total*100:.1f}%)")
print(f" SKIPPED: {skip} ({skip/total*100:.1f}%)")
if success / max(total, 1) < 0.90:
print("\n[WARNING] Credential success rate below 90% - investigate failures before scanning")
# Protocol breakdown
print("\nBy Protocol:")
for proto in df["protocol"].unique():
proto_df = df[df["protocol"] == proto]
proto_success = len(proto_df[proto_df["status"] == "SUCCESS"])
print(f" {proto}: {proto_success}/{len(proto_df)} "
f"({proto_success/len(proto_df)*100:.1f}%)")
# Failed hosts
failures = df[df["status"] == "FAIL"]
if not failures.empty:
print(f"\nFailed Hosts ({len(failures)}):")
for _, row in failures.iterrows():
print(f" {row['host']}:{row['port']} ({row['protocol']}): {row['details'][:80]}")
if output_path:
df.to_csv(output_path, index=False)
print(f"\n[+] Report saved to: {output_path}")
return df
def main():
parser = argparse.ArgumentParser(description="Authenticated Scan Credential Validator")
subparsers = parser.add_subparsers(dest="command")
# Validate command
val_parser = subparsers.add_parser("validate", help="Validate credentials against targets")
val_parser.add_argument("--targets", required=True, help="File with target IPs (one per line)")
val_parser.add_argument("--creds", required=True, help="JSON file with credentials")
val_parser.add_argument("--output", default=None, help="Output CSV report path")
val_parser.add_argument("--workers", type=int, default=20, help="Max parallel workers")
val_parser.add_argument("--timeout", type=int, default=10, help="Connection timeout seconds")
args = parser.parse_args()
if args.command == "validate":
with open(args.targets) as f:
targets = [line.strip() for line in f if line.strip() and not line.startswith("#")]
with open(args.creds) as f:
credentials = json.load(f)
print(f"[*] Validating credentials against {len(targets)} targets")
print(f"[*] Protocols: {', '.join(credentials.keys())}")
validator = CredentialValidator(timeout=args.timeout)
validator.validate_all(targets, credentials, max_workers=args.workers)
output = args.output or f"cred_validation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
validator.generate_report(output)
else:
parser.print_help()
if __name__ == "__main__":
main()