Implicaciones de seguridad de ejecutar perl -ne '...' *

27

Al parecer, corriendo:

perl -n -e 'some perl code' *

O

find . ... -exec perl -n -e '...' {} +

(lo mismo con en -plugar de -n)

O

perl -e 'some code using <>' *

a menudo se encuentra en una línea publicada en este sitio, tiene implicaciones de seguridad. ¿Cual es el trato? ¿Cómo evitarlo?

Stéphane Chazelas
fuente

Respuestas:

33

Cuál es el problema

Primero, como para muchas utilidades, tendrás un problema con los nombres de archivos que comienzan con -. Mientras en:

sh -c 'inline sh script here' other args

Los otros argumentos se pasan al inline sh script; con el perlequivalente

perl -e 'inline perl script here' other args

Los otros argumentos se analizan en busca de más opciones para perl primero, no para el script en línea. Entonces, por ejemplo, si hay un archivo llamado -eBEGIN{do something evil}en el directorio actual,

perl -ne 'inline perl script here;' *

(con o sin -n) hará algo malvado.

Al igual que para otras utilidades, la solución es usar el marcador de fin de opciones ( --):

perl -ne 'inline perl script here;' -- *

Pero incluso entonces, sigue siendo peligroso y eso depende del <>operador utilizado por -n/ -p.

El problema se explica en la perldoc perlopdocumentación.

Ese operador especial se usa para leer una línea (un registro, los registros son líneas por defecto) de entrada, donde esa entrada proviene de cada uno de los argumentos a su vez pasados @ARGV.

En:

perl -pe '' a b

-pimplica un while (<>)bucle alrededor del código (aquí vacío).

<>primero se abrirá a, leerá los registros línea por línea hasta que se agote el archivo y luego se abrirá b...

El problema es que, para abrir el archivo, utiliza la primera forma insegura de open:

open ARGV, "the file as provided"

Con esa forma, si el argumento es

  • "> afile", se abre afileen modo de escritura,
  • "cmd|", se ejecuta cmdy lee su salida.
  • "|cmd", tienes una secuencia abierta para escribir en la entrada de cmd.

Entonces, por ejemplo:

perl -pe '' 'uname|'

No genera el contenido del archivo llamado uname|(un nombre de archivo perfectamente válido por cierto), sino el resultado del unamecomando.

Si estás corriendo:

perl -ne 'something' -- *

Y alguien ha creado un archivo llamado rm -rf "$HOME"|(de nuevo un nombre de archivo perfectamente válido) en el directorio actual (por ejemplo, porque ese directorio fue una vez escrito por otros, o ha extraído un archivo dudoso, o ha ejecutado algún comando dudoso, o otra vulnerabilidad en algún otro software fue explotada), entonces estás en un gran problema. Las áreas donde es importante tener en cuenta ese problema son las herramientas que procesan archivos automáticamente en áreas públicas como /tmp(o herramientas que pueden ser llamadas por tales herramientas).

Archivos llamados > foo, foo|, |fooson un problema. Pero en menor medida < fooy foocon caracteres de espaciado ASCII iniciales o finales (incluyendo espacio, tabulación, nueva línea, cr ...) y eso significa que esos archivos no se procesarán o que el incorrecto sí lo será.

También tenga en cuenta que algunos caracteres en algunos conjuntos de caracteres de varios bytes (como ǖen BIG5-HKSCS) terminan en el byte 0x7c, la codificación de |.

$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000  88  7c
        210   |
0000002

Entonces, en las configuraciones regionales que usan ese juego de caracteres,

 perl -pe '' ./nǖ

¡Intentaría ejecutar el ./n\x88comando ya perlque no intentaría interpretar ese nombre de archivo en la configuración regional del usuario!

Cómo arreglar / solucionar

AFAIK, no hay nada que pueda hacer para cambiar ese comportamiento predeterminado inseguro de perluna vez por todas en todo el sistema.

Primero, el problema ocurre solo con los caracteres al principio y al final del nombre del archivo. Entonces, mientras perl -ne '' *o perl -ne '' *.txtson un problema,

perl -ne 'some code' ./*.txt

no se debe a todos los argumentos ahora comienzan con ./y terminan en .txt(lo que no -, <, >, |, espacio ...). En general, es una buena idea prefijar los globos con ./. Eso también evita problemas con los archivos llamados -o que comienzan con -muchas otras utilidades (y aquí, eso significa que ya no necesita el --marcador de fin de opciones ( )).

Usar -Tpara activar el taintmodo ayuda hasta cierto punto. Anulará el comando si se encuentra dicho archivo malicioso (solo para los casos >y |, sin <embargo , o espacios en blanco).

Eso es útil cuando se utilizan dichos comandos de forma interactiva, ya que te avisa de que hay algo dudoso. Sin embargo, eso puede no ser deseable cuando se realiza un procesamiento automático, ya que eso significa que alguien puede hacer que ese procesamiento falle simplemente creando un archivo.

Si desea procesar cada archivo, independientemente de su nombre, puede usar el ARGV::readonly perlmódulo en CPAN (desafortunadamente, por lo general, no está instalado de manera predeterminada). Ese es un módulo muy corto que hace:

sub import{
   # Tom Christiansen in Message-ID: <24692.1217339882@chthon>
   # reccomends essentially the following:
   for (@ARGV){
       s/^(\s+)/.\/$1/;   # leading whitespace preserved
       s/^/< /;       # force open for input
       $_.=qq/\0/;    # trailing whitespace preserved & pipes forbidden
   };
};

Básicamente, desinfecta @ARGV convirtiéndolo, " foo|"por ejemplo, en "< ./ foo|\0".

Puede hacer lo mismo en una BEGINdeclaración en su perl -n/-pcomando:

perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*

Aquí lo simplificamos suponiendo que ./se está utilizando.

Un efecto secundario de que (y ARGV::readonly) es sin embargo que $ARGVen your code hereespectáculos que se arrastra carácter NUL.

Actualizar 2015-06-03

perlv5.21.5 y superior tienen un nuevo <<>>operador que se comporta como, <>excepto que no realizará ese procesamiento especial. Los argumentos solo se considerarán como nombres de archivo. Entonces, con esas versiones, ahora puede escribir:

perl -e 'while(<<>>){ ...;}' -- *

(no olvide --o use ./*) sin temor a sobrescribir archivos o ejecutar comandos inesperados.

-nSin -pembargo, todavía uso la <>forma peligrosa . Y tenga cuidado con los enlaces simbólicos, de modo que eso no significa necesariamente que sea seguro usarlo en directorios que no sean de confianza.

Stéphane Chazelas
fuente
2
has estado trabajando en eso todo el día, apostaré. bien hecho.
mikeserv
2
buena actualización de perl, pero es extraño que los desarrolladores de perl no agreguen las opciones -P y -N para hacer uso de ella (no puede cambiar los existentes -p y -n porque algunos scripts pueden depender del comportamiento inseguro)
cas
9

Además de la respuesta de @ Stéphane Chazelas , no tenemos que preocuparnos por este problema si utilizamos la -iopción de línea de comando:

$ perl -pe '' 'uname|'
Linux

$ perl -i -pe '' 'uname|'
Can't open uname|: No such file or directory.

Porque cuando usaba la -iopción, perlusaba stat para verificar el estado del archivo antes de procesarlo:

$ strace -fe trace=stat perl -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
Process 6106 attached
Linux
Process 6105 suspended
Process 6105 resumed
Process 6106 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

$ strace -fe trace=stat perl -i -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("uname|", 0x785f40)                = -1 ENOENT (No such file or directory)
Can't open uname|: No such file or directory.
Cuonglm
fuente
1
¿No hay una posible condición de carrera entre la statverificación y el procesamiento efectivo de Perl que se realiza justo después?
Totor
@Totor: creo que no.
Cuonglm
No se trata stat. Es solo -ipara editar archivos en su lugar, por lo que no tiene sentido aceptar argumentos que no sean rutas de archivos reales, por lo que con -iese procesamiento especial no se realiza.
Stéphane Chazelas