02-08-2024 03:27 AM - edited 02-08-2024 03:33 AM
I am working on a script that will produce an alert message using Swift Dialog if CrowdStrike has logged any malicious behaviors. This is meant for my support team who may be helping one of our users find out if CrowdStrike may be responsible for an issue they are having. I created a Self Service policy that runs the script. I was given this command from our CrowdStrike rep:
log show --predicate 'process == "Falcon Notifications"' --last 1h
I used this command to create a variable:
detection=$(log show --predicate 'process == "Falcon Notifications"' --last 1h | grep "malicious behavior detected")
If I simulate an incident of malicious behavior, the phrase that follows grep will appear in the log. Therefore, when I echo "$detection" it will output a result that contains that phrase. If the log doesn't have that phrase, the output of $detection will be nothing.
Later in the script, I have a conditional statement:
if [ "$detection" ]; then
echo "Malicious behavior detected"
# Display alert
somethingBad
else
echo "No malicious behavior detected"
# Display alert
allGood
fi
The "somethingBad" is the function that generates the alert in Swift Dialog saying that malicious behavior was detected. The "allGood" is a function that generates an alert saying that malicious behavior was not detected. If I simulate a detection event and then run the entire script through CodeRunner, the result will be a Swift Dialog window informing me that malicious behavior was detected. If I run it without first simulating a detection event, the result is the alert that malicious behavior was not detected. When I add this script to a policy in Jamf Pro, I see an error in the policy log stating:
Script result: /Library/Application Support/JAMF/tmp/CrowdStrike Detection Alert:detection:12: too many arguments
Because of the error, the script will always generate an alert saying that no malicious behavior was found. "$detection" will always result in an output of nothing.
I have seen this error before and it usually means that my variable contains whitespace somewhere. I don't think that's what is happening in this case. I have checked, double checked, triple checked, quadruple checked the command in the variable. There is no white space. What else could be causing this error? I'm happy to post the entire script if needed. The logic part of the script is actually the shortest part. It's just a conditional statement that checks if the log has the phrase "malicious behavior detected" and then runs the appropriate function for the output of "$detection". More lines of code are devoted to creating the Swift Dialog alerts than anything else. Those work perfectly. I took on this task to make the job of my support team easier but also to get better at working with Swift Dialog.
What could be causing the "too many arguments" error? I would appreciate some help figuring this out.
Solved! Go to Solution.
Posted on 02-08-2024 11:11 AM
So this is a bit in the weeds, so TLDR use "/usr/bin/log" instead of log.
The reason for this is that ZSH has a shell builtin named log which overrides the /usr/bin/log command found on the PATH. So when you type just log in your script, it finds the shell builtin rather than the macOS log command.
❯ /bin/zsh -c "which -a log"
log: shell built-in command
/usr/bin/log
The reason you do not experience this behavior when running the script locally is that you are using an interactive shell. Interactive shell's load the zshrc files (first at /etc/zshrc and then at ~/.zshrc), where non-interactive shells so not. In order to make the /usr/bin/log command easily accessible on macOS, Apple chose to include the line "disable log" in the default /etc/zshrc.
❯ cat /etc/zshrc
[...]
# Disable the log builtin, so we don't conflict with /usr/bin/log
disable log
[...]
This causes the log builtin for zsh to be disabled in interactive shells and thus when running log the shell only finds the /usr/bin/log command. You can see this behavior by running zsh with the -i flag for interactive. All users login shells are interactive by default (and so is your CommandRunner app's shell)
❯ /bin/zsh -i -c "which -a log"
/usr/bin/log
The Jamf binary, however, runs non-interactive shells so when you have Jamf run your script it does not load the /etc/zshrc file and does not disable the log builtin so your "log show ..." command is running a completely different command which requires different arguments. Thus, the "to many arguments" error.
Posted on 02-08-2024 05:42 AM
When using if [ ] you need an operator. In this case the operator should be "-n".
so your if statement should be the following
if [ -n "detection" ]; then
02-08-2024 07:10 AM - edited 02-08-2024 07:29 AM
Thanks for this! I should have thought about that. I use operators all the time so I'm not sure why I didn't think to use one. Since I don't want to get my security team all stirred up I am avoiding manually triggering a detection event in CrowdStrike so what I have been testing with is using logging by Safari. I just used this variable:
detection=$(log show --predicate 'process == "Safari"' --last 1h | grep "tcp_input")
In CodeRunner this will trigger the alert that malicious behavior was detected. Since I have an echo step right before the conditional statement runs, I can see what the output is for the "$detection" variable. When I put this same script in Jamf Pro, the echo step doesn't show up in the policy log. I get the alert saying no malicious behavior was detected, and I still see:
Script result: /Library/Application Support/JAMF/tmp/CrowdStrike Detection Alert:log:12: too many arguments
It's like Jamf Pro has this problem but otherwise the script runs on my MacBook Pro just fine.
Here is the entire script that I have been testing in CodedRunner:
#!/bin/zsh
###########################
# Displays a Swift Dialog alert if CrowdStrike has logged a malicious detection. A button is provided to
# collect the logs from CrowdStrike. If no detection was logged, the alert will state that no detections
# have been logged.
#
# 2/7/24 | Howie Isaacks
###########################
#### Variables ####
detection=$(log show --predicate 'process == "Falcon Notifications"' --last 1h | grep "malicious behavior detected")
dialogBinary="/usr/local/bin/dialog"
#### Functions ####
function somethingBad(){
echo "Displaying alert that malicious behavior was detected"
"$dialogBinary" "${dialog1_cmd[@]}"
# Running collect logs function
collectLogs
}
function allGood(){
echo "Displaying message no detected malicious behavior."
"$dialogBinary" "${dialog3_cmd[@]}"
}
function collectLogs(){
echo "Collecting logs"
"$dialogBinary" "${dialog2_cmd[@]}"
/Applications/Falcon.app/Contents/Resources/falconctl diagnose
}
#### Dialog settings - dialog 1, dialog 2, dialog 3 ####
# If malicious behavior is detected
dialog1_cmd=(
-p
-o
--title "Malicious behavior was found"
--titlefont "size=18, colour=red"
--message "Malicious behavior was found. Click **Collect** to collect the logs."
--messagealignment "left"
--messageposition "top"
--height '300'
--width '650'
--icon "sf=eye.trianglebadge.exclamationmark,palette=yellow,red"
--iconsize "150"
--button1text "Collect"
)
# Generate logs prompt
dialog2_cmd=(
-p
-o
--title "Generate logs..."
--titlefont "size=18, colour=blue"
--message "Logs are being collected. When the process is complete, a Finder window will appear showing the location of the collected logs. Look for a file called **falconctl_diagnose**. Click OK to continue."
--messagealignment "left"
--messageposition "top"
--height '300'
--width '650'
--icon "sf=hand.raised.circle.fill,palette=blue,white"
--iconsize "150"
--button1text "OK"
)
# No malicious behavior
dialog3_cmd=(
-p
-o
--title "No malicious behavior found"
--titlefont "size=18, colour=green"
--message "CrowdStrike has not detected any malicious behavior."
--messagealignment "left"
--messageposition "top"
--height '300'
--width '650'
--icon "sf=checkmark.seal.fill,colour=green"
--iconsize "150"
--button1text "OK"
)
#### Logic ####
# Display the output of the log show command
echo "$detection"
if [ -n "$detection" ]; then
# Display alert
somethingBad
elif [ -z "$detection" ]; then
# Display alert
allGood
fi
exit 0
Posted on 02-08-2024 11:11 AM
So this is a bit in the weeds, so TLDR use "/usr/bin/log" instead of log.
The reason for this is that ZSH has a shell builtin named log which overrides the /usr/bin/log command found on the PATH. So when you type just log in your script, it finds the shell builtin rather than the macOS log command.
❯ /bin/zsh -c "which -a log"
log: shell built-in command
/usr/bin/log
The reason you do not experience this behavior when running the script locally is that you are using an interactive shell. Interactive shell's load the zshrc files (first at /etc/zshrc and then at ~/.zshrc), where non-interactive shells so not. In order to make the /usr/bin/log command easily accessible on macOS, Apple chose to include the line "disable log" in the default /etc/zshrc.
❯ cat /etc/zshrc
[...]
# Disable the log builtin, so we don't conflict with /usr/bin/log
disable log
[...]
This causes the log builtin for zsh to be disabled in interactive shells and thus when running log the shell only finds the /usr/bin/log command. You can see this behavior by running zsh with the -i flag for interactive. All users login shells are interactive by default (and so is your CommandRunner app's shell)
❯ /bin/zsh -i -c "which -a log"
/usr/bin/log
The Jamf binary, however, runs non-interactive shells so when you have Jamf run your script it does not load the /etc/zshrc file and does not disable the log builtin so your "log show ..." command is running a completely different command which requires different arguments. Thus, the "to many arguments" error.
02-08-2024 11:42 AM - edited 02-08-2024 11:53 AM
Posted on 02-08-2024 08:22 AM
Running this script through Terminal also works exactly as intended. For testing, I use logging from Safari so that it will produce a result that malicious behavior was found. Otherwise, I would need to simulate a detection event from CrowdStrike. Terminal displays the output of the "$detection" variable. It does not show a "too many arguments" error. Why would the script error out only when it is ran through Jamf Pro?