From a465039887bc299be2c8b2a3aab634ee3ae19c32 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Fri, 26 May 2023 15:14:34 -0600 Subject: [PATCH 1/7] 2 new capabilities: send-file and import-file --- salt/soc/files/bin/salt-relay.sh | 76 +++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index a98a587a6..bb0fa82a3 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -17,7 +17,7 @@ function log() { function make_pipe() { path=$1 - log "Creating pipe: $path" + log "Creating pipe: $path" rm -f "${path}" mkfifo "${path}" chmod 0660 "${path}" @@ -172,6 +172,74 @@ function manage_salt() { fi } +function send_file() { + request=$1 + from=$(echo "$request" | jq -r .from) + to=$(echo "$request" | jq -r .to) + node=$(echo "$request" | jq -r .node) + [ $(echo "$request" | jq -r .cleanup) != "true" ] ; cleanup=$? + + log "From: $from" + log "To: $to" + log "Node: $node" + log "Cleanup: $cleanup" + + response=$($CMD_PREFIX salt-cp -C "$node" "$from" "$to") + exit_code=$? + + log Response:$'\n'"$response" + log "Exit Code: $exit_code" + + if [[ exit_code -eq 0 ]]; then + if [[ $cleanup -eq 1 ]]; then + log "Cleaning up file $from" + rm -f "$from" + fi + $(echo "true" > "${SOC_PIPE}") + else + $(echo "false" > "${SOC_PIPE}") + fi +} + +function import_file() { + request=$1 + node=$(echo "$request" | jq -r .node) + file=$(echo "$request" | jq -r .file) + importer=$(echo "$request" | jq -r .importer) + + log "Node: $node" + log "File: $file" + log "Importer: $importer" + + case $importer in + pcap) + response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-pcap $file'") + exit_code=$? + ;; + evtx) + response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-evtx $file'") + exit_code=$? + ;; + *) + response="Unsupported importer: $importer" + exit_code=1 + ;; + esac + + rm "$file" + + log Response:$'\n'"$response" + log "Exit Code: $exit_code" + + if [[ exit_code -eq 0 ]]; then + log "true" + $(echo "true" > "${SOC_PIPE}") + else + log "false" + $(echo "false" > "${SOC_PIPE}") + fi +} + while true; do log "Listening for request" request=$(cat ${SOC_PIPE}) @@ -191,6 +259,12 @@ while true; do manage-salt) manage_salt "${request}" ;; + send-file) + send_file "${request}" + ;; + import-file) + import_file "${request}" + ;; *) log "Unsupported command: $command" $(echo "false" > "${SOC_PIPE}") From 49055e260f0d2e5535760934f04c9fdfc6f4d5c4 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 31 May 2023 09:47:52 -0600 Subject: [PATCH 2/7] salt-relay import-file reporting On successful import, return dashboard URL --- salt/soc/files/bin/salt-relay.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index bb0fa82a3..832633c77 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -232,8 +232,8 @@ function import_file() { log "Exit Code: $exit_code" if [[ exit_code -eq 0 ]]; then - log "true" - $(echo "true" > "${SOC_PIPE}") + url=$(echo "$response" | sed ':a;N;$!ba;s/\n//g' | grep -E -o "https://\S*&z=UTC") + $(echo "$url" > "${SOC_PIPE}") else log "false" $(echo "false" > "${SOC_PIPE}") From 89d789fe0f7cfd1707ee2bae7f230ce96b185919 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 31 May 2023 12:47:44 -0600 Subject: [PATCH 3/7] New folder for salt to maintain This folder is where a manager will initially store uploaded PCAP/EVTX files before sending to sensors. Sensors will store uploads in this folder on their own system. --- salt/soc/config.sls | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/salt/soc/config.sls b/salt/soc/config.sls index 399ff72cd..42c24c9e0 100644 --- a/salt/soc/config.sls +++ b/salt/soc/config.sls @@ -1,5 +1,5 @@ # Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one -# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at +# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at # https://securityonion.net/license; you may not use this file except in compliance with the # Elastic License 2.0. @@ -78,7 +78,7 @@ soc_sbin: # - name: /usr/sbin # - source: salt://soc/tools/sbin_jinja # - user: 939 -# - group: 939 +# - group: 939 # - file_mode: 755 # - template: jinja @@ -106,6 +106,13 @@ socusersroles: - require: - sls: manager.sync_es_users +socuploaddir: + file.directory: + - name: /nsm/soc/uploads + - user: 939 + - group: 939 + - makedirs: True + {% else %} {{sls}}_state_not_allowed: From 1b7095fa817fae2264139bd897465b69b6d80e3c Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 31 May 2023 16:31:40 -0600 Subject: [PATCH 4/7] Improved import-file url regex sed doesn't remove ALL whitespace, only newlines. It's better to stop at the first whitespace than to stop at a particular, maybe-not-last query string parameter. --- salt/soc/files/bin/salt-relay.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index 832633c77..d15b3ee22 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -232,7 +232,7 @@ function import_file() { log "Exit Code: $exit_code" if [[ exit_code -eq 0 ]]; then - url=$(echo "$response" | sed ':a;N;$!ba;s/\n//g' | grep -E -o "https://\S*&z=UTC") + url=$(echo "$response" | sed ':a;N;$!ba;s/\n//g' | grep -E -o "https://\S*") $(echo "$url" > "${SOC_PIPE}") else log "false" From 451a4784a1d21b63cfe0e27466dc7a0f7acd5e4e Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Fri, 2 Jun 2023 13:12:37 -0600 Subject: [PATCH 5/7] send-file and import-file security Encrypt the file with a passphrase before sending and decrypt the file with the same passphrase before importing. --- salt/soc/files/bin/salt-relay.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index d15b3ee22..313a21141 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -184,9 +184,17 @@ function send_file() { log "Node: $node" log "Cleanup: $cleanup" - response=$($CMD_PREFIX salt-cp -C "$node" "$from" "$to") + log "encrypting..." + gpg --passphrase "infected" --batch --symmetric --cipher-algo AES256 "$from" + + fromgpg="$from.gpg" + + log "sending..." + response=$($CMD_PREFIX salt-cp -C "$node" "$fromgpg" "$to") exit_code=$? + rm -f "$fromgpg" + log Response:$'\n'"$response" log "Exit Code: $exit_code" @@ -211,6 +219,12 @@ function import_file() { log "File: $file" log "Importer: $importer" + filegpg="$file.gpg" + + log "decrypting..." + gpg --passphrase "infected" --batch --decrypt "$filegpg" > "$file" + + log "importing..." case $importer in pcap) response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-pcap $file'") @@ -226,7 +240,7 @@ function import_file() { ;; esac - rm "$file" + rm "$file" "$filegpg" log Response:$'\n'"$response" log "Exit Code: $exit_code" From 41951659eced5b46b1f2a0b72f8103a87bc83d7c Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Tue, 13 Jun 2023 14:54:15 -0600 Subject: [PATCH 6/7] Use importer's new --json flag. Using the new --json flag is not only more reliable than using a regex, the way the import script was written even re-imports will provide a url. This means that in more cases we can provide the results to the users (even if nothing changed). --- salt/soc/files/bin/salt-relay.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index 313a21141..e59fb41ab 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -227,11 +227,11 @@ function import_file() { log "importing..." case $importer in pcap) - response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-pcap $file'") + response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-pcap $file --json'") exit_code=$? ;; evtx) - response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-evtx $file'") + response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-evtx $file --json'") exit_code=$? ;; *) @@ -246,7 +246,8 @@ function import_file() { log "Exit Code: $exit_code" if [[ exit_code -eq 0 ]]; then - url=$(echo "$response" | sed ':a;N;$!ba;s/\n//g' | grep -E -o "https://\S*") + # trim off the node header ("manager_standalone:\n") and parse out the URL + url=$(echo "$response" | tail -n +2 | jq -r .url) $(echo "$url" > "${SOC_PIPE}") else log "false" From ad28ea275ff60530c63d57beb83df1cdc12c89a8 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 14 Jun 2023 12:10:34 -0600 Subject: [PATCH 7/7] Better state management When salt-cp runs it's course and finds it can't send a file, it outputs a report saying as much but the exit code will be zero. Now we remove the filename and node from the response and look for `True` to know if it succeeded. Also, respect the cleanup flag on success or failure. Check the status of the decryption process before importing. No longer decrypt locally, issue salt command for the remote client to do the decrypting. --- salt/soc/files/bin/salt-relay.sh | 54 +++++++++++++++++++------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index e59fb41ab..3e893e64c 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -188,9 +188,14 @@ function send_file() { gpg --passphrase "infected" --batch --symmetric --cipher-algo AES256 "$from" fromgpg="$from.gpg" + filename=$(basename "$fromgpg") log "sending..." response=$($CMD_PREFIX salt-cp -C "$node" "$fromgpg" "$to") + # salt-cp returns 0 even if the file transfer fails, so we need to check the response. + # Remove the node and filename from the response on the off-chance they contain + # the word "True" in them + echo $response | sed "s/$node//" | sed "s/$filename//" | grep True exit_code=$? rm -f "$fromgpg" @@ -198,11 +203,12 @@ function send_file() { log Response:$'\n'"$response" log "Exit Code: $exit_code" + if [[ $cleanup -eq 1 ]]; then + log "Cleaning up file $from" + rm -f "$from" + fi + if [[ exit_code -eq 0 ]]; then - if [[ $cleanup -eq 1 ]]; then - log "Cleaning up file $from" - rm -f "$from" - fi $(echo "true" > "${SOC_PIPE}") else $(echo "false" > "${SOC_PIPE}") @@ -222,25 +228,31 @@ function import_file() { filegpg="$file.gpg" log "decrypting..." - gpg --passphrase "infected" --batch --decrypt "$filegpg" > "$file" + $CMD_PREFIX "salt '$node' cmd.run 'gpg --passphrase \"infected\" --batch --decrypt \"$filegpg\" > \"$file\"'" + decrypt_code=$? - log "importing..." - case $importer in - pcap) - response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-pcap $file --json'") - exit_code=$? - ;; - evtx) - response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-evtx $file --json'") - exit_code=$? - ;; - *) - response="Unsupported importer: $importer" - exit_code=1 - ;; - esac + if [[ $decrypt_code -eq 0 ]]; then + log "importing..." + case $importer in + pcap) + response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-pcap $file --json'") + exit_code=$? + ;; + evtx) + response=$($CMD_PREFIX "salt '$node' cmd.run 'so-import-evtx $file --json'") + exit_code=$? + ;; + *) + response="Unsupported importer: $importer" + exit_code=1 + ;; + esac + else + response="Failed to decrypt file: $file" + exit_code=$decrypt_code + fi - rm "$file" "$filegpg" + rm -f "$file" "$filegpg" log Response:$'\n'"$response" log "Exit Code: $exit_code"