Here's someone that felt the need to put something better together. ---------- Forwarded message ---------- Date: Mon, 13 Jul 1998 04:22:15 -0400 From: Richard Thomas <rthomas@sy.net> To: BUGTRAQ@NETSPACE.ORG Subject: Slackware Shadow Insecurity Discovered by Ted Hickman: Recently I noticed something rather "insecure" about the slackware 3.4 /bin/login (and probably other versions). If the /etc/group file does not exist, any user who logs into the system is given uid 0 gid 0. buglord login: meek Password: initgroups: No such file or directory Linux 2.0.34. Last login: Fri Jul 10 20:56:17 on tty6. You have mail. buglord:~# id uid=0(root) gid=0 The following is recorded in the syslog: Jul 10 22:28:42 buglord login[25795]: initgroups failed for user `meek`: No such file or directory Analysis by Richard Thomas: An examination of shadow-971001 shows the following in libmisc/setugid.c: /* * setup_uid_gid() performs the following steps - * * set the supplementary group IDs * optionally call specified function which may add more groups * set the group ID to the value from the password file entry * set the user ID to the value from the password file entry * * Returns 0 on success, or -1 on failure. */ int setup_uid_gid(info, is_console) const struct passwd *info; int is_console; { #ifdef HAVE_INITGROUPS /* * For systems which support multiple concurrent groups, go get * the group set from the /etc/group file. */ if (initgroups (info->pw_name, info->pw_gid) == -1) { perror("initgroups"); SYSLOG((LOG_ERR, "initgroups failed for user `%s': %m\n", info->pw_name)); closelog(); return -1; } [snip] And src/login.c line 960: setup_uid_gid(&pwent, is_console); Does this look bad to anyone else? The call to setup_uid_gid() in login.c does no checking of the return value. If initgroups cannot open /etc/group, the setup_uid_gid() function returns before it gets around to the setgid() and setuid() calls (hence the uid and gid are both still 0). This threw me off for quite some time because I thought setup_uid_gid() was being called from the setup() function in libmisc/setup.c, which DOES check the return value and exits if there is a problem. I make no special claims to know what is happening in shadow, but it appears that they decided to do the calls in setup() individually, directly in login.c. Seems to me that if setup_uid_gid is nice enough to return the success or failure status, the least we could do is check it (ESPECIALLY since this is also the status of the setgid and setuid calls!). Why do they still include the setup() function if its not used? Beats me. So whats the fix? Well first of all, change src/login.c to: if (setup_uid_gid(&pwent, is_console)) exit(1); If we wanted to be fancy we could continue to login even if initgroups() fails (most likely you don't "need" those extra groups to get into the system and fix it), but we gotta save something for the shadow authors. =) How does this become a security hole? Well if you can find a way to delete the /etc/group file things could become rather messy. Finding a way to do that is left as an exercise for the reader (don't feel too bad I couldn't find one off the top of my head either). Sorry kids, nothing you can cut and paste to become a "pH34Rs0m3 hAx0r" today. Some extra tidbits: This does not affect redhat The file has to actually be non-existent (chmod 000 don't cut it). This seems to be a problem with all modern shadows, including the one in slackware 3.5 (980529). Shameless Plug: Visit http://shell.sy.net for the best shells available this side of heaven. - Ted Hickman <thickman@sy.net> - Richard Thomas <rthomas@sy.net>