用户输入清理程序,它采用特定数量的参数并将执行传递给 bash 脚本

问题描述 投票:0回答:1

我正在尝试编写一个小程序,它可以抵抗缓冲区溢出和类似的漏洞。

由于我们不能信任用户输入,我认为最好将输入的所有字符串连接成一个字符串,然后将其传递到静态路径,该路径是同一文件夹中的 bash 脚本,然后添加所有参数/标志/参数(例如 ./script.sh test1 test2 test3 test4)。

我的纸面逻辑如下:

  1. 检查 argc 的数量是否正好为 5(即程序名称 + 4 个参数),如果不是 - 立即退出
  2. 初始化最大长度为 4096 字节的 5 个字符串(即数组元素)的特定 char 数组。
  3. 由于
    argv[0]
    等于程序名称,因此我们需要跳过它。因此,循环从 1(第一个参数)开始,到 argc-1 结束。到目前为止还不错
  4. 我们将 argv[1] 字符串追加到内存中的结尾
    .key
  5. 我们
    memcpy
    所有的字符串使其可以安全使用
  6. 对于每次迭代,我们都不会忘记在字符串末尾添加空终止符。
  7. 在我们拥有可以安全使用的所有参数后,我们将其连接到函数 parse_output 中,并调用名为 script.sh 的 bash 脚本并添加所有必需的参数
  8. 我们将
    script.sh 4argshere
    的输出返回给用户

我尝试像评论中那样释放内存,但它似乎不起作用,或者我在其他地方有错误。

我的段错误概念证明:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define BUFSIZE 1000

char *concatenate(size_t size, char *array[size], const char *joint);

char *concatenate(size_t size, char *array[size], const char *joint){
    size_t jlen, lens[size];
    size_t i, total_size = (size-1) * (jlen=strlen(joint)) + 1;
    char *result, *p;


    for(i=0;i<size;++i){
        total_size += (lens[i]=strlen(array[i]));
    }
    p = result = malloc(total_size);
    for(i=0;i<size;++i){
        memcpy(p, array[i], lens[i]);
        p += lens[i];
        if(i<size-1){
            memcpy(p, joint, jlen);
            p += jlen;
        }
    }
    *p = '\0';
    return result;
}


int parse_output(char *safeargv[]) {
    char safeargs = *concatenate(5, safeargv, " ");

    char cmd[BUFSIZE];
    snprintf(cmd, BUFSIZE, "./script.sh %s", safeargs);

    char buf[BUFSIZE] = {0};
    FILE *fp;

    if ((fp = popen(cmd, "r")) == NULL) {
        printf("Error opening pipe!\n");
        //free(safeargs);
        return -1;
    }

    while (fgets(buf, BUFSIZE, fp) != NULL) {
        printf("OUTPUT: %s", buf);
    }

    if (pclose(fp)) {
        printf("Command not found or exited with error status\n");
       //free(safeargs);
        return -1;
    }
    //free(safeargs);
    return 0;
}

int main(int argc, char *argv[]) {
    if(argc != 5) {
        exit(1);
    }

    char *safeargv[5][4096];

    for (int i = 1; i < argc - 1; i++) {
        if (i == 1)
            strcat(argv[1], ".key");
        for (int x = 0; x < strlen(argv[i]); x++) {
            char *unsafe_string = argv[i];
            size_t max_len = 4096;
            size_t len = strnlen(unsafe_string, max_len) + 1;
            char *x = malloc(len * sizeof(char));
            if (x != NULL) {
                strncpy(x, unsafe_string, len);
                x[len-1] = '\0'; // Ensure null-termination
                strcpy(safeargv[i], x);
                free(x);
            }
        }
    }
    parse_output(safeargv);
    return 0;
}


我在编译时收到的警告:

chal.c: In function 'main':
chal.c:78:32: warning: passing argument 1 of 'strcpy' from incompatible pointer type [-Wincompatible-pointer-types]
   78 |                 strcpy(safeargv[i], x);
      |                        ~~~~~~~~^~~
      |                                |
      |                                char **
In file included from chal.c:2:
/usr/include/string.h:141:39: note: expected 'char * restrict' but argument is of type 'char **'
  141 | extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
      |                      ~~~~~~~~~~~~~~~~~^~~~~~
chal.c:83:18: warning: passing argument 1 of 'parse_output' from incompatible pointer type [-Wincompatible-pointer-types]
   83 |     parse_output(safeargv);
      |                  ^~~~~~~~
      |                  |
      |                  char * (*)[4096]
chal.c:32:24: note: expected 'char **' but argument is of type 'char * (*)[4096]'
   32 | int parse_output(char *safeargv[]) {
      |                  ~~~~~~^~~~~~~~~~

似乎只有我的 argc 检查有效,因为如果我调用 ./programname abc abc abc abc 它会出现段错误。另外,如果脚本参数中有拼写错误,检测错误的正确方法是什么?

我犯了什么错误?

我的最后

ltrace
行:

strlen(" ")                                                                                                                                                                                         = 1
strlen(nil <no return ...>

它看起来好像会尝试检测空字符或类似字符的长度。

arrays c string strcpy strlen
1个回答
0
投票
  1. main()
    strcat(argv[1], ".key")
    是缓冲区溢出。您向没有空闲空间的字符串写入 4 个字节。

  2. (性能)

    main()
    for (int x = 0; x < strlen(argv[i]); x++)
    strlen()
    移动到循环之前,这样您就不必在每次迭代时对其进行评估。

  3. main()
    :当封闭循环使用变量
    char *x = malloc(len * sizeof(char));
    时,
    int x = 0
    会令人困惑。

  4. (性能)

    main()
    for (int x = 0; x < strlen(argv[i]); x++)
    做同样的事情
    strlen(argv[i])
    次。消除循环,只需执行一次即可。

  5. main()
    strcpy(safeargv[i], x)
    不正确,因为
    strcpy()
    需要
    char *
    ,但
    safeargv[i]
    属于
    char *[4096]
    类型。我怀疑您想将类型修复为
    char safeargv[5][4096]

  6. main(): With the type fixed 
    strcpy(safeargv[i], x)
    is still problematic as you start i=1 so
    safeargv[0]` 未初始化。

  7. concatenate()
    array[5] = { NULL, NULL, NULL, NULL, NULL}
    一起调用,并且
    strlen(NULL)
    会导致段错误。

© www.soinside.com 2019 - 2024. All rights reserved.