├── ok.c ├── ok2.c ├── strcpy.c ├── strcpy2.c ├── tricky.c ├── fdleak.c ├── Makefile ├── bof.c ├── confuse.cpp ├── use_after_free.c ├── LICENSE ├── double_free.cpp ├── andreas.cpp ├── encrypt.c ├── hardcoded_password.c └── README.md /ok.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main(int argc, char *argv[]) { 3 | // BUG: this has no bugs 4 | if (argc > 1) { 5 | (void)printf("%s\n", argv[1]); 6 | } 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /ok2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) { 4 | char fmt[] = "%s\n"; 5 | if (argc > 1) { 6 | (void)printf(fmt, argv[1]); 7 | } 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /strcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) { 5 | char buf[16] = ""; 6 | char *in = argv[1], *out = buf; 7 | 8 | if (argc > 1) { 9 | strcpy(out, in); 10 | } 11 | (void)printf(buf); 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /strcpy2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) { 4 | char buf[16] = ""; 5 | char *in = argv[1], *out = buf; 6 | 7 | if (argc > 1) { 8 | while (*in != 0) { 9 | *out++ = *in++; 10 | } 11 | *out = 0; 12 | } 13 | (void)printf(buf); 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tricky.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | char buf[128]; 8 | int user_len = atoi(argv[2]); 9 | int buf_size = sizeof(buf); 10 | 11 | if (user_len > buf_size) { 12 | printf("Attempted Overflow Detected\n"); 13 | return -1; 14 | } 15 | 16 | memcpy(buf, argv[1], user_len); 17 | if (buf[0] != 0) 18 | return 0; 19 | else 20 | return -1; 21 | } 22 | -------------------------------------------------------------------------------- /fdleak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | int fd; 10 | 11 | if ((fd = open("/var/log/app.log", O_RDWR | O_APPEND | O_CREAT, 0600)) < 0) { 12 | close(fd); 13 | return -1; 14 | } 15 | 16 | if (geteuid() != getuid()) { 17 | setegid(getgid()); 18 | seteuid(getuid()); 19 | } 20 | 21 | write(1, "Hello\n", 6); 22 | 23 | system("/bin/bash"); 24 | 25 | close(fd); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | C=ok ok2 bof fdleak strcpy strcpy2 tricky encrypt use_after_free hardcoded_password 2 | CPP=confuse andreas double_free 3 | # unresolv 4 | 5 | all: c cpp 6 | 7 | c: $(C) 8 | 9 | cpp: $(CPP) 10 | 11 | confuse: confuse.cpp 12 | $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) 13 | 14 | andreas: andreas.cpp 15 | $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) 16 | 17 | double_free: double_free.cpp 18 | $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) 19 | 20 | encrypt: encrypt.c 21 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -lcrypto 22 | 23 | %: %.c 24 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) 25 | 26 | clean: 27 | rm -f $(C) $(CPP) 28 | 29 | cleanall: clean 30 | -~/src_artikel/clean_all.sh all 31 | -------------------------------------------------------------------------------- /bof.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | FILE *f; 10 | char line[64], *data; 11 | int size; 12 | 13 | if (argc < 3) 14 | return -1; 15 | 16 | if ((f = fopen(argv[1], "r")) == NULL) 17 | return -1; 18 | 19 | if (fgets(line, sizeof(line), f) == NULL) 20 | return -1; 21 | 22 | size = atoi(line); 23 | 24 | if ((data = malloc(size)) == NULL) 25 | return -1; 26 | 27 | fread(data, 1, 1024, f); 28 | data[1023] = 0; 29 | 30 | printf("Data: %s\n", data); 31 | 32 | fclose(f); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /confuse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | class Base {}; 7 | 8 | class Exec : public Base { 9 | public: 10 | virtual void exec(const char *prg) { system(prg); } 11 | }; 12 | 13 | class Greeter : public Base { 14 | public: 15 | virtual void sayHi(const char *str) { std::cout << str << std::endl; } 16 | }; 17 | 18 | int main(int argc, char *argv[]) { 19 | Base *b1 = new Greeter(); 20 | Base *b2 = new Exec(); 21 | Greeter *g; 22 | if (argc != 3) { 23 | printf("Syntax: %s 1|2 greeting\n", argv[0]); 24 | exit(-1); 25 | } 26 | if (argv[1][0] == '1') 27 | g = static_cast(b1); 28 | else if (argv[1][0] == '2') 29 | g = static_cast(b2); 30 | else { 31 | printf("wrong mode\n"); 32 | exit(-1); 33 | } 34 | g->sayHi(argv[2]); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /use_after_free.c: -------------------------------------------------------------------------------- 1 | 2 | /* This software was developed at the National Institute of Standards and 3 | * Technology by employees of the Federal Government in the course of their 4 | * official duties. Pursuant to title 17 Section 105 of the United States 5 | * Code this software is not subject to copyright protection and is in the 6 | * public domain. NIST assumes no responsibility whatsoever for its use by 7 | * other parties, and makes no guarantees, expressed or implied, about its 8 | * quality, reliability, or any other characteristic. 9 | 10 | * We would appreciate acknowledgement if the software is used. 11 | * The SAMATE project website is: http://samate.nist.gov 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | typedef struct str_t str; 18 | struct str_t { 19 | union { 20 | int a; 21 | char *b; 22 | } foo; 23 | }; 24 | int main(int argc, char *argv[]) { 25 | str container; 26 | 27 | container.foo.a = 0; 28 | if ((container.foo.b = (char *)malloc(256 * sizeof(char))) != NULL) { 29 | strcpy(container.foo.b, "Falut!"); 30 | container.foo.b[0] = 'S'; 31 | printf("%s\n", container.foo.b); 32 | free(container.foo.b); 33 | if (container.foo.b) 34 | container.foo.b[0] = 'S'; 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, van Hauser 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /double_free.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace DF_01 { 8 | 9 | class FoobarClass { 10 | public: 11 | FoobarClass(const char *data) { 12 | if (data) { 13 | this->data = new char[strlen(data) + 1]; 14 | strcpy(this->data, data); 15 | } else { 16 | this->data = new char[1]; 17 | *(this->data) = '\0'; 18 | } 19 | } 20 | 21 | ~FoobarClass() { delete[] data; } 22 | 23 | void printData() { printf("%s\n", data); } 24 | 25 | FoobarClass &operator=(const FoobarClass &otherxClassObject) { 26 | if (&otherxClassObject != this) { 27 | this->data = new char[strlen(otherxClassObject.data) + 1]; 28 | strcpy(this->data, otherxClassObject.data); 29 | } 30 | return *this; 31 | } 32 | 33 | private: 34 | char *data; 35 | }; 36 | 37 | void otherx() { 38 | FoobarClass otherxClassObject("One"); 39 | 40 | /* FLAW: There is no copy constructor in the class - this will cause a double 41 | * free in the destructor */ 42 | FoobarClass otherxClassObjectCopy(otherxClassObject); 43 | 44 | otherxClassObjectCopy.printData(); 45 | } 46 | 47 | } // namespace DF_01 48 | 49 | /* Below is the main(). It is only used when building this testcase on 50 | * its own for testing or for building a binary to use in testing binary 51 | * analysis tools. It is not used when compiling all the testcases as one 52 | * application, which is how source code analysis tools are tested. 53 | */ 54 | 55 | using namespace DF_01; /* so that we can use othery 56 | and otherx easily */ 57 | 58 | int main(int argc, char *argv[]) { 59 | /* seed randomness */ 60 | srand((unsigned)time(NULL)); 61 | printf("Calling otherx()..."); 62 | otherx(); 63 | printf("Finished otherx()"); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /andreas.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct WebRequestForm { 5 | std::string username; 6 | std::string password; 7 | double amount_of_money; 8 | }; 9 | bool IsValidString(const std::string &string) { return !string.empty(); } 10 | bool IsMoneyInRange(double amount_of_money) { return amount_of_money > 0.0; } 11 | bool LoginUser(const std::string &username, const std::string &password) { return true; } 12 | void LogoutUser(const std::string &username) {} 13 | void ReponseToClient(const std::string &string) { std::cout << "Response to client: " << string << std::endl; } 14 | void LoggingReponseToClient(const std::string &string) { std::cout << "Logging to client: " << string << std::endl; } 15 | void WebRequest(const WebRequestForm &request) { 16 | static std::string username = ""; 17 | static std::string password = ""; 18 | static double amount_of_money = 0.0; 19 | bool is_input_valid = IsValidString(request.username) && IsValidString(request.password) && IsMoneyInRange(request.amount_of_money); 20 | if (is_input_valid) { 21 | username = request.username; 22 | password = request.password; 23 | amount_of_money = request.amount_of_money; 24 | } else 25 | ReponseToClient("You must provide a valid username and a valid password and a valid " 26 | "amount as input!"); 27 | if (is_input_valid && LoginUser(username, password)) { 28 | // here we do stuff ... 29 | LogoutUser(username); 30 | ReponseToClient("Successfully did weird stuff with the money"); 31 | } 32 | LoggingReponseToClient("Called the service with username = " + username + " and password = " + password); 33 | } 34 | int main(int argc, char *argv[]) { 35 | std::cout << "admin user does something" << std::endl; 36 | WebRequestForm admin_request = {"admin", "TopSecret", 5000.0}; 37 | WebRequest(admin_request); 38 | std::cout << "\n\n--- some time is passing ---\n\n" << std::endl; 39 | std::cout << "attacker does something" << std::endl; 40 | double d = strtod(argv[3], NULL); 41 | std::string arg1(argv[1]); 42 | std::string arg2(argv[2]); 43 | WebRequestForm attacker_request = {arg1, arg2, d}; 44 | WebRequest(attacker_request); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /encrypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext) { 7 | EVP_CIPHER_CTX *ctx; 8 | int len; 9 | int ciphertext_len; 10 | 11 | /* Create and initialise the context */ 12 | if (!(ctx = EVP_CIPHER_CTX_new())) 13 | exit(-1); 14 | 15 | /* 16 | * Initialise the encryption operation. IMPORTANT - ensure you use a key 17 | * and IV size appropriate for your cipher 18 | * In this example we are using 256 bit AES (i.e. a 256 bit key). The 19 | * IV size for *most* modes is the same as the block size. For AES this 20 | * is 128 bits 21 | */ 22 | if (1 != EVP_EncryptInit_ex(ctx, EVP_des_ecb(), NULL, key, iv)) 23 | exit(-1); 24 | 25 | /* 26 | * Provide the message to be encrypted, and obtain the encrypted output. 27 | * EVP_EncryptUpdate can be called multiple times if necessary 28 | */ 29 | if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) 30 | exit(-1); 31 | ciphertext_len = len; 32 | 33 | /* 34 | * Finalise the encryption. Further ciphertext bytes may be written at 35 | * this stage. 36 | */ 37 | if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) 38 | exit(-1); 39 | ciphertext_len += len; 40 | 41 | /* Clean up */ 42 | EVP_CIPHER_CTX_free(ctx); 43 | 44 | return ciphertext_len; 45 | } 46 | 47 | int main(int argc, char *argv[]) { 48 | /* 49 | * Set up the key and iv. Do I need to say to not hard code these in a 50 | * real application? :-) 51 | */ 52 | if (argc < 3) 53 | exit(-1); 54 | 55 | /* A 256 bit key */ 56 | unsigned char *key = argv[1]; 57 | 58 | /* A 128 bit IV */ 59 | unsigned char *iv = (unsigned char *)"0123456789012345"; 60 | 61 | /* Message to be encrypted */ 62 | unsigned char *plaintext = argv[2]; 63 | 64 | /* 65 | * Buffer for ciphertext. Ensure the buffer is long enough for the 66 | * ciphertext which may be longer than the plaintext, depending on the 67 | * algorithm and mode. 68 | */ 69 | unsigned char ciphertext[1024]; 70 | 71 | int ciphertext_len; 72 | 73 | /* Encrypt the plaintext */ 74 | ciphertext_len = encrypt(plaintext, strlen((char *)plaintext), key, iv, ciphertext); 75 | 76 | /* Do something useful with the ciphertext here */ 77 | BIO_dump_fp(stdout, (const char *)ciphertext, ciphertext_len); 78 | 79 | return ciphertext_len; 80 | } 81 | -------------------------------------------------------------------------------- /hardcoded_password.c: -------------------------------------------------------------------------------- 1 | 2 | /* This software was developed at the National Institute of Standards and 3 | * Technology by employees of the Federal Government in the course of their 4 | * official duties. Pursuant to title 17 Section 105 of the United States 5 | * Code this software is not subject to copyright protection and is in the 6 | * public domain. NIST assumes no responsibility whatsoever for its use by 7 | * other parties, and makes no guarantees, expressed or implied, about its 8 | * quality, reliability, or any other characteristic. 9 | 10 | * We would appreciate acknowledgement if the software is used. 11 | * The SAMATE project website is: http://samate.nist.gov 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | static bool loggedin = false; 25 | 26 | /* Replacement to memset() that cannot be optimized out */ 27 | char *my_memset_s(void *s, int c, size_t n) { 28 | volatile uint8_t *p = (uint8_t *)s; 29 | 30 | while (n--) 31 | *p++ = c; 32 | 33 | return s; 34 | } 35 | 36 | /* Reads a password from a terminal - replaces obsolete getpass() function */ 37 | char *getpass_r(const char *prompt) { 38 | struct termios oflags, nflags; 39 | char password[64] = {'\0'}; 40 | char *ret = NULL; 41 | 42 | /* Disabling echo */ 43 | if (tcgetattr(fileno(stdin), &oflags)) 44 | return NULL; 45 | 46 | nflags = oflags; 47 | nflags.c_lflag &= ~ECHO; 48 | nflags.c_lflag |= ECHONL; 49 | 50 | if (tcsetattr(fileno(stdin), TCSAFLUSH, &nflags)) 51 | return NULL; 52 | 53 | /* Read the password */ 54 | fprintf(stderr, "%s", prompt); 55 | ret = fgets(password, sizeof(password), stdin); 56 | 57 | /* Restore echo */ 58 | if (tcsetattr(fileno(stdin), TCSAFLUSH, &oflags)) { 59 | my_memset_s(password, 0, sizeof(password)); 60 | return NULL; 61 | } 62 | 63 | if (ret) { 64 | strtok(password, "\n"); 65 | ret = strdup(password); 66 | my_memset_s(password, 0, sizeof(password)); 67 | } 68 | 69 | return ret; 70 | } 71 | 72 | int main(int argc, char *argv[]) { 73 | int attempts = 0; 74 | char *password; 75 | 76 | while (attempts++ < 3 && !loggedin) { 77 | password = getpass_r("Password: "); 78 | 79 | if (password != NULL) { 80 | if (strcmp(password, "0xDEADBEEF") == 0) { 81 | loggedin = true; 82 | printf("Logged in\n"); 83 | } else 84 | printf("Wrong password\n"); 85 | 86 | my_memset_s(password, 0, strlen(password)); 87 | free(password); 88 | } 89 | } 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Little test programs to test static code analysis software 2 | 3 | ## Introduction 4 | 5 | These small code pieces were used by me to analyse the detective abilities 6 | of a few SCA solutions to identify security vulnerabilities in source code. 7 | This was not a comprehensive test, but rather a quick analysis. 8 | 9 | Initially I wanted to create sophisticated bugs and check if they can find 10 | them but found out that even easy test cases can be too hard ;-) 11 | 12 | The result of my testing can be read in the german article here: 13 | [https://www.heise.de/hintergrund/Pruefstand-fuer-Testwerkzeuge-Codeanalyse-im-Praxiseinsatz-4679430.html?seite=all](https://www.heise.de/hintergrund/Pruefstand-fuer-Testwerkzeuge-Codeanalyse-im-Praxiseinsatz-4679430.html?seite=all) 14 | 15 | ## The test source code 16 | 17 | Alphabetically: 18 | 19 | - andreas.cpp - hard coded credentials 20 | - bof.c - buffer overflow based on untrusted length information 21 | - confuse.cpp - type confusion vulnerability 22 | - double_free.cpp - copy of a CWE415 NIST test source code 23 | - encrypt.c - weak encryption mode 24 | - fdleak.c - leaking a file descriptor to a child process 25 | - hardcoded_password.c - hard coded credentials 26 | - ok.c - nothing wrong here, for testing false positives and standard "warnings" 27 | - ok2.c - nothing wrong here, for testing false positives and standard "warnings" 28 | - strcpy.c - standard strcpy buffer overflow (and format string vulnerability which was not the test target) 29 | - strcpy2.c - standard string copy via while-loop (and format string vulnerability which was not the test target) 30 | - tricky.c - buffer overflow based on untrusted length information 31 | - use_after_free.c - use after free vulnerability 32 | 33 | ## Results 34 | 35 | All vendors reviewed the setup and verified that the results were real and not 36 | based on a misconfiguration. 37 | And yes, most of them were not happy with the results. 38 | 39 | | CODE/SOFTWARE |Perforce Klocwork 19.01|Parasoft C/C++test 10.4.2|Mathworks Bugfinder R2019a (2)|Viva 64 PVS Studio|CodeQL Oct 2022|joern Oct 2022|semgrep Oct 2022|cppcheck 2.9|llvm 14 clang-analyzer|ChatGPT 4.0| 40 | |:------------------:|:------:|:-------:|:--------|:-------------:|:------:|:------:|:------:|:------------:|:------------:|:-----:| 41 | | andreas | yes | no | no | no | no | no | no | no | no | yes | 42 | | bof | yes | no | yes | no | yes | no | no | no | yes(3) | yes | 43 | | confuse | no | no | no | no | no | no | no | no | no | yes | 44 | | double_free | yes | no | no | yes | no | no | no | no | yes | yes | 45 | | encrypt | yes | no | yes | no | yes(1) | no | no | no | no | yes | 46 | | fdleak | no | no | no | no | no | no | no | no | no | partial | 47 | | hardcoded_password | yes | no | no | no | no | no | no | no | no | yes | 48 | | strcpy | no | yes | no | no | yes(1) | no | no | no | no | yes | 49 | | strcpy2 | no | no | no | no | no | no | no | no | no | yes | 50 | | tricky |partial | no | no | yes | no | no | no | no | no | yes | 51 | | use_after_free | yes | yes | yes | yes | no | no | yes | yes | yes | yes | 52 | 53 | (1) Not in cpp-lgtm.qls but from cpp/ql/src/Security 54 | 55 | (2) For Mathworks Bugfinder, there is also the Code Prover product which checks 56 | for robustness which includes buffer overruns. This might detect the buffer 57 | overflow bugs but is a seperate product that is not a SCA comparable to the 58 | others. 59 | 60 | (3) with alpha.security checkers enabled 61 | --------------------------------------------------------------------------------