從 posix_spawn() 函數窺探漏洞逃逸

posix_spawn() 函數是用來在Linux上建立子進程的,頭文件是 #include <spawn.h> ,語法以下:html

#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[]);

咱們能夠看到一共要傳入六個參數,語法參數說明以下:python

  • 子進程 pid(pid 參數指向一個緩衝區,該緩衝區用於返回新的子進程的進程ID)
  • 可執行文件的路徑 path(其實就是能夠調用某些系統命令,只不過要指定其完整路徑)
  • file_actions 參數指向生成文件操做對象,該對象指定要在子對象之間執行的與文件相關的操做
  • attrp 參數指向一個屬性對象,該對象指定建立的子進程的各類屬性。
  • argv 和 envp 參數指定在子進程中執行的程序的參數列表和環境

詳細文檔能夠經過 man posix_spawn 查看相關文檔:shell

既然咱們知道了這些參數,咱們該如何利用這個呢?ubuntu

咱們先給一個 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 參數由用戶輸入。spa

咱們以 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 鏈接以後,會發現不管輸入什麼命令,你會發現輸入的全部字母都被替換成了大寫字母,無法進行任何操做。code

在這裏,咱們不得不提到一個有意思的東西,$0htm

這個 $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

因而咱們就能夠進行以下更加簡便的操做:

  1. 先調用 os.system 調用 C 語言中的 system 函數

  1. 而後執行 system 模塊中的 posix_spawn 函數

  1. 最後調用當前的 shell

這道題目若是沒有屏蔽掉黑名單的話,仍是有其餘解題思路的,能夠直接經過通配符來解決問題,payload 以下:

/???/???/?38?
相關文章
相關標籤/搜索