Hi, I believe the following to be accurate and of some interest to bugtraq readers, although i did not have time to extensively test it, nor did i warn the vendor, since 1) this is at most a undirect risk - IMHO - and 2) i am going on holidays so i had to balance betweeen disclosing now and letting people think about it and patch it OR delaying publication and forgetting it for some months as usual ;) But as i said, it is only a subtle local flaw that, i think, do not have direct security consequences in most linux distributions. Unless someone see something i missed. Also, I apologise if i am totally wrong here, no time to cross check it. OVERVIEW On current stable linux systems the setgid system call does not behave correctly in certain conditions: - Setgid-only programs cannot fully drop privileges. - Programs with both setuid and setgid flags which call setuid(getuid) before setgid(getgid) do not fully drop privileges. "priviledges are not fully dropped" means that the saved gid remains 0, although both gid, egid, fsgid, uid, euid, suid, and fsuid are set to the unprivileged user id. CONSEQUENCES A setuid or setgid program can wish to give up its privileges as soon as it does not need them anymore, if the program is written to minimise the impact of a vulnerability in the now unprivileged part of the code. Note that most linux set[ug]id progs seems to don't care too much about it. Surely in owl linux and such they care more (and in openbsd for sure, but...). The problem is, if a vulnerability (like a buffer overflow) appears in an unprivileged part of the code, with current linux kernels the cracker will still be able to get group id 0 (from the saved gid) ! That is a good launch pad for gaining full root privileges. Don't panic, the impact of this vulnerability is LOW in most (all ?) linux distributions: - it is local only (well, unless you wrote a daemon that thinks it can drop group privileges *after* doing the setuid(userid) ! And unless you have a setgid daemon or network client program). - it needs for a setgid or setuid program to have an exploitable vulnerability. - it could be a serious vulnerability if security was not so low in current linux systems. Most set[ug]id programs do not even bother to give up their privileges, so locally exploiting them gives instant root. - Programs which drop privileges before calling execve are not vulnerable since exceve reset the saved uid (at least it should: not tested). However, if you can find on your system a program that relies too much on the setgid behavior and gives full control to the user on the process, this problem would become a very serious vulnerability. I did not find any program of this type on my Mandrake systems. DETAILS I tested this with a 2.4.3 kernel, and the latest 2.4.18 with the (excellent) grsecurity patch. >From the setgid manpage (conforming to most unix systems if not all): "If the user is root or the program is setgid root, special care must be taken. The setgid function checks the effective gid of the caller and if it is the superuser, all process related group ID's are set to gid. After this has occurred, it is impossible for the program to regain root privi leges." However: [fozzy@defcon10 fozzy]$ uname -a Linux 2.4.18-grsec-1.9.4 #5 (...) [fozzy@defcon10 fozzy]$ ls -l dg -r-xr-sr-x 1 root root 15525 jui 19 04:18 dg* [fozzy@defcon10 fozzy]$ ./dg uid=501, euid=501, gid=501, egid=0 --> suid=501 and sgid=0 Dropping privileges... Privileges dropped : uid=501, euid=501, gid=501, egid=501 --> suid=501 and sgid=0 After trying to recover here is what we've got: uid=501, euid=501, gid=501, egid=0 /\/\/\/\ See the attached source code of the program. IN-DEPTH DETAILS Here is the interesting part of the advisory: WHY ? Well, easy. Let's take a look at the main part of the setgid syscall: ------------------------ if (capable(CAP_SETGID)) { if(old_egid != gid) current->dumpable=0; current->gid = current->egid = current->sgid = current->fsgid = gid; } else if ((gid == current->gid) || (gid == current->sgid)) { if(old_egid != gid) current->dumpable=0; current->egid = current->fsgid = gid; } ----------------------- If the process do not have the CAP_SETGID capability, current->sgid is never modified ! It will remain 0 no matter what you do (well, actually, a setregid will change the sgid, and a setresgid also, but everybody call the standard setgid). This capability is set only if you are the superuser (unless you have set up a real capability-aware system of course). So it is not set when running setgid programs, and in setuid programs it is unset when you do a setuid(user). As an untested 30 seconds-reflexion patch I would suggest editing the setgid syscall in kernel/sys.c like this (last line in the above source code): 439c439 < current->egid = current->fsgid = gid; --- > current->egid = current->fsgid = current->sgid = gid; CONCLUSION See you in Marocco :) FozZy Hackademy & Hackerz Voice Director
Attachment:
drop_gid.c
Description: Binary data