<<< Date Index >>>     <<< Thread Index >>>

Hot fix for do_brk bug



Greetings,

I've written a linux kernel module that can be used to hot fix a
Linux system for the bug in do_brk.  It scans the
kernel space and replaces jmp and calls to do_brk
to point to a wrapper routine instead.  It also maps
the symbol table to point to the wrapper.  This only
works on x86 and it has only been tested with RH kernels
2.4.18-27.7.xsmp and 2.4.20-20.7smp.  It is quite possible
this could crash or screw-up a system, so use at your own
risk.  I've tested the module against the proof of concept code
written and posted by Christophe Devine.  The module catches
the exploit and logs the attempt.

--Shane Canon

------brk_fix.c------------
#define MODULE
#define __KERNEL__

/* some constants used in our module */
#define MODULE_NAME "brk_fix"
#define MODULE_VERSION "0.01"

/* brk_fix
 * Verions: 0.01
 *
 * WARNING WARNING WARNING
 * This module intentionally modifies kernel memory.
 * This module could potentially crash or damage your system.  
 * Use at your own risks.
 *
 *  Hot fix for integer overflow in do_brk
 *   
 *  This module scans kernel code space looking for jumps and calls
 *  to do_brk.  It then changes these calls to instead call my_brk,
 *  which is a wrapper routine that does some checks before calling do_brk.
 *  The module also scans for the address of do_brk and replaces it
 *  with my_brk.  This would mainly occur in the exported symbols table.
 *
 *  This has been tested with RH 2.4.20-20.7smp and RH 2.4.18-27.7.xsmp on i686
 *
 *  Sample use...
 *  gcc -O3 -I/lib/modules/`uname -r`/build/include   -c -o brk_fix.o brk_fix.c
 *  insmod ./brk_fix.o
 *
 *  Written by Shane Canon <canon@xxxxxxxxx>
 *  (c) Copyright 2003 Regents of the University of California
 *             All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/slab.h>

extern void * __start___kallsyms;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
#endif

unsigned long my_brk(unsigned long addr, unsigned long len);

int init_module(void)
{
  unsigned char *ptr;
  unsigned char *newptr;
  long *lptr;
  unsigned char *cptr;
  long diff;
  int count=0;
  unsigned char *start=(unsigned char *) (TASK_SIZE);  /* Start at the 
beginning of kernel space */
  unsigned char *end=(unsigned char *)&__start___kallsyms;    
                                             /* Want something that is well 
past the kernel test */
                                             /* This seems to fit the bill     
                  */


  ptr=(unsigned char *)(do_brk);              /* We are looking for 
calls/jumps to this function */
  newptr=(unsigned char *)(my_brk);           /* This is what we will change 
it to               */
  for (cptr=start;cptr<end;cptr++){           /* Lets scan all of kernel space 
                  */
    if (*cptr==0xe8||*cptr==0xe9){            /* Look for calls or jumps       
                  */
      cptr++;                                 /* If you find one look at the 
next dword          */
      lptr=(long *)cptr;
      cptr+=4;
      if ((cptr+*lptr)==(ptr)){               /* See if the offset would point 
to do_brk         */
        printk("fixing 0x%08lx\n",lptr);      /* If so, change it to our new 
routine             */
        *lptr=(newptr-cptr);
        count++;
      }
    }
    else{
      lptr=(long *)cptr;                      /* Look for the address as well. 
 This would show  */
      if ((unsigned char*)(*lptr)==ptr){      /* up in the symbol table.       
                  */
        printk("Fixing address at 0x%08lx\n",lptr);
        *lptr=(long)(newptr);
      }
    }
  }
  printk ("Fix brk installed..\n");           /* All done. */
  MOD_INC_USE_COUNT;                          /* We can't unload this one.  So 
lets inc the mod  */
                                              /* count and leave it there. */
  return 0;                                   /* success */

}


void cleanup_module(void)
{
  /* Can't remove it */
}

/* These are the first couple of lines from the patched mmap.c */
/* Do the new checks and then call the original do_brk         */

unsigned long my_brk(unsigned long addr, unsigned long len)
{
        len = PAGE_ALIGN(len);
        if (!len)
                return addr;

        if ((addr + len) > TASK_SIZE || (addr + len) < addr){   /* Let's make 
sure its in bounds */
                printk("caught do_brk exploit!!!\n");
                return -EINVAL;
        }
        return do_brk(addr,len);                                /* Call the 
real 
do_brk          */
}

-------end---------

------------------------------------------------------------------------
Shane Canon
National Energy Research Scientific
  Computing Center                       
------------------------------------------------------------------------