No importa cómo resuelva esto, todavía no servirá de mucho. El cdúnico tiene efecto dentro del bash -ccaparazón.
Kusalananda
Solo estoy dando un ejemplo mínimo para la pregunta, no es una solución que funcione para mi problema real, que es unix.stackexchange.com/a/269080/674 más que en la cadena de comandos dada sudo bash -ctengo una expansión variable para un nombre de ruta que puede contener espacios en blanco.
Tim
Respuestas:
13
No hay división de palabras (como en la función que divide las variables en expansiones sin comillas) en ese código, ya $myvarque no está sin comillas.
Sin embargo, existe una vulnerabilidad de inyección de comandos que $myvarse expande antes de pasar a bash. ¡Entonces su contenido se interpreta como código bash!
Los espacios allí causarán que se pasen varios argumentos cd, no debido a la división de palabras , sino porque se analizarán como varios tokens en la sintaxis del shell. ¡Con un valor de bye;reboot, eso se reiniciará! ¹
Aquí te gustaría:
sudo bash -c 'cd -P -- "$1"' bash "$myvar"
(donde pasa el contenido de $myvarcomo el primer argumento de esa secuencia de comandos en línea; tenga en cuenta cómo ambos $myvary $1fueron citados por su respectivo shell para evitar la división de palabras IFS (y globbing)).
O:
sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'
(donde pasa el contenido de $myvaren una variable de entorno).
Por supuesto, no logrará nada útil ejecutando solocd en ese script en línea (aparte de verificar si rootpuede hacerlo cdallí). Presumiblemente, desea que ese script esté cdallí y luego haga algo más como:
Si la intención era usar sudopara poder cdingresar a un directorio al que de otra manera no tendría acceso, entonces eso realmente no puede funcionar.
sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"
iniciará un interactivo bashcon su directorio actual en $myvar. Pero ese shell se ejecutará como root.
Podrías hacerlo:
sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
Para obtener una interacción no privilegiada bashcon el directorio actual $myvar, pero si no tenía los permisos para cdingresar a ese directorio en primer lugar, no podrá hacer nada en ese directorio, incluso si es su directorio de trabajo actual.
$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.':Permission denied
Una excepción sería si tiene permiso de búsqueda para el directorio en sí pero no para uno de los componentes del directorio de su ruta:
$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 01
$ cd 1/2
cd: permission denied:1/2
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd:/home/stephane/1/2:Permission denied
¹ estrictamente hablando, para valores de $myvarlike $(seq 10)(literalmente), habría división de palabras, por supuesto, al expandir la sustitución de ese comando por el bash shell iniciado comoroot
Hay una nueva magia de citas (bash 4.4) que te permite hacer más directamente lo que quieres. Dependiendo del contexto más amplio, usar una de las otras técnicas podría ser mejor, pero funciona en este contexto limitado.
ARGUMENTO se imprime en un formato que se puede reutilizar como entrada de shell, escapando los caracteres no imprimibles con la $''sintaxis POSIX propuesta .
Vea mi respuesta para una versión portátil de esto.
R .. GitHub DEJA DE AYUDAR AL HIELO
GNU printfsolo se invocará allí en los sistemas GNU y si $(printf '%q' "$myvar")se ejecuta en un shell donde printfno está integrado. La mayoría de las conchas se han printfincorporado hoy en día. No todos admiten un %qpensamiento y cuando lo hacen, citarían las cosas en un formato compatible con el shell correspondiente, que puede no ser necesariamente el mismo que el entendido bash. En realidad, bash's printffalla citar cosas correctamente, incluso para sí mismo para algunos valores de $myvaren algunos lugares.
Stéphane Chazelas
Con bash-4.3al menos, eso es todavía una vulnerabilidad de inyección de comandos con myvar=$'\xa3``reboot`\xa3`' en un zh_HK.big5hkscs locale por ejemplo.
Stéphane Chazelas
3
En primer lugar, como otros han notado, su cdcomando es inútil ya que ocurre en el contexto de un shell que se cierra inmediatamente. Pero en general la pregunta tiene sentido. Lo que necesita es una forma de citar shell una cadena arbitraria , de modo que pueda usarse en un contexto donde se interpretará como una cadena citada por shell. Tengo un ejemplo de esto en mi página de trucos sh :
quote (){ printf %s\\n "$1"| sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/";}
Lo que sugiere Stéphane Chazelas es definitivamente la mejor manera de hacer esto. Solo proporcionaré una respuesta sobre cómo cotizar de manera segura con la sintaxis de expansión de parámetros en lugar de usar un sedsubproceso, como en la respuesta de R ...
Eso protegerá el CD contra archivos que comienzan con un guión (-) o que siguen enlaces que pueden generar problemas.
Eso funcionará si puede confiar en que la cadena $myvares una ruta.
Sin embargo, la "inyección de código" todavía es posible ya que está "ejecutando una cadena":
Las comillas simples no funcionarán aquí, ya que su variable no se expandirá. Si está seguro de que la variable para su ruta está desinfectada, la solución más simple es simplemente agregar comillas alrededor de la expansión resultante, una vez que esté en el nuevo shell bash:
cd
único tiene efecto dentro delbash -c
caparazón.sudo bash -c
tengo una expansión variable para un nombre de ruta que puede contener espacios en blanco.Respuestas:
No hay división de palabras (como en la función que divide las variables en expansiones sin comillas) en ese código, ya
$myvar
que no está sin comillas.Sin embargo, existe una vulnerabilidad de inyección de comandos que
$myvar
se expande antes de pasar abash
. ¡Entonces su contenido se interpreta como código bash!Los espacios allí causarán que se pasen varios argumentos
cd
, no debido a la división de palabras , sino porque se analizarán como varios tokens en la sintaxis del shell. ¡Con un valor debye;reboot
, eso se reiniciará! ¹Aquí te gustaría:
(donde pasa el contenido de
$myvar
como el primer argumento de esa secuencia de comandos en línea; tenga en cuenta cómo ambos$myvar
y$1
fueron citados por su respectivo shell para evitar la división de palabras IFS (y globbing)).O:
(donde pasa el contenido de
$myvar
en una variable de entorno).Por supuesto, no logrará nada útil ejecutando solo
cd
en ese script en línea (aparte de verificar siroot
puede hacerlocd
allí). Presumiblemente, desea que ese script estécd
allí y luego haga algo más como:Si la intención era usar
sudo
para podercd
ingresar a un directorio al que de otra manera no tendría acceso, entonces eso realmente no puede funcionar.iniciará un interactivo
bash
con su directorio actual en$myvar
. Pero ese shell se ejecutará comoroot
.Podrías hacerlo:
Para obtener una interacción no privilegiada
bash
con el directorio actual$myvar
, pero si no tenía los permisos paracd
ingresar a ese directorio en primer lugar, no podrá hacer nada en ese directorio, incluso si es su directorio de trabajo actual.Una excepción sería si tiene permiso de búsqueda para el directorio en sí pero no para uno de los componentes del directorio de su ruta:
¹ estrictamente hablando, para valores de
$myvar
like$(seq 10)
(literalmente), habría división de palabras, por supuesto, al expandir la sustitución de ese comando por elbash
shell iniciado comoroot
fuente
Hay una nueva magia de citas (bash 4.4) que te permite hacer más directamente lo que quieres. Dependiendo del contexto más amplio, usar una de las otras técnicas podría ser mejor, pero funciona en este contexto limitado.
fuente
Si no puede confiar en el contenido de
$myvar
, el único enfoque razonable es usar GNUprintf
para crear una versión escapada:Como
printf
dice la página del manual:fuente
printf
solo se invocará allí en los sistemas GNU y si$(printf '%q' "$myvar")
se ejecuta en un shell dondeprintf
no está integrado. La mayoría de las conchas se hanprintf
incorporado hoy en día. No todos admiten un%q
pensamiento y cuando lo hacen, citarían las cosas en un formato compatible con el shell correspondiente, que puede no ser necesariamente el mismo que el entendidobash
. En realidad,bash
'sprintf
falla citar cosas correctamente, incluso para sí mismo para algunos valores de$myvar
en algunos lugares.bash-4.3
al menos, eso es todavía una vulnerabilidad de inyección de comandos conmyvar=$'\xa3``reboot`\xa3`'
en un zh_HK.big5hkscs locale por ejemplo.En primer lugar, como otros han notado, su
cd
comando es inútil ya que ocurre en el contexto de un shell que se cierra inmediatamente. Pero en general la pregunta tiene sentido. Lo que necesita es una forma de citar shell una cadena arbitraria , de modo que pueda usarse en un contexto donde se interpretará como una cadena citada por shell. Tengo un ejemplo de esto en mi página de trucos sh :Con esta función, puede hacer:
fuente
Lo que sugiere Stéphane Chazelas es definitivamente la mejor manera de hacer esto. Solo proporcionaré una respuesta sobre cómo cotizar de manera segura con la sintaxis de expansión de parámetros en lugar de usar un
sed
subproceso, como en la respuesta de R ...La salida de ese script es:
fuente
Sí, hay división de palabras.
Bueno, técnicamente, la división de la línea de comando en bash analiza la línea.
Primero comencemos citando bien:
La solución más simple (e insegura) para que el comando funcione como creo que querrá que sea es:
Confirmemos lo que sucede (suponiendo
myvar="/path to/my directory"
):Como puede ver, la cadena de ruta se dividió en espacios. Y:
No está dividido
Sin embargo, el comando (como está escrito) es inseguro. Mejor uso:
Eso protegerá el CD contra archivos que comienzan con un guión (-) o que siguen enlaces que pueden generar problemas.
Eso funcionará si puede confiar en que la cadena
$myvar
es una ruta.Sin embargo, la "inyección de código" todavía es posible ya que está "ejecutando una cadena":
Necesita una cita más fuerte de la cadena, como:
Como puede ver arriba, el valor no se interpretó sino que se usó como
"$1'
.Ahora podemos (con seguridad) usar sudo:
Si hay varios argumentos, puede usar:
fuente
Las comillas simples no funcionarán aquí, ya que su variable no se expandirá. Si está seguro de que la variable para su ruta está desinfectada, la solución más simple es simplemente agregar comillas alrededor de la expansión resultante, una vez que esté en el nuevo shell bash:
fuente
$myvar
igual$(reboot)
o"; reboot; #
sudo bash -c "cd -P -- '$myvar'"
limitaría el problema a solo valores$myvar
que contengan comillas simples que serían más fáciles de desinfectar.