posix_spawn() 函数是用来在 Linux 上创建子进程的, 头文件是 #include <spawn.h> , 语法如下:
- #include <spawn.h>
- 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 查看相关文档:
既然我们知道了这些参数, 我们该如何利用这个呢?
我们先给一个 Demo 看看:
- /*
- * @Author: python
- * @Date: 2020-01-12 17:28:31
- * @Last Modified by: python
- * @Last Modified time: 2020-01-12 17:32:28
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include <spawn.h>
- #include <sys/wait.h>
- /*
- 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;
- }
运行结果如下图所示:
我们从结果可以看到,/bin/sh 的效果就类似于 sh 脚本中开头的 #!/bin/sh, 指定了系统命令 sh 的路径, argv 就类似于 shell 脚本中要执行的代码, 比如这里执行 sh -c cmd, 而 cmd 参数由用户输入.
我们以 xman 第三界冬令营选拔赛 shellmaster 为例, 由于环境已经宕机了, 所以我找出题人拿到了源码, 有兴趣的可以尝试用源码重新复现一下环境.
- import os
- import sys
- import time
- blacklist = [
- "$",
- "-",
- "_",
- "{",
- "}",
- "*",
- "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 是什么东西呢, 我们可以尝试打印一下:
我们可以看出,$0 事实上就是调用当前的 shell 了, 是不是都是这样呢?
我们尝试自己写个例子看看:
我们可以看到, 执行并且测试以后, 发现输出的结果正好是当前脚本的名字, 当前的 $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 函数来实现其功能的:
详细文档可以参考: https://docs.python.org/3/library/os.html
于是我们就可以进行如下更加简便的操作:
先调用 os.system 调用 C 语言中的 system 函数
然后执行 system 模块中的 posix_spawn 函数
最后调用当前的 shell
这道题目如果没有屏蔽掉黑名单的话, 还是有其他解题思路的, 可以直接通过通配符来解决问题, payload 如下:
/???/???/?38?
来源: https://www.cnblogs.com/ECJTUACM-873284962/p/12183348.html