Home page logo
/

bugtraq logo Bugtraq mailing list archives

Linux kernel: sys_set*id(uid_t...) confusion
From: lcamtuf () boss staszic waw pl (Michal Zalewski)
Date: Fri, 19 Jun 1998 16:17:18 +0200


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 [lcamtuf () boss staszic waw pl] <= 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]



  By Date           By Thread  

Current thread:
[ Nmap | Sec Tools | Mailing Lists | Site News | About/Contact | Advertising | Privacy ]
AlienVault