Merge pull request #6184 from github/rdmarsh2/improve-exec-tainted

C++: Refactor ExecTainted.ql to only report results after string concatenation
This commit is contained in:
Jonas Jensen
2021-09-29 19:21:13 +02:00
committed by GitHub
18 changed files with 793 additions and 103 deletions

View File

@@ -1 +1,17 @@
| tests.cpp:53:16:53:19 | data | This argument to an OS command is derived from $@ and then passed to system(string) | tests.cpp:33:34:33:39 | call to getenv | user input (getenv) |
edges
| tests.cpp:33:34:33:39 | call to getenv | tests.cpp:38:39:38:49 | environment indirection |
| tests.cpp:38:25:38:36 | strncat output argument | tests.cpp:42:5:42:16 | Phi |
| tests.cpp:38:39:38:49 | environment indirection | tests.cpp:38:25:38:36 | strncat output argument |
| tests.cpp:38:39:38:49 | environment indirection | tests.cpp:38:25:38:36 | strncat output argument |
| tests.cpp:42:5:42:16 | Phi | tests.cpp:51:22:51:25 | badSource output argument |
| tests.cpp:51:22:51:25 | badSource output argument | tests.cpp:53:16:53:19 | data indirection |
nodes
| tests.cpp:33:34:33:39 | call to getenv | semmle.label | call to getenv |
| tests.cpp:38:25:38:36 | strncat output argument | semmle.label | strncat output argument |
| tests.cpp:38:39:38:49 | environment indirection | semmle.label | environment indirection |
| tests.cpp:42:5:42:16 | Phi | semmle.label | Phi |
| tests.cpp:51:22:51:25 | badSource output argument | semmle.label | badSource output argument |
| tests.cpp:53:16:53:19 | data indirection | semmle.label | data indirection |
subpaths
#select
| tests.cpp:53:16:53:19 | data | tests.cpp:33:34:33:39 | call to getenv | tests.cpp:53:16:53:19 | data indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | tests.cpp:33:34:33:39 | call to getenv | user input (an environment variable) | tests.cpp:38:25:38:36 | strncat output argument | strncat output argument |

View File

@@ -1 +1,67 @@
| test.c:21:12:21:19 | command1 | This argument to an OS command is derived from $@ and then passed to system(string) | test.c:14:20:14:23 | argv | user input (argv) |
edges
| test.cpp:16:20:16:23 | argv | test.cpp:22:45:22:52 | userName indirection |
| test.cpp:22:13:22:20 | sprintf output argument | test.cpp:23:12:23:19 | command1 indirection |
| test.cpp:22:45:22:52 | userName indirection | test.cpp:22:13:22:20 | sprintf output argument |
| test.cpp:22:45:22:52 | userName indirection | test.cpp:22:13:22:20 | sprintf output argument |
| test.cpp:47:21:47:26 | call to getenv | test.cpp:50:35:50:43 | envCflags indirection |
| test.cpp:50:11:50:17 | sprintf output argument | test.cpp:51:10:51:16 | command indirection |
| test.cpp:50:35:50:43 | envCflags indirection | test.cpp:50:11:50:17 | sprintf output argument |
| test.cpp:50:35:50:43 | envCflags indirection | test.cpp:50:11:50:17 | sprintf output argument |
| test.cpp:62:9:62:16 | fread output argument | test.cpp:64:20:64:27 | filename indirection |
| test.cpp:64:11:64:17 | strncat output argument | test.cpp:65:10:65:16 | command indirection |
| test.cpp:64:20:64:27 | filename indirection | test.cpp:64:11:64:17 | strncat output argument |
| test.cpp:64:20:64:27 | filename indirection | test.cpp:64:11:64:17 | strncat output argument |
| test.cpp:82:9:82:16 | fread output argument | test.cpp:84:20:84:27 | filename indirection |
| test.cpp:84:11:84:17 | strncat output argument | test.cpp:85:32:85:38 | command indirection |
| test.cpp:84:20:84:27 | filename indirection | test.cpp:84:11:84:17 | strncat output argument |
| test.cpp:84:20:84:27 | filename indirection | test.cpp:84:11:84:17 | strncat output argument |
| test.cpp:91:9:91:16 | fread output argument | test.cpp:93:17:93:24 | filename indirection |
| test.cpp:93:11:93:14 | strncat output argument | test.cpp:94:45:94:48 | path indirection |
| test.cpp:93:17:93:24 | filename indirection | test.cpp:93:11:93:14 | strncat output argument |
| test.cpp:93:17:93:24 | filename indirection | test.cpp:93:11:93:14 | strncat output argument |
| test.cpp:106:20:106:25 | call to getenv | test.cpp:107:33:107:36 | path indirection |
| test.cpp:113:20:113:25 | call to getenv | test.cpp:114:19:114:22 | path indirection |
| test.cpp:119:20:119:25 | call to getenv | test.cpp:120:19:120:22 | path indirection |
| test.cpp:140:9:140:11 | fread output argument | test.cpp:142:31:142:33 | str indirection |
| test.cpp:142:11:142:17 | sprintf output argument | test.cpp:143:10:143:16 | command indirection |
| test.cpp:142:31:142:33 | str indirection | test.cpp:142:11:142:17 | sprintf output argument |
| test.cpp:142:31:142:33 | str indirection | test.cpp:142:11:142:17 | sprintf output argument |
nodes
| test.cpp:16:20:16:23 | argv | semmle.label | argv |
| test.cpp:22:13:22:20 | sprintf output argument | semmle.label | sprintf output argument |
| test.cpp:22:45:22:52 | userName indirection | semmle.label | userName indirection |
| test.cpp:23:12:23:19 | command1 indirection | semmle.label | command1 indirection |
| test.cpp:47:21:47:26 | call to getenv | semmle.label | call to getenv |
| test.cpp:50:11:50:17 | sprintf output argument | semmle.label | sprintf output argument |
| test.cpp:50:35:50:43 | envCflags indirection | semmle.label | envCflags indirection |
| test.cpp:51:10:51:16 | command indirection | semmle.label | command indirection |
| test.cpp:62:9:62:16 | fread output argument | semmle.label | fread output argument |
| test.cpp:64:11:64:17 | strncat output argument | semmle.label | strncat output argument |
| test.cpp:64:20:64:27 | filename indirection | semmle.label | filename indirection |
| test.cpp:65:10:65:16 | command indirection | semmle.label | command indirection |
| test.cpp:82:9:82:16 | fread output argument | semmle.label | fread output argument |
| test.cpp:84:11:84:17 | strncat output argument | semmle.label | strncat output argument |
| test.cpp:84:20:84:27 | filename indirection | semmle.label | filename indirection |
| test.cpp:85:32:85:38 | command indirection | semmle.label | command indirection |
| test.cpp:91:9:91:16 | fread output argument | semmle.label | fread output argument |
| test.cpp:93:11:93:14 | strncat output argument | semmle.label | strncat output argument |
| test.cpp:93:17:93:24 | filename indirection | semmle.label | filename indirection |
| test.cpp:94:45:94:48 | path indirection | semmle.label | path indirection |
| test.cpp:106:20:106:25 | call to getenv | semmle.label | call to getenv |
| test.cpp:107:33:107:36 | path indirection | semmle.label | path indirection |
| test.cpp:113:20:113:25 | call to getenv | semmle.label | call to getenv |
| test.cpp:114:19:114:22 | path indirection | semmle.label | path indirection |
| test.cpp:119:20:119:25 | call to getenv | semmle.label | call to getenv |
| test.cpp:120:19:120:22 | path indirection | semmle.label | path indirection |
| test.cpp:140:9:140:11 | fread output argument | semmle.label | fread output argument |
| test.cpp:142:11:142:17 | sprintf output argument | semmle.label | sprintf output argument |
| test.cpp:142:31:142:33 | str indirection | semmle.label | str indirection |
| test.cpp:143:10:143:16 | command indirection | semmle.label | command indirection |
subpaths
#select
| test.cpp:23:12:23:19 | command1 | test.cpp:16:20:16:23 | argv | test.cpp:23:12:23:19 | command1 indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:16:20:16:23 | argv | user input (a command-line argument) | test.cpp:22:13:22:20 | sprintf output argument | sprintf output argument |
| test.cpp:51:10:51:16 | command | test.cpp:47:21:47:26 | call to getenv | test.cpp:51:10:51:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:47:21:47:26 | call to getenv | user input (an environment variable) | test.cpp:50:11:50:17 | sprintf output argument | sprintf output argument |
| test.cpp:65:10:65:16 | command | test.cpp:62:9:62:16 | fread output argument | test.cpp:65:10:65:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:62:9:62:16 | fread output argument | user input (String read by fread) | test.cpp:64:11:64:17 | strncat output argument | strncat output argument |
| test.cpp:85:32:85:38 | command | test.cpp:82:9:82:16 | fread output argument | test.cpp:85:32:85:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl | test.cpp:82:9:82:16 | fread output argument | user input (String read by fread) | test.cpp:84:11:84:17 | strncat output argument | strncat output argument |
| test.cpp:94:45:94:48 | path | test.cpp:91:9:91:16 | fread output argument | test.cpp:94:45:94:48 | path indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl | test.cpp:91:9:91:16 | fread output argument | user input (String read by fread) | test.cpp:93:11:93:14 | strncat output argument | strncat output argument |
| test.cpp:143:10:143:16 | command | test.cpp:140:9:140:11 | fread output argument | test.cpp:143:10:143:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:140:9:140:11 | fread output argument | user input (String read by fread) | test.cpp:142:11:142:17 | sprintf output argument | sprintf output argument |

View File

@@ -1,33 +0,0 @@
// Semmle test case for rule ExecTainted.ql (Uncontrolled data used in OS command)
// Associated with CWE-078: OS Command Injection. http://cwe.mitre.org/data/definitions/78.html
///// Library routines /////
int sprintf(char *s, const char *format, ...);
int system(const char *string);
extern void encodeShellString(char *shellStr, int maxChars, const char* cStr);
///// Test code /////
int main(int argc, char** argv) {
char *userName = argv[2];
{
// BAD: a string from the user is injected directly into
// a command.
char command1[1000] = {0};
sprintf(command1, "userinfo -v \"%s\"", userName);
system(command1);
}
{
// GOOD: the user string is encoded by a library routine.
char userNameQuoted[1000] = {0};
encodeShellString(userNameQuoted, 1000, userName);
char command2[1000] = {0};
sprintf(command2, "userinfo -v %s", userNameQuoted);
system(command2);
}
}

View File

@@ -0,0 +1,174 @@
// Semmle test case for rule ExecTainted.ql (Uncontrolled data used in OS command)
// Associated with CWE-078: OS Command Injection. http://cwe.mitre.org/data/definitions/78.html
///// Library routines /////
int sprintf(char *s, const char *format, ...);
int system(const char *string);
char *getenv(char *var);
extern void encodeShellString(char *shellStr, int maxChars, const char* cStr);
#include "../../../../../../include/string.h"
///// Test code /////
int main(int argc, char** argv) {
char *userName = argv[2];
{
// BAD: a string from the user is injected directly into
// a command.
char command1[1000] = {0};
sprintf(command1, "userinfo -v \"%s\"", userName);
system(command1);
}
{
// GOOD: the user string is encoded by a library routine.
char userNameQuoted[1000] = {0};
encodeShellString(userNameQuoted, 1000, userName);
char command2[1000] = {0};
sprintf(command2, "userinfo -v %s", userNameQuoted);
system(command2);
}
}
void test2(char* arg2) {
// GOOD?: the user string is the *first* part of the command, like $CC in many environments
char *envCC = getenv("CC");
char command[1000];
sprintf("%s %s", envCC, arg2);
system(command);
}
void test3(char* arg1) {
// GOOD?: the user string is a `$CFLAGS` environment variable
char *envCflags = getenv("CFLAGS");
char command[1000];
sprintf(command, "%s %s", arg1, envCflags);
system(command);
}
typedef unsigned long size_t;
typedef void FILE;
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
char *strncat(char *s1, const char *s2, size_t n);
void test4(FILE *f) {
// BAD: the user string is injected directly into a command
char command[1000] = "mv ", filename[1000];
fread(filename, 1, 1000, f);
strncat(command, filename, 1000);
system(command);
}
void test5(FILE *f) {
// GOOD?: the user string is the start of a command
char command[1000], filename[1000] = " test.txt";
fread(command, 1, 1000, f);
strncat(command, filename, 1000);
system(command);
}
int execl(char *path, char *arg1, ...);
void test6(FILE *f) {
// BAD: the user string is injected directly into a command
char command[1000] = "mv ", filename[1000];
fread(filename, 1, 1000, f);
strncat(command, filename, 1000);
execl("/bin/sh", "sh", "-c", command);
}
void test7(FILE *f) {
// GOOD [FALSE POSITIVE]: the user string is a positional argument to a shell script
char path[1000] = "/home/me/", filename[1000];
fread(filename, 1, 1000, f);
strncat(path, filename, 1000);
execl("/bin/sh", "sh", "-c", "script.sh", path);
}
void test8(char *arg2) {
// GOOD?: the user string is the *first* part of the command, like $CC in many environments
std::string envCC(getenv("CC"));
std::string command = envCC + arg2;
system(command.c_str());
}
void test9(FILE *f) {
// BAD: the user string is injected directly into a command
std::string path(getenv("something"));
std::string command = "mv " + path;
system(command.c_str());
}
void test10(FILE *f) {
// BAD: the user string is injected directly into a command
std::string path(getenv("something"));
system(("mv " + path).c_str());
}
void test11(FILE *f) {
// BAD: the user string is injected directly into a command
std::string path(getenv("something"));
system(("mv " + path).data());
}
int atoi(char *);
void test12(FILE *f) {
char temp[10];
char command[1000];
fread(temp, 1, 10, f);
int x = atoi(temp);
sprintf(command, "tail -n %d foo.log", x);
system(command); // GOOD: the user string was converted to an integer and back
}
void test13(FILE *f) {
char str[1000];
char command[1000];
fread(str, 1, 1000, f);
sprintf(command, "echo %s", str);
system(command); // BAD: the user string was printed into the command with the %s specifier
}
void test14(FILE *f) {
char str[1000];
char command[1000];
fread(str, 1, 1000, f);
sprintf(command, "echo %p", str);
system(command); // GOOD: the user string's address was printed into the command with the %p specifier
}
void test15(FILE *f) {
char temp[10];
char command[1000];
fread(temp, 1, 10, f);
int x = atoi(temp);
char temp2[10];
sprintf(temp2, "%d", x);
sprintf(command, "tail -n %s foo.log", temp2);
system(command); // GOOD: the user string was converted to an integer and back
}
// TODO: test for call context sensitivity at concatenation site
// open question: do we want to report certain sources even when they're the start of the string?