Necesito ocultar algunos argumentos sensibles a un programa que estoy ejecutando, pero no tengo acceso al código fuente. También estoy ejecutando esto en un servidor compartido, así que no puedo usar algo como hidepid
porque no tengo privilegios de sudo.
Aquí hay algunas cosas que he intentado:
export SECRET=[my arguments]
, seguido de una llamada a./program $SECRET
, pero esto no parece ayudar../program `cat secret.txt`
dondesecret.txt
contiene mis argumentos, pero el Todopoderosops
es capaz de descubrir mis secretos.
¿Hay alguna otra forma de ocultar mis argumentos que no implique la intervención del administrador?
ps
no está haciendo nada mágico para "descubrir tus secretos". De todos modos, los programas razonablemente escritos deberían ofrecer una opción de línea de comandos para leer un secreto de un archivo especificado o de stdin en lugar de tomarlo directamente como argumento.Respuestas:
Como se explica aquí , Linux coloca los argumentos de un programa en el espacio de datos del programa y mantiene un puntero al inicio de esta área. Esto es lo que es usado por
ps
y así sucesivamente para encontrar y mostrar los argumentos del programa.Como los datos están en el espacio del programa, pueden manipularlos. Hacer esto sin cambiar el programa en sí mismo implica cargar una cuña con una
main()
función que se llamará antes que la parte principal real del programa. Esta cuña puede copiar los argumentos reales en un nuevo espacio, luego sobrescribir los argumentos originales para queps
solo se vean nuls.El siguiente código C hace esto.
No es posible intervenir
main()
, pero puede intervenir en la función de biblioteca C estándar__libc_start_main
, que pasa a llamar a main. Compile este archivoshim_main.c
como se indica en el comentario al inicio y ejecútelo como se muestra. He dejado unprintf
código en el código para que compruebes que realmente se está llamando. Por ejemplo, ejecutarluego haga un
ps
y verá un comando en blanco y argumentos que se muestran.Todavía hay una pequeña cantidad de tiempo que los argumentos del comando pueden ser visibles. Para evitar esto, podría, por ejemplo, cambiar la cuña para leer su secreto de un archivo y agregarlo a los argumentos pasados al programa.
fuente
/proc/pid/cmdline
se mostrará el secreto (igual que cuandocurl
intenta ocultar la contraseña que se le da en la línea de comando). Mientras utiliza LD_PRELOAD, puede ajustar main para que el secreto se copie del entorno al argumento que recibe main. Como llamarLD_PRELOAD=x SECRET=y cmd
donde llamasmain()
conargv[]
ser[argv[0], getenv("SECRET")]
/proc/pid/environ
. Esto puede sobrescribirse de la misma manera que los argumentos, pero deja la misma ventana./proc/pid/cmdline
es público,/proc/pid/environ
no lo es. Hubo algunos sistemas dondeps
(un ejecutable setuid allí) expuso el entorno de cualquier proceso, pero no creo que se encuentre con ninguno hoy en día. El medio ambiente generalmente se considera lo suficientemente seguro . No es seguro extraer de los procesos con el mismo euid, pero de todos modos a menudo pueden leer la memoria de los procesos del mismo euid, por lo que no hay mucho que pueda hacer al respecto.main
método del programa envuelto también elimine la variable de entorno para evitar fugas accidentales a los procesos secundarios. Alternativamente, el contenedor podría leer todos los argumentos de la línea de comandos de un archivo.Lea la documentación de la interfaz de línea de comandos de la aplicación en cuestión. Puede haber una opción para proporcionar el secreto de un archivo en lugar de como un argumento directamente.
Si eso falla, presente un informe de error contra la aplicación alegando que no hay una forma segura de proporcionarle un secreto.
Siempre puede (cuidadosamente) adaptar la solución en la respuesta de meuh a sus necesidades específicas. Preste especial atención al comentario de Stéphane y sus seguimientos.
fuente
Si necesita pasar argumentos al programa para que funcione, no tendrá suerte, no importa lo que haga si no puede usarlo
hidepid
en procfs.Como mencionó que este es un script bash, ya debería tener el código fuente disponible, ya que bash no es un lenguaje compilado.
De lo contrario, puede volver a escribir el cmdline del proceso usando
gdb
o similar y jugando conargc
/argv
una vez que ya ha comenzado, pero:Realmente solo recomiendo obtener el código fuente, o hablar con el proveedor para modificar el código. El suministro de secretos en la línea de comandos en un sistema operativo POSIX es incompatible con una operación segura.
fuente
Cuando un proceso ejecuta un comando (a través de la
execve()
llamada al sistema), su memoria se borra. Para pasar información a través de la ejecución, lasexecve()
llamadas al sistema toman dos argumentos para eso: las matricesargv[]
yenvp[]
.Esas son dos matrices de cadenas:
argv[]
contiene los argumentosenvp[]
contiene las definiciones de variables de entorno como cadenas en elvar=value
formato (por convención).Cuando tu lo hagas:
(Aquí se agregan las comillas faltantes alrededor de la expansión del parámetro).
Estás ejecutando
cmd
con el secreto (value
) pasado tanto enargv[]
yenvp[]
.argv[]
será["cmd", "value"]
yenvp[]
algo así[..., "PATH=/bin:...", "HOME=...", ..., "SECRET=value", "TERM=xterm", ...]
. Comocmd
no está haciendo nadagetenv("SECRET")
o equivalente para recuperar el valor del secreto de esaSECRET
variable de entorno, ponerlo en el entorno no es útil.argv[]
Es de conocimiento público. Se muestra en la salida deps
.envp[]
hoy en día no lo es. En Linux, se muestra en/proc/pid/environ
. Se muestra en la salida de losps ewww
BSD (y con procps-ngps
en Linux), pero solo en los procesos que se ejecutan con el mismo uid efectivo (y con más restricciones para los ejecutables setuid / setgid). Puede aparecer en algunos registros de auditoría, pero solo los administradores deben tener acceso a esos registros de auditoría.En resumen, el entorno que se pasa a un ejecutable debe ser privado o al menos tan privado como la memoria interna de un proceso (que, en algunas circunstancias, otro proceso con los privilegios correctos también puede acceder con un depurador, por ejemplo, y puede también ser volcado al disco).
Dado que
argv[]
es de conocimiento público, el diseño rompe un comando que espera que los datos destinados a ser secretos en su línea de comando se rompan.Por lo general, los comandos que necesitan un secreto, le proporcionan otra interfaz para hacerlo, como a través de una variable de entorno. Por ejemplo:
O a través de un descriptor de archivo dedicado como stdin:
(
echo
estando integrado, no se muestra en la salida deps
)O un archivo, como
.netrc
forftp
y algunos otros comandos oAlgunas aplicaciones como
curl
(y ese también es el enfoque adoptado por @meuh aquí ) intentan ocultar la contraseña que recibieronargv[]
de miradas indiscretas (en algunos sistemas sobrescribiendo la porción de memoria dondeargv[]
se almacenaron las cadenas). Pero eso no ayuda mucho y da una falsa promesa de seguridad. Eso deja una ventana entreexecve()
y la sobrescritura dondeps
aún se mostrará el secreto.Por ejemplo, si un atacante sabe que está ejecutando un script haciendo
curl -u user:somesecret https://...
(por ejemplo, en un trabajo cron), todo lo que tiene que hacer es expulsar del caché las (muchas) bibliotecas quecurl
usa (por ejemplo ejecutando ash -c 'a=a;while :; do a=$a$a;done'
) para ralentizar su inicio, e incluso hacer un método muy ineficienteuntil grep 'curl.*[-]u' /proc/*/cmdline; do :; done
es suficiente para atrapar esa contraseña en mis pruebas.Si los argumentos son la única forma en que puede pasar el secreto a los comandos, aún puede intentar hacer algunas cosas.
En algunos sistemas, incluidas las versiones anteriores de Linux, solo se pueden consultar los primeros bytes (4096 en Linux 4.1 y anteriores) de las cadenas
argv[]
.Ahí puedes hacer:
Y el secreto estaría oculto porque ya pasaron los primeros 4096 bytes. Ahora las personas que han usado ese método deben arrepentirse ahora ya que Linux desde 4.2 ya no trunca la lista de argumentos
/proc/pid/cmdline
. También tenga en cuenta que no es porqueps
no muestre más que tantos bytes de una línea de comando (como en FreeBSD donde parece estar limitado a 2048) que no se puede usar los mismosps
usos de API para obtener más. Sin embargo, ese enfoque es válido en sistemas en los queps
es la única forma en que un usuario normal puede recuperar esa información (como cuando la API tiene privilegios yps
está configurada o configurada para usarla), pero aún no está allí a prueba de futuro.Otro enfoque sería no pasar el secreto
argv[]
sino inyectar código en el programa (usandogdb
o$LD_PRELOAD
piratear) antes de quemain()
se inicie, que inserta el secreto en elargv[]
recibidoexecve()
.Con
LD_PRELOAD
, para ejecutables no setuid / setgid vinculados dinámicamente en un sistema GNU:Luego:
En ningún momento habría
ps
mostrado elps -opid,args
allí (-opid,args
siendo el secreto en este ejemplo). Tenga en cuenta que estamos reemplazando elementos de laargv[]
matriz de punteros , no anulando las cadenas señaladas por esos punteros, por lo que nuestras modificaciones no se muestran en la salida deps
.Con
gdb
, aún para ejecutables vinculados dinámicamente no setuid / setgid y en sistemas GNU:Aún así
gdb
, un enfoque específico que no sea GNU que no se base en que los ejecutables estén vinculados dinámicamente o que tengan símbolos de depuración y que debería funcionar para cualquier ejecutable ELF en Linux al menos podría ser:Prueba con un ejecutable vinculado estáticamente:
Cuando el ejecutable puede ser estático, no tenemos una forma confiable de asignar memoria para almacenar el secreto, por lo que debemos obtener el secreto de otro lugar que ya esté en la memoria del proceso. Es por eso que el medio ambiente es la opción obvia aquí. También ocultamos ese
SECRET
entorno al proceso (cambiándolo aSECRE=
) para evitar que se filtre si el proceso decide volcar su entorno por algún motivo o ejecutar aplicaciones no confiables.Que también funciona en Solaris 11 (siempre que el BGF y se instalan GNU binutils (puede que tenga que cambiar el nombre
objdump
agobjdump
).En FreeBSD (al menos x86_64, no estoy seguro de lo que los primeros 24 bytes (que se convierten en 16 cuando GDB (8.0.1) es interactivo sugiere que puede haber un error en el BGF allí) en la pila son), reemplazan el
argc
yargv
definiciones con:(es posible que también necesite instalar el
gdb
paquete / puerto ya que la versión que viene con el sistema es antigua).fuente
Lo que puedes hacer es
luego, suponiendo que está escribiendo su
./program
en C (o alguien más lo hace, y puede cambiarlo o mejorarlo por usted), use getenv (3) en ese programa, tal vez comoy después de
export
que acaba de correr./program
en el mismo shell. O se le puede pasar el nombre de la variable de entorno (ejecutando./program --secret-var=SECRET
etc ...)ps
no contará sobre su secreto, pero proc (5) aún puede proporcionar mucha información (al menos a otros procesos del mismo usuario).Vea también esto para ayudar a diseñar una mejor manera de pasar los argumentos del programa.
Consulte esta respuesta para obtener una mejor explicación sobre el globbing y el papel de un shell.
Quizás
program
tenga otras formas de obtener datos (o utilizar la comunicación entre procesos de manera más inteligente) que los argumentos simples del programa (ciertamente debería hacerlo, si está destinado a procesar información confidencial). Lee su documentación. O tal vez está abusando de ese programa (que no está destinado a procesar datos secretos).Ocultar datos secretos es realmente difícil. No pasarlo por los argumentos del programa no es suficiente.
fuente
./program
, por lo que la primera mitad de esta respuesta no parece ser relevante.