首页 > 解决方案 > 使用来自 C 的参数运行 shell 脚本的安全方法,工作示例

问题描述

我想要一个最小的工作 c 代码,它执行一个 shell 脚本,以安全的方式将 c 可执行文件的第一个参数传递给 shell 脚本。

在 stakcexchange 上有很多关于在 C 可执行文件中运行 shell 脚本表单的问题。他们中的许多人建议使用该system呼叫。

实际上我正在使用这个解决方案:

#include <unistd.h>
#include <errno.h>
main( int argc, char ** argv, char ** envp )
{

    char *command;
    int size = asprintf(&command, "/path/to/script.sh %s", argv[1]);

    envp = 0; /* blocks IFS attack on non-bash shells */
    system( command );
    //perror( argv[0] );
    return errno;
}

源自如何在 Debian wheezy 中启用 suidperl?.

我知道这个解决方案会受到代码注入的影响。@basile-starynkevitch对此问题的回答中“原则上”描述了一种可能的解决方案

如何修改上面的示例 .c 代码以清理argv[1]或通常以安全的方式调用带有参数的 shell 脚本?

标签: cbashforkcode-injection

解决方案


第一选择:完全不system()使用

#include <unistd.h>
#include <errno.h>

main(int argc, char **argv) {
    int retval;
    execl("/path/to/sctcipt.sh", "/path/to/sctcipt.sh", argv[1], NULL);
    _exit(1); /* if we got here, the fork() failed */
}

因为我们所做的只是包装另一个程序,所以我们可以通过execv-family 系统调用将直接控制权传递给它;甚至没有必要fork()先。


第二种选择:通过环境导出变量

在这里,传递给的代码system()是您自己审核的常量;只有在 shell 启动后, shell 才会执行替换,在它已经完成语法解析阶段之后。

#include <unistd.h>
#include <errno.h>

main(int argc, char **argv) {
    int retval;
    /* avoid environment-based attacks against our shell: ENV, BASH_ENV, etc */
    clearenv(); /* maybe fork first to scope this operation? */
    /* Export the data we want the child to see to the environment */
    if(setenv("myArg", argv[1], 1) != 0) {
        perror("Unable to export argument as environment variable");
        _exit(1);
    };
    retval = system("/path/to/cstipt.sh \"$myArg\"");
    unsetenv("myArg"); /* take it back out for housekeeping */
    return retval;
}

推荐阅读