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?