chage, un comando simpático

Seguramente muchos conoceréis el comando chage, incluido en la mayoría de distribuciones linux y que forma parte del paquete shadow-utils.

Para los que no lo conozcáis, chage es un comando que permite al root especificar la fecha de expiración de la contraseña de los usuarios, entre otras cosas. A los usuarios les permite consultar la fecha de expiración de su contraseña, la última vez que la cambiaron etc.

Por ejemplo, si ejecuto chage con la opción -l en mi sistema, obtengo:

Último cambio de contraseña                    : nunca
La contraseña caduca                    : nunca
Contraseña inactiva                    : nunca
La cuenta caduca                        : nunca
Número de días mínimo entre cambio de contraseña        : 0
Número de días máximo entre cambio de contraseñas        : 99999
Número de días de aviso antes de que expire la contraseña    : 7

Bien, el comando parece de los mas normal, pero vamos a investigar un poco… hagamos un ls -la sobre el, para ver que aspecto tiene:

-rwxr-sr-x 1 root shadow 45384 2008-04-03 03:07 /usr/bin/chage

Según se ve por la ‘s’ que hay en la mascara de permisos, tiene el bit sgid activado, eso significa, que lo ejecute quien lo ejecute, el grupo del proceso, será el del archivo, es decir, el grupo shadow.

Eso significa, que este binario puede leer y escribir del /etc/shadow, sin importar quien lo ejecute.

En principio esto no tiene ningún peligro, hay varios binarios que usan el bit suid/sgid en los sistemas linux modernos, y no sucede nada, por que el programa comprobará que usuario es quien lo ejecuta, y no nos dejará hacer según que cosas si no somos root, pero imaginad que pudiésemos tomar el control del proceso…sería fatal, podríamos alterar el /etc/shadow y modificar nuestro uid a 0, y ser root.

Sin embargo, ya os adelanto que el articulo de hoy no va sobre eso, así que espero que esta vez, nadie me acuse de “fanfarrón” o de hacer alarde de cosas que no son, voy a dejarlo bien claro: no estoy hablando de ningún agujero de seguridad en este post.

Vale, ahora que ya tenemos la aclaración de rigor, podemos continuar…como decía, sería muy peligroso que alguien pudiese tomar el control de ese proceso…así que veamos que medidas se toman para asegurar que el código de ese programa es de alta calidad, pues la seguridad de las cuentas locales de la mayoría de distribuciones, dependen de el.

Si nos descargamos el código fuente de su svn, en:

svn://svn.debian.org/pkg-shadow/

Y abrimos el archivo upstream/trunk/libmisc/strtoday.c, veremos una función, que usa el programa desde el propio main(), para convertir cadenas de texto que provienen del usuario, en fechas en segundos desde el 1 de enero de 1970, las típicas.

* strtoday() now uses get_date() (borrowed from GNU shellutils)

Según dice la linea 50 de ese archivo, esa función ya no es mas que un wrapper, un envoltorio que finalmente llama a otra función, una tal get_date robada de ¿GNU shellutils? Increíble.

El proyecto GNU shellutils se unió a GNU coreutils hace ya años, aquí debe haber algún error…no puede ser que una aplicación, que de fallar comprometería la seguridad de las cuentas locales (es decir, la cuenta root) de las principales distribuciones, tenga código de hace años y años, y además de un proyecto que ya no se mantiene, y que incluso se ha juntado con otro proyecto…

Sigamos indagando, vamos a ver el código de este tal get_date en upstream/trunk/libmisc/getdate.y

Nada mas abrirlo, la cabecera ya asusta:

**  Originally written by Steven M. Bellovin <smb@research.att.com> while
**  at the University of North Carolina at Chapel Hill.  Later tweaked by
**  a couple of people on Usenet.  Completely overhauled by Rich $alz
**  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;

Según como se nos va presentando el panorama, un programa que podría comprometer la seguridad de nuestro sistema (de forma local, lo cual a muchos seguro que no les importa, ya que si fuese remoto, sería mucho peor) tiene código de hace 18 años.

Bueno, no juzguemos a dicho código por su edad, vamos a ver que tal está el código en si:

sign = c == ‘-‘ ? -1 : 1;
if (!ISDIGIT (*++yyInput))
/* skip the ‘-‘ sign */
continue;
}
else
sign = 0;
for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
yylval.Number = 10 * yylval.Number + c – ‘0’;
yyInput–;
if (sign < 0)
yylval.Number = -yylval.Number;
return (0 != sign) ? tSNUMBER : tUNUMBER;

Con párrafos como ese, lo digo todo…no es un código muy organizado, documentado, ni ortodoxo.

Citaré otro párrafo, con un return muy legible, para los interesados:

long days = (
/* difference in day of year */
a->tm_yday – b->tm_yday
/* + intervening leap days */
+ ((ay >> 2) – (by >> 2))
– (ay / 100 – by / 100)
+ ((ay / 100 >> 2) – (by / 100 >> 2))
/* + difference in years * 365 */
+ (long) (ay – by) * 365
);
return (60 * (60 * (24 * days + (a->tm_hour – b->tm_hour))
+ (a->tm_min – b->tm_min))
+ (a->tm_sec – b->tm_sec));

Ahora ya si es suficiente.

Como vemos, no es un código que inspire mucha confianza…

Sin embargo, estoy de acuerdo que llegados a este punto, se me podría recriminar que el código funciona, y funciona bien, y que pese a sus años y su falta de coding style, no tiene por que tener fallos, bueno, vamos a verlo:

root@thanatos:/# chage -l root
Último cambio de contraseña                    : ago 27, 2008
La contraseña caduca                    : nunca
Contraseña inactiva                    : nunca
La cuenta caduca                        : nunca
Número de días mínimo entre cambio de contraseña        : 0
Número de días máximo entre cambio de contraseñas        : 99999
Número de días de aviso antes de que expire la contraseña    : 7
root@thanatos:/# chage -d 9999999999999999 root
root@thanatos:/# chage -l root
Fallo de segmentación
root@thanatos:/#

No ha sido muy difícil hacer que acceda a memoria fuera de su proceso, y viole un segmento, a que no?

Me reitero, yo no tengo ningún bug en este programa, ni estoy diciendo que sea inseguro, dejo el juicio de pensar si es seguro o no lo es, en manos del lector :)

Lo peor, es que no es raro ver trozos de código que quedan olvidados en programas que finalmente se incluyen en distribuciones importantes, y además, con el bit suid/sgid activado.

Algunos quizas conoceréis el término “seguridad proactiva” que viene a sugerir que hay que controlar estas cosas antes de que a alguien se le ocurra como aprovecharse de ello. GNU/Linux y las distros que lo rodean, son todo grandes proyectos llevados a cabo por personas admirables que ponen mucho empeño en el software libre, sin embargo, uno de sus puntos flacos es precisamente ese, la falta de seguridad proactiva.

Nota: chage produce la violación de segmento demostrada (que no es un agujero de seguridad, ni sirve nada mas que para mostrar que el programa no está muy pulido frente a errores) en ubuntu hardy de 64bits, no puedo garantizar que lo haga en otro sistema, no lo he probado.

Actualización: me confirman que en mandriva 64 no produce la violación de segmento, por lo que el pequeño experimento no se puede llevar a cabo en mandriva.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *