The data entry proposed in the question causes code execution before passing the variable to the script.
The use of subshell $()
executes the command immediately and replaces its result in the string:
nunks@yokoi:~$ TESTE="Diretorio atual: $(pwd)"
nunks@yokoi:~$ echo $TESTE
Diretorio atual: /home/nunks
Thus, the proposed execution is first removing the /etc and then calling the script. It is not the script that executes malicious code, it is the proper assignment of $MINHAVARIVEL
user-made.
To make the script cause this malicious execution, you would need to make (ill-)use of eval
:
#!/bin/bash
#script.sh
eval $MINHAVARIAVEL;
~$ MIN='rm -Rf /etc' . /script.sh
Mandatory notice on the use of eval
Rebound here that, as a rule, should not be used eval
for nothing. The above example is just one case in which its use represents a security breach. It is virtually impossible to guarantee the validity of all inputs (malicious users tend to be quite smart), being much better to interpret the user’s data entry and define what will run within the script. It is inherently unsafe to allow the user to execute operating system commands within his script.
Use of control replacement $(comando)
or `comando`
Opening a subshell within parentheses $(comando)
or severe accents `comando`
is called by the bash of control override: the command in question is evaluated first and its return is passed to the "out" command, being very useful for assigning variables dynamically.
It is important to stress that this kind of expansion works as eval
, and shall be treated with the same care. The above malicious execution can also be triggered with replacements:
#!/bin/bash
#script.sh
echo $($MINHAVARIAVEL);
~$ MIN='rm -Rf /etc' . /script.sh
Use of process substitution <(comando)
and >(comando)
The bash also provides process substitution, enabling them to function as "files" by creating dynamic Fifos or "numbered" files in the /dev/fd. This is very practical to, for example, work with the output of complex pipelines without the need to first store them in file.
The entire overwrite is used as if it were a file, and the command passed between parentheses is evaluated and executed as in the command overwrites mentioned above, but its output is sent to a temporary file or FIFO to be read immediately by the process that called it. So, also works as eval
and should also be used with care. Example of the same above malicious execution, now with process overwriting:
#!/bin/bash
#script.sh
cat <($MINHAVARIAVEL);
~$ MIN='rm -Rf /etc' . /script.sh
Therefore...
There is more than one way to execute code contained in variables in bash, and none of them should be used without having control of what is inside the variable in question. They’re all as dangerous as the eval
.
This serves primarily as a warning that user input should never be used as data, and should always be checked. Strictly define the "format" or "type" of input data and, first of all, check if they fit within the expected.
Verification of input data
That said, it is always important to clean the data entries to avoid not only malicious use, but also many bugs inherent to the creativity of the user in choosing the data to pass to the script.
The bash offers several types of testing to facilitate the identification of variables, such as the test to see if it points to an existing file path (-a
):
#!/bin/bash
if [[ ! -a $MINHAVARIAVEL ]]; then
echo MINHAVARIAVEL deve ser um arquivo!;
exit 1;
fi;
For cases where the format of the string passed is known and strict, I usually use regular expressions. Example with date string:
#!/bin/bash
if [[ ! $ANOMESDIA =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
#sei que a regex acima e bastante simploria para teste de data,
#trata-se de mero exemplo
echo O formato de ANOMESDIA deve ser AAAA-MM-DD!;
exit 1;
fi;
In order to make life easier for the end user, my scripts tend to contain a mountain of tests before starting the actual execution, seeking to ensure that the input data is entered in a format defined as strictly as possible. At each false test I offer an error explanatory message and a basic help of the command, then finish with error code:
#!/bin/bash
#testaidade.sh
help() {
echo
echo Ajuda:
echo Passe sua idade para script!;
echo Exemplo: $0 22;
}
IDADE=$1;
if [[ -z $IDADE ]]; then #$IDADE está vazia
help;
exit 1;
fi;
if [[ ! $IDADE =~ ^[0-9]+$ ]]; then
echo Sua idade deve ser um valor numérico.;
help;
exit 1;
fi;
echo Idade testada. O valor informado foi $IDADE;
Thus, you can manipulate the information in your script without any extra concern as to the integrity of the entries informed by the user.
This "injection" code already occurs at the time of assignment. Expanding the variable will not execute the
rm -rf
– Jefferson Quesado
it is. when you put a command between
$()
he is executed immediately. in that case there,$MINHAVARIAVEL
contains the result ofrm
, and that is what is passed to the script’s enironment. Executing a string as a command is the role of theeval
(with all the risks inherent to its use)– nunks