Linux kernel: sys_set*id(uid_t...) confusion

From: Michal Zalewski (lcamtufat_private)
Date: Fri Jun 19 1998 - 07:17:18 PDT

  • Next message: Paul Boehm: "ncftp 2.4.3 bug"

    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