¿Es peligroso ejecutar echo sin comillas?


11

He visto un par de temas similares, pero se refieren a no citar variables, lo que sé que podría conducir a resultados no deseados.

Vi este código y me preguntaba si sería posible inyectar algo para ejecutar cuando se ejecuta esta línea de código:

echo run after_bundle


Me encontré con esto cuando tuve: target = "*** LIVE SERVER ***"; echo target: $ target; y el *** se expandió en una lista de carpetas ...
Matt

Respuestas:


17

Para el caso específico

echo run after_bundle

No es necesario citar. No es necesario citar porque el argumento echoes cadenas estáticas que no contienen expansiones variables o sustituciones de comandos, etc. Son "solo dos palabras" (y, como señala Stéphane , se construyen adicionalmente a partir del conjunto de caracteres portátil ).

El "peligro" viene cuando manejas datos variables que el shell puede expandir o interpretar. En tales casos, se debe tener cuidado de que el shell haga lo correcto y que el resultado sea lo que se pretende.

Las siguientes dos preguntas contienen información relevante al respecto:


echoa veces se usa para "proteger" comandos potencialmente dañinos en las respuestas de este sitio. Por ejemplo, puedo mostrar cómo eliminar archivos o mover archivos a un nuevo destino usando

echo rm "${name##*/}.txt"

o

echo mv "$name" "/new_dir/$newname"

Esto generaría comandos en el terminal en lugar de eliminar o cambiar el nombre de los archivos. El usuario podría inspeccionar los comandos, decidir que se ven bien, eliminarlos echoy volver a ejecutarlos.

Su comando echo run after_bundlepuede ser una instrucción para el usuario, o puede ser un código "comentado" que es demasiado peligroso para ejecutar sin saber las consecuencias.

Usando echoesto, uno tiene que saber qué hace el comando modificado y uno debe garantizar que el comando modificado realmente sea seguro (potencialmente no lo sería si contuviera redirecciones, y usarlo en una tubería no funciona, etc.)


Sin embargo, agregar comillas no es suficiente para saber lo que haría un shell, así como no se puede decir que echo rm "first file.txt" "second file.txt"es de ninguna manera diferente echo rm "first" "file.txt" "second" "file.txt", la salida de ambos es la misma. Si desea generar un comando de shell como salida, debe usar printf '%q ' rm "first file.txt" "second file.txt"; echoo algo equivalente que vuelva a generar citas sintácticas que se evalúen como argvaprobadas.
Charles Duffy

@CharlesDuffy ¡ Realmente espero que nadie copie y pegue la salida de depuración y la ejecute en el shell!
Kusalananda

1
Generar comandos de shell y luego canalizarlos shno es exactamente un patrón poco común, y ver a la gente preguntar "¿por qué foofunciona cuando lo ejecuto en una línea de comando, pero este script que emite esa cadena exacta echoen frente de la línea no lo hace? " sucede todo el tiempo aquí. Más aún, la salida de depuración no es útil si oculta sus errores, y si sus errores están relacionados con citas, entonces echono los revelará.
Charles Duffy,

27

Solo una nota adicional sobre la excelente respuesta de @ Kusalananda .

echo run after_bundle

está bien porque ninguno de los caracteres en esos 3 argumentos¹ pasados ​​para echocontener caracteres que son especiales para el shell.

Y (el punto extra que quiero destacar aquí) no hay una configuración regional del sistema donde esos bytes puedan traducirse a caracteres que sean especiales para el shell.

Todos esos caracteres están en lo que POSIX llama el juego de caracteres portátil . Esos caracteres deben estar presentes y codificados de la misma manera en todos los juegos de caracteres en un sistema POSIX².

De modo que esa línea de comando se interpretará de la misma forma independientemente de la configuración regional.

Ahora, si comenzamos a usar caracteres fuera de ese conjunto de caracteres portátil, es una buena idea citarlos incluso si no son especiales para el shell, porque en otro entorno local, los bytes que los constituyen pueden interpretarse como caracteres diferentes que podrían convertirse en especial a la concha. Tenga en cuenta que es si está utilizando echoo cualquier otro comando, el problema no es echocon la forma en que el shell analiza su código.

Por ejemplo en un UTF-8:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

Eso àestá codificado como 0xc3 0xa0. Ahora, si tiene esa línea de código en una secuencia de comandos de shell y la secuencia de comandos de shell es invocada por un usuario que utiliza una configuración regional cuyo conjunto de caracteres no es UTF-8, esos dos bytes podrían tener caracteres muy diferentes.

Por ejemplo, en un fr_FR.ISO8859-15entorno local, un entorno local típico francés que utiliza el juego de caracteres estándar de un solo byte que cubre el idioma francés (el mismo que se usa para la mayoría de los idiomas de Europa occidental, incluido el inglés), ese byte 0xc3 se interpreta como el Ãcarácter y 0xa0 como el no Rompiendo el carácter del espacio.

Y en algunos sistemas como NetBSD³, ese espacio que no se rompe se considera como un carácter en blanco ( isblank()en él devuelve verdadero, coincide con [[:blank:]]) y los shells como, bashpor lo tanto, lo tratan como un delimitador de token en su sintaxis.

Esto significa que en lugar de correr echocon $'voil\xc3\xa0'como argumento, se ejecutan con $'voil\xc3'como argumento, lo que significa que no se imprimirá voilàcorrectamente.

Se pone mucho peor con los juegos de caracteres chinos como Big5, Big5-HKSCS, GB18030, GBK que tienen muchos personajes cuya codificación contiene la misma codificación que el |, `, \(por nombrar el peor) (también que SJIS ridícula, también conocido como Microsoft kanji, excepto que es en ¥lugar de \, pero aún así es tratado \por la mayoría de las herramientas ya que está codificado como 0x5c allí).

Por ejemplo, si está en un zh_CN.gb18030entorno chino, escribe un script como:

echo  reboot

Ese script se generará 詜 rebooten un entorno local utilizando GB18030 o GBK, 唰 rebooten un entorno local utilizando BIG5 o BIG5-HKSCS, pero en un entorno local C utilizando ASCII o un entorno local utilizando ISO8859-15 o UTF-8, se rebootejecutará porque la codificación GB18030 de es 0xd4 0x7c y 0x7c es la codificación de |ASCII, por lo que terminamos ejecutando:

 echo �| reboot

(que representa sin embargo el byte 0xd4 se representa en la configuración regional). Ejemplo usando el menos dañino en unamelugar de reboot:

$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$

( unameSe corrió).

Por lo tanto, mi consejo sería citar todas las cadenas que contienen caracteres fuera del conjunto de caracteres portátil.

Sin embargo, tenga en cuenta que, dado que la codificación de \y `se encuentra en la codificación de algunos de esos caracteres, es mejor no usar \o "..."o $'...'(dentro de los cuales `y / o \todavía son especiales), sino '...'citar caracteres fuera del conjunto de caracteres portátil.

No conozco ningún sistema que tenga una configuración regional donde el juego de caracteres tenga algún carácter (aparte de 'sí mismo, por supuesto) cuya codificación contenga la codificación ', por lo '...'que definitivamente debería ser el más seguro.

Tenga en cuenta que varios shells también admiten una $'\uXXXX'notación para expresar caracteres en función de su punto de código Unicode. En shells como zshy bash, el carácter se inserta codificado en el juego de caracteres de la configuración regional (aunque puede causar comportamientos inesperados si ese juego de caracteres no tiene ese carácter). Eso le permite evitar insertar caracteres no ASCII en su código de shell.

Así arriba:

echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'

O:

echo $'voil\u00e0'
echo $'\u8a5c reboot'

(con la advertencia, podría romper el script cuando se ejecuta en entornos locales que no tienen esos caracteres).

O mejor, ya \que también es especial para echo(o al menos algunas echo implementaciones, al menos las que cumplen con Unix):

printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'

(tenga en cuenta que \también es especial en el primer argumento printf, por lo que también es mejor evitar los caracteres no ASCII en caso de que puedan contener la codificación de \).

Tenga en cuenta que también puede hacer:

'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'

(eso sería excesivo, pero podría darte un poco de tranquilidad si no estás seguro de qué personajes están en el conjunto de caracteres portátil)

Además, asegúrese de nunca usar la antigua `...`forma de sustitución de comandos (que introduce otro nivel de procesamiento de barra invertida), pero en su $(...)lugar use .


¹ técnicamente, echotambién se pasa como argumento a la echoutilidad (a decirle cómo se invoca), que es el argv[0]y argces 3, aunque en la mayoría de las conchas hoy en día echose orden interna, de manera que exec()de un /bin/echoarchivo con una lista de 3 argumentos es simulado por el cáscara. También es común considerar que la lista de argumentos comienza con el segundo ( argv[1]to argv[argc - 1]), ya que es sobre los que actúan principalmente los comandos.

² ¡una notable excepción a que es la ridícula ja_JP.SJISlocalización de los sistemas FreeBSD cuyo juego de caracteres no tiene \ni ~carácter!

³ tenga en cuenta que si bien muchos sistemas (FreeBSD, Solaris, no GNU) consideran U + 00A0 como [[:blank:]]en configuraciones regionales UTF-8, pocos lo hacen en otras configuraciones regionales como las que usan ISO8859-15, posiblemente para evitar este tipo de problema.


En su primer párrafo, nos dice "... de los caracteres en esos 3 argumentos pasados ​​a echo...", solo cuento 2 argumentos pasados ​​al comando echo, los argumentos que puedo contar son runy after_bundle, me gustaría explicar cómo contado y llegó a 3 argumentos?
Ferrybig

1
@ViktorFonic, vea la edición sobre el número de argumentos (y que el problema principal no es con echo). Vea (exec -a foo /bin/echo --help)en un sistema GNU y con el shell GNU cómo pasar un primer argumento arbitrario a la /bin/echoutilidad.
Stéphane Chazelas

@Ferrybig Vea la edición de Stephane, nota al pie 1. Los argumentos para ordenar en el estilo C habitual son una serie de argumentos, con argv [0] como nombre ejecutable. Un poco similar a los $0parámetros posicionales en conchas.
Sergiy Kolodyazhnyy

Hay 373 codificaciones iconven las que ESCse convierte '. Prueba (como ejemplo):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
NotAnUnixNazi

Hay 173 codificaciones en las que algún punto de código (que no sea ESC) se convierte en a '. Tratar printf '\u2804' | iconv -f utf8 -t BRF | xxd. Hay codificaciones en las que hay muchos puntos de código que se convierten '. Alrededor de 8695 puntos de código en UCS-4 se convierten '. Tratar printf '\U627' | iconv -cf utf-8 -t UCS-4. Varias (37) codificaciones convierten el carácter 0x127 en a '. Probarprintf '\U127' | iconv -cf utf8 -t UCS2 |xxd
NotAnUnixNazi
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.