[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
First Patch :)
Hi traqers of bugs !
Here is my first patch for Linux. Its purpose is to stop some
exploits based on using SUID bit. For better protection it should be
combined with Solar Designer's one (but it could work independently
too). It doesn't make writing exploits impossible, but at least a bit
tougher. I have tested it on my computer, but it is possible that it
won't work in some cases.
How does it work - for each process it stores a new uid (I
have choosen a name RUID = Real UID). Purpose of RUID is to keep
track of who is real owner of this process (it is inherited from
parent process and changed only when root's process runs process
under different EUID). When process tries to spawn (fork/exec)
process under EUID!=RUID and it is not originally root's process
(RUID==0), it is reported to console and EUID is forced to RUID.
Of course, sometimes it is required for process to spawn something
under different EUID (example is 'su'). I needed to somehow mark programs
that are allowed to spawn under different EUID. For this purpose I have
choosen new bit (thus it cannot be marked by chmod). So, programs marked
in this way CAN spawn programs under different UID.
Explanation how it works on exploits. Ex, standard old
exploit by Solar D. (the one using NLSPATH on "/bin/su"):
1) user (hacker) COOLER runs program a.out
2) process a.out gets ruid=(uid of COOLER)
3) process a.out prepares what it needs (set variable,...)
4) it runs process '/bin/su'
5) process 'su' gets RUID=(uid of COOLER) and EUID=0
6) something happens (exploit code gets control)
7) it does 'setuid(0)' to set its *UID=0
8) when trying to 'exec("/bin/sh"...)', my code checks if it can do
so (variable 'secure' is used to). If not, message is sent to console
and EUID=(uid of COOLER).
values of secure:
0 = unsecure program
1 = secure program
2 = program that isn't secure, but was runned by secure program. This
isn't used in present version of patch, but probably will be used
in some future.
There are some other things that this patch should do.
To mark secure programs, I needed to use some technique. At this moment
I use the S_ISVTX (+t bit), but I have another idea - use new bit.
Problem is that in standard inode attributes is no free space :)
So I decided to use inode->flags, but I still don't know how is it
possible to set flags :) Many other ideas will be implemented in next
version of this patch.
Of course, this patch is not absolutely proof. Here are two ways
of bypassing the exploit
1) hacker doesn't need to exec anything (he can do everything in the
exploit code. ex, write something to passwd/shadow,..., because at
that moment it runs under EUID=0).
programs that require marking using my patch to work properly:
/bin/su
possibly some programs (xterm,...) from X11 package, but I haven't found
and problems without this bit.
Hope this patch will help :)
g00bER
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Name: Peter Kosinar
Work :) student and 'co-admin' of school Novell and Linux server
E-Mail: goober@ganga.gjh.schools.sk (preferred)
goober@dunaj.gjh.schools.sk
URL: http://www.gjh.schools.sk/~goober (under reconstruction now)
Interests: crypto, [anti]virus, bugs
*** include/linux/fs.h.old Thu May 28 20:57:01 1998
--- include/linux/fs.h Thu May 28 20:59:57 1998
***************
*** 70,79 ****
--- 70,80 ----
#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
#define S_WRITE 128 /* Write on file/directory/symlink */
#define S_APPEND 256 /* Append-only file */
#define S_IMMUTABLE 512 /* Immutable file */
+ #define S_SECURE 1024 /* Secure file */
/*
* Flags that can be altered by MS_REMOUNT
*/
#define MS_RMT_MASK (MS_RDONLY)
***************
*** 99,108 ****
--- 100,110 ----
#define IS_SYNC(inode) ((inode)->i_flags & MS_SYNCHRONOUS)
#define IS_WRITABLE(inode) ((inode)->i_flags & S_WRITE)
#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
+ #define IS_SECURE(inode) ((inode->i_flags & S_SECURE)
/* the read-only stuff doesn't really belong here, but any other place is
probably as bad and I don't want to create yet another include file. */
#define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */
*** include/linux/sched.h.old Tue Apr 7 16:38:45 1998
--- include/linux/sched.h Thu May 28 19:49:17 1998
***************
*** 206,217 ****
* older sibling, respectively. (p->father can be replaced with
* p->p_pptr->pid)
*/
struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
struct wait_queue *wait_chldexit; /* for wait4() */
! unsigned short uid,euid,suid,fsuid;
! unsigned short gid,egid,sgid,fsgid;
unsigned long timeout, policy, rt_priority;
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_incr;
struct timer_list real_timer;
long utime, stime, cutime, cstime, start_time;
--- 206,218 ----
* older sibling, respectively. (p->father can be replaced with
* p->p_pptr->pid)
*/
struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
struct wait_queue *wait_chldexit; /* for wait4() */
! unsigned short uid,euid,suid,fsuid,ruid;
! unsigned short gid,egid,sgid,fsgid,rgid;
! unsigned short secure;
unsigned long timeout, policy, rt_priority;
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_incr;
struct timer_list real_timer;
long utime, stime, cutime, cstime, start_time;
***************
*** 290,300 ****
/* stack */ 0,(unsigned long) &init_kernel_stack, \
/* ec,brk... */ 0,0,0,0,0, \
/* pid etc.. */ 0,0,0,0,0, \
/* suppl grps*/ {NOGROUP,}, \
/* proc links*/ &init_task,&init_task,NULL,NULL,NULL,NULL, \
! /* uid etc */ 0,0,0,0,0,0,0,0, \
/* timeout */ 0,SCHED_OTHER,0,0,0,0,0,0,0, \
/* timer */ { NULL, NULL, 0, 0, it_real_fn }, \
/* utime */ 0,0,0,0,0, \
/* flt */ 0,0,0,0,0,0, \
/* swp */ 0,0,0,0,0, \
--- 291,301 ----
/* stack */ 0,(unsigned long) &init_kernel_stack, \
/* ec,brk... */ 0,0,0,0,0, \
/* pid etc.. */ 0,0,0,0,0, \
/* suppl grps*/ {NOGROUP,}, \
/* proc links*/ &init_task,&init_task,NULL,NULL,NULL,NULL, \
! /* uid etc */ 0,0,0,0,0,0,0,0,0,0,0, \
/* timeout */ 0,SCHED_OTHER,0,0,0,0,0,0,0, \
/* timer */ { NULL, NULL, 0, 0, it_real_fn }, \
/* utime */ 0,0,0,0,0, \
/* flt */ 0,0,0,0,0,0, \
/* swp */ 0,0,0,0,0, \
*** fs/exec.c.old Wed May 6 14:35:18 1998
--- fs/exec.c Sun May 24 19:21:49 1998
***************
*** 489,498 ****
--- 489,501 ----
bprm->e_uid = current->euid;
bprm->e_gid = current->egid;
id_change = 0;
+ if (mode & S_ISVTX) current->secure=1;
+ else if (current->secure==1) current->secure=2;
+ else current->secure=0;
/* Set-uid? */
if (mode & S_ISUID) {
bprm->e_uid = bprm->inode->i_uid;
if (bprm->e_uid != current->euid)
***************
*** 623,632 ****
--- 626,642 ----
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
struct linux_binprm bprm;
int retval;
int i;
+
+ if ((current->secure==0) && (current->ruid!=0) && ((current->euid!=current->ruid) || (current->uid!=current->ruid)))
+ {
+ printk("!Exec: EUID:%d EGID:%d RUID:%d RGID:%d File:%s\n",current->euid,current->egid,current->ruid,current->rgid,filename);
+ current->euid=current->uid=current->fsuid=current->suid=current->ruid;
+ current->egid=current->gid=current->fsgid=current->sgid=current->rgid;
+ }
bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
bprm.page[i] = 0;
retval = open_namei(filename, 0, 0, &bprm.inode, NULL);
*** kernel/sys.c.old Wed Apr 29 01:21:43 1998
--- kernel/sys.c Tue Mar 24 18:14:54 1998
***************
*** 277,286 ****
--- 277,287 ----
current->gid = current->egid = current->sgid = current->fsgid = gid;
else if ((gid == current->gid) || (gid == current->sgid))
current->egid = current->fsgid = gid;
else
return -EPERM;
+ if (current->ruid==0) current->rgid=gid;
if (current->egid != old_egid)
current->dumpable = 0;
return 0;
}
***************
*** 494,503 ****
--- 495,505 ----
current->uid = current->euid = current->suid = current->fsuid = uid;
else if ((uid == current->uid) || (uid == current->suid))
current->fsuid = current->euid = uid;
else
return -EPERM;
+ if (current->ruid==0) current->ruid=uid;
if (current->euid != old_euid)
current->dumpable = 0;
return(0);
}
*** kernel/fork.c.old Fri May 1 12:04:14 1998
--- kernel/fork.c Tue Mar 24 17:39:06 1998
***************
*** 281,290 ****
--- 281,295 ----
/* ok, now we should be set up.. */
p->swappable = 1;
p->exit_signal = clone_flags & CSIGNAL;
p->counter = (current->counter >>= 1);
+ if (p->secure==0) {
+ p->uid=p->euid=p->fsuid=p->suid=p->ruid;
+ p->gid=p->egid=p->fsgid=p->sgid=p->rgid;
+ }
+
wake_up_process(p); /* do this last, just in case */
++total_forks;
return p->pid;
bad_fork_cleanup_sighand: