C语言调用指令避免命令注入

C语言调用指令避免命令注入

在C语言中,有时候需要执行一些指令对数据做一些快捷方便的处理,并且获取执行结果来查看指令是否执行成功。经常用的 system 或者 popen 函数。但是,如果指令中的参数来自外部,那么就有可能存在命令注入漏洞。这就需要对指令做一些转义处理。

本文记录了一种方法,使用 单引号 对指令做转义,来避免指令注入漏洞。这种方法的缺点是:指令中所有的 `\$\{ 等方式获取便令或者函数结果的方式都不会生效,所以需要仔细分析场景是否适用。

// gcc -Wall test.c -o test

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

/**
 * 对字符串进行 shell 转义,使其可安全用于 popen/system 等调用。
 * 使用单引号包裹,并转义内部单引号。
 *
 * @param input: 待转义的原始字符串
 * @return: 转义后的新字符串(需用 free() 释放),失败返回 NULL
 */
char* shell_escape(const char* input) {
    if (input == NULL) {
        return NULL;
    }

    size_t len = strlen(input);
    // 最坏情况:每个字符都是单引号,需要扩展为4倍 + 2个外层单引号 + 1个\0
    // 例如:' -> '\'' (4字符)
    size_t max_len = len * 4 + 3;
    char* escaped = (char*)malloc(max_len); // 需要在函数外部释放
    if (escaped == NULL) {
        return NULL;
    }

    char* p = escaped;
    *p++ = '\'';  // 开始单引号

    for (size_t i = 0; i < len; i++) {
        if (input[i] == '\'') {
            // 转义单引号:结束当前单引号,插入转义单引号,再开新单引号
            *p++ = '\'';
            *p++ = '\\';
            *p++ = '\'';
            *p++ = '\'';
        } else {
            *p++ = input[i];
        }
    }

    *p++ = '\'';  // 结束单引号
    *p = '\0';

    return escaped;
}

// 测试
int main() {

    // 测试指令
    const char* filename = "touch aa bb dd &*()&%{[]]}'098feff%@#@0#1'921'2{{} jojfaefae \" '/tmp/t01'; touch /tmp/t02";
    char* escaped = shell_escape(filename);
    if (escaped == NULL) {
        perror("shell_escape");
        return 1;
    }

    char command[512];
    printf("filename [%s]\n", escaped) ;
    snprintf(command, sizeof(command), "mkdir -p %s", escaped);
    printf("Executing: [%s]\n", command);

    FILE* fp = popen(command, "r");
    if (fp == NULL) {
        perror("popen");
        free(escaped);
        return 1;
    }

    // 读取输出
    char buffer[256];
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }

    pclose(fp);
    // 注意:一定要释放escaped的空间,这里是shell_escape函数内部申请的内存
    free(escaped);
    return 0;
}

测试:

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注