specific_actions: # ============================================================================ # EXAMPLE A — Port-based condition (well-known, stable ports) # Use contains: when you know the port never changes in practice. # Single spawn per host — no fan-out. # ============================================================================ - name: "smb_example" condition: contains: "445 AND 139" # must have BOTH ports open # contains: "80 OR 443" # must have ANY of these ports open # equals: "22" # must have EXACTLY this one port (rare) has_tag: "smb AND Windows" # tag AND logic # has_tag: "web OR database" # tag OR logic has_not_tag: "anonymous-smb" # skip if already tagged (avoids re-running) command: "nxc smb {{IP}} -u '' -p '' --shares" timeout_s: 120 tags: - pattern: "STATUS_SUCCESS" tag: "anonymous-smb" - pattern: "SMB.*signing.*False" tag: "smb-signing-disabled" - pattern: "(?i)domain.*corp" # (?i) = case-insensitive match tag: "domain-corp-member" - pattern: "VULNERABLE.*(MS17-010|EternalBlue)" tag: "vulnerable-ms17-010" # ============================================================================ # EXAMPLE B — Service-based condition: fires once per matching service instance # has_service: "" implies per-instance fan-out automatically. # One task per port nmap identified as that service — handles non-standard ports # and multiple simultaneous instances (e.g. MSSQL named instances). # {{SERVICE_PORT}} resolves to the current instance's port. # ============================================================================ - name: "mssql_example" condition: has_service: "mssql" # fires once per port nmap identified as mssql # 1 instance → 1 task; 3 instances → 3 tasks # {{SERVICE_PORT}} → the actual port for this instance # works whether mssql is on 1433, 1434, or 49152 command: "nxc mssql {{IP}} -p {{SERVICE_PORT}} -u '' -p ''" timeout_s: 60 tags: - pattern: "\\[\\+\\]" tag: "mssql-anonymous-access" - name: "rdp_example" condition: has_service: "rdp" # catches 3389 AND any non-standard RDP port command: "nxc rdp {{IP}} -p {{SERVICE_PORT}}" timeout_s: 30 tags: - pattern: "NLA.*False" tag: "rdp-nla-disabled" - name: "ftp_example" condition: has_service: "ftp" command: "nxc ftp {{IP}} -p {{SERVICE_PORT}} -u 'anonymous' -p 'anonymous'" timeout_s: 30 tags: - pattern: "\\[\\+\\]" tag: "ftp-anonymous-access" # ============================================================================ # EXAMPLE C — HTTP service fan-out # has_http_service: true fires once per discovered HTTP service on the host. # {{HTTP_URL}} resolves to the correct http:// or https:// URL per iteration. # Outputs stored as "nuclei_scan__80", "nuclei_scan__443", etc. # ============================================================================ - name: "nuclei_scan" condition: has_http_service: true # fires per HTTP service; optional tags filter further has_tag: "interesting" # host-level tag condition still applies command: "nuclei -u {{HTTP_URL}} -silent -severity medium,high,critical" timeout_s: 300 tags: - pattern: "\\[critical\\]" tag: "nuclei-critical" - pattern: "\\[high\\]" tag: "nuclei-high" - name: "ffuf_fuzz" condition: has_http_service: true command: "ffuf -u {{HTTP_URL}}/FUZZ -w /opt/wordlists/common.txt -mc 200,301,302 -s" timeout_s: 600 - name: "curl_banner" condition: has_http_service: true # Mix {{HTTP_PROTOCOL}} and {{HTTP_PORT}} freely command: "curl -sk -I {{HTTP_PROTOCOL}}://{{IP}}:{{HTTP_PORT}}" timeout_s: 15 tags: - pattern: "(?i)X-Powered-By:" tag: "http-powered-by-header" # ============================================================================ # PLACEHOLDERS # ============================================================================ # {{IP}} / {{ip}} / Target IP address # {{PORT}} First port in the raw open port list # (order-dependent — prefer {{PORT:service}}) # {{PORT:service}} First port where nmap identified 'service' # Useful in single-spawn actions to cross-reference # another service's port # {{PORTS:service}} All ports running 'service', comma-separated # e.g. "139,445" when smb runs on both # {{URL}} / {{BASE_URL}} http(s)://IP:PORT (first HTTP service) # {{PROTOCOL}} http or https (first HTTP service) # # Per-instance placeholders — available in has_service and has_http_service actions: # {{SERVICE_PORT}} Port for the current instance # {{SERVICE_PROTOCOL}} Protocol for the current instance (HTTP only) # {{HTTP_URL}} http(s)://IP:PORT for the current HTTP instance # {{HTTP_PORT}} Port for the current HTTP instance # {{HTTP_PROTOCOL}} http or https for the current HTTP instance # ============================================================================ # CONDITIONS — field reference # ============================================================================ # contains: "445" Port 445 is in the open port list # contains: "80 AND 443" Port 80 AND 443 both open # contains: "80 OR 443" Port 80 OR 443 open # equals: "22" ONLY port 22 open # has_tag: "windows" Tag present in automated/manual/refined tags # has_tag: "smb AND windows" Both tags present # has_tag: "smb OR netbios" Either tag present # has_not_tag: "anonymous-smb" Skip host if tag IS present (inverse gate) # has_not_tag: "exploited AND owned" Skip if BOTH tags present # has_not_tag: "low-value OR noise" Skip if EITHER tag present # has_service: "mssql" nmap -sV identified mssql — fires per instance # Single name only: AND/OR stays a boolean gate # has_service: "rdp OR winrm" Either service present (single spawn, no fan-out) # has_http_service: true/false HTTP service confirmed — fires per HTTP service # reconnaissance_complete: true Always enforced — no need to specify # # All specified fields must pass simultaneously (AND between fields). # OR/AND logic applies within a single field value only. # # Fan-out behaviour: # has_service: "mssql" → 1 task per port running mssql (0 tasks if absent) # has_http_service: true → 1 task per HTTP service (0 tasks if absent) # all other conditions → single spawn per host # ============================================================================ # SERVICE NAMES — after normalize_service_name() (use these in has_service:) # Matching is exact, case-insensitive. "MSSQL" matches "mssql"; "sql" does NOT. # ============================================================================ # # Core Windows services: # smb (nmap: microsoft-ds, cifs) # mssql (nmap: ms-sql-s, ms-sql-m, ms-sql-monitor, ms-sql-ds) # rdp (nmap: ms-wbt-server) # winrm (nmap: wsman) # kerberos (nmap: kerberos-sec) # netbios (nmap: netbios-ssn, netbios-ns) # rpc (nmap: msrpc, epmap, ncacn_http, ncacn_ip_tcp, msrpc-base) # dns (nmap: domain) # # Common Linux/cross-platform (pass-through, already clean): # ssh, ftp, http, https, smtp, pop3, imap, ldap, mysql, postgresql # vnc, telnet, snmp, redis, mongodb, nfs, ntp # # Pass-through but worth knowing: # rpcbind Unix portmapper (port 111) — intentionally NOT mapped to "rpc" # (different tooling from Windows msrpc; use has_service: "rpcbind") # oracle-tns Oracle TNS listener — clean identifier, no normalization needed # ajp13 Tomcat AJP connector (port 8009) — clean identifier # # TLS variants — normalized to base service: # ldaps → ldap | imaps → imap | smtps → smtp | pop3s → pop3 | ftps → ftp # ssl/http → http | ssl/ldap → ldap (ssl/ prefix stripped automatically) # # HTTP on alternate ports: # http-alt → http (use has_service: "http") # http-proxy kept as-is — Squid/HAProxy, NOT generic http # # Niche/application-specific services (elasticsearch, squid, etc.): # Use the action builder — workflow.yaml covers systematic services only # ============================================================================ # HOW AUTOMATED TAGS ARE GENERATED FROM NMAP # ============================================================================ # nmap -sV produces two distinct fields per port — both become automated tags: # # 1. service name → normalize_service_name() → short canonical tag # Raw: "ms-sql-s" Tag: "mssql" # Raw: "microsoft-ds" Tag: "smb" # Raw: "ms-wbt-server" Tag: "rdp" # See SERVICE NAMES section above for the full mapping table. # # 2. product string → lowercased → full-string tag (refined by hardcoded rules) # nmap: product="Apache httpd" Tag: "apache" (hardcoded rule) # nmap: product="nginx" Tag: "nginx" (hardcoded rule) # nmap: product="Microsoft IIS httpd" Tag: "iis" (hardcoded rule) # nmap: product="Squid http proxy" Tag: "squid http proxy" (no rule — stays as-is) # # Well-known products normalized by hardcoded rules (produces short tags): # smb, rdp, winrm, dns, kerberos, ldap, rpc, mssql, iis, apache, nginx, # tomcat, mysql, mariadb, postgresql, ssh # # Niche products (no hardcoded rule) — tag is the full product string lowercased. # Target them in the action builder: browse available tags to find the exact value, # or use has_service: for broader protocol-level matching. # # Examples: # Squid proxy: has_service: "http-proxy" (all proxies) # has_tag: "squid http proxy" (specifically Squid, via action builder) # HAProxy: has_service: "http-proxy" (same service name) # has_tag: "haproxy" (if nmap product is just "HAProxy") # ============================================================================ # TAG PATTERN SYNTAX # ============================================================================ # - Standard regex # - Case-sensitive by default — prefix (?i) for case-insensitive # - Matched against full stdout + stderr of the command # - Multiple patterns are independent — each applies its own tag # - Tags are written immediately after the action completes; # subsequent actions can depend on them via has_tag: