Lo que cambió es que /bin/sh
cualquiera se convirtió en bash
o se quedó dash
que obtuvo una bandera adicional -p
imitando el comportamiento de bash.
Bash requiere el -p
marcar para no eliminar el privilegio setuid como se explica en su página man:
Si el shell se inicia con la identificación de usuario (grupo) efectiva que no es igual a la identificación de usuario (grupo) real, y no se proporciona la opción -p, no se leen los archivos de inicio, las funciones de shell no se heredan del entorno, SHELLOPTS, Las variables BASHOPTS, CDPATH y GLOBIGNORE, si aparecen en el entorno, se ignoran, y la identificación de usuario efectiva se establece en la identificación de usuario real . Si se proporciona la opción -p en la invocación, el comportamiento de inicio es el mismo, pero la identificación de usuario efectiva no se restablece.
Antes, dash
no se preocupó por esto y permitió la ejecución de setuid (sin hacer nada para evitarlo). Pero el dash
de Ubuntu 16.04 La página de manual tiene una opción adicional descrita, similar a bash
:
-p privado
No intente restablecer el uid efectivo si no coincide con el uid. Esto no está configurado de forma predeterminada para ayudar a evitar un uso incorrecto bysetuid programas raíz a través de system(3) o popen(3).
Esta opción no existía en upstream (que podría no haber sido reactiva a un parche propuesto) ni en Debian 9, pero está presente en Debian buster, que recibió el parche desde 2018.
NOTA:como explica Stéphane Chazelas, es demasiado tarde para invocar "/bin/sh -p"
en system()
porque system()
ejecuta cualquier cosa dada a través de /bin/sh
y entonces el setuid ya está descartado. la respuesta de derobert explica cómo manejar esto, en el código antes de system()
.
más detalles sobre la historia aquí y allá.
Probablemente, el shell está cambiando su identificación de usuario efectiva de nuevo a la identificación de usuario real como parte de su inicio por algún motivo u otro. Puede verificar esto agregando:
/* needs _GNU_SOURCE; non-Linux users see setregid/setreuid instead */
uid_t euid = geteuid(), egid = getegid();
setresgid(egid, egid, egid);
setresuid(euid, euid, euid);
antes de tu system()
. (En realidad, incluso en Linux, probablemente solo necesite configurar los reales; los guardados deberían estar bien para dejarlos solos. Esto es solo fuerza bruta para depurar. Dependiendo de por qué está configurado, es posible que, por supuesto, necesite para guardar las identificaciones reales en algún lugar también).
[Además, si esto no es solo un ejercicio para aprender cómo funciona setid, entonces hay muchos problemas de seguridad de los que preocuparse, especialmente cuando se llama a un shell. Hay muchas variables de entorno, por ejemplo, que afectan el comportamiento del shell. Preferir un enfoque ya existente como sudo
si es posible.]