El bit setuid se puede establecer en un archivo ejecutable para que, cuando se ejecute, el programa tenga los privilegios del propietario del archivo en lugar del usuario real, si son diferentes. Esta es la diferencia entre uid efectivo (ID de usuario) y uid real .
Algunas utilidades comunes, tales como passwd
, son propiedad de root y se configuran de esta manera por necesidad ( passwd
necesita acceso /etc/shadow
que solo puede leerse desde root).
La mejor estrategia al hacer esto es hacer lo que sea necesario como superusuario de inmediato y luego reducir los privilegios para que sea menos probable que ocurran errores o mal uso mientras se ejecuta root. Para hacer esto, establezca el uid efectivo del proceso en su uid real. En POSIX C:
#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void report (uid_t real) {
printf (
"Real UID: %d Effective UID: %d\n",
real,
geteuid()
);
}
int main (void) {
uid_t real = getuid();
report(real);
seteuid(real);
report(real);
return 0;
}
Las funciones relevantes, que deberían tener un equivalente en la mayoría de los lenguajes de nivel superior si se usan comúnmente en los sistemas POSIX:
getuid()
: Obtén el uid real .
geteuid()
: Obtenga el uid efectivo .
seteuid()
: Establece el uid efectivo .
No puede hacer nada con el último inapropiado para el uid real, excepto en la medida en que el bit setuid esté configurado en el ejecutable . Para probar esto, compile gcc test.c -o testuid
. Entonces necesita, con privilegios:
chown root testuid
chmod u+s testuid
El último establece el bit setuid. Si ahora se ejecuta ./testuid
como un usuario normal, verá que el proceso se ejecuta por defecto con uid 0, root efectivo.
¿Qué pasa con los guiones?
Esto varía de una plataforma a otra , pero en Linux, las cosas que requieren un intérprete, incluido el código de bytes, no pueden usar el bit setuid a menos que esté configurado en el intérprete (lo cual sería muy, muy estúpido). Aquí hay una secuencia de comandos perl simple que imita el código C anterior:
#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);
print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n";
Fiel a sus raíces * nixy, perl ha incorporado variables especiales para uid ( $>
) y uid real ( $<
) efectivos . Pero si intenta lo mismo chown
y lo chmod
utiliza con el ejecutable compilado (de C, ejemplo anterior), no habrá ninguna diferencia. El script no puede obtener privilegios.
La respuesta a esto es usar un binario setuid para ejecutar el script:
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[]) {
if (argc < 2) {
puts("Path to perl script required.");
return 1;
}
const char *perl = "perl";
argv[0] = (char*)perl;
return execv("/usr/bin/perl", argv);
}
Compila esto gcc --std=c99 whatever.c -o perlsuid
, entonces chown root perlsuid && chmod u+s perlsuid
. Ahora puede ejecutar cualquier script perl con un uid efectivo de 0, independientemente de quién sea el propietario.
Una estrategia similar funcionará con php, python, etc. Pero ...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
POR FAVOR, NO DEJES este tipo de cosas por ahí . Lo más probable es que realmente desee compilar el nombre del script como una ruta absoluta , es decir, reemplazar todo el código main()
con:
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Asegúrate de que /opt/suid_scripts
todo lo que contiene sea de solo lectura para usuarios no root. De lo contrario, alguien podría cambiar cualquier cosa por whatever.pl
.
Además, tenga en cuenta que muchos lenguajes de script permiten que las variables de entorno cambien la forma en que ejecutan un script . Por ejemplo, una variable de entorno puede hacer que se cargue una biblioteca suministrada por la persona que llama, lo que le permite ejecutar código arbitrario como root. Por lo tanto, a menos que sepa que tanto el intérprete como el script en sí son robustos frente a todas las posibles variables de entorno, NO HAGA ESTO .
Entonces, ¿qué debo hacer en su lugar?
Una forma más segura de permitir que un usuario no root ejecute un script como root es agregar una regla sudo y hacer que el usuario se ejecute sudo /path/to/script
. Sudo elimina la mayoría de las variables de entorno y también permite al administrador seleccionar con precisión quién puede ejecutar el comando y con qué argumentos. Consulte ¿Cómo ejecutar un programa específico como root sin una solicitud de contraseña? para un ejemplo.
-privileged
shell y, si se llama desde un script responsable, también se puede invocar desuid root
esa manera de forma segura . Sin embargo, me temo que la noción de un guión responsable es una especie de sueño en el mundo real. De todos modos, es probable que vale la pena mencionar lo importante que es para las escrituras de evitarse de todo tipo, si es posible, mientras que los permisos son elevados ...-privileged
shell" y "script responsable". ¿Quieres hacer otra respuesta sobre conchas? No puedo prometerle la marca, por supuesto;) No hay muchas buenas razones para hacerlo,sudo
es una opción mucho mejor si es posible, lo escribí como una respuesta genérica derivada de una pregunta temprana sobre la autenticación de contraseña del sistema en php .sudo
- considerosudo
como psicótico en el que aparenta no serroot
. Incluso puedes poner globos de concha en tusudoers
.su
es mejor en mi opinión para fines de secuencias de comandos, aunquesudo
es bueno para aplicaciones en vivo. Pero tampoco es tan explícita como una secuencia de comandos con un#!/bin/ksh -p
bangline o cualquiera que sea elsuid ksh
de$PATH
es.Sí, puedes, pero probablemente sea una muy mala idea . Por lo general, no establecería el bit SUID directamente en su ejecutable, sino que usaría un sudo (8) o su (1) para ejecutarlo (y limitar quién puede ejecutarlo)
Sin embargo, tenga en cuenta que existen muchos problemas de seguridad al permitir que los usuarios regulares ejecuten programas (¡y especialmente scripts!) Como root. La mayoría de ellos tienen que hacer eso a menos que el programa esté escrito específicamente y con mucho cuidado para manejar eso, los usuarios malintencionados podrían usarlo para obtener shell shell fácilmente.
Entonces, incluso si lo hiciera, sería una buena idea desinfectar TODAS las entradas al programa que desea ejecutar como root, incluido su entorno, parámetros de línea de comandos, STDIN y cualquier archivo que procese, datos provenientes de conexiones de red se abre, etc., etc. Es extremadamente difícil de hacer, y aún más difícil de hacer bien.
Por ejemplo, puede permitir que los usuarios solo editen algunos archivos con el editor. Pero el editor permite la ejecución de comandos de ayudantes externos o la suspensión, por lo que los usuarios pueden hacer lo que deseen. O puede estar ejecutando scripts de shell que le parecen muy estrictos para la seguridad, pero el usuario puede ejecutarlo con $ IFS modificado u otras variables de entorno que cambien completamente su comportamiento. O podría ser un script PHP que se supone que ejecuta "ls", pero el usuario lo ejecuta con $ PATH modificado y, por lo tanto, hace que ejecute un shell que él llama "ls". O docenas de otros problemas de seguridad.
Si el programa realmente debe hacer parte de su trabajo como root, probablemente sería mejor modificarlo para que se ejecute como usuarios normales, pero solo ejecute (después de desinfectar tanto como sea posible) la parte que absolutamente necesita root a través del programa auxiliar de suid.
Tenga en cuenta que incluso si confía completamente en todos sus usuarios locales (como, les da una contraseña de root), es una mala idea, ya que cualquier supervisión de seguridad involuntaria por CUALQUIERA de ellos (como tener un script PHP en línea o una contraseña débil, o lo que sea) de repente permite que el atacante remoto comprometa por completo toda la máquina.
fuente
man sh
, e incluso eso no puede hacer uncommand -p env -i ...
. Es bastante difícil de hacer después de todo. Aún así, si se hace correctamente, para fines explícitos, puede ser mejor parasuid
un intérprete capaz que usarsu
osudo
, ya que el comportamiento de cualquiera de ellos siempre estará fuera del control del guión (aunquesu
es menos probable que arroje bolas curvas, ya que tiende ser un poco menos loco) . Agrupar todo el paquete es la única apuesta segura: es mejor estar seguro de que es todo.