Del manual de findutils:
Por ejemplo construcciones como estos dos comandos
# risky find -exec sh -c "something {}" \; find -execdir sh -c "something {}" \;
son muy peligrosos La razón de esto es que el '{}' se expande a un nombre de archivo que puede contener un punto y coma u otros caracteres especiales para el shell. Si, por ejemplo, alguien crea el archivo
/tmp/foo; rm -rf $HOME
, los dos comandos anteriores podrían eliminar el directorio de inicio de alguien.Por esta razón, no ejecute ningún comando que pasará datos no confiables (como los nombres de archivos) a comandos que interpreten argumentos como comandos para ser interpretados más a fondo (por ejemplo, 'sh').
En el caso del shell, hay una solución inteligente para este problema:
# safer find -exec sh -c 'something "$@"' sh {} \; find -execdir sh -c 'something "$@"' sh {} \;
No se garantiza que este enfoque evite todos los problemas, pero es mucho más seguro que sustituir los datos de la elección de un atacante en el texto de un comando de shell.
- ¿La causa del problema es
find -exec sh -c "something {}" \;
que el reemplazo de{}
no está entre comillas y, por lo tanto, no se trata como una sola cadena? En la solución
find -exec sh -c 'something "$@"' sh {} \;
,primero
{}
se reemplaza, pero como{}
no se cita, ¿no"$@"
tiene el mismo problema que el comando original? Por ejemplo,"$@"
se ampliará a"/tmp/foo;"
,"rm"
,"-rf"
, y"$HOME"
?¿Por qué
{}
no se escapa o se cita?
- ¿Podría dar otros ejemplos (aún con
sh -c
o sin él si corresponde; con o sin losfind
cuales pueden no ser necesarios) en los que se aplica el mismo tipo de problema y solución, y que son ejemplos mínimos para que podamos centrarnos en el problema y la solución con poca distracción posible? Consulte Formas de proporcionar argumentos a un comando ejecutado por `bash -c`
Gracias.
Respuestas:
Esto no está realmente relacionado con las citas, sino más bien con el procesamiento de argumentos.
Considere el ejemplo arriesgado:
Esta es analizada por el caparazón, y dividido en seis palabras:
find
,-exec
,sh
,-c
,something {}
(sin comillas ya),;
. No hay nada que ampliar. El shell se ejecutafind
con esas seis palabras como argumentos.Cuando
find
encuentra algo para procesar, por ejemplofoo; rm -rf $HOME
, reemplaza{}
confoo; rm -rf $HOME
, y se ejecutash
con los argumentossh
,-c
ysomething foo; rm -rf $HOME
.sh
ahora ve-c
y, como resultado, analizasomething foo; rm -rf $HOME
( el primer argumento sin opción ) y ejecuta el resultado.Ahora considere la variante más segura:
Las carreras de concha
find
con los argumentosfind
,-exec
,sh
,-c
,something "$@"
,sh
,{}
,;
.Ahora, cuando
find
los hallazgosfoo; rm -rf $HOME
, al que sustituye{}
de nuevo, y se ejecutash
con los argumentossh
,-c
,something "$@"
,sh
,foo; rm -rf $HOME
.sh
ve-c
, y analizasomething "$@"
como el comando para ejecutar, ysh
yfoo; rm -rf $HOME
como los parámetros posicionales ( a partir de$0
), se expande"$@"
afoo; rm -rf $HOME
como un valor único , y se ejecutasomething
con el solo argumentofoo; rm -rf $HOME
.Puedes ver esto usando
printf
. Crea un nuevo directorio, ingrésalo y ejecutaEjecutando la primera variante de la siguiente manera
produce
mientras que la segunda variante, corre como
produce
fuente
{}
no se escapa o se cita?;
y{}
) necesitan ser escapadas (con un '\') o citadas para protegerlas de la expansión del shell". ¿Quieres decir que es incorrecto para bash?findutils
manual se remonta al menos a 1996 ... Por supuesto, no hace daño citar{}
, como'{}'
o"{}"
, pero no es necesario.find some/path -exec sh -c 'something "$@"' {} \;
en realidad hay tres capas de procesamiento / paso de argumentos, dos de la variedad de shell y una de la variedad básica del sistema operativo sin procesar.Parte 1:
find
solo usa reemplazo de texto.Sí, si lo hiciste sin comillas, así:
y un atacante pudo crear un archivo llamado
; echo owned
, entonces se ejecutaríalo que resultaría en la ejecución de la shell en
echo
ese momentoecho owned
.Pero si agregaste comillas, el atacante podría terminar tus comillas y luego colocar el comando malicioso después de crear un archivo llamado
'; echo owned
:que daría lugar a la cáscara de funcionamiento
echo ''
,echo owned
.(si intercambió las comillas dobles por comillas simples, el atacante también podría usar el otro tipo de comillas).
Parte 2:
En
find -exec sh -c 'something "$@"' sh {} \;
, el{}
shell no lo interpreta inicialmente, se ejecuta directamente conexecve
, por lo que agregar comillas de shell no ayudaría.no tiene ningún efecto, ya que el shell elimina las comillas dobles antes de ejecutarlo
find
.agrega citas que el shell no trata especialmente, por lo que en la mayoría de los casos solo significa que el comando no hará lo que desea.
Tener que se expanda a
/tmp/foo;
,rm
,-rf
,$HOME
no debería ser un problema, porque esos son argumentos asomething
, ysomething
probablemente no trata a sus argumentos como comandos para su ejecución.Parte 3:
Supongo que se aplican consideraciones similares para cualquier cosa que tome una entrada no confiable y la ejecute como (parte de) un comando, por ejemplo,
xargs
yparallel
.fuente
xargs
solo es peligroso si-I
o-J
se usa. En el funcionamiento normal, solo agrega argumentos al final de la lista, al igual que lo-exec ... {} +
hace.parallel
, por el contrario, ejecuta un shell de forma predeterminada sin una solicitud explícita del usuario, lo que aumenta la superficie vulnerable; este es un tema que ha sido objeto de mucha discusión; consulte unix.stackexchange.com/questions/349483/… para obtener una explicación, y el enlace incluido a lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html ).parallel
proceso de "receptor" remoto en el otro extremo de cualquier zócalo, puede hacer un proceso directo sin shellexecv
. Que es exactamente lo que hubiera hecho, si estuviera en su lugar implementando la herramienta. Tener un programa no interactivo se comporta de manera diferente en función del valor actual deSHELL
es apenas el menor asombro.execv
proporciona, eso es más simple y menos sorprendente, y una regla que no cambia en ubicaciones / entornos de tiempo de ejecución.En cierto sentido, pero las citas no pueden ayudar aquí . El nombre de archivo que se reemplaza en lugar de
{}
puede contener cualquier carácter, incluidas las comillas . Cualquiera sea la forma de cita utilizada, el nombre de archivo puede contener el mismo y "romper" la cita.No. se
"$@"
expande a los parámetros posicionales, como palabras separadas, y no los divide más. Aquí,{}
es un argumentofind
en sí mismo, yfind
pasa el nombre de archivo actual también como un argumento distinto ash
. Está directamente disponible como una variable en el script de shell, no se procesa como un comando de shell en sí.No necesita ser, en la mayoría de los depósitos. Si ejecuta
fish
, debe ser:fish -c 'echo {}'
imprime una línea vacía. Pero no importa si lo cita, el shell simplemente eliminará las comillas.Cada vez que expande un nombre de archivo (u otra cadena no controlada) como está dentro de una cadena que se toma como algún tipo de código (*) , existe la posibilidad de ejecución de comandos arbitrarios.
Por ejemplo, esto expande
$f
directamente el código Perl y causará problemas si un nombre de archivo contiene una comilla doble. La cita en el nombre del archivo finalizará la cita en el código Perl, y el resto del nombre del archivo puede contener cualquier código Perl:(El nombre del archivo debe ser un poco extraño ya que Perl analiza el código completo por adelantado, antes de ejecutarlo. Por lo tanto, tendremos que evitar un error de análisis).
Si bien esto lo pasa de manera segura a través de un argumento:
(No tiene sentido ejecutar otro shell directamente desde un shell, pero si lo hace, es similar al
find -exec sh ...
caso)(* algún tipo de código incluye SQL, XKCD obligatorio: https://xkcd.com/327/ más explicación: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )
fuente
Su preocupación es precisamente la razón por la cual GNU Parallel cita entradas:
Esto no se ejecutará
echo pwned
.Ejecutará un shell, de modo que si extiende su comando, no se sorprenderá de repente:
Para obtener más detalles sobre el problema de spawning-a-shell, consulte: https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell
fuente
find . -print0 | xargs -0 printf "Argument: %s\n"
es igual de seguro (o más bien, más, ya que con-print0
uno maneja los nombres de archivo con nuevas líneas correctamente); Las citas paralelas son una solución para un problema que no existe cuando no hay ningún shell.| wc
al comando, debe saltar a través de los aros para que sea seguro. GNU Parallel es seguro por defecto.foo {} | wc
ensh -c 'foo "$1" | wc
sh {} `, eso podría ser diferente.