Summary
The Bash Scripting for Blue Team module is designed for cybersecurity professionals working specifically in the defensive Blue Team capacity, System and network administrators involved in cybersecurity defense and incident response, Individuals specializing in threat detection and proactive security measures within cybersecurity, and Professionals seeking to enhance their scripting skills for defensive security operations. Throughout this module, students will develop mastery of Bash scripting techniques specific to cybersecurity defense for the Blue Team, enhanced capability to proactively manage security threats through automated scripting, and improved response time and efficiency in handling security incidents.
Recommended prior knowledge:
- Basic understanding of Linux/Unix systems and command line operations
- Familiarity with fundamental cybersecurity concepts and practices
- Basic knowledge of Bash or scripting languages is beneficial but not mandatory
Security of the Bash Scripts - 1
The topic of our module is Bash Scripting for Security. But in order not to be hunted on the go, we must first be able to write safe scripts. For this, it is the right way to progress first by learning how to write secure scripts and then starting to do great work with these scripts in security.
Just like a burglar finds a window to break into a house, an attacker can infiltrate your system when they find a security flaw. In this case, the attacker may have the power to steal, replace or even delete your data. This could mean the loss of personal information, customer data, or important business-related data, which can be a huge disaster.
Also, a security bug can affect the performance of your system. An attacker can slow down or completely stop your system by consuming your system's resources or installing malicious software. This can disrupt your business continuity and be very costly.
Common Mistakes
Bash is in advanced interaction with the operating system due to its structure and design. While this is a serious advantage when used carefully, but otherwise, in case of its use carelessly, it can lead to disasters. Without further do, let's review what we should do and how to implement them to be considered using the Bash carefully as well as what we should not do and why we should not do.
Escaping Errors
When adding values of variables directly to commands, there is always a risk that these values are manipulated and may cause a command to run differently than expected.
The simplest way to avoid this error is to use double quotes when using the variable ("$variable" instead of $variable). Let's examine this with an example.
#!/bin/bash
drectory_name="lets defend"
cd $directory_name
In this example, we do not use double quotes when using our variable. Therefore, the “cd” command is misinterpreted and gives the error message you see below.

So why too many arguments? Because we called the directory name variable without quotes and executed a command just like the one below.
[!bash!]$ cd lets defend
The cd command read the word "defend" as an argument, but, because it did not expect such an argument, it could not understand it and gave an error. So, what would happen if we used it in the following way?
#!/bin/bash
drectory_name="lets defend"
cd "$directory_name"
In this case, when the script runs, the command we actually run would be as follows:
[!bash!]$ cd 'lets defend'
And (if the 'lets defend' directory actually exists) the script would run just fine.
Here, we have tried to explain the subject as simply as possible. What you should not forget is that we create the values of the variables programmatically and we cannot predict which values may come during the execution of the script most of the time. It would be a great starting point to call our variable names in double quotes to be prepared for any situation for safe bash scripts.
Untrusted Inputs
Having your bash scripts accept untrusted input allows attackers to execute malicious code. To prevent this type of attack, we must always verify and sanitize user inputs.
In the example code below, we ask the user for a file name and print the contents of the file with that name to the console.
#!/bin/bash
echo "Input file name:"
read file
cat $file
However, this is an untrusted script because the user input is not checked and it was used directly in the “cat” command. A malicious user may attempt to wipe the entire file system by entering a malicious command like “rm -rf /” to the file variable. (This result may vary depending on the bash version the script is running)
Therefore, when receiving an input from the user, we must always filter the values that the input can take and accept only what we need. Let's rearrange the above example as follows:
#!/bin/bash
echo "input file name:"
read file
if [[ -f $file && $file == *.txt ]]; then
cat "$file"
else
echo "Please input .txt file name"
fi
In this example, the variable file checks for a file (-f $file) and checks if it has a .txt extension ($file == *.txt). If these conditions are not met, an error message is given to the user. This ensures that the user input is as expected and prevents potentially malicious logins.
Let's do another example:
In this example, the user-input variable filename is used directly in the cp command. A malicious user can change the target filename, causing unexpected results.
#!/bin/bash
echo "input file name:"
read filename
cp $filename /tmp/backup
The correct approach is to check and sanitize the user's input:
#!/bin/bash
echo "input file name:"
read filename
if [[ $filename =~ ^[a-zA-Z0-9_-]+$ ]]; then
cp "$filename" /tmp/backup
else
echo "invalid file name."
fi
In this example, we are checking that only alphanumeric characters, hyphen (-) and underscore (_) characters are accepted using a regular expression for the filename variable. In this way, we take an important step to prevent the user from modifying the target file with malicious input.
Now, let's do a final example:
When working with bash scripts, we often run some system commands from within the script. The correct functioning of our script depends on the smooth operation of these system commands. The outputs of these commands are actually inputs for our script and we need to check these inputs as well.
A very common mistake is to continue the execution of the script without verifying whether these commands are successfully executed or not. If not, our script may start producing unpredictable results.
In our example, we scp a file to a remote server and then delete the local file. Although everything seems working fine, we actually have a faulty code because we have not checked whether the required connection for scp is available or not, whether the scp suns successfully or not.
#!/bin/bash
remote_host="192.168.0.100"
remote_user="user"
filename="important.txt"
scp $remote_user@$remote_host:$filename /tmp/backup >/dev/null 2>&1
echo "File copied successfully."
rm -rf filename
So how could we have we done it?
First, we could have tested the remote connection, and continued if the remote connection was healthy. We delete the local file after checking the status of the scp command to make sure everything is fine. In fact, we could have taken this check one step further and compared the hash of the local file with the hash of the file on the remote system to make sure that the file transmitted without errors.
#!/bin/bash
remote_host="192.168.0.100"
remote_user="user"
filename="important.txt"
ssh $remote_user@$remote_host "ls -l $filename" >/dev/null 2>&1
if [ $? -eq 0 ]; then
scp $remote_user@$remote_host:$filename /tmp/backup
if [ $? -eq 0 ]; then
echo "File copied, deleting orginal"
rm -rf filename
else
echo “file not send”
else
echo "Connection problem"
fi
In these examples the use of [ $? The use of -eq 0 ] must have caught your attention. This usage in the bash script gives us the exit code returned by the operating system of the last command executed. The “0” code means that the command has run successfully and its lifecycle has ended normally.
When writing bash scripts, other exit codes that might work for our scenario are as follows:

When we use these exit codes in the right place and in the right way, we can ensure that the script we write is resistant to all possible errors. For example: we can check for possible argument errors during scp command execution by using the expression as [ $? eq 2 ] instead of [ $? -eq 0 ].
Command Injection
When writing bash scripts, built-in bash commands such as eval , system , exec can be exploited by a malicious user if not used carefully. For this reason, we should avoid using these commands in the script as much as possible, and in cases where we have to use them, we need to pass the inputs we provide to these commands through very strict filters.
#!/bin/bash
echo "Enter a command:"
read user_command
eval $user_command
In this script, the file name is taken from the user and requested to list this file with the ls command. However, if a malicious user inputs this as: my_file; rm -rf ` the `rm -rf command is also run after the ls command and all files in the current directory are deleted. We should still note that this may vary depending on the bash version running on the system.
To prevent such malicious command injections, you must ensure that the input received from the user is safe. One way to achieve this is to filter the input. For example:
#!/bin/bash
echo "Input Command:"
read user_command
case $user_command in
"ls")
ls
;;
"pwd")
pwd
;;
*)
echo "Not Allowed"
;;
esac
This script limits the commands the user can enter to “ls” and “pwd”. This prevents the user from running unexpected or potentially harmful commands. Although this example is fairly simple, it is quite common to use this type of approach to determine what kind of commands to run in a controlled environment.
#!/bin/bash
echo "Input Command"
read user_command
user_command=$(echo $file_name | tr -dc '[:alnum:]\n\r' | tr '[:upper:]' '[:lower:]')
eval $user_command
In this example, the tr command is used to ensure that the filename contains only alphanumeric characters. This prevents command injection because special characters and spaces are not included.
We should still remind that these are only basic examples and do not provide complete security. We may need to do more complex filtering or validation in cases where we need certain characters or special characters when processing. Ideally, it is recommended to test your own solutions for input validation, error checking, debugging, and other security practices and make sure everything is fine before you deploy your scripts.