這幾天遇到一個狀況,
一隻用來收檔案的cgi被種了指令,指令是埋在Post payload的某欄位混進來的,
欄位值被系統當作檔名操作的時候,就順便root執行這行指令了。
嘗試了一些迴避方法,
根據這裡的建議,寫出了一個wrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
//#define UNIT_TEST | |
#ifdef UNIT_TEST | |
#ifdef LOG_ERROR | |
#undef LOG_ERROR | |
#define LOG_ERROR(...) printf("[ERROR]");printf(__VA_ARGS__);printf("\n") | |
#endif | |
#ifdef LOG_DEBUG | |
#undef LOG_DEBUG | |
#define LOG_DEBUG(...) printf("[DEBUG]");printf(__VA_ARGS__);printf("\n") | |
#endif | |
#endif | |
int system_safe(char const **cmd, const char *output_file) | |
{ | |
pid_t pid; | |
int status; | |
pid_t ret; | |
char *env[]={0}; | |
int fd_null = NULL; | |
int fd_output = NULL; | |
if (cmd == NULL || *cmd == NULL || strlen(cmd[0]) == 0){ | |
LOG_ERROR("Invalid executable."); | |
return -1; | |
} | |
LOG_DEBUG("cmd[0]: %s", cmd[0]); | |
pid = fork(); | |
if (pid == 0){ | |
//dump stderr | |
fd_null = open("/dev/null", O_WRONLY); | |
if (fd_null != NULL){ | |
if(dup2(fd_null, 2) == -1){//stderr | |
LOG_ERROR("dup2 failed with errno %s", strerror(errno)); | |
} | |
} | |
//if output file path specified, redirect stdout to file. | |
if (output_file != NULL){ | |
fd_output = open(output_file, O_WRONLY | O_CREAT); | |
if (fd_output != NULL){ | |
if(dup2(fd_output, 1) == -1){//stdout | |
LOG_ERROR("dup2 failed with errno %s", strerror(errno)); | |
} | |
} | |
else{ | |
if(dup2(fd_null, 1) == -1){//stdout | |
LOG_ERROR("dup2 failed with errno %s", strerror(errno)); | |
} | |
} | |
} | |
else{ | |
if (fd_null != NULL){ | |
if(dup2(fd_null, 1) == -1){//stdout | |
LOG_ERROR("dup2 failed with errno %s", strerror(errno)); | |
} | |
} | |
} | |
if (fd_null != NULL){ | |
close(fd_null);//close fd | |
} | |
if (fd_output != NULL){ | |
close(fd_output); | |
} | |
if (execve(cmd[0], &cmd[0], env) == -1) { | |
LOG_ERROR("execve %s failed with : %s", cmd[0], strerror(errno)); | |
return -1; | |
} | |
} | |
else if(pid == -1){ | |
LOG_ERROR("fork failed"); | |
return -1; | |
} | |
else{ | |
LOG_DEBUG("pid: %d", pid); | |
//wait for only specified child process | |
//set option 0 to let it block. | |
ret = waitpid(pid, &status, 0); | |
//waitpid returns given pid when end successfully. | |
//otherwise check errno | |
if(ret == -1) { | |
if (errno != EINTR) { | |
//child process was not terminated by parent's received signal | |
LOG_ERROR("child ended with errno %s", strerror(errno)); | |
return -1; | |
} | |
} | |
else{ | |
if(WIFEXITED(status)){ | |
//should always go this way | |
LOG_DEBUG("execve status %d", WEXITSTATUS(status)); | |
return WEXITSTATUS(status); | |
} | |
else{ | |
//child process ended normally but status was wrong | |
LOG_ERROR("child ended with !WIFEXITED(status)"); | |
return -1; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Executing system command safe from command injection. | |
* @param cmd: system command. ex: char *cmd[]={"/usr/bin/test", "-e", "readme", NULL}; | |
* Executable must be absolute path, NULL is a must at end of array. | |
* @param output_file: file path to redirect stdout to, put NULL to ignore stdout. | |
* @return : If command executed sucessfully, return command's exit code, otherwise return -1. | |
*/ | |
int system_safe(char const **cmd, const char *output_file); |
裡面小撞牆的地方是,Executable要給absolute path,不然errno會一直報「找不到」
然後SIGNAL還沒有處理.....
繼續奮鬥 zzzZZZZ
參考資料
waitpid用法詳解。
http://linux.die.net/man/2/waitpid
http://www.gnu.org/software/libc/manual/html_node/Process-Completion.html
留言
張貼留言