Le problème se pose dans les cas où le contenu de $x
n'a pas été filtré et contient des données qui pourraient potentiellement être sous le contrôle d'un attaquant dans les cas où le code shell pourrait finir par être utilisé dans un contexte d'escalade de privilèges (par exemple un script appelé par un setuid application, un script sudoers ou utilisé pour traiter des données off-the-network (CGI, hook DHCP ...) directement ou indirectement).
Si:
x='(PATH=2)'
Alors:
x=$((1-$x)))
a pour effet secondaire de se mettre PATH
sur 2
(un chemin relatif qui pourrait très bien être sous le contrôle de l'attaquant). Vous pouvez remplacer PATH
par LD_LIBRARY_PATH
ou IFS
... La même chose se produit avec x=$((1-x))
en bash, zsh ou ksh (pas dash ni yash qui n'acceptent que les constantes numériques dans les variables).
Notez que:
x=$((1-$x))
ne fonctionnera pas correctement pour les valeurs négatives de $x
dans certains shells qui implémentent l' --
opérateur ( optionnel selon POSIX) (décrément) (comme avec x=-1
, cela signifie demander au shell d'évaluer l' 1--1
expression arithmétique). "$((1-x))"
n'a pas le problème car il x
est développé dans le cadre (pas avant) de l'évaluation arithmétique.
Dans bash
, zsh
et ksh
(pas dash
ou yash
), si x
est:
x='a[0$(uname>&2)]'
Ensuite, l'expansion de $((1-$x))
ou $((1-x))
provoque uname
l'exécution de cette commande (pour zsh
, a
doit être une variable de tableau, mais on peut l'utiliser psvar
par exemple pour cela).
En résumé, on ne doit pas utiliser ou non initialisées données externes non aseptisé dans les expressions arithmétiques dans les coquilles (notez que l' évaluation arithmétique peut être fait par $((...))
(alias $[...]
dans bash
ou zsh
) , mais aussi en fonction de la coquille dans le let
, [
/ test
, declare/typeset/export...
, return
, break
, continue
, exit
, printf
, les commandes print
intégrées, les indices de tableau ((..))
et les [[...]]
constructions pour n'en nommer que quelques-uns).
Pour vérifier qu'une variable contient un nombre entier décimal littéral, vous pouvez utiliser POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
Attention, [0-9]
dans certains pays, les correspondances à plus de 0123456789. [[:digit:]]
devraient être OK mais je ne parierais pas dessus.
Rappelez-vous également que les nombres avec des zéros en tête sont traités comme octaux dans certains contextes ( 010
parfois 10, parfois 8) et attention à ce que la vérification ci-dessus laisse passer des nombres potentiellement plus grands que l'entier maximum pris en charge par votre système (ou quelle que soit l'application que vous utiliserez) utilisez cet entier dans; bash par exemple traite 18446744073709551616 comme 0 car c'est 2 64 ). Donc, vous voudrez peut-être ajouter des vérifications supplémentaires dans cette déclaration de cas ci-dessus, comme:
(0?* | -0?*)
echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;
Exemples:
$ export 'x=psvar[0$(uname>&2)]'
$ ksh93 -c 'echo "$((x))"'
Linux
ksh93: psvar: parameter not set
$ ksh93 -c '[ x -lt 2 ]'
Linux
ksh93: [: psvar: parameter not set
$ bash -c 'echo "$((x))"'
Linux
0
$ bash -c '[[ $x -lt 2 ]]'
Linux
$ bash -c 'typeset -i a; export a="$x"'
Linux
$ bash -c 'typeset -a a=([x]=1)'
Linux
$ bash -c '[ -v "$x" ]'
Linux
$ mksh -c '[[ $x -lt 2 ]]'
Linux
$ zsh -c 'echo "$((x))"'
Linux
0
$ zsh -c 'printf %d $x'
Linux
0
$ zsh -c 'integer x'
Linux
$ zsh -c 'exit $x'
Linux
Plus de lecture sur:
x='P=3'; : $(($x + 5))
sera misP
à 8, maisx='P=3'; : $((x + 5))
sera misP
à3
(enzsh
,ksh
oubash
). "La même chose se produit avec$((x + 1))
..." n'est pas correct maintenant; il sera misPATH
à2
, comme autrefois.