Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
Overview
Deploy Cisco Duo multi-factor authentication across enterprise applications, VPN, RDP, and SSH access points. This skill covers Duo integration methods, adaptive authentication policies, device trust assessment, and phishing-resistant MFA deployment aligned with NIST 800-63B AAL2/AAL3 requirements.
When to Use
- When deploying or configuring configuring multi factor authentication with duo capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Familiarity with identity access management concepts and tools
- Access to a test or lab environment for safe execution
- Python 3.8+ with required dependencies installed
- Appropriate authorization for any testing activities
Objectives
- Configure Duo MFA for VPN, RDP, SSH, and web applications
- Implement adaptive access policies based on user, device, and network context
- Deploy phishing-resistant authentication (Duo Verified Push, WebAuthn)
- Configure device health policies (trusted endpoints, OS version enforcement)
- Set up Duo Admin Panel monitoring and reporting
- Implement MFA bypass and emergency access procedures
Key Concepts
Duo Authentication Methods (by security strength)
- Security Keys (WebAuthn/FIDO2): Phishing-resistant, AAL3 capable
- Duo Verified Push: Requires code entry, resistant to MFA fatigue attacks
- Duo Push: Push notification to Duo Mobile app
- TOTP (Duo Mobile Passcode): Time-based one-time password
- Hardware Tokens: OTP from physical token
- SMS/Phone Call: Least secure, use only as fallback
Duo Integration Architecture
- Duo Authentication Proxy: On-premises proxy for RADIUS/LDAP integration
- Duo Web SDK: Embed Duo MFA in web applications
- Duo OIDC/SAML: SSO integration for cloud applications
- Duo for RDP: Windows Logon MFA
- Duo Unix: PAM-based MFA for SSH
Adaptive Access Policies
- Trusted Networks: Reduce MFA friction for corporate networks
- Remembered Devices: Skip MFA for trusted devices (configurable duration)
- Device Health: Block or require MFA based on OS patch level, encryption, firewall
- Risk-Based Authentication: Step-up MFA for anomalous login patterns
Workflow
Step 1: Duo Authentication Proxy Setup
- Deploy Duo Authentication Proxy on Windows/Linux server
- Configure primary authentication (AD/LDAP or RADIUS)
- Configure Duo API credentials (Integration Key, Secret Key, API Hostname)
- Set failmode (safe=deny if Duo unreachable, secure=allow)
- Test proxy connectivity to Duo cloud and AD
Step 2: VPN MFA Integration
- Configure VPN concentrator for RADIUS authentication
- Point RADIUS to Duo Authentication Proxy
- Configure Duo proxy with [radius_server_auto] section
- Test VPN login with Duo Push
- Deploy to all VPN users with enrollment period
Step 3: RDP/Windows Logon MFA
- Install Duo Authentication for Windows Logon on target servers
- Configure Duo application in Admin Panel
- Set offline access options (allow N offline logins)
- Configure bypass for service accounts
- Test RDP login with Duo MFA
Step 4: Adaptive Policy Configuration
- Create user groups (Standard, Privileged, Contractors)
- Configure per-group authentication policies:
- Standard: Duo Push allowed, remembered device 7 days
- Privileged: Verified Push required, no remembered device
- Contractors: WebAuthn required, no remembered device
- Configure device health policies:
- Require encrypted disk
- Block outdated OS versions
- Require firewall enabled
- Set trusted network exceptions for corporate IPs
Step 5: Phishing-Resistant MFA Deployment
- Enable Verified Push (requires entering 3-digit code from login screen)
- Register WebAuthn/FIDO2 security keys for privileged users
- Disable SMS and phone call for high-risk groups
- Configure Duo Risk-Based Factor Selection
- Monitor for MFA fatigue attack patterns
Step 6: Monitoring and Response
- Configure Duo Admin Panel alerts
- Set up authentication log forwarding to SIEM
- Monitor for: MFA denial patterns, bypass usage, new device enrollments
- Create incident response playbook for MFA compromise
- Regular review of bypass and exception policies
Security Controls
| Control | NIST 800-53 | Description |
|---|---|---|
| MFA | IA-2(1) | Multi-factor authentication for network access |
| MFA for Privileged | IA-2(2) | MFA for privileged account access |
| Replay Resistance | IA-2(8) | Replay-resistant authentication |
| Device Identification | IA-3 | Device identity and trust |
| Authenticator Management | IA-5 | MFA enrollment and lifecycle |
Common Pitfalls
- Not deploying phishing-resistant MFA (Verified Push/FIDO2) for privileged accounts
- Setting failmode to "safe" (allow access when Duo is down) in production
- Not disabling SMS/phone call for users with app-capable devices
- Forgetting to configure offline access for laptops
- Not monitoring for MFA fatigue/prompt bombing attacks
Verification
- VPN login requires Duo MFA
- RDP to servers requires Duo MFA
- SSH access requires Duo MFA
- Verified Push enabled for privileged users
- Device health policy blocks non-compliant devices
- Authentication logs forwarded to SIEM
- Bypass/emergency access procedures tested
- MFA fatigue detection alerts configured
Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.5 KB
Duo MFA Configuration — API Reference
Libraries
| Library | Install | Purpose |
|---|---|---|
| duo_client | pip install duo_client |
Official Duo SDK for Python |
| requests | pip install requests |
HTTP client for Admin API |
Duo Admin API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /admin/v1/users |
List all users with enrollment status |
| GET | /admin/v1/users/{user_id} |
Get user details and devices |
| GET | /admin/v1/info/summary |
Account summary (user count, integrations) |
| GET | /admin/v2/logs/authentication |
Authentication logs (v2 with paging) |
| POST | /admin/v1/users/enroll |
Enroll new user for MFA |
| POST | /admin/v1/users/{id}/bypass_codes |
Generate bypass codes |
Authentication (HMAC Signing)
import duo_client
admin_api = duo_client.Admin(
ikey="DIXXXXXXXXXXXXXXXXXX",
skey="YourSecretKey",
host="api-XXXXXXXX.duosecurity.com"
)
users = admin_api.get_users()User Status Values
| Status | Description |
|---|---|
| active | User enrolled and can authenticate |
| bypass | MFA bypassed — security risk |
| disabled | User account disabled |
| locked_out | Temporarily locked due to failed attempts |
External References
standards.md1.2 KB
Standards and References - MFA with Duo
NIST Standards
- NIST SP 800-63B: Digital Identity Guidelines - Authentication and Lifecycle Management
- AAL1: Single-factor authentication
- AAL2: Multi-factor authentication (Duo Push, TOTP)
- AAL3: Hardware-based phishing-resistant (FIDO2, PIV)
- NIST SP 800-53 Rev 5: IA-2, IA-2(1), IA-2(2), IA-2(6), IA-2(8), IA-3, IA-5
Duo Documentation
- Duo Authentication Proxy: https://duo.com/docs/authproxy-reference
- Duo for RDP: https://duo.com/docs/rdp
- Duo Unix (SSH): https://duo.com/docs/duounix
- Duo Web SDK: https://duo.com/docs/duoweb
- Duo Verified Push: https://duo.com/blog/webauthn-passwordless-fido2-explained-componens-passwordless-architecture
- Duo Admin API: https://duo.com/docs/adminapi
CISA Guidance
- CISA MFA Guidance: Phishing-resistant MFA requirement for federal agencies
- EO 14028: Executive Order on Improving the Nation's Cybersecurity - MFA mandate
Compliance
- PCI DSS 4.0: Requirement 8.3.1 - MFA for all access to CDE
- HIPAA: 45 CFR 164.312(d) - Person or entity authentication
- SOX: MFA for privileged financial system access
- CMMC: Level 2 - IA.L2-3.5.3 Multi-factor authentication
workflows.md1.6 KB
MFA with Duo Workflows
Workflow 1: Duo Authentication Proxy Deployment
- Install Duo Authentication Proxy on dedicated server
- Configure authproxy.cfg with AD/LDAP primary auth
- Add Duo API credentials (ikey, skey, api_host)
- Set failmode=safe for initial testing, change to secure for production
- Start Duo proxy service, verify connectivity
- Configure VPN/application to use proxy as RADIUS server
- Test with pilot group before full deployment
Workflow 2: User Enrollment
- Admin creates Duo user (manual or AD sync)
- User receives enrollment email/link
- User installs Duo Mobile app
- User scans QR code to link device
- User completes test authentication
- Admin verifies enrollment status in Admin Panel
Workflow 3: MFA Fatigue Attack Response
- Detect multiple rapid push notifications to single user
- Alert security team via SIEM integration
- Temporarily lock user's Duo account
- Contact user to verify if they initiated authentication
- If unauthorized: reset credentials, investigate source
- If authorized: educate user, enable Verified Push
- Update policy to require Verified Push for affected group
Workflow 4: Duo Failover and Emergency Access
- Duo cloud service becomes unreachable
- Authentication Proxy checks failmode setting
- If failmode=secure: deny all access (most secure)
- If failmode=safe: allow primary auth only (business continuity)
- Admin monitors Duo status page for resolution
- After restoration: review all authentications during outage
- Investigate any suspicious access during failover period
Scripts 2
agent.py4.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Duo MFA configuration and audit agent using Duo Admin API."""
import json
import sys
import argparse
import hmac
import hashlib
import email.utils
import urllib.parse
from datetime import datetime
try:
import requests
except ImportError:
print("Install: pip install requests")
sys.exit(1)
class DuoAdminClient:
"""Duo Admin API client for MFA management."""
def __init__(self, ikey, skey, host):
self.ikey = ikey
self.skey = skey
self.host = host
def _sign(self, method, path, params):
now = email.utils.formatdate()
canon = [now, method.upper(), self.host.lower(), path]
body = urllib.parse.urlencode(sorted(params.items()))
canon.append(body)
canon_str = "\n".join(canon)
sig = hmac.new(self.skey.encode(), canon_str.encode(), hashlib.sha1).hexdigest()
auth = f"{self.ikey}:{sig}"
import base64
return {"Date": now, "Authorization": f"Basic {base64.b64encode(auth.encode()).decode()}"}
def _get(self, endpoint, params=None):
params = params or {}
headers = self._sign("GET", endpoint, params)
resp = requests.get(f"https://{self.host}{endpoint}",
headers=headers, params=params, timeout=30)
resp.raise_for_status()
return resp.json()
def list_users(self):
return self._get("/admin/v1/users")
def get_user(self, user_id):
return self._get(f"/admin/v1/users/{user_id}")
def list_auth_logs(self, mintime=None):
params = {}
if mintime:
params["mintime"] = str(int(mintime))
return self._get("/admin/v2/logs/authentication", params)
def get_info_summary(self):
return self._get("/admin/v1/info/summary")
def audit_mfa_coverage(users_data):
"""Audit MFA enrollment coverage."""
users = users_data.get("response", [])
total = len(users)
enrolled = sum(1 for u in users if u.get("status") == "active" and u.get("phones"))
bypass = sum(1 for u in users if u.get("status") == "bypass")
disabled = sum(1 for u in users if u.get("status") == "disabled")
no_device = [u["username"] for u in users if not u.get("phones") and u.get("status") == "active"]
return {
"total_users": total,
"enrolled": enrolled,
"bypass_mode": bypass,
"disabled": disabled,
"no_device": no_device[:20],
"enrollment_rate": round(enrolled / max(total, 1) * 100, 1),
"findings": [
{"severity": "HIGH", "issue": f"{bypass} users in bypass mode"} if bypass else None,
{"severity": "MEDIUM", "issue": f"{len(no_device)} users without MFA device"} if no_device else None,
],
}
def analyze_auth_logs(logs_data):
"""Analyze authentication logs for anomalies."""
logs = logs_data.get("response", {}).get("authlogs", [])
denied = [l for l in logs if l.get("result") == "denied"]
fraud = [l for l in logs if l.get("result") == "fraud"]
return {
"total_authentications": len(logs),
"denied": len(denied),
"fraud_reported": len(fraud),
"top_denied_users": list(set(l.get("user", {}).get("name", "") for l in denied[:10])),
}
def run_audit(ikey, skey, host):
"""Execute Duo MFA audit."""
client = DuoAdminClient(ikey, skey, host)
print(f"\n{'='*60}")
print(f" DUO MFA CONFIGURATION AUDIT")
print(f" Host: {host}")
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
print(f"{'='*60}\n")
summary = client.get_info_summary()
info = summary.get("response", {})
print(f"--- ACCOUNT SUMMARY ---")
print(f" Users: {info.get('user_count', 0)}")
print(f" Integrations: {info.get('integration_count', 0)}")
users = client.list_users()
coverage = audit_mfa_coverage(users)
print(f"\n--- MFA COVERAGE ---")
print(f" Enrollment rate: {coverage['enrollment_rate']}%")
print(f" Bypass mode: {coverage['bypass_mode']}")
print(f" No device: {len(coverage['no_device'])}")
return {"summary": info, "coverage": coverage}
def main():
parser = argparse.ArgumentParser(description="Duo MFA Audit Agent")
parser.add_argument("--ikey", required=True, help="Duo integration key")
parser.add_argument("--skey", required=True, help="Duo secret key")
parser.add_argument("--host", required=True, help="Duo API hostname")
parser.add_argument("--output", help="Save report to JSON file")
args = parser.parse_args()
report = run_audit(args.ikey, args.skey, args.host)
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"\n[+] Report saved to {args.output}")
if __name__ == "__main__":
main()
process.py11.4 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Duo MFA Configuration Auditor and Health Checker
Audits Duo MFA deployment configuration, checks policy compliance,
detects MFA fatigue patterns, and monitors authentication health.
"""
import json
import datetime
from typing import Dict, List, Optional
from dataclasses import dataclass, field
from collections import defaultdict
@dataclass
class DuoPolicy:
"""Duo authentication policy configuration."""
policy_name: str
group: str
allowed_methods: List[str] # push, verified_push, webauthn, totp, sms, phone
remembered_devices_days: int = 0
require_device_health: bool = False
require_encryption: bool = False
min_os_version: str = ""
trusted_networks: List[str] = field(default_factory=list)
failmode: str = "secure" # secure or safe
@dataclass
class AuthEvent:
"""Authentication event from Duo logs."""
timestamp: str
username: str
factor: str # push, verified_push, webauthn, totp, sms, phone, bypass
result: str # success, denied, fraud, timeout
ip_address: str
device_os: str = ""
application: str = ""
reason: str = ""
@dataclass
class MFAAuditFinding:
"""MFA audit finding."""
severity: str
category: str
title: str
description: str
recommendation: str = ""
class DuoMFAAuditor:
"""Audits Duo MFA configuration and detects anomalies."""
PHISHING_RESISTANT = {"verified_push", "webauthn", "fido2"}
WEAK_METHODS = {"sms", "phone"}
AAL2_METHODS = {"push", "verified_push", "webauthn", "totp", "fido2"}
def __init__(self):
self.policies: List[DuoPolicy] = []
self.auth_events: List[AuthEvent] = []
self.findings: List[MFAAuditFinding] = []
def load_policies(self, policies: List[Dict]):
for p in policies:
self.policies.append(DuoPolicy(**p))
def load_auth_events(self, events: List[Dict]):
for e in events:
self.auth_events.append(AuthEvent(**e))
def audit_all(self) -> List[MFAAuditFinding]:
self.findings = []
self._audit_policy_strength()
self._audit_weak_methods()
self._audit_device_health()
self._audit_failmode()
self._audit_remembered_devices()
self._detect_mfa_fatigue()
self._detect_bypass_usage()
self._audit_method_distribution()
return self.findings
def _audit_policy_strength(self):
for policy in self.policies:
has_phishing_resistant = any(
m in self.PHISHING_RESISTANT for m in policy.allowed_methods
)
if "privileged" in policy.group.lower() or "admin" in policy.group.lower():
if not has_phishing_resistant:
self.findings.append(MFAAuditFinding(
severity="critical",
category="Policy Strength",
title=f"Privileged group '{policy.group}' lacks phishing-resistant MFA",
description=f"Policy '{policy.policy_name}' allows only: {', '.join(policy.allowed_methods)}",
recommendation="Enable Verified Push or WebAuthn for privileged users per CISA guidance"
))
def _audit_weak_methods(self):
for policy in self.policies:
weak = set(policy.allowed_methods) & self.WEAK_METHODS
if weak:
self.findings.append(MFAAuditFinding(
severity="high",
category="Weak Methods",
title=f"Weak MFA methods enabled for '{policy.group}'",
description=f"SMS and/or phone call enabled: {', '.join(weak)}. "
"These are vulnerable to SIM swapping and social engineering.",
recommendation="Disable SMS/phone for users with smartphone access. Use push or WebAuthn."
))
def _audit_device_health(self):
for policy in self.policies:
if not policy.require_device_health:
self.findings.append(MFAAuditFinding(
severity="medium",
category="Device Health",
title=f"Device health not enforced for '{policy.group}'",
description="Unmanaged or unhealthy devices can authenticate without restriction.",
recommendation="Enable device health policy to check OS version, encryption, and firewall."
))
def _audit_failmode(self):
for policy in self.policies:
if policy.failmode == "safe":
self.findings.append(MFAAuditFinding(
severity="high",
category="Failmode",
title=f"Failmode set to 'safe' for '{policy.policy_name}'",
description="When Duo is unreachable, users are allowed in without MFA.",
recommendation="Set failmode to 'secure' to deny access when Duo is unavailable."
))
def _audit_remembered_devices(self):
for policy in self.policies:
if policy.remembered_devices_days > 30:
self.findings.append(MFAAuditFinding(
severity="medium",
category="Remembered Devices",
title=f"Long remembered device period for '{policy.group}'",
description=f"Devices remembered for {policy.remembered_devices_days} days. "
"Stolen devices retain MFA bypass.",
recommendation="Reduce remembered device period to 7 days or less."
))
def _detect_mfa_fatigue(self):
"""Detect potential MFA prompt bombing / fatigue attacks."""
user_denials = defaultdict(list)
for event in self.auth_events:
if event.result in ("denied", "timeout", "fraud"):
user_denials[event.username].append(event)
for user, denials in user_denials.items():
if len(denials) >= 5:
# Check if denials happened within a short window
timestamps = sorted(denials, key=lambda e: e.timestamp)
if len(timestamps) >= 5:
self.findings.append(MFAAuditFinding(
severity="critical",
category="MFA Fatigue",
title=f"Potential MFA fatigue attack on user '{user}'",
description=f"{len(denials)} denied/timeout MFA attempts detected. "
"This pattern indicates potential MFA prompt bombing.",
recommendation=f"Lock account '{user}', verify with user, enable Verified Push, "
"investigate source IPs."
))
def _detect_bypass_usage(self):
bypass_events = [e for e in self.auth_events if e.factor == "bypass"]
if bypass_events:
users = set(e.username for e in bypass_events)
self.findings.append(MFAAuditFinding(
severity="high",
category="Bypass Usage",
title=f"{len(bypass_events)} MFA bypass authentications detected",
description=f"Users with bypass codes: {', '.join(users)}. "
"Bypass codes circumvent MFA protection.",
recommendation="Review bypass usage. Ensure bypass codes are single-use and time-limited."
))
def _audit_method_distribution(self):
method_counts = defaultdict(int)
for event in self.auth_events:
if event.result == "success":
method_counts[event.factor] += 1
total = sum(method_counts.values())
if total > 0:
weak_pct = sum(method_counts.get(m, 0) for m in self.WEAK_METHODS) / total * 100
if weak_pct > 10:
self.findings.append(MFAAuditFinding(
severity="medium",
category="Method Distribution",
title=f"{weak_pct:.1f}% of authentications use weak MFA methods",
description="Significant portion of users still using SMS/phone.",
recommendation="Migrate users to Duo Push, Verified Push, or WebAuthn."
))
def generate_report(self) -> str:
if not self.findings:
self.audit_all()
lines = [
"=" * 70,
"DUO MFA CONFIGURATION AUDIT REPORT",
"=" * 70,
f"Report Date: {datetime.datetime.now().isoformat()}",
f"Policies Audited: {len(self.policies)}",
f"Auth Events Analyzed: {len(self.auth_events)}",
f"Findings: {len(self.findings)}",
"-" * 70, ""
]
severity_order = {"critical": 0, "high": 1, "medium": 2, "low": 3, "info": 4}
for f in sorted(self.findings, key=lambda x: severity_order.get(x.severity, 5)):
lines.append(f"[{f.severity.upper()}] {f.title}")
lines.append(f" Category: {f.category}")
lines.append(f" {f.description}")
if f.recommendation:
lines.append(f" Fix: {f.recommendation}")
lines.append("")
lines.append("=" * 70)
critical = sum(1 for f in self.findings if f.severity == "critical")
lines.append(f"OVERALL: {'FAIL' if critical > 0 else 'PASS WITH FINDINGS' if self.findings else 'PASS'}")
lines.append("=" * 70)
return "\n".join(lines)
def main():
auditor = DuoMFAAuditor()
auditor.load_policies([
{"policy_name": "Standard Users", "group": "standard_users",
"allowed_methods": ["push", "totp", "sms"], "remembered_devices_days": 7,
"failmode": "secure"},
{"policy_name": "Privileged Admins", "group": "privileged_admins",
"allowed_methods": ["push", "totp"], "remembered_devices_days": 0,
"require_device_health": True, "failmode": "safe"},
{"policy_name": "Contractors", "group": "contractors",
"allowed_methods": ["push", "phone", "sms"], "remembered_devices_days": 45,
"failmode": "secure"},
])
auditor.load_auth_events([
{"timestamp": "2026-02-23T08:00:00", "username": "alice", "factor": "push",
"result": "success", "ip_address": "10.0.1.50", "application": "VPN"},
{"timestamp": "2026-02-23T08:01:00", "username": "bob", "factor": "sms",
"result": "success", "ip_address": "192.168.1.100", "application": "VPN"},
{"timestamp": "2026-02-23T08:02:00", "username": "charlie", "factor": "push",
"result": "denied", "ip_address": "203.0.113.50", "application": "RDP"},
{"timestamp": "2026-02-23T08:02:30", "username": "charlie", "factor": "push",
"result": "denied", "ip_address": "203.0.113.50", "application": "RDP"},
{"timestamp": "2026-02-23T08:03:00", "username": "charlie", "factor": "push",
"result": "denied", "ip_address": "203.0.113.50", "application": "RDP"},
{"timestamp": "2026-02-23T08:03:30", "username": "charlie", "factor": "push",
"result": "timeout", "ip_address": "203.0.113.50", "application": "RDP"},
{"timestamp": "2026-02-23T08:04:00", "username": "charlie", "factor": "push",
"result": "denied", "ip_address": "203.0.113.50", "application": "RDP"},
{"timestamp": "2026-02-23T09:00:00", "username": "dave", "factor": "bypass",
"result": "success", "ip_address": "10.0.2.75", "application": "SSH"},
])
print(auditor.generate_report())
if __name__ == "__main__":
main()
Assets 1
template.mdtext/markdown · 1.4 KBKeep exploring