waitpid函数_从 posix_spawn() 函数窥探漏洞逃逸

32fa7f88377b20b3eff6bfdfe4e7f406.png

posix_spawn() 函数是用来在Linux上创建子进程的,头文件是 #include ,语法如下:

#include int posix_spawn(pid_t *pid, const char *path,                const posix_spawn_file_actions_t *file_actions,                const posix_spawnattr_t *attrp,                char *const argv[], char *const envp[]);

我们可以看到一共要传入六个参数,语法参数说明如下:

  • 子进程 pid(pid 参数指向一个缓冲区,该缓冲区用于返回新的子进程的进程ID)

  • 可执行文件的路径 path(其实就是可以调用某些系统命令,只不过要指定其完整路径)

  • file_actions 参数指向生成文件操作对象,该对象指定要在子对象之间执行的与文件相关的操作

  • attrp 参数指向一个属性对象,该对象指定创建的子进程的各种属性。

  • argv 和 envp 参数指定在子进程中执行的程序的参数列表和环境

详细文档可以通过 man posix_spawn 查看相关文档:

3be7e463aac7e6db9dea392c25af0144.png

既然我们知道了这些参数,我们该如何利用这个呢?

我们先给一个 Demo 看看:

/** @Author: python* @Date:   2020-01-12 17:28:31* @Last Modified by:   python* @Last Modified time: 2020-01-12 17:32:28*/#include #include #include #include #include #include /*int posix_spawn(pid_t *pid, const char *path,                const posix_spawn_file_actions_t *file_actions,                const posix_spawnattr_t *attrp,                char *const argv[], char *const envp[]);*/extern char **environ;void run_cmd(char *cmd){    pid_t pid;    char *argv[] = {"sh", "-c", cmd, NULL};    int status;    printf("Run command: %s\n", cmd);    status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ);    if (status == 0) {        printf("Child pid: %i\n", pid);        if (waitpid(pid, &status, 0) != -1) {            printf("Child exited with status %i\n", status);        } else {            perror("waitpid");        }    } else {        printf("posix_spawn: %s\n", strerror(status));    }}int main(int argc, char* argv[]){    run_cmd(argv[1]);    return 0;}

运行结果如下图所示:

c66cc486508f7d837421c1eb84fa6c39.png

我们从结果可以看到,/bin/sh 的效果就类似于 sh 脚本中开头的 #!/bin/sh,指定了系统命令 sh 的路径,argv 就类似于 shell 脚本中要执行的代码,比如这里执行 sh -c cmd,而 cmd 参数由用户输入。

我们以 xman 第三界冬令营选拔赛 shellmaster 为例,由于环境已经宕机了,所以我找出题人拿到了源码,有兴趣的可以尝试用源码重新复现一下环境。

import osimport sysimport timeblacklist = [    "$",    "-",    "_",    "{",    "}",    "*",    "2",    "4"]def handler():    print "Oops, are you a master?"    os._exit(0)print "Welcome to shell master!"print "Start to wake up the shell ..."time.sleep(3)sys.stdout.flush()while True:    sys.stdout.write("master@ubuntu:~$ ")    sys.stdout.flush()    cmd = raw_input().upper()    '''for i in blacklist:        if i in cmd:            print "blacklist: "+i            handler()if len(cmd) > 16:        print "len:"+len(cmd)        handler()'''    cmd += " 2>&1"    print os.system(cmd)

我们从源码可以看到,输入的命令中所有字母都被替换成了大写字母,所以你如果通过 nc 连接之后,会发现无论输入什么命令,你会发现输入的所有字母都被替换成了大写字母,没法进行任何操作。

在这里,我们不得不提到一个有意思的东西,$0

这个 $0 是什么东西呢,我们可以尝试打印一下:

f30fe635c908a1042b69f8523d2b88df.png

我们可以看出,$0 事实上就是调用当前的 shell 了,是不是都是这样呢?

我们尝试自己写个例子看看:

436e9f105364c2ee7b70b5b85a43554d.png

我们可以看到,执行并且测试以后,发现输出的结果正好是当前脚本的名字,当前的 $0 就是 ./test.sh

我们从以上这个例子可以看出,在 shell 脚本中,通过使用 $0 就可以获取到脚本的名字或者说脚本本身。

既然这玩意能直接调用当前的 shell,利用方式就有很多种了。

我们可以通过 posix_spawn 这个函数,创建一个子进程,这个子进程可以是系统的默认的命令(进程实质上就是一个程序嘛),这个子进程如果调用的是当前的 shell,我们就可以直接利用这个 shell 来获取相关权限的信息,从而实现逃逸这一过程。

我们可以尝试通过系统的一些方法传入 $0 来实现逃逸这一过程。

那我们既然已经知道了这一点,我们就可以尝试去

那么什么时候会调用 posix_spawn 函数?

由于 posix_spawn 函数是 C 语言中 system.c 创建线程默认调用的功能模块。

C 源码官方下载:http://ftp.gnu.org/gnu/libc/,定义 system 的 c 文件在 glibc/sysdeps/posix/system.c,当然我们也可以在 https://code.woboq.org/userspace/glibc/sysdeps/posix/system.c.html 在线查看。

到这里为止,我们基本思路已经很清楚了,我们可以通过使用 system 模块来调用 posix_spawn 函数来创建子进程,让这个子进程调用当前的 shell,也就是使用 $0 ,然后获取到相关的权限信息,实现逃逸这一过程。我们可以直接写相关的 C 程序来解决。

而在 python 中,os.system 是通过调用 C 语言中的 system 函数来实现其功能的:

9fa97a38d0d557a4ad06f58bfa8f2d45.png

详细文档可以参考:https://docs.python.org/3/library/os.html

于是我们就可以进行如下更加简便的操作:

  1. 先调用 os.system 调用 C 语言中的 system 函数

e3db09da9b8f818b66d23999ecb0bffd.png

  1. 然后执行 system 模块中的 posix_spawn 函数

ecbe2f88ade781ed014e6fa94acd5d22.png

  1. 最后调用当前的 shell

dbee9f3957a863c333523dd1a1e415c6.png

这道题目如果没有屏蔽掉黑名单的话,还是有其他解题思路的,可以直接通过通配符来解决问题,payload 如下:

/???/???/?38?
	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`posix_spawn` 函数是一个用于创建新进程的函数,它比 `fork/exec` 更加高效、安全和灵活。下面是一个 `posix_spawn` 函数的使用实例: ```c #include <stdio.h> #include <stdlib.h> #include <spawn.h> #include <unistd.h> int main(int argc, char *argv[]) { pid_t pid; char *args[] = {"/bin/ls", "-al", NULL}; posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_addclose(&actions, STDIN_FILENO); posix_spawn_file_actions_addclose(&actions, STDOUT_FILENO); posix_spawn_file_actions_addclose(&actions, STDERR_FILENO); if (posix_spawn(&pid, args[0], &actions, NULL, args, NULL) != 0) { perror("posix_spawn"); return EXIT_FAILURE; } waitpid(pid, NULL, 0); posix_spawn_file_actions_destroy(&actions); return EXIT_SUCCESS; } ``` 在上面的例子中,我们首先定义了一个 `args` 数组,它包含了我们要执行的命令及其参数。然后,我们初始化了一个 `posix_spawn_file_actions_t` 对象,并为其添加了三个关闭文件描述符的操作,以避免子进程继承父进程的标准输入、标准输出和标准错误流。 接着,我们调用了 `posix_spawn` 函数来创建新进程。如果成功,该函数将返回子进程的进程 ID。最后,我们调用了 `waitpid` 函数等待子进程退出,并销毁了 `posix_spawn_file_actions_t` 对象。 需要注意的是,`posix_spawn` 函数在创建新进程时会执行一个新的程序映像,因此它不会继承父进程的内存空间和上下文。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值