在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;
}
测试:

发表回复