Maybe this problem is well-known, but it seems to be not fixed yet: 'Physically', UID is stored by kernel in single word. Due to this limitation, UID/GID must satisfy condition 0<=UID<=65535. But there are serious problems with kernel sys_setuid(uid_t) and uid_t type itself: - uid_t (UID/GID handling type), unsigned integer, is able to handle large integers (>65535), eg. 131072. - sys_setuid(uid_t) and other kernel UID/GID manipulation routines (and their libc aliases, like setuid), silently strips higher uid_t bits, then returns 'success' value (0). So, attacker may change /etc/passwd UID of any account to 131072 (binary: 10 00000000 00000000) - it won't be traced by any intrusion-detection programs looking for '0' UIDs, because uid_t is able to store this value, and 131072 for sure is NOT equal to 0. But when he will login using this account - he should get root shell (setuid, as I noticed above, silently discards two highest bits, so 131072 becomes 0). Nice trick, but that's not all. If you have eg. securetty installed to prevent root logins from outside (or any other mechanism to prevent unprivledged root access, eg. restricted 'su') - attacker will be able to fool it, because UID retreived from /etc/passwd (uid_t) using standard libc routines, is NOT equal to 0, so this account looks like unprivledged... Fixes: First solution - rewrite kernel UID/GID code to extend UID address space using eg. 4 bytes instead of 2 (whoow!) - but it will probably harm many programs. Second solution - patch your kernel to return EINVAL when uid_t is too big. Here's the patch: --- linux/kernel/sys.c.orig Tue Apr 8 17:47:47 1997 +++ linux/kernel/sys.c Fri Jun 19 16:00:28 1998 @@ -237,6 +237,8 @@ { int old_rgid = current->gid; int old_egid = current->egid; + + if (rgid>0xffff || egid>0xffff) return -EINVAL; if (rgid != (gid_t) -1) { if ((old_rgid == rgid) || @@ -272,6 +274,8 @@ asmlinkage int sys_setgid(gid_t gid) { int old_egid = current->egid; + + if (gid>0xffff) return -EINVAL; if (suser()) current->gid = current->egid = current->sgid = current->fsgid = gid; @@ -489,6 +493,8 @@ asmlinkage int sys_setuid(uid_t uid) { int old_euid = current->euid; + + if (uid>0xffff) return -EINVAL; if (suser()) current->uid = current->euid = current->suid = current->fsuid = uid; @@ -510,6 +516,8 @@ asmlinkage int sys_setfsuid(uid_t uid) { int old_fsuid = current->fsuid; + + if (uid>0xffff) return -EINVAL; if (uid == current->uid || uid == current->euid || uid == current->suid || uid == current->fsuid || suser()) @@ -525,6 +533,8 @@ asmlinkage int sys_setfsgid(gid_t gid) { int old_fsgid = current->fsgid; + + if (gid>0xffff) return -EINVAL; if (gid == current->gid || gid == current->egid || gid == current->sgid || gid == current->fsgid || suser()) @@ -563,6 +573,8 @@ asmlinkage int sys_setpgid(pid_t pid, pid_t pgid) { struct task_struct * p; + + if (pid>0xffff || pgid>0xffff) return -EINVAL; if (!pid) pid = current->pid; _______________________________________________________________________ Michal Zalewski [lcamtufat_private] <= finger for pub PGP key Iterowac jest rzecza ludzka, wykonywac rekursywnie - boska [P. Deutsch] [echo "\$0&\$0">_;chmod +x _;./_] <=------=> [tel +48 (0) 22 813 25 86]
This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 13:59:04 PDT