¿Cómo forzo explícita y segura el uso de un comando incorporado en bash


19

Hay una pregunta similar que trata con el escenario de 'envoltura', donde desea reemplazar, por ejemplo, cdcon un comando que llama al incorporado cd.

Sin embargo, a la luz de shellshock et al y sabiendo que bash importa funciones del entorno, he realizado algunas pruebas y no puedo encontrar una manera segura de llamar al builtin cddesde mi script.

Considera esto

cd() { echo "muahaha"; }
export -f cd

Cualquier script llamado en este entorno usando cdse romperá (considere los efectos de algo así cd dir && rm -rf .).

Hay comandos para verificar el tipo de comando (convenientemente llamado type) y comandos para ejecutar la versión incorporada en lugar de una función ( builtiny command). Pero, he aquí, estos pueden ser anulados usando funciones también

builtin() { "$@"; }
command() { "$@"; }
type() { echo "$1 is a shell builtin"; }

Producirá lo siguiente:

$ type cd
cd is a shell builtin
$ cd x
muahaha
$ builtin cd x
muahaha
$ command cd x
muahaha

¿Hay alguna manera de forzar a bash de manera segura a usar el comando incorporado, o al menos detectar que un comando no es un incorporado, sin borrar todo el entorno?

Me doy cuenta de que si alguien controla su entorno, probablemente esté jodido de todos modos, pero al menos para los alias tiene la opción de no llamar al alias insertando un \antes.


puede ejecutar su script usando un envcomando antes, como este:env -i <SCRIPT.sh>
Arkadiusz Drabczyk

Solo si envno se redefine como una función también. Esto es aterrador Primero pensé que los caracteres especiales ayudarían: llamar con la ruta completa que incluye /, usar .a la fuente, etc. ¡Pero esos también se pueden usar para nombres de funciones! Usted puede redefinir cualquier función que desee, pero es difícil volver a llamar al mandato original.
orion

1
Es cierto, pero si vas a ejecutar cualquier script en una máquina comprometida, estás jodido de todos modos. Alternativamente, escriba su script #/bin/shsi este no es un shell interactivo predeterminado.
Arkadiusz Drabczyk

Respuestas:


16

Olivier D es casi correcto, pero debe configurarlo POSIXLY_CORRECT=1antes de ejecutarlo unset. POSIX tiene una noción de Special Built-ins , y bash lo admite . unsetes uno de esos incorporados. Buscar SPECIAL_BUILTINen builtins/*.cla fuente de fiesta para una lista, que incluye set, unset, export, evaly source.

$ unset() { echo muahaha-unset; }
$ unset unset
muahaha-unset
$ POSIXLY_CORRECT=1
$ unset unset

El pícaro unsetahora ha sido retirado del medio ambiente, si de borra command, type, builtinentonces usted debería ser capaz de proceder, pero unset POSIXLY_CORRECTsi usted está confiando en un comportamiento no POSIX o características de bash avanzadas.

Sin embargo, esto no aborda los alias, por lo que debe usar \unsetpara asegurarse de que funciona en un shell interactivo (o siempre, en caso de que expand_aliasesesté vigente).

Para los paranoicos, esto debería arreglarlo todo, creo:

POSIXLY_CORRECT=1
\unset -f help read unset
\unset POSIXLY_CORRECT
re='^([a-z:.\[]+):' # =~ is troublesome to escape
while \read cmd; do 
    [[ "$cmd" =~ $re ]] && \unset -f ${BASH_REMATCH[1]}; 
done < <( \help -s "*" )

( while, do, doneY [[son palabras reservadas y no necesitan precauciones.) Tenga en cuenta que estamos utilizando unset -fpara asegurarse de que las funciones no definidas, aunque variables y funciones comparten el mismo espacio de nombres que es posible tanto para existir simultáneamente (gracias a Etan Reisner), en cuyo caso Inmovilizar dos veces también haría el truco. Usted puede marcar una función de sólo lectura, golpe del no impide que desarmar una función de sólo lectura hasta e incluyendo bash-4.2, bash-4.3 le impide pero todavía rinde homenaje a las órdenes internas especiales cuando POSIXLY_CORRECTse establece.

Un solo lectura POSIXLY_CORRECTno es un problema real, no es un booleano o marca su presencia habilita el modo POSIX, por lo que si existe como solo lectura puede confiar en las características POSIX, incluso si el valor está vacío o 0. Simplemente necesitará desarma las funciones problemáticas de una manera diferente a la anterior, quizás con algo de cortar y pegar:

\help -s "*" | while IFS=": " read cmd junk; do echo \\unset -f $cmd; done

(e ignorar cualquier error) o participar en otros scriptobatics .


Otras notas:

  • functiones una palabra reservada, puede ser alias pero no anulada con una función. (El alias functiones levemente problemático porque \function no es aceptable como una forma de evitarlo)
  • [[, ]]son palabras reservadas, pueden tener un alias (que será ignorado) pero no anularse con una función (aunque las funciones pueden llamarse así)
  • (( no es un nombre válido para una función, ni un alias

Interesante, gracias! Me parece extraño que bash no lo proteja por completo y nounset se sobrescriba.
falstro

Esto necesita el -fargumento para unsetno? De lo contrario, si se definen una variable y una función con el mismo nombre que una función incorporada, unsetprimero se seleccionará la variable para desarmar. También esto falla si alguna de las funciones oscurecedoras está configurada, readonly¿no? (Aunque eso es detectable y se puede cometer un error fatal). También [parece que esto falla .
Etan Reisner

1
No creo que \unset -f unsettenga sentido, dado que se hará en el ciclo de lectura. Si unsetes una función \unsetque normalmente resolvería en lugar de la integrada, pero puede usarla con \unsetseguridad siempre que el modo POSIX esté vigente. Explícitamente llamar \unset -fen [, .y :podría ser una buena idea, aunque, como sus expresiones regulares excluye ellos. También agregaría -fal \unseten el bucle, y lo haría \unset POSIXLY_CORRECTdespués del bucle, en lugar de antes. \unalias -a(after \unset -f unalias) también le permite a uno renunciar de manera segura al escape en los comandos posteriores.
Adrian Günter

1
@ AdrianGünter Intente: [[ ${POSIXLY_CORRECT?non-POSIX mode shell is untrusted}x ]]esto provocará que una secuencia de comandos no interactiva salga con el error indicado si la variable no está configurada o continúe si está configurada (vacía o cualquier valor).
mr.spuratic

1
"debe usar \unset" ... Debe tenerse en cuenta que la cita de cualquier carácter funcionaría para evitar la expansión del alias, no solo el primero. un"se"tfuncionaría igual de bien, pero menos legible. "La primera palabra de cada comando simple, si no se cita, se verifica para ver si tiene un alias". - Bash Ref
Robin A. Meade

6

Me doy cuenta de que si alguien controla tu entorno, probablemente estés jodido de todos modos

Si, eso. Si ejecuta un script en un entorno desconocido, todo tipo de cosas pueden salir mal, comenzando por LD_PRELOADhacer que el proceso de shell ejecute código arbitrario antes de que incluso lea su script. Intentar protegerse contra un entorno hostil desde el interior del script es inútil.

Sudo ha estado desinfectando el medio ambiente eliminando todo lo que parece una definición de función bash durante más de una década. Desde Shellshock , otros entornos que ejecutan script de shell en un entorno no totalmente confiable han seguido su ejemplo.

No puede ejecutar de forma segura un script en un entorno que haya sido configurado por una entidad no confiable. Por lo tanto, preocuparse por las definiciones de funciones no es productivo. Desinfecte su entorno y, al hacerlo, las variables que bash interpretarían como definiciones de funciones.


1
No estoy de acuerdo con esto por completo. La seguridad no es una propuesta binaria centrada en "desinfectado" o "no desinfectado". Se trata de eliminar las oportunidades de explotación. Y desafortunadamente, la complejidad de los sistemas modernos significa que hay muchas oportunidades de explotación que deben bloquearse adecuadamente. Muchas oportunidades de explotación se centran en ataques aguas arriba que parecen inocuos, como cambiar la variable PATH, redefinir las funciones integradas, etc. Ofrezca a una sola persona o programa la oportunidad de hacerlo, y todo su sistema es vulnerable.
Dejay Clayton

@DejayClayton Estoy totalmente de acuerdo con tu comentario, excepto por la primera oración, porque no veo cómo contradice mi respuesta de ninguna manera. Eliminar una oportunidad de explotación ayuda, pero no eliminar solo la mitad cuando un ajuste trivial en el ataque le permite seguir funcionando.
Gilles 'SO- deja de ser malvado'

Mi punto es que no puedes simplemente "desinfectar tu entorno" y luego dejar de preocuparte por varios vectores de ataque. A veces vale la pena "proteger contra un entorno hostil desde el interior del script". Incluso si resuelve el problema de "definición de función", aún debe preocuparse por cosas como tener una RUTA donde alguien podría poner un script personalizado llamado "cat" o "ls" en un directorio, y luego cualquier script que invoque "cat" "o" ls "en lugar de" / bin / cat "y" / bin / ls "está ejecutando efectivamente un exploit.
Dejay Clayton

@DejayClayton Obviamente, desinfectar el entorno no ayuda con los vectores de ataque que no están relacionados con el entorno. (Por ejemplo, un script que llama /bin/cata un chroot podría estar llamando a cualquier cosa). Desinfectar el medio ambiente te da una PATHrazón (suponiendo que lo estás haciendo bien, por supuesto). Todavía no entiendo tu punto.
Gilles 'SO- deja de ser malvado'

Teniendo en cuenta que PATH es una de las mayores oportunidades para exploits, y que muchas prácticas cuestionables de PATH están documentadas como consejos recomendados incluso en blogs de seguridad, y también teniendo en cuenta que a veces las aplicaciones instaladas reordenan PATH para garantizar que tales aplicaciones funcionen, no puedo ver cómo La codificación defensiva dentro de un guión sería una mala idea. Lo que consideras una RUTA sana puede ser vulnerable a exploits que no has encontrado.
Dejay Clayton

1

Puede usar unset -fpara eliminar las funciones incorporadas, comando y tipo.


2
lo siento, unset() { true; }se encarga de eso.
falstro

1
¡Maldición! Y enumerar las funciones definidas también se basa en una función integrada. ¡Me rindo!
odc
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.