原文地址https://www.madbull.site/?p2369欢迎参观我的网站无敌牛 – 技术/著作/典籍/分享等在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#19212{{} 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; }测试
C语言调用指令避免命令注入
原文地址https://www.madbull.site/?p2369欢迎参观我的网站无敌牛 – 技术/著作/典籍/分享等在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#19212{{} 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; }测试