#!/usr/bin/env bash set -euo pipefail # Usage: # scripts/send-wazuh-proposal-required-events.sh [selector] [count] [delay_seconds] # # selector: # all | a1 | a2 | a3 | a4 | # example usecase_id: A2-01, A3-05, A4-24 SELECTOR="${1:-all}" COUNT="${2:-1}" DELAY="${3:-0.3}" EVENT_DELAY="${EVENT_DELAY:-0.05}" DRY_RUN="${DRY_RUN:-0}" FOREVER="false" PROFILE="${PROFILE:-simulation}" for arg in "${@:4}"; do case "${arg}" in --forever) FOREVER="true" ;; --profile=*) PROFILE="${arg#*=}" ;; *) echo "error: unexpected argument '${arg}'" echo "usage: scripts/send-wazuh-proposal-required-events.sh [selector] [count] [delay_seconds] [--forever] [--profile=simulation|production]" exit 1 ;; esac done WAZUH_SYSLOG_HOST="${WAZUH_SYSLOG_HOST:-127.0.0.1}" WAZUH_SYSLOG_PORT="${WAZUH_SYSLOG_PORT:-514}" FGT_DEVNAME="${FGT_DEVNAME:-FGT80F-Branch01}" FGT_DEVID="${FGT_DEVID:-FGT80FTK20000001}" WIN_HOST="${WIN_HOST:-win-dc01}" DNS_HOST="${DNS_HOST:-dns-fw-01}" SIM_VPN_USER="${SIM_VPN_USER:-remote.user}" if ! [[ "${COUNT}" =~ ^[0-9]+$ ]] || [[ "${COUNT}" -lt 1 ]]; then echo "error: count must be a positive integer" exit 1 fi if ! [[ "${DELAY}" =~ ^[0-9]+([.][0-9]+)?$ ]]; then echo "error: delay must be numeric" exit 1 fi if ! [[ "${EVENT_DELAY}" =~ ^[0-9]+([.][0-9]+)?$ ]]; then echo "error: EVENT_DELAY must be numeric" exit 1 fi if [[ "${PROFILE}" != "simulation" && "${PROFILE}" != "production" ]]; then echo "error: profile must be simulation or production" exit 1 fi rand_public_ip() { if [[ $((RANDOM % 2)) -eq 0 ]]; then echo "198.51.100.$((RANDOM % 240 + 10))" else echo "203.0.113.$((RANDOM % 240 + 10))" fi } rand_private_ip() { echo "10.$((RANDOM % 20 + 10)).$((RANDOM % 200 + 1)).$((RANDOM % 240 + 10))" } rand_domain() { echo "ioc-$((RANDOM % 9000 + 1000)).malicious.example" } emit_syslog() { local msg="$1" local sent="false" if [[ "${DRY_RUN}" == "1" ]]; then echo "[DRY_RUN $(date -u +'%Y-%m-%dT%H:%M:%SZ')] ${msg}" return 0 fi if command -v nc >/dev/null 2>&1; then if printf "%s\n" "${msg}" | nc -u -w1 "${WAZUH_SYSLOG_HOST}" "${WAZUH_SYSLOG_PORT}"; then sent="true" fi fi if [[ "${sent}" != "true" ]]; then if printf "%s\n" "${msg}" >"/dev/udp/${WAZUH_SYSLOG_HOST}/${WAZUH_SYSLOG_PORT}" 2>/dev/null; then sent="true" fi fi if [[ "${sent}" != "true" ]]; then echo "error: failed to send syslog event to ${WAZUH_SYSLOG_HOST}:${WAZUH_SYSLOG_PORT}/udp" return 1 fi echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] sent: ${msg}" } selector_matches() { local id="$1" local section="$2" local sel sel="$(echo "${SELECTOR}" | tr '[:upper:]' '[:lower:]')" local idl idl="$(echo "${id}" | tr '[:upper:]' '[:lower:]')" local sec sec="$(echo "${section}" | tr '[:upper:]' '[:lower:]')" [[ "${sel}" == "all" || "${sel}" == "${sec}" || "${sel}" == "${idl}" ]] } emit_fgt_usecase() { local id="$1" local section="$2" local severity="$3" local usecase="$4" local body="$5" selector_matches "${id}" "${section}" || return 0 local tags if [[ "${PROFILE}" == "production" ]]; then tags="soc_mvp_test=true source=fortigate severity=${severity}" else tags="soc_mvp_test=true source=fortigate section=${section} usecase_id=${id} severity=${severity} usecase=\"${usecase}\"" fi emit_syslog "<190>date=$(date '+%Y-%m-%d') time=$(date '+%H:%M:%S') devname=\"${FGT_DEVNAME}\" devid=\"${FGT_DEVID}\" eventtime=$(date +%s) vd=\"root\" ${tags} ${body}" sleep "${EVENT_DELAY}" } emit_dns_usecase() { local id="$1" local section="$2" local severity="$3" local usecase="$4" local body="$5" selector_matches "${id}" "${section}" || return 0 local tags if [[ "${PROFILE}" == "production" ]]; then tags="soc_mvp_test=true source=dns severity=${severity}" else tags="soc_mvp_test=true source=dns section=${section} usecase_id=${id} severity=${severity} usecase=\"${usecase}\"" fi emit_syslog "<189>$(date '+%b %d %H:%M:%S') ${DNS_HOST} ${tags} ${body}" sleep "${EVENT_DELAY}" } emit_windows_usecase() { local id="$1" local section="$2" local severity="$3" local usecase="$4" local body="$5" selector_matches "${id}" "${section}" || return 0 local tags if [[ "${PROFILE}" == "production" ]]; then tags="soc_mvp_test=true source=windows severity=${severity}" else tags="soc_mvp_test=true source=windows section=${section} usecase_id=${id} severity=${severity} usecase=\"${usecase}\"" fi emit_syslog "<182>$(date '+%b %d %H:%M:%S') ${WIN_HOST} ${tags} ${body}" sleep "${EVENT_DELAY}" } emit_a1() { local sip local domain local mip sip="$(rand_private_ip)" domain="$(rand_domain)" mip="$(rand_public_ip)" emit_dns_usecase "A1-01" "A1" "medium" \ "DNS Network Traffic Communicate to Malicious Domain" \ "event_type=ioc_dns_traffic src_ip=${sip} query=${domain} resolved_ip=${mip} action=blocked" emit_dns_usecase "A1-02" "A1" "medium" \ "DNS Network Traffic Malicious Domain IOCs Detection" \ "event_type=ioc_domain_match src_ip=${sip} ioc_type=domain ioc_value=${domain} feed=threatintel_main confidence=high action=alert" } emit_a2() { local pub local sip pub="$(rand_public_ip)" sip="$(rand_private_ip)" emit_fgt_usecase "A2-01" "A2" "high" "IPS IDS Network Traffic Allowed RDP from Public IPs" \ "logid=\"0000000013\" type=\"traffic\" subtype=\"forward\" srcip=${pub} dstip=${sip} dstport=3389 service=\"RDP\" action=\"accept\" policyid=44" emit_fgt_usecase "A2-02" "A2" "high" "IPS IDS Firewall Account Admin Password Change" \ "logid=\"0100044547\" type=\"event\" subtype=\"system\" user=\"admin\" action=\"password-change\" target_account=\"admin\"" emit_fgt_usecase "A2-03" "A2" "high" "IPS IDS Firewall Account Create Add Admin Account" \ "logid=\"0100044548\" type=\"event\" subtype=\"system\" user=\"admin\" action=\"create-admin\" target_account=\"secops_admin\"" emit_fgt_usecase "A2-04" "A2" "high" "IPS IDS Firewall Configure Disabled Email Notification" \ "logid=\"0100044551\" type=\"event\" subtype=\"system\" action=\"config-change\" config_item=\"alertemail\" config_value=\"disable\"" emit_fgt_usecase "A2-05" "A2" "low" "IPS IDS Firewall Configure Download Configure FW" \ "logid=\"0100044552\" type=\"event\" subtype=\"system\" action=\"download-config\" user=\"admin\"" emit_fgt_usecase "A2-06" "A2" "medium" "IPS IDS IDS Alert Multiple Critical High" \ "logid=\"0720018432\" type=\"utm\" subtype=\"ips\" action=\"detected\" attack=\"Multiple.Critical.High.Signatures\" severity=\"high\" count=7" emit_fgt_usecase "A2-07" "A2" "low" "IPS IDS Network Traffic Port Scanning" \ "logid=\"0720018433\" type=\"utm\" subtype=\"anomaly\" attack=\"TCP.Port.Scan\" srcip=${pub} dstip=${sip} action=\"detected\"" emit_fgt_usecase "A2-08" "A2" "medium" "IPS IDS Network Traffic IOC Detection" \ "logid=\"0720018434\" type=\"utm\" subtype=\"ips\" ioc_type=ip ioc_value=$(rand_public_ip) action=\"blocked\"" emit_fgt_usecase "A2-09" "A2" "medium" "IPS IDS Network Traffic Port Scanning from Private IP" \ "logid=\"0720018435\" type=\"utm\" subtype=\"anomaly\" attack=\"Internal.Port.Scan\" srcip=$(rand_private_ip) dstip=$(rand_private_ip) action=\"detected\"" emit_fgt_usecase "A2-10" "A2" "medium" "IPS IDS Network Traffic Communicate to Malicious IP" \ "logid=\"0000000013\" type=\"traffic\" subtype=\"forward\" srcip=$(rand_private_ip) dstip=$(rand_public_ip) threat_label=\"known-c2\" action=\"accept\"" } emit_a3() { local out_th out_th="$(rand_public_ip)" emit_fgt_usecase "A3-01" "A3" "high" "VPN Authentication Success from Guest Account" \ "logid=\"0101037131\" type=\"event\" subtype=\"vpn\" action=\"ssl-login-success\" user=\"guest\" srcip=${out_th} country=\"TH\"" emit_fgt_usecase "A3-02" "A3" "high" "VPN Authentication Success from Multiple Country" \ "logid=\"0101037132\" type=\"event\" subtype=\"vpn\" action=\"ssl-login-success\" user=\"${SIM_VPN_USER}\" srcip=${out_th} country=\"US\" previous_country=\"TH\"" emit_fgt_usecase "A3-03" "A3" "high" "VPN Authentication Brute Force Success" \ "logid=\"0101037133\" type=\"event\" subtype=\"vpn\" action=\"ssl-login-success\" user=\"${SIM_VPN_USER}\" srcip=${out_th} failed_attempts_before_success=18" emit_fgt_usecase "A3-04" "A3" "low" "VPN Authentication Multiple Fail Many Accounts from One Source" \ "logid=\"0101037134\" type=\"event\" subtype=\"vpn\" action=\"ssl-login-fail\" srcip=${out_th} failed_accounts=12" emit_fgt_usecase "A3-05" "A3" "high" "VPN Authentication Success from Outside Thailand" \ "logid=\"0101037135\" type=\"event\" subtype=\"vpn\" action=\"ssl-login-success\" user=\"${SIM_VPN_USER}\" srcip=${out_th} country=\"US\" expected_country=\"TH\"" } emit_a4() { emit_windows_usecase "A4-01" "A4" "medium" "Windows Authentication Multiple Fail from Privileged Account" \ "event_id=4625 account=\"administrator\" src_ip=$(rand_private_ip) fail_count=9" emit_windows_usecase "A4-02" "A4" "medium" "Windows Authentication Multiple Fail from Service Account" \ "event_id=4625 account=\"svc_backup\" src_ip=$(rand_private_ip) fail_count=11" emit_windows_usecase "A4-03" "A4" "medium" "Windows AD Enumeration with Malicious Tools" \ "event_id=4688 process=\"adfind.exe\" user=\"user1\" host=\"${WIN_HOST}\"" emit_windows_usecase "A4-04" "A4" "medium" "Windows Authentication Fail from Public IPs" \ "event_id=4625 account=\"user1\" src_ip=$(rand_public_ip) fail_count=4" emit_windows_usecase "A4-05" "A4" "medium" "Windows File Share Enumeration to Single Destination" \ "event_id=5145 account=\"user1\" src_ip=$(rand_private_ip) share=\"\\\\\\\\fileserver\\\\finance\" object_count=87" emit_windows_usecase "A4-06" "A4" "high" "Windows Authentication Success from Public IPs" \ "event_id=4624 account=\"user2\" src_ip=$(rand_public_ip) logon_type=10" emit_windows_usecase "A4-07" "A4" "high" "Windows Authentication Privileged Account Impersonation" \ "event_id=4624 account=\"administrator\" impersonation=true source_account=\"user2\"" emit_windows_usecase "A4-08" "A4" "high" "Windows Authentication Successful Pass the Hash RDP" \ "event_id=4624 account=\"administrator\" logon_type=10 auth_package=\"NTLM\" pth_indicator=true" emit_windows_usecase "A4-09" "A4" "high" "Windows Authentication Success from Guest Account" \ "event_id=4624 account=\"guest\" logon_type=3" emit_windows_usecase "A4-10" "A4" "high" "Windows Authentication Interactive Logon Success by Service Account" \ "event_id=4624 account=\"svc_backup\" logon_type=2" emit_windows_usecase "A4-11" "A4" "high" "Windows Account Added to Privileged Custom Group" \ "event_id=4732 account=\"user3\" target_group=\"SOC-Privileged-Custom\"" emit_windows_usecase "A4-12" "A4" "high" "Windows Account Added to Privileged Group" \ "event_id=4728 account=\"user3\" target_group=\"Domain Admins\"" emit_windows_usecase "A4-13" "A4" "high" "Windows Domain Configure DSRM Password Reset" \ "event_id=4794 account=\"administrator\" action=\"dsrm-password-reset\"" emit_windows_usecase "A4-14" "A4" "low" "Windows Authentication Multiple Fail One Account from Many Sources" \ "event_id=4625 account=\"user4\" src_count=15 fail_count=28" emit_windows_usecase "A4-15" "A4" "low" "Windows Authentication Multiple Fail Many Accounts from One Source" \ "event_id=4625 src_ip=$(rand_private_ip) account_count=18 fail_count=42" emit_windows_usecase "A4-16" "A4" "low" "Windows Authentication Multiple Fail from Guest Account" \ "event_id=4625 account=\"guest\" fail_count=9" emit_windows_usecase "A4-17" "A4" "low" "Windows Authentication Multiple Fail One Account from One Source" \ "event_id=4625 account=\"user5\" src_ip=$(rand_private_ip) fail_count=10" emit_windows_usecase "A4-18" "A4" "low" "Windows Authentication Multiple Interactive Logon Denied" \ "event_id=4625 account=\"user6\" logon_type=2 fail_count=7" emit_windows_usecase "A4-19" "A4" "low" "Windows Authentication Password Spray" \ "event_id=4625 spray=true src_ip=$(rand_public_ip) attempted_accounts=25" emit_windows_usecase "A4-20" "A4" "low" "Windows Authentication Attempt from Disabled Account" \ "event_id=4625 account=\"disabled.user\" status=\"0xC0000072\"" emit_windows_usecase "A4-21" "A4" "low" "Windows Domain Account Created" \ "event_id=4720 account=\"new.domain.user\" account_type=\"domain\"" emit_windows_usecase "A4-22" "A4" "low" "Windows Local Account Re Enabled" \ "event_id=4722 account=\"local.user\" account_type=\"local\"" emit_windows_usecase "A4-23" "A4" "low" "Windows Local Account Created" \ "event_id=4720 account=\"local.new\" account_type=\"local\"" emit_windows_usecase "A4-24" "A4" "low" "Windows Domain Account Re Enabled" \ "event_id=4722 account=\"domain.reenabled\" account_type=\"domain\"" } emit_selected_set() { local sel sel="$(echo "${SELECTOR}" | tr '[:upper:]' '[:lower:]')" case "${sel}" in all) emit_a1 emit_a2 emit_a3 emit_a4 ;; a1|a1-*) emit_a1 ;; a2|a2-*) emit_a2 ;; a3|a3-*) emit_a3 ;; a4|a4-*) emit_a4 ;; *) # Exact usecase selectors (e.g. A3-05) are handled by selector_matches. emit_a1 emit_a2 emit_a3 emit_a4 ;; esac } echo "starting proposal-required log simulator" echo "selector=${SELECTOR} count=${COUNT} delay=${DELAY}s event_delay=${EVENT_DELAY}s dry_run=${DRY_RUN} profile=${PROFILE}" echo "target=${WAZUH_SYSLOG_HOST}:${WAZUH_SYSLOG_PORT}/udp" if [[ "${FOREVER}" == "true" ]]; then echo "running forever with interval ${DELAY}s (Ctrl+C to stop)" trap 'echo; echo "stopped"; exit 0' INT TERM while true; do emit_selected_set sleep "${DELAY}" done else for ((i=1; i<=COUNT; i++)); do emit_selected_set if [[ "${i}" -lt "${COUNT}" ]]; then sleep "${DELAY}" fi done echo "done" fi