El problema está en los casos en que el contenido de $x
no ha sido desinfectadas y contiene datos que podrían ser bajo el control de un atacante en los casos que el código shell puede llegar a ser utilizado en un contexto de escalada de privilegios (por ejemplo una secuencia de comandos invoca por un setuid aplicación, un script de sudoers o utilizado para procesar datos fuera de la red (CGI, enlace DHCP ...) directa o indirectamente).
Si:
x='(PATH=2)'
Luego:
x=$((1-$x)))
tiene el efecto secundario de establecerse PATH
en 2
(una ruta relativa que bien podría estar bajo el control del atacante). Puede reemplazar PATH
con LD_LIBRARY_PATH
o IFS
... Lo mismo sucede con x=$((1-x))
bash, zsh o ksh (no guiones ni guiones que solo aceptan constantes numéricas en las variables allí).
Tenga en cuenta que:
x=$((1-$x))
no funcionará correctamente para valores negativos de $x
en algunas conchas que implementan la (opcional según POSIX) --
operador (decremento) (como con x=-1
, eso significa que piden la cáscara para evaluar la 1--1
expresión aritmética). "$((1-x))"
no tiene el problema ya que x
se expande como parte (no antes) de la evaluación aritmética.
En bash
, zsh
y ksh
(no dash
o yash
), si x
es:
x='a[0$(uname>&2)]'
Luego, la expansión de $((1-$x))
o $((1-x))
hace que uname
se ejecute ese comando (para zsh
, a
debe ser una variable de matriz, pero se puede usar, psvar
por ejemplo, para eso).
En resumen, no se debe utilizar sin inicializar o no desinfectados datos externos en expresiones aritméticas en conchas (nota que la evaluación aritmética puede ser realizado por $((...))
(también conocido $[...]
en bash
o zsh
), sino también en función de la cáscara en el let
, [
/ test
, declare/typeset/export...
, return
, break
, continue
, exit
, printf
, print
incorporados, índices de matriz ((..))
y [[...]]
construcciones, por nombrar algunos).
Para comprobar que una variable contiene un número decimal entero literal, puede utilizar POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
Tenga [0-9]
en cuenta que en algunos locales coincide con más de 0123456789. [[:digit:]]
Debería estar bien, pero no apostaría por ello.
Recordar también que los números con ceros a la izquierda son tratados como octal en algunos contextos ( 010
a veces 10, a veces 8) y ten en cuenta que la comprobación anterior le permitirá a través de números que son potencialmente más grande que el número entero máxima soportada por el sistema (o cualquier aplicación que se use ese entero en; bash, por ejemplo, trata 18446744073709551616 como 0, ya que es 2 64 ). Por lo tanto, es posible que desee agregar verificaciones adicionales en esa declaración de caso anterior como:
(0?* | -0?*)
echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;
Ejemplos:
$ 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
Más lectura en:
x='P=3'; : $(($x + 5))
estableceráP
a 8, perox='P=3'; : $((x + 5))
estableceráP
a3
(enzsh
,ksh
obash
). "Lo mismo sucede con$((x + 1))
..." no es correcto ahora; se estableceráPATH
en2
, como en el pasado.