¿Qué es esta "seguridad" de la que hablas? ¿Seguro con respecto a qué?
waltinator
¿Qué quieres lograr con este comando?
jochen
Respuestas:
23
¡No intentes esto en casa! Puede bloquear su sistema y, si no tiene suerte, podría dañar un periférico o hacer que su computadora no se pueda arrancar.
En realidad, en la mayoría de las plataformas, simplemente falla con un error, pero eso depende de la arquitectura del hardware. Definitivamente no hay garantía de que esto sea inofensivo a menos que ejecute el comando como un usuario sin privilegios. Con un usuario sin privilegios, el comando es perfectamente inofensivo porque no se puede abrir /dev/mem.
Cuando ejecuta un comando como root, se supone que debe saber lo que está haciendo. El núcleo a veces le impedirá hacer algo peligroso, pero no siempre. /dev/memes una de esas cosas potencialmente peligrosas donde realmente se supone que debes saber lo que estás haciendo.
Voy a ver cómo funciona una escritura /dev/memen Linux. El principio general sería el mismo en otros Unices, pero cosas como las opciones del kernel son completamente diferentes.
Lo que sucede cuando un proceso lee o escribe en un archivo de dispositivo depende del núcleo. Un acceso a un archivo de dispositivo ejecuta un código en el controlador que maneja este archivo de dispositivo. Por ejemplo, escribir en /dev/meminvoca la función write_memendrivers/char/mem.c . Esta función toma 4 argumentos: una estructura de datos que representa el archivo abierto, un puntero a los datos a escribir, el número de bytes a escribir y la posición actual en el archivo.
Tenga en cuenta que solo llega tan lejos si la persona que llama tenía permiso para abrir el archivo en primer lugar. Los archivos del dispositivo obedecen los permisos de archivo normalmente. Los permisos normales de /dev/memson crw-r-----propiedad de root:kmem, por lo que si intenta abrirlo para escribir sin ser root, simplemente obtendrá un "permiso denegado" (EACCESS). Pero si eres root (o si root ha cambiado los permisos de este archivo), la apertura continúa y luego puedes intentar una escritura.
El código en la write_memfunción realiza algunas comprobaciones de cordura, pero estas comprobaciones no son suficientes para proteger contra todo lo malo. Lo primero que hace es convertir la posición actual del archivo *pposa una dirección física. Si eso falla (en la práctica, porque está en una plataforma con direcciones físicas de 32 bits pero con compensaciones de archivos de 64 bits y el desplazamiento del archivo es mayor que 2 ^ 32), la escritura falla con EFBIG (archivo demasiado grande). La siguiente comprobación es si el rango de direcciones físicas para escribir es válido en esta arquitectura de procesador en particular, y si se produce una falla en EFAULT (dirección incorrecta).
A continuación, en Sparc y m68k, cualquier parte de la escritura en la primera página física se omite silenciosamente.
Ahora hemos llegado al bucle principal que itera sobre los datos en bloques que pueden caber dentro de una página MMU .
/dev/memaccede a la memoria física, no a la memoria virtual, pero las instrucciones del procesador para cargar y almacenar datos en la memoria usan direcciones virtuales, por lo que el código debe organizarse para asignar la memoria física en alguna dirección virtual. En Linux, dependiendo de la arquitectura del procesador y la configuración del núcleo, esta asignación existe de forma permanente o debe realizarse sobre la marcha; ese es el trabajo de xlate_dev_mem_ptr(y unxlate_dev_mem_ptrdeshace lo xlate_dev_mem_ptrque sea que haga). Luego, la función copy_from_userlee del búfer que se pasó alwritellama al sistema y simplemente escribe en la dirección virtual donde está asignada actualmente la memoria física. El código emite instrucciones normales de almacenamiento de memoria, y lo que esto significa depende del hardware.
Antes de discutir que una escritura en una dirección física sí, hablaré sobre un cheque que ocurre antes de esta escritura. Dentro del bucle, la función page_is_allowedbloquea los accesos a ciertas direcciones si la opción de configuración del núcleo CONFIG_STRICT_DEVMEMestá habilitada (que es el caso por defecto): solo devmem_is_allowedse puede acceder a las direcciones permitidas por /dev/mem, para otros la escritura falla con EPERM (operación no permitida). La descripción de esta opción establece:
Si esta opción está activada y IO_STRICT_DEVMEM = n, el archivo / dev / mem solo permite el acceso del espacio de usuario al espacio PCI y al código del BIOS y las regiones de datos. Esto es suficiente para dosemu y X y para todos los usuarios comunes de / dev / mem.
Esta es una descripción muy centrada en x86. De hecho, de manera más genérica, CONFIG_STRICT_DEVMEMbloquea el acceso a las direcciones de memoria física que se asignan a la RAM, pero permite el acceso a las direcciones que no se asignan a la RAM. Los detalles de qué rangos de dirección física están permitidos dependen de la arquitectura del procesador, pero todos ellos excluyen la RAM donde se almacenan los datos del kernel y de los procesos de aterrizaje del usuario. La opción adicional CONFIG_IO_STRICT_DEVMEM(deshabilitada a partir de Ubuntu 18.04) bloquea los accesos a las direcciones físicas que son reclamadas por un controlador.
Direcciones de memoria física que se asignan a RAM . Entonces, ¿hay direcciones de memoria física que no se asignan a la RAM? Si. Esa es la discusión que prometí anteriormente sobre lo que significa escribir en una dirección.
Una instrucción de almacenamiento de memoria no necesariamente escribe en la RAM. El procesador descompone la dirección y decide a qué periférico enviar la tienda. (Cuando digo "el procesador", incluyo controladores periféricos que pueden no provenir del mismo fabricante). La RAM es solo uno de esos periféricos. La forma en que se realiza el despacho depende mucho de la arquitectura del procesador, pero los fundamentos son más o menos los mismos en todas las arquitecturas. El procesador básicamente descompone los bits más altos de la dirección y los busca en algunas tablas que se rellenan en función de información codificada, información obtenida al sondear algunos buses e información configurada por el software. Puede haber una gran cantidad de almacenamiento en caché y almacenamiento en búfer, pero en pocas palabras, después de esta descomposición,autobús y luego depende del periférico manejarlo. (O el resultado de la búsqueda en la tabla podría ser que no hay periféricos en esta dirección, en cuyo caso el procesador ingresa en un estado de trampa donde ejecuta algún código en el núcleo que normalmente resulta en un SIGBUS para el proceso de llamada).
Una tienda a una dirección que se asigna a la RAM no "hace" nada más que sobrescribir el valor que se almacenó previamente en esta dirección, con la promesa de que una carga posterior en la misma dirección devolverá el último valor almacenado. Pero incluso la RAM tiene algunas direcciones que no se comportan de esta manera: tiene algunos registros que pueden controlar cosas como la frecuencia de actualización y el voltaje.
En general, una lectura o escritura en un registro de hardware hace lo que el hardware está programado para hacer. La mayoría de los accesos al hardware funcionan de esta manera: el software (normalmente el código del núcleo) accede a una determinada dirección física, llega al bus que conecta el procesador con el periférico, y el periférico hace lo suyo. Algunos procesadores (en particular, x86) también tienen instrucciones de CPU separadas que provocan lecturas / escrituras en periféricos que son distintos de la carga y almacenamiento de memoria, pero incluso en x86, muchos periféricos se alcanzan a través de carga / almacenamiento.
El comando dd if=/dev/urandom of=/dev/memescribe datos aleatorios en cualquier periférico asignado en la dirección 0 (y direcciones posteriores, siempre que las escrituras tengan éxito). En la práctica, espero que en muchas arquitecturas, la dirección física 0 no tenga ningún periférico asignado o RAM, y por lo tanto, el primer intento de escritura falla. Pero si hay un periférico asignado en la dirección 0, o si cambia el comando para escribir en una dirección diferente, activará algo impredecible en el periférico. Con datos aleatorios en direcciones crecientes, es poco probable que haga algo interesante, pero en principio podría apagar la computadora (probablemente haya una dirección que de hecho haga esto), sobrescribir alguna configuración del BIOS que hace que sea imposible arrancar, o incluso golpear algunas periférico con errores de manera que lo dañe.
alias Russian_roulette='dd if=/dev/urandom of=/dev/mem seek=$((4096*RANDOM+4096*32768*RANDOM))'
¡Gracias! ¡Esto es lo que estaba buscando! ¡Estaba confundido si / dev / mem permite el acceso a la memoria a cosas como periféricos y cosas relacionadas con el hardware!
Coder14
Un periférico no se puede asignar a la dirección física 0 en x86 pc; esa configuración nunca arrancaría.
Joshua
Esto no es cierto
Yvain
1
Por supuesto, puede dañar el núcleo, pero no la BIOS
Yvain
1
@Yvain ¿Qué no es verdad? Y en realidad no puede dañar el núcleo si CONFIG_STRICT_DEVMEMestá habilitado.
Gilles 'SO- deja de ser malvado'
12
Es seguro si ha configurado correctamente el núcleo (seguro porque no funcionará)
/ dev / mem es un archivo de dispositivo de caracteres que es una imagen de la memoria principal de la computadora. Puede usarse, por ejemplo, para examinar (e incluso parchear) el sistema.
Entonces, en teoría, dd if=/dev/urandom of=/dev/memdebería sobrescribir todo el espacio de direcciones de la memoria física que ha instalado, y dado que el núcleo y otros programas se ejecutan desde la memoria, esto debería bloquear efectivamente el sistema. En la práctica, hay un límite. Desde la misma página de manual:
Desde Linux 2.6.26, y dependiendo de la arquitectura, la opción de configuración del núcleo CONFIG_STRICT_DEVMEM limita las áreas a las que se puede acceder a través de este archivo.
Al intentar esto en la máquina virtual Ubuntu 18.04, devuelve un error dd: writing to '/dev/mem': Operation not permittedincluso con sudoy a pesar de los permisos para root crw-r-----. De Ubuntu Wiki :
/ dev / mem protección
Algunas aplicaciones (Xorg) necesitan acceso directo a la memoria física desde el espacio del usuario. El archivo especial / dev / mem existe para proporcionar este acceso. En el pasado, era posible ver y cambiar la memoria del núcleo de este archivo si un atacante tenía acceso a la raíz. La opción de kernel CONFIG_STRICT_DEVMEM se introdujo para bloquear el acceso a la memoria que no es del dispositivo (originalmente llamado CONFIG_NONPROMISC_DEVMEM).
Entonces, técnicamente, no, no es seguro (ya que bloquearía el sistema) y si la opción del núcleo CONFIG_STRICT_DEVMEMestá deshabilitada, es un agujero de seguridad, pero por lo que veo hasta ahora, el comando no se ejecutaría si esa opción está habilitada. De acuerdo con el duplicado entre sitios , un reinicio solucionará cualquier problema con él, pero, por supuesto, los datos en la RAM en ese momento se perderían y no se descargarían en el disco (si es necesario).
Hay un método sugerido en el duplicado vinculado anteriormente, por busybox devmemlo que si está decidido a perder el tiempo con RAM, puede haber una manera después de todo.
"Es seguro" No, ciertamente no lo es. Incluso con CONFIG_STRICT_DEVMEM, puede acceder a las regiones de memoria donde se asigna un periférico, que es el punto de tener /dev/mem. Si escribes cosas al azar en periféricos, puede pasar cualquier cosa. Obtiene "operación no permitida" si intenta acceder a una dirección que no está asignada, y el comando comienza en la dirección 0. Si la dirección 0 se asigna a algo malo depende de la arquitectura del hardware. Por lo que sé, es posible que nunca se asigne a nada en una PC, pero en general no es seguro.
Gilles 'SO- deja de ser malvado'
1
@Gilles En x86 (no estoy seguro acerca de x86-64), el primer 1 KiB de RAM (direcciones 0x0 a 0x3ff) contiene los vectores de interrupción; cuatro bytes de dirección por vector. Si logra sobrescribir a aquellos con basura aleatoria, es probable que sucedan todo tipo de cosas interesantes muy pronto. Lo más probable es que termines con una falla doble o triple y el sistema se bloqueará, pero no hay garantías ...
un CVn
@aCVn Ciertamente hay algo mapeado ( head -c 1024 </dev/mem | od -tx1), pero no sé si se usan cuando el procesador no está en modo real (modo 8088). No creo que puedan usarse en el modo de 64 bits: después de todo, los vectores de interrupción 8088 solo tienen 32 bits para la dirección. Y, por cierto, esto es accesible con CONFIG_STRICT_DEVMEMset, así que supongo que Linux no lo usa.
Gilles 'SO- deja de ser malvado'
@Gilles: la página 0 en x86 está reservada para v86, gestor de arranque, etc. esa es la tabla de vectores de interrupción en modo real allí. En modo protegido, el IVT está en otro lugar (un registro de máquina dice dónde).
Respuestas:
¡No intentes esto en casa! Puede bloquear su sistema y, si no tiene suerte, podría dañar un periférico o hacer que su computadora no se pueda arrancar.
En realidad, en la mayoría de las plataformas, simplemente falla con un error, pero eso depende de la arquitectura del hardware. Definitivamente no hay garantía de que esto sea inofensivo a menos que ejecute el comando como un usuario sin privilegios. Con un usuario sin privilegios, el comando es perfectamente inofensivo porque no se puede abrir
/dev/mem
.Cuando ejecuta un comando como root, se supone que debe saber lo que está haciendo. El núcleo a veces le impedirá hacer algo peligroso, pero no siempre.
/dev/mem
es una de esas cosas potencialmente peligrosas donde realmente se supone que debes saber lo que estás haciendo.Voy a ver cómo funciona una escritura
/dev/mem
en Linux. El principio general sería el mismo en otros Unices, pero cosas como las opciones del kernel son completamente diferentes.Lo que sucede cuando un proceso lee o escribe en un archivo de dispositivo depende del núcleo. Un acceso a un archivo de dispositivo ejecuta un código en el controlador que maneja este archivo de dispositivo. Por ejemplo, escribir en
/dev/mem
invoca la funciónwrite_mem
endrivers/char/mem.c
. Esta función toma 4 argumentos: una estructura de datos que representa el archivo abierto, un puntero a los datos a escribir, el número de bytes a escribir y la posición actual en el archivo.Tenga en cuenta que solo llega tan lejos si la persona que llama tenía permiso para abrir el archivo en primer lugar. Los archivos del dispositivo obedecen los permisos de archivo normalmente. Los permisos normales de
/dev/mem
soncrw-r-----
propiedad deroot:kmem
, por lo que si intenta abrirlo para escribir sin ser root, simplemente obtendrá un "permiso denegado" (EACCESS). Pero si eres root (o si root ha cambiado los permisos de este archivo), la apertura continúa y luego puedes intentar una escritura.El código en la
write_mem
función realiza algunas comprobaciones de cordura, pero estas comprobaciones no son suficientes para proteger contra todo lo malo. Lo primero que hace es convertir la posición actual del archivo*ppos
a una dirección física. Si eso falla (en la práctica, porque está en una plataforma con direcciones físicas de 32 bits pero con compensaciones de archivos de 64 bits y el desplazamiento del archivo es mayor que 2 ^ 32), la escritura falla con EFBIG (archivo demasiado grande). La siguiente comprobación es si el rango de direcciones físicas para escribir es válido en esta arquitectura de procesador en particular, y si se produce una falla en EFAULT (dirección incorrecta).A continuación, en Sparc y m68k, cualquier parte de la escritura en la primera página física se omite silenciosamente.
Ahora hemos llegado al bucle principal que itera sobre los datos en bloques que pueden caber dentro de una página MMU .
/dev/mem
accede a la memoria física, no a la memoria virtual, pero las instrucciones del procesador para cargar y almacenar datos en la memoria usan direcciones virtuales, por lo que el código debe organizarse para asignar la memoria física en alguna dirección virtual. En Linux, dependiendo de la arquitectura del procesador y la configuración del núcleo, esta asignación existe de forma permanente o debe realizarse sobre la marcha; ese es el trabajo dexlate_dev_mem_ptr
(yunxlate_dev_mem_ptr
deshace loxlate_dev_mem_ptr
que sea que haga). Luego, la funcióncopy_from_user
lee del búfer que se pasó alwrite
llama al sistema y simplemente escribe en la dirección virtual donde está asignada actualmente la memoria física. El código emite instrucciones normales de almacenamiento de memoria, y lo que esto significa depende del hardware.Antes de discutir que una escritura en una dirección física sí, hablaré sobre un cheque que ocurre antes de esta escritura. Dentro del bucle, la función
page_is_allowed
bloquea los accesos a ciertas direcciones si la opción de configuración del núcleoCONFIG_STRICT_DEVMEM
está habilitada (que es el caso por defecto): solodevmem_is_allowed
se puede acceder a las direcciones permitidas por/dev/mem
, para otros la escritura falla con EPERM (operación no permitida). La descripción de esta opción establece:Esta es una descripción muy centrada en x86. De hecho, de manera más genérica,
CONFIG_STRICT_DEVMEM
bloquea el acceso a las direcciones de memoria física que se asignan a la RAM, pero permite el acceso a las direcciones que no se asignan a la RAM. Los detalles de qué rangos de dirección física están permitidos dependen de la arquitectura del procesador, pero todos ellos excluyen la RAM donde se almacenan los datos del kernel y de los procesos de aterrizaje del usuario. La opción adicionalCONFIG_IO_STRICT_DEVMEM
(deshabilitada a partir de Ubuntu 18.04) bloquea los accesos a las direcciones físicas que son reclamadas por un controlador.Direcciones de memoria física que se asignan a RAM . Entonces, ¿hay direcciones de memoria física que no se asignan a la RAM? Si. Esa es la discusión que prometí anteriormente sobre lo que significa escribir en una dirección.
Una instrucción de almacenamiento de memoria no necesariamente escribe en la RAM. El procesador descompone la dirección y decide a qué periférico enviar la tienda. (Cuando digo "el procesador", incluyo controladores periféricos que pueden no provenir del mismo fabricante). La RAM es solo uno de esos periféricos. La forma en que se realiza el despacho depende mucho de la arquitectura del procesador, pero los fundamentos son más o menos los mismos en todas las arquitecturas. El procesador básicamente descompone los bits más altos de la dirección y los busca en algunas tablas que se rellenan en función de información codificada, información obtenida al sondear algunos buses e información configurada por el software. Puede haber una gran cantidad de almacenamiento en caché y almacenamiento en búfer, pero en pocas palabras, después de esta descomposición,autobús y luego depende del periférico manejarlo. (O el resultado de la búsqueda en la tabla podría ser que no hay periféricos en esta dirección, en cuyo caso el procesador ingresa en un estado de trampa donde ejecuta algún código en el núcleo que normalmente resulta en un SIGBUS para el proceso de llamada).
Una tienda a una dirección que se asigna a la RAM no "hace" nada más que sobrescribir el valor que se almacenó previamente en esta dirección, con la promesa de que una carga posterior en la misma dirección devolverá el último valor almacenado. Pero incluso la RAM tiene algunas direcciones que no se comportan de esta manera: tiene algunos registros que pueden controlar cosas como la frecuencia de actualización y el voltaje.
En general, una lectura o escritura en un registro de hardware hace lo que el hardware está programado para hacer. La mayoría de los accesos al hardware funcionan de esta manera: el software (normalmente el código del núcleo) accede a una determinada dirección física, llega al bus que conecta el procesador con el periférico, y el periférico hace lo suyo. Algunos procesadores (en particular, x86) también tienen instrucciones de CPU separadas que provocan lecturas / escrituras en periféricos que son distintos de la carga y almacenamiento de memoria, pero incluso en x86, muchos periféricos se alcanzan a través de carga / almacenamiento.
El comando
dd if=/dev/urandom of=/dev/mem
escribe datos aleatorios en cualquier periférico asignado en la dirección 0 (y direcciones posteriores, siempre que las escrituras tengan éxito). En la práctica, espero que en muchas arquitecturas, la dirección física 0 no tenga ningún periférico asignado o RAM, y por lo tanto, el primer intento de escritura falla. Pero si hay un periférico asignado en la dirección 0, o si cambia el comando para escribir en una dirección diferente, activará algo impredecible en el periférico. Con datos aleatorios en direcciones crecientes, es poco probable que haga algo interesante, pero en principio podría apagar la computadora (probablemente haya una dirección que de hecho haga esto), sobrescribir alguna configuración del BIOS que hace que sea imposible arrancar, o incluso golpear algunas periférico con errores de manera que lo dañe.fuente
CONFIG_STRICT_DEVMEM
está habilitado.Es seguro si ha configurado correctamente el núcleo (seguro porque no funcionará)
Por página manual mem (4) :
Entonces, en teoría,
dd if=/dev/urandom of=/dev/mem
debería sobrescribir todo el espacio de direcciones de la memoria física que ha instalado, y dado que el núcleo y otros programas se ejecutan desde la memoria, esto debería bloquear efectivamente el sistema. En la práctica, hay un límite. Desde la misma página de manual:Al intentar esto en la máquina virtual Ubuntu 18.04, devuelve un error
dd: writing to '/dev/mem': Operation not permitted
incluso consudo
y a pesar de los permisos para rootcrw-r-----
. De Ubuntu Wiki :Entonces, técnicamente, no, no es seguro (ya que bloquearía el sistema) y si la opción del núcleo
CONFIG_STRICT_DEVMEM
está deshabilitada, es un agujero de seguridad, pero por lo que veo hasta ahora, el comando no se ejecutaría si esa opción está habilitada. De acuerdo con el duplicado entre sitios , un reinicio solucionará cualquier problema con él, pero, por supuesto, los datos en la RAM en ese momento se perderían y no se descargarían en el disco (si es necesario).Hay un método sugerido en el duplicado vinculado anteriormente, por
busybox devmem
lo que si está decidido a perder el tiempo con RAM, puede haber una manera después de todo.fuente
CONFIG_STRICT_DEVMEM
, puede acceder a las regiones de memoria donde se asigna un periférico, que es el punto de tener/dev/mem
. Si escribes cosas al azar en periféricos, puede pasar cualquier cosa. Obtiene "operación no permitida" si intenta acceder a una dirección que no está asignada, y el comando comienza en la dirección 0. Si la dirección 0 se asigna a algo malo depende de la arquitectura del hardware. Por lo que sé, es posible que nunca se asigne a nada en una PC, pero en general no es seguro.head -c 1024 </dev/mem | od -tx1
), pero no sé si se usan cuando el procesador no está en modo real (modo 8088). No creo que puedan usarse en el modo de 64 bits: después de todo, los vectores de interrupción 8088 solo tienen 32 bits para la dirección. Y, por cierto, esto es accesible conCONFIG_STRICT_DEVMEM
set, así que supongo que Linux no lo usa.