我正在尝试编写一个小程序,它可以抵抗缓冲区溢出和类似的漏洞。
由于我们不能信任用户输入,我认为最好将输入的所有字符串连接成一个字符串,然后将其传递到静态路径,该路径是同一文件夹中的 bash 脚本,然后添加所有参数/标志/参数(例如 ./script.sh test1 test2 test3 test4)。
我的纸面逻辑如下:
argv[0]
等于程序名称,因此我们需要跳过它。因此,循环从 1(第一个参数)开始,到 argc-1 结束。到目前为止还不错.key
。memcpy
所有的字符串使其可以安全使用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 ...>
它看起来好像会尝试检测空字符或类似字符的长度。
main()
:strcat(argv[1], ".key")
是缓冲区溢出。您向没有空闲空间的字符串写入 4 个字节。
(性能)
main()
:for (int x = 0; x < strlen(argv[i]); x++)
将strlen()
移动到循环之前,这样您就不必在每次迭代时对其进行评估。
main()
:当封闭循环使用变量char *x = malloc(len * sizeof(char));
时,int x = 0
会令人困惑。
(性能)
main()
:for (int x = 0; x < strlen(argv[i]); x++)
做同样的事情strlen(argv[i])
次。消除循环,只需执行一次即可。
main()
:strcpy(safeargv[i], x)
不正确,因为 strcpy()
需要 char *
,但 safeargv[i]
属于 char *[4096]
类型。我怀疑您想将类型修复为 char safeargv[5][4096]
。
main(): With the type fixed
strcpy(safeargv[i], x)is still problematic as you start i=1 so
safeargv[0]` 未初始化。
concatenate()
与 array[5] = { NULL, NULL, NULL, NULL, NULL}
一起调用,并且 strlen(NULL)
会导致段错误。