GNU/Linux >> Tutoriales Linux >  >> Linux

cambiar /proc/PID/environ después del inicio del proceso

En Linux, puede sobrescribir el valor de las cadenas de entorno en la pila.

Entonces puede ocultar la entrada sobrescribiéndola con ceros o cualquier otra cosa:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[], char* envp[]) {
  char cmd[100];

  while (*envp) {
    if (strncmp(*envp, "k=", 2) == 0)
      memset(*envp, 0, strlen(*envp));

    envp++;
  }

  sprintf(cmd, "cat /proc/%u/environ", getpid());

  system(cmd);
  return 0;
}

Ejecutar como:

$ env -i a=foo k=v b=bar ./wipe-env | hd
00000000  61 3d 66 6f 6f 00 00 00  00 00 62 3d 62 61 72 00  |a=foo.....b=bar.|
00000010

el k=v se ha sobrescrito con \0\0\0 .

Tenga en cuenta que setenv("k", "", 1) sobrescribir el valor no funcionará como en ese caso, un nuevo "k=" se asigna la cadena.

Si no ha modificado de otro modo el k variable de entorno con setenv() /putenv() , entonces también debería poder hacer algo como esto para obtener la dirección del k=v cadena en la pila (bueno, de uno de ellos):

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char* argv[]) {
  char cmd[100];
  char *e = getenv("k");

  if (e) {
    e -= strlen("k=");
    memset(e, 0, strlen(e));
  }

  sprintf(cmd, "cat /proc/%u/environ", getpid());

  system(cmd);
  return 0;
}

Sin embargo, tenga en cuenta que solo elimina uno del k=v Entradas recibidas en el entorno. Por lo general, solo hay uno, pero nada impide que alguien pase ambos k=v1 y k=v2 (o k=v dos veces) en la lista env pasada a execve() . Esa ha sido la causa de vulnerabilidades de seguridad en el pasado, como CVE-2016-2381. Realmente podría suceder con bash antes de shellshock al exportar tanto una variable como una función con el mismo nombre.

En cualquier caso, siempre habrá una pequeña ventana durante la cual la cadena env var aún no se ha anulado, por lo que es posible que desee encontrar otra forma de pasar el secreto información al comando (como una tubería, por ejemplo) si se expone a través de /proc/pid/environ es una preocupación.

También tenga en cuenta que contrariamente a /proc/pid/cmdline , /proc/pid/environment solo es accesible por procesos con el mismo euid o root (o root solo si el euid y el ruid del proceso no son los mismos que parece).

Puede ocultarles ese valor en /proc/pid/environ , pero aún pueden obtener cualquier otra copia que haya hecho de la cadena en la memoria, por ejemplo, al adjuntarle un depurador.

Consulte https://www.kernel.org/doc/Documentation/security/Yama.txt para conocer formas de evitar que al menos los usuarios no root hagan eso.


No ha sido necesario sobrescribir las cadenas anteriores (no realmente on ) la pila del subproceso principal en Linux desde 2010.

Ambos /proc/self/cmdline y /proc/self/environ son modificables por el propio proceso en tiempo de ejecución, a fuerza de llamar al prctl() función con respectivamente PR_SET_MM_ARG_START +PR_SET_MM_ARG_END o PR_SET_MM_ENV_START +PR_SET_MM_ENV_END . Estos establecen directamente los punteros de memoria en el espacio de memoria de la aplicación del proceso, mantenido por el kernel para cada proceso, que se utilizan para recuperar el contenido de /proc/${PID}/cmdline y /proc/${PID}/environ , y por lo tanto la línea de comando y el entorno informado por el ps comando.

Entonces, uno simplemente necesita construir un nuevo argumento o cadena de entorno (no un vector, observe:la memoria a la que apunta debe ser la cadena de datos real, concatenada y -delimited) y decirle al kernel dónde está.

Esto está documentado en la página del manual de Linux para el prctl(2) así como el environ(7) página de manuales. Lo que no es documentado es que el núcleo rechaza cualquier intento de establecer la dirección de inicio por encima de la dirección final, o la dirección final por debajo de la dirección de inicio; o para (re)establecer cualquiera de las direcciones a cero. Además, este no es el mecanismo original propuesto por Bryan Donlan en 2009, que permitía establecer principio y fin en una sola operación, de forma atómica. Además, el kernel no proporciona ninguna forma de obtener los valores actuales de estos punteros.

Esto hace que sea complicado modificar el entorno y las áreas de línea de comando con prctl() . Uno tiene que llamar al prctl() función hasta cuatro veces porque los primeros intentos pueden resultar en intentos de establecer el puntero de inicio más alto que el puntero final, dependiendo de dónde se encuentren los datos antiguos y nuevos en la memoria. Uno tiene que llamarlo más cuatro veces si uno quiere asegurarse de que esto no resulte en una ventana de oportunidad para que otros procesos en el sistema inspeccionen un rango arbitrario del espacio de memoria del proceso en el período en el que se ha establecido el nuevo inicio/final pero el nuevo final /inicio no ha sido.

Una única llamada al sistema atómico que establece todo el rango de una sola vez habría sido mucho más fácil de usar de forma segura para los programas de aplicaciones.

Otro inconveniente es que, sin ninguna buena razón (dadas las comprobaciones en el núcleo, la sobreescritura de las áreas de datos originales de todos modos , y el hecho de que los equivalentes no son operaciones privilegiadas en ninguno de los BSD), en Linux esto requiere privilegios de superusuario.

Escribí setprocargv() bastante simple y setprocenvv() funciones para mis conjuntos de herramientas, que emplean esto. Programas de carga en cadena de los conjuntos de herramientas integrados, como setenv y foreground , por lo tanto, reflejan los argumentos y el entorno del comando encadenado, donde Linux lo permite.

# /package/admin/nosh/command/clearenv setenv WIBBLE wobble foreground pause \; true &
[1] 1057
# hexdump -C /proc/1057/cmdline
00000000  66 6f 72 65 67 72 6f 75  6e 64 00 70 61 75 73 65  |foreground.pause|
00000010  00 3b 00 74 72 75 65 00                           |.;.true.|
00000018
# hexdump -C /proc/1057/environ
00000000  57 49 42 42 4c 45 3d 77  6f 62 62 6c 65 00        |WIBBLE=wobble.|
0000000e
# hexdump -C /proc/1058/cmdline
00000000  70 61 75 73 65 00                                 |pause.|
00000006
# hexdump -C /proc/1058/environ
00000000  57 49 42 42 4c 45 3d 77  6f 62 62 6c 65 00        |WIBBLE=wobble.|
0000000e
# 

Tenga en cuenta que esto no va en contra de las cosas que rastrean el proceso y acceden a su memoria directamente por otros medios (en lugar de a través de estos dos pseudoarchivos) y, por supuesto, deja una ventana antes de que se modifiquen las cadenas donde se puede ver esta información, solo como sobrescribir los datos sobre la pila del subproceso principal. Y al igual que ocurre con la sobrescritura de datos, esto no tiene en cuenta las bibliotecas de tiempo de ejecución de lenguaje que hacen copias del entorno (en el montón) en diversas circunstancias. En general, no considere que este sea un mecanismo tan bueno para pasar "secretos" a un programa como (digamos) heredar un descriptor de archivo abierto al final de lectura de una canalización sin nombre, leer en un búfer de entrada totalmente bajo su control que luego borras.

Lecturas adicionales

  • Timo Sirainen (2009-10-02). Se agregó la opción PR_SET_PROCTITLE_AREA para prctl() . Lista de correo del kernel de Linux.
  • https://unix.stackexchange.com/a/432681/5132
  • Daniel J. Bernstein. La interfaz de verificación de contraseña . cr.yp.a.
  • https://github.com/jdebp/nosh/blob/master/source/setprocargv.cpp
  • https://github.com/jdebp/nosh/blob/master/source/setprocenvv.cpp

Linux
  1. ¿Cómo obtengo la ruta de un proceso en Unix/Linux?

  2. Matar proceso por archivo pid

  3. ¿Cómo saber desde qué carpeta se está ejecutando un proceso?

  4. ¿Por qué cambia MemTotal en /proc/meminfo?

  5. Cómo cambiar el valor predeterminado /tmp a /home/user/tmp

Una guía para el sistema de archivos '/proc' en Linux

Archivos /proc/cpuinfo y /proc/meminfo en Linux

Comprender los archivos /proc/mounts, /etc/mtab y /proc/partitions

¿Cuándo debo usar /dev/shm/ y cuándo debo usar /tmp/?

¿Deberían vivir los sitios web en /var/ o /usr/ según el uso recomendado?

Advenedizo:iniciar el servicio después de un proceso no advenedizo