Publi

Ocultar los parámetros de nuestro proceso a ps

password_flickrEn ocasiones, estamos desarrollando una aplicación, y ésta necesita que le pasemos como parámetro, por ejemplo, una contraseña. El gran peligro que esto tiene es que cualquier usuario, pidiendo un listado de procesos con ps podrá ver la contraseña.

Imaginemos que tenemos una aplicación (que hemos hecho nosotros) que conecta con un servidor, y dado que hemos hecho un script para automatizar el proceso, el nombre de usuario y contraseña los pasaremos como parámetro al ejecutable. Dicho proceso se llama “aplicacionsegura”, dicho programa lo hemos lanzado en un servidor y otros usuarios tendrán acceso a dicha máquina. Ahora uno de ellos ejecuta lo siguiente:

$ ps ax
….
10560 pts/3    S+     0:00 ./aplicacionsegura –user=usuario –passwd=micontraseña

¡Nuestro gozo en un pozo! Otro usuario ya tiene nuestra contraseña, y ha sido muy sencillo. Y además lo podremos ver (de hecho ps es lo que hace) viendo el archivo /proc/PID/cmdline. Aunque debemos ver qué opciones tenemos:

Modificar el parámetro passwd

Para ilustrar el ejemplo, vamos a fijar la posición de –passwd=xxxxx al segundo parámetro (la captura de parámetros no es objeto de este post). Lo primero que vamos a probar es introducir un terminador en las cadenas de los parámetros, Si la cadena empieza por ‘\0’ estará vacía:

1
2
3
4
...
argv[1][0]='\0';
argv[2][0]='\0';
....

Aunque tardaremos poco en darnos cuenta que da igual, por una parte, no podríamos borrar todos los rastros de información, ya que si escribimos en la posición 0 de la cadena “–passwd=micontraseña” un terminador, el resto de la cadena se quedará en memoria y podrá ser leída. De todas formas, la cadena parece que sólo tiene un espacio para ps, es más, el \0 le ha dado igual.

Probemos ahora escribiendo “HOLA” en la cadena de la contraseña (ahora sí pongo el código completo, test.c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
  /* Reservamos memoria de sobra */
  char password[40];
  char usuario[40];

  if (argc<3)
    {
      fprintf(stderr,"Debe tener dos parámetros:\n%s --user=USUARIO --passwd=CLAVE\n", argv[0]);
      return EXIT_FAILURE;
    }

  /* Esto es un simple ejemplo, vamos a quitar --user y --passwd de una forma fea */

  /* Copiamos el usuario en otra variable */
  strcpy(usuario, argv[1]+7);   /* +7, para eliminar --user= (7 caracteres) */

  /* Copiamos la contraseña en otra variable */
  strcpy(password, argv[2]+9);  /* +9, para eliminar --passwd= (9 caracteres) */

  strcpy(argv[2]+9, "HOLA"); // EN LA CLAVE PONEMOS "HOLA"

  printf("user: %s ; pass: %s", usuario, password);
  /* Esperamos una tecla para terminar (sólo para que no se cierre demasiado rápido */
  /* y nos dé tiempo a mirar la información de ps */
  getchar();
  return EXIT_SUCCESS;
}

Ahora ejecutaremos lo siguiente:

$ gcc -o test test.c
$ ./test & # El programa test esperará una tecla para terminar, pero lo ejecutaremos de fondo, para que no se cierre.
$ ps ax
….
10809 pts/3 S+ 0:00 ./test –user=usuario –passwd=HOLA traseña
….
$ killall test # Para matar el proceso

Un efecto curioso, las cadenas de los parámetros no son NULL-terminated, de cara a /proc/PID/cmdline por lo que cuando ponemos un terminador, lo toma como un espacio. Y se ve parte del parámetro. Lo que sí podemos hacer es sustituir por asteriscos, tantos asteriscos como letras tenga el parámetro.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void rellenaConAsteriscos(char *cadena);

int main(int argc, char *argv[])
{
  /* Reservamos memoria de sobra */
  char password[40];
  char usuario[40];

  if (argc<3)
    {
      fprintf(stderr,"Debe tener dos parámetros:\n%s --user=USUARIO --passwd=CLAVE\n", argv[0]);
      return EXIT_FAILURE;
    }

  /* Esto es un simple ejemplo, vamos a quitar --user y --passwd de una forma fea */

  /* Copiamos el usuario en otra variable */
  strcpy(usuario, argv[1]+7);   /* +7, para eliminar --user= (7 caracteres) */

  /* Copiamos la contraseña en otra variable */
  strcpy(password, argv[2]+9);  /* +9, para eliminar --passwd= (9 caracteres) */

  rellenaConAsteriscos(argv[1]+7);
  rellenaConAsteriscos(argv[2]+9);

  printf("user: %s ; pass: %s", usuario, password);
  /* Esperamos una tecla para terminar (sólo para que no se cierre demasiado rápido */
  /* y nos dé tiempo a mirar la información de ps */
  getchar();
  return EXIT_SUCCESS;
}

void rellenaConAsteriscos(char *cadena)
{
  while (*cadena!='\0')
    *(cadena++)='*';
}

Ahora al ejecutar ps:

$ ps ax

10906 pts/3 S+ 0:00 ./test –user=******* –passwd=*************

Aunque esta forma revela en cierto modo la longitud de las contraseñas y el nombre de usuario, puede que no nos interese, que queramos eliminar el parámetro entero y que no se vea:

La manera rápida pero peligrosa y de andar por casa

El objetivo es escribir algo más largo que el último parámetro. Es decir, si el parámetro de contraseña tiene 22 letras, tenemos que escribir algo más grande, unas 23 letras, o cargarnos el terminador del segundo argumento (no son NULL-terminated, el kernel sabe dónde empieza y dónde termina la cadena, pero hay un NULL para comprobar que todo va bien).

En este caso podemos hacer:

1
  strcpy(argv[2]+9, "Esto es una cadena muy grande para ver si cargándome el segundo parámetro desaparece toda la información");

y veremos cómo ps no nos dice nada, en principio no es demasiado elegante, nos estamos cargando un terminador, que nosotros podríamos necesitar, para lectura, y estamos escribiendo en una zona de memoria que no es nuestra, ya que el argumento tiene reservado un tamaño de memoria, y estamos escribiendo más de lo que debemos (podemos producir una violación de segmento).

Decirle al kernel que no queremos los parámetros

Parecida a la forma anterior, podemos hacer:

1
argv[2][strlen(argv[2])]=' ';

lo que no es intuitivo para nada, porque nos estamos cargando el terminador, que controla el paso de parámetros. De esta forma, no tendremos ningún parámetro. Tambén podemos hacer una forma general

1
argv[argc-1][strlen(argv[argc-1])]=' ';

De esta forma desaparecerán todos los argumentos de /proc/PID/cmdline ; sólo tendremos la ruta y el nombre del ejecutable, y nuestros datos, al menos por esta parte, estarán seguros.
Foto: marc falardeau (Flickr)

También podría interesarte...

Only 1 comment left Ir a comentario

  1. Pingback: Bitacoras.com /

Leave a Reply