Implicaciones de seguridad del uso de datos no desinfectados en la evaluación aritmética de Shell

17

En un comentario a una pregunta reciente , Stéphane Chazelas menciona que existen implicaciones de seguridad para la aritmética de paréntesis dobles como:

x=$((1-$x))

en la mayoría de las conchas.

Mis habilidades en Google parecen estar oxidadas y no puedo encontrar nada. ¿Cuáles son las implicaciones de seguridad de la aritmética de paréntesis dobles?

garethTheRed
fuente

Respuestas:

22

El problema está en los casos en que el contenido de $xno 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 PATHen 2(una ruta relativa que bien podría estar bajo el control del atacante). Puede reemplazar PATHcon LD_LIBRARY_PATHo 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 $xen 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--1expresión aritmética). "$((1-x))"no tiene el problema ya que xse expande como parte (no antes) de la evaluación aritmética.

En bash, zshy ksh(no dasho yash), si xes:

x='a[0$(uname>&2)]'

Luego, la expansión de $((1-$x))o $((1-x))hace que unamese ejecute ese comando (para zsh, adebe ser una variable de matriz, pero se puede usar, psvarpor 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 basho zsh), sino también en función de la cáscara en el let, [/ test, declare/typeset/export..., return, break, continue, exit, printf, printincorporados, í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 ( 010a 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:

Stéphane Chazelas
fuente
x='P=3'; : $(($x + 5))establecerá Pa 8, pero x='P=3'; : $((x + 5))establecerá Pa 3(en zsh, ksho bash). "Lo mismo sucede con $((x + 1))..." no es correcto ahora; se establecerá PATHen 2, como en el pasado.
mosvy
@mosvy, gracias (y por tu edición anterior). Corregido ahora.
Stéphane Chazelas