43 votos

Cómo ocultar una contraseña que se pasa como argumento de línea de comandos?

Estoy corriendo un software demonio que requiere de ciertas acciones para introducir una frase contraseña para desbloquear algunas de las características que se ve, por ejemplo, que:

$ darkcoind masternode start <mypassphrase>

Ahora tengo algunos problemas de seguridad en mi sin cabeza servidor de debian.

Siempre que puedo buscar en mi bash historia por ejemplo Ctrl+R puedo ver esta super fuerte de la contraseña. Ahora me imagino que mi servidor es comprometidas y algún intruso tiene acceso shell y puede, simplemente, Ctrl+R encontrar mi contraseña en la historia.

Hay una manera de introducir la frase de paso sin que se muestran en bash historia, ps, /proc o en cualquier otro lugar?


Actualización 1: Pasar sin contraseña para que el demonio se produce un error. Esta no es una opción.


Actualización 2: no me digas para eliminar el software o otros consejos útiles como colgar los desarrolladores. Sé que esto no es un ejemplo de mejores prácticas, pero este software está basado en bitcoin y todas bitcoin clientes son algún tipo de json servidor rpc que escucha a estos comandos y su conocido problema de seguridad sigue siendo discutido (un, b, c).


Actualización 3: El demonio ya está en marcha y funcionando con el comando

$ darkcoind -daemon

Haciendo ps muestra sólo la orden de inicio.

$ ps aux | grep darkcoin
user     12337  0.0  0.0  10916  1084 pts/4    S+   09:19   0:00 grep darkcoin
user     21626  0.6  0.3 1849716 130292 ?      SLl  May02   6:48 darkcoind -daemon

Por lo que pasar los comandos con la frase de contraseña no se muestra en ps o /proc en todo.

$ darkcoind masternode start <mypassphrase>
$ ps aux | grep darkcoin
user     12929  0.0  0.0  10916  1088 pts/4    S+   09:23   0:00 grep darkcoin
user     21626  0.6  0.3 1849716 130292 ?      SLl  May02   6:49 darkcoind -daemon

Esto deja a la pregunta ¿de dónde viene la historia? Sólo en .bash_history?

65voto

MvG Puntos 814

Realmente, este debe ser fijado en la propia aplicación. Y tales aplicaciones deben ser de código abierto, por lo que la fijación de la cuestión en la aplicación en sí debe ser una opción. De seguridad relacionados con la aplicación que hace que este tipo de error podría hacer que otros errores, así que yo no confiar en ella.

Simple mediadora

Pero estaban pidiendo de una manera diferente, así que aquí va uno:

#define _GNU_SOURCE
#include <dlfcn.h>

int __libc_start_main(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  )
{
  int (*next)(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  ) = dlsym(RTLD_NEXT, "__libc_start_main");
  ubp_av[argc - 1] = "secret password";
  return next(main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}

Compilar este con

gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl

a continuación, ejecute el proceso con

LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase

La interposición de la biblioteca se ejecute este código antes de la main función de su aplicación es ejecutada. Se sustituye el último argumento de línea de comandos por la contraseña actual en la llamada a la principal. La línea de comandos como impresas en /proc/*/cmdline (y por lo tanto visto por herramientas tales como ps) todavía va a contener el falso argumento, sin embargo. Obviamente tendrías que hacer el código fuente y la biblioteca de compilar desde es legible sólo para usted, así que mejor funcionan en un chmod 0700 directorio. Y dado que la contraseña no es parte de la invocación de la orden, su bash historia es seguro así.

Más avanzados de la interposición de

Si quieres hacer algo más elaborado, usted debe tener en cuenta que __libc_start_main se ejecuta antes de que la biblioteca de tiempo de ejecución ha sido inicializado correctamente. Así que te sugerimos evitar las llamadas de función a menos que sean absolutamente esenciales. Si usted quiere ser capaz de llamar a las funciones al contenido de su corazón, asegúrese de hacerlo justo antes de main sí se invoca, después de todo inicialización se realiza. Para el siguiente ejemplo, tengo que agradecer a Grubermensch quien señaló cómo ocultar una contraseña que se pasa como argumento de línea de comandos que trajo getpass mi atención.

#define _GNU_SOURCE
#include <dlfcn.h>
#include <unistd.h>

static int (*real_main) (int, char * *, char * *);

static int my_main(int argc, char * * argv, char * * env) {
  char *pass = getpass(argv[argc - 1]);
  if (pass == NULL) return 1;
  argv[argc - 1] = pass;
  return real_main(argc, argv, env);
}

int __libc_start_main(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  )
{
  int (*next)(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  ) = dlsym(RTLD_NEXT, "__libc_start_main");
  real_main = main;
  return next(my_main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}

Esto pide la contraseña, así que usted no tiene que mantener la interposición de la biblioteca de un secreto. El argumento de marcador de posición se reutiliza como indicador de solicitud de contraseña, para invocar esta como

LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "

Otra alternativa sería leer la contraseña de un descriptor de archivo (como por ejemplo gpg --passphrase-fd ), o de x11-ssh-askpass, o lo que sea.

28voto

Tonny Puntos 5020

No es sólo la historia. Se va a mostrar en ps salida así.

Quien escribió ese pedazo de software debe ser colgado, arrastrado y descuartizado. Es un NO absoluto a tener que proporcionar una contraseña en la línea de comandos sin importar lo que el software es.
Para un demonio proceso es aún MÁS imperdonable...

Además , rm-f en el software en sí mismo no conozco ninguna solución para esto. Honestamente: Encontrar otro software para hacer el trabajo. No usar tales basura.

17voto

Matthew Ife Puntos 12680

Esto borrará la ps de salida.

SER MUY CONSCIENTE deque podría romper la aplicación. Está debidamente advertido de que aquí hay dragones.

  • Extranjeros procesos no debería estar jugando en un procesos de la memoria.
  • Si el proceso se basa en esta región de la contraseña, usted puede romper su aplicación.
  • Esto podría corromper cualquier trabajo de datos en ese proceso.
  • Este es un loco hack.

Ahora usted debidamente notificado de esas advertencias. Esto borrará la salida mostrada en ps. No va a borrar el historial, ni es claro el bash historial de trabajo (tales como el proceso en ejecución como myprocess myargs &). Pero ps dejará de mostrar los argumentos.

#!/usr/bin/python
import os, sys
import re

PAGESIZE=4096

if __name__ == "__main__":
  if len(sys.argv) < 2:
    sys.stderr.write("Must provide a pid\n")
    sys.exit(1)

  pid = sys.argv[1]

  try:
    cmdline = open("/proc/{0}/cmdline".format(pid)).read(8192)

    ## On linux, at least, argv is located in the stack. This is likely o/s
    ## independent.
    ## Open the maps file and obtain the stack address.
    maps = open("/proc/{0}/maps".format(pid)).read(65536)
    m = re.search('([0-9a-f]+)-([0-9a-f]+)\s+rw.+\[stack\]\n', maps)
    if not m:
      sys.stderr.write("Could not find stack in process\n");
      sys.exit(1)

    start = int("0x"+m.group(1), 0)
    end = int("0x"+m.group(2), 0)

    ## Open the mem file
    mem = open('/proc/{0}/mem'.format(pid), 'r+')
    ## As the stack grows downwards, start at the end. It is expected
    ## that the value we are looking for will be at the top of the stack
    ## somewhere
    ## Seek to the end of the stack minus a couple of pages.
    mem.seek(end-(2*PAGESIZE))

    ## Read this buffer to the end of the stack
    stackportion = mem.read(8192)
    ## look for a string matching cmdline. This is pretty dangerous.
    ## HERE BE DRAGONS
    m = re.search(cmdline, stackportion)
    if not m:
      ## cause this is an example dont try to search exhaustively, just give up
      sys.stderr.write("Could not find command line in the stack. Giving up.")
      sys.exit(1)

    ## Else, we got a hit. Rewind our file descriptor, plus where we found the first argument.
    mem.seek(end-(2*PAGESIZE)+m.start())
    ## Additionally, we'll keep arg0, as thats the program name.
    arg0len = len(cmdline.split("\x00")[0]) + 1
    mem.seek(arg0len, 1)

    ## lastly overwrite the remaining region with nulls.
    writeover = "\x00" * (len(cmdline)-arg0len)
    mem.write(writeover)

    ## cleanup
    mem.close()

  except OSError, IOError:
    sys.stderr.write("Cannot find pid\n")
    sys.exit(1)

Invocar el programa de ahorro, chmod +x . A continuación, haciendo ./whatever <pidoftarget> Si esto funciona, no va a producir ningún resultado. Si se produce un error, se quejan de algo y dejar de fumar.

10voto

miracle2k Puntos 3285

Se puede pasar el argumento de un archivo, accesible sólo por el root o el usuario requiere?

Es un GRAN no-no para escribir las contraseñas en la consola, pero el último recurso...iniciar su línea con un espacio por lo que no aparece en la historia.

2voto

Daniele Testa Puntos 152

Tal vez esto funciona (?):

darkcoind masternode start `cat password.txt`

EnMiMaquinaFunciona.com

EnMiMaquinaFunciona es una comunidad de administradores de sistemas en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros sysadmin, hacer tus propias preguntas o resolver las de los demás.

Powered by: