diff options
author | heqnx <root@heqnx.com> | 2025-05-13 12:36:45 +0300 |
---|---|---|
committer | heqnx <root@heqnx.com> | 2025-05-13 12:36:45 +0300 |
commit | 5c23f03b29cad13844eac9b473801c99ff557279 (patch) | |
tree | 914bbb629892aaf66c9244076ba0aca323a671f4 | |
parent | dc6d6e028489996d4bfd12b28147b86e5cf0551e (diff) | |
download | gists-5c23f03b29cad13844eac9b473801c99ff557279.tar.gz gists-5c23f03b29cad13844eac9b473801c99ff557279.zip |
added setup-command-canaries.sh
-rw-r--r-- | setup-command-canaries.sh | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/setup-command-canaries.sh b/setup-command-canaries.sh new file mode 100644 index 0000000..60324bd --- /dev/null +++ b/setup-command-canaries.sh @@ -0,0 +1,138 @@ +#!/bin/bash +set -e + +if ! command -v curl &>/dev/null; then + printf "%s\n" "[err] curl not found" + exit 1 +fi + +function usage() { + printf "%s\n" \ + "set up command canaries for early intrusion detection via webhook notifications" \ + "usage: $(basename ${0}) <args>" \ + "" \ + "options" \ + "-c <cmd> commands to monitor; repeatable, e.g., -c whoami -c id" \ + "-s <service> notification service: discord|slack" \ + "-u <url> webhook url" \ + "-m <method> http method, default POST" \ + "-x <header> webhook headers; repeatable, e.g., -x 'key1: value1' -x 'key2: value2'" \ + "-t <channel> channel or destination id for notifications" \ + "-h print this help message and exit" \ + "" \ + "examples" \ + "discord" \ + "$(basename ${0}) -s discord -c whoami -c id -u https://discord.com/api/webhooks/xxx" \ + "" \ + "slack" \ + "$(basename ${0}) -s slack -c whoami -c id -u https://slack.com/api/chat.postmessage -t mychannel -x 'authorization: bearer xoxb-a-b-c'" \ + "" \ + "output" \ + "generates canaries.txt with shell functions to append to /etc/bash.bashrc or ~/.bashrc." + exit 1 +} + +function validate_url() { + local url="${1}" + if ! [[ "${url}" =~ ^https:// ]]; then + printf "%s\n" "[err] invalid url: ${url} (must be https)" + exit 1 + fi +} + +function validate_method() { + local method="${1}" + if ! [[ "${method}" =~ ^(GET|POST|PUT)$ ]]; then + printf "[err] invalid http method: ${method} (use GET, POST, or PUT)" + exit 1 + fi +} + +function validate_command() { + local cmd="${1}" + if ! command -v "${cmd}" &>/dev/null; then + printf "[wrn] command '${cmd}' not found in path" + fi +} + +output_file="canaries.txt" +method="POST" +commands=() +webhook_headers=() +channel="" + +while getopts "c:s:u:m:x:t:h" opts; do + case "${opts}" in + c) commands+=("${OPTARG}");; + s) service="${OPTARG}";; + u) webhook_url="${OPTARG}";; + m) method="${OPTARG}";; + x) webhook_headers+=("${OPTARG}");; + t) channel="${OPTARG}";; + h) usage;; + *) usage;; + esac +done + +if test ${#commands[@]} -eq 0; then + printf "%s\n" "[err] at least one command (-c) is required" + usage +fi +if ! test "${webhook_url}" && ! test "${service}"; then + printf "%s\n" "[err] webhook url (-u) or service (-s) is required" + usage +fi + +validate_method "${method}" +for cmd in "${commands[@]}"; do + validate_command "${cmd}" +done + +test "${webhook_url}" && validate_url "${webhook_url}" + +case "${service}" in + discord) + webhook_url="${webhook_url:-}" + webhook_headers+=("Content-Type: application/json") + webhook_data="{\"username\":\"notifications-bot\", \"content\":\"**Command Canary Triggered**\nHostname: \${HOSTNAME}\nCommand: \${FUNCNAME[0]}\nTime: \$(date -u --iso-8601=seconds)\"}" + ;; + + slack) + webhook_headers+=("Content-Type: application/json") + ! test "${channel}" || { printf "%s\n" "[err] slack requires a channel (-t)"; exit 1; } + webhook_data="{\"text\":\"**Command Canary Triggered**\\nHostname: \${HOSTNAME}\\nCommand: \${FUNCNAME[0]}\\nTime: \$(date -u --iso-8601=seconds)\",\"channel\":\"${channel}\"}" + ;; + *) + usage + ;; +esac + +curl_headers="" +for header in "${webhook_headers[@]}"; do + curl_headers+="-H '${header}' " +done + +>"${output_file}" + +for cmd in "${commands[@]}"; do + sanitized_data=$(printf "%s" "${webhook_data}" | sed -e 's/"/\\"/g') + cat >> "${output_file}" << EOF +function ${cmd}() { + ( + $(which curl) -sSkL \\ + -X ${method} \\ + ${curl_headers} \\ + --data "${sanitized_data}" \\ + ${webhook_url} \\ + -o /dev/null 2>&1 >/dev/null & + ) + command ${cmd} "\${@}" +} +EOF +done + +printf "%s\n" \ + "[inf] successfully created ${output_file}" \ + "[inf] to enable canaries, append the contents to:" \ + " - System-wide: /etc/bash.bashrc" \ + " - User-specific: ~/.bashrc" |