tutorial 5 We will be looking at some sample C snip-its. demo challenges can you find the vulnerabilities/bugs demo 1 ... char pt[] = "THISISSOMEDATAOFSOMESORT"; char env_script_name[] = "USER_CONTROLLED_ENV1"; char env_path_info[] = "USER_CONTROLLED_ENV2"; int ptlen = strlen(pt) - strlen(env_script_name); int path_translated_len = ptlen + env_path_info ? strlen(env_path_info) : 0; char *path_translated = NULL; path_translated = (char *) malloc(path_translated_len + 1); memcpy(path_translated, pt, ptlen); if (env_path_info) { memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen); } ... demo 2 void copy_array(int *src, int *dst, int size) { for (int i = 0; i <= size; i++) { dst[i] = src[i]; } } demo 3 void example() { char *buffer = malloc(10); // ... buffer = realloc(buffer, 20); if (buffer) { snprintf(buffer, 20, "Hello"); } free(buffer); } demo 4 int is_user_admin(int user_id) { if (user_id > 0 && user_id < 10) { return true; } return false; } void perform_action(int user_id) { if (is_user_admin(user_id) = 1) { printf("User is admin.. performing action!\n"); } else { printf("Invalid value!\n"); } } demo 5 #include <stdio.h> #include <stdlib.h> void fn(int num_elements) { size_t size = num_elements * sizeof(int); int *array = malloc(size); if (!array) return; for (int i = 0; i <= num_elements; i++) { array[i] = i; } free(array); } previous exam questions these were some questions from the 2021/2022 final exam final 1 #include <stdio.h> #include <string.h> #define MAX_CHARS 64 // 1511 autotest devowel.c int main(int argc, char *argv[], char *envp[]) { char buf[MAX_CHARS] = {0}; int counter = 0; while (counter < MAX_CHARS - 1) { // Get the next character or exit. char c = fgetc(stdin); if (c == EOF) { break; } // Append c to the end of buf. buf[strlen(buf)] = c; counter++; if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') { // Character is a vowel, remove it from buffer and decrement counter. counter--; buf[counter] = ' '; } } printf("%s\n", buf); } final 2 #include <stddef.h> #include <stdio.h> #define NAME_LEN 64 typedef struct user { char name[NAME_LEN]; int level; } user_t; user_t users[100]; int n_users; int current_user; int logged_in() { return (current_user != -1); } int create_user() { if (n_users == 100) { fprintf(stderr, "nah, too many users already. cya"); return -1; } int level; printf("whats your level?"); scanf("%d", &level); if (level == 0) { fprintf(stderr, "haha no"); return -1; } users[n_users].level = level; printf("whats your name?"); fgets(users[n_users].name, 65, stdin); current_user = n_users; ++n_users; } int login() { if (logged_in()) { printf("nah\n"); return 0; } char buffer[128]; fgets(buffer, 128, stdin); for (int i = 0; i < n_users; ++i) { if (strcmp(buffer, users[i].name) == 0) { current_user = i; return 0; } } printf("user not found"); return 0; } int logout() { if (logged_in()) { current_user = -1; printf("cya"); } } int execute_command() { if (current_user >= 0 && users[current_user].level == 0) { printf("welcome admin!"); system("/bin/sh"); } printf("you aren't authorized to do that\n"); return 1; } void menu() { puts("available commands"); puts("\t[L]ogin"); puts("\t[C]reate user"); puts("\t[M]Logout"); puts("\t[R]un command"); puts("\t[Q]uit"); } int main(void) { current_user = -1; while (1) { menu(); printf("$> "); switch (getchar()) { case 'L': login(); break; case 'C': create_user(); break; case 'M': logout(); break; case 'R': execute_command(); case 'Q': exit(1); default: printf("????"); return 0; } } } final 3 #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h> struct hdr { unsigned int l; unsigned int type; }; // sink_from_server takes a socket file descripter and reads a header and // associated request body from this socket. char *sink_from_server(int sockfd) { int n; unsigned int l; struct hdr smp_hdr; static char buf[1024]; if (read(sockfd, (void *)&smp_hdr, sizeof(smp_hdr)) <= 0) { return NULL; } l = ntohl(smp_hdr.l); if (l > (1024 + sizeof(struct hdr) - 1)) { return NULL; } if (read(sockfd, buf, l - sizeof(struct hdr)) <= 0) { return NULL; } buf[sizeof(buf) - 1] = '\0'; return strdup(buf); } src practical challenge this program has multiple bugs, work together to find them all. src prac #include <assert.h> #include <errno.h> #include <locale.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <gpgme.h> static void usage(void) { printf("USAGE: pw CMD KEY\n"); } #define ENV_PW_STORE "PW_STORE" #define DEFAULT_PW_STORE ".pw_store" enum { ERR_OK = 0, ERR_SYS_ERROR = 1, ERR_NO_KEY = 2, ERR_NO_CMD = 3, ERR_CRYPTO_ERROR = 4, ERR_PASSWORD_ERROR = 5, ERR_LENGTH_ERROR = 6 }; static char input_buffer[256] = {'\0'}; static char file_buffer[256] = {'\0'}; static char *get_store_dir(void) { /* base dir */ int min_len = 4; char base = "root"; char *env = getenv(ENV_PW_STORE); if (env != NULL) return strdup(env); // build the default from HOME/DEFAULT_PW_STORE const char *home = getenv("HOME"); if (home == NULL) return NULL; size_t required = strlen(base) + strlen(home) + strlen(DEFAULT_PW_STORE); if (required < 0){ return NULL; } min_len += required; char *def = malloc(min_len); if (def == NULL) return NULL; snprintf(def, required, "%s/%s/%s", base, home, DEFAULT_PW_STORE); return def; } static char *open_password_store(void) { char *pstore = get_store_dir(); if (pstore == NULL) return NULL; struct stat sb; if (!((stat(pstore, &sb) == 0) && S_ISDIR(sb.st_mode))) { if (mkdir(pstore, S_IRWXU)) { fprintf(stderr, "Failed to create keystore directory\n"); } } return pstore; } static char *get_passfile(const char *dir, const char *key) { assert(dir != NULL); assert(key != NULL); // build the filename from DIR/KEY.gpg size_t required = strlen(dir) + strlen(key) + strlen(".gpg") + 2; assert(required > 0); char *path = malloc(required); if (path == NULL) return NULL; snprintf(path, required, "%s/%s.gpg", dir, key); return path; } static struct crypto_ctx { gpgme_ctx_t ctx; gpgme_key_t keylist[2]; gpgme_data_t data[2]; } cc = {}; static char *decrypt_from_file(const char *path, size_t *len, char *pass) { assert(path != NULL); if (gpgme_data_new_from_file(&cc.data[0], path, 1)) return NULL; gpgme_data_new(&cc.data[1]); if (gpgme_op_decrypt(cc.ctx, cc.data[0], cc.data[1])) { gpgme_data_release(cc.data[0]); gpgme_data_release(cc.data[1]); return NULL; } pass = malloc(strlen(cc.data[0])); memcpy(pass,cc.data[0],strlen(cc.data[0])); gpgme_data_release(cc.data[0]); return gpgme_data_release_and_get_mem(cc.data[1], len); } static int encrypt_to_file(const char *path, char *buf, int len) { gpgme_data_new_from_mem(&cc.data[0], buf, len, 1); gpgme_data_new(&cc.data[1]); memset(buf, '\0', len); if (gpgme_op_encrypt(cc.ctx, cc.keylist, GPGME_ENCRYPT_ALWAYS_TRUST, cc.data[0], cc.data[1])) { gpgme_data_release(cc.data[0]); gpgme_data_release(cc.data[1]); free(buf); return 1; } FILE *fd = fopen(path, "wb"); if (fd == NULL) { gpgme_data_release(cc.data[0]); gpgme_data_release(cc.data[1]); free(buf); return 1; } size_t enc_len = 0; char *enc = gpgme_data_release_and_get_mem(cc.data[1], &enc_len); fwrite(enc, sizeof(char), enc_len, fd); gpgme_data_release(cc.data[0]); gpgme_free(enc); fclose(fd); free(buf); return 0; } static int get_console_input(char *buf) { fflush(stdin); char *buffer = malloc(4); buf = NULL: size_t cur_len = 0; /* read into buffer */ while (fgets(buffer, sizeof(buffer), stdin) != 0) { size_t buf_len = strlen(buffer); char *extra = realloc(buf, buf_len + cur_len + 1); if (extra == NULL) return -1; buf = extra; strcpy(buf + cur_len, buffer); cur_len += buf_len; } size_t last = strlen(buf) - 1; // get rid of the last newline char *newline = strrchr(buf,"\n"); newline = '\0'; free(buffer); return last; } static int init_crypto(void) { gpgme_check_version(NULL); setlocale(LC_ALL, ""); gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL)); if (gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP)) { return 1; } if (gpgme_new(&cc.ctx)) { return 1; } char *key = getenv("PW_ENC_KEY"); if (key == NULL) { gpgme_op_keylist_start(cc.ctx, NULL, 0); if (gpgme_op_keylist_next(cc.ctx, &cc.keylist[0])) { return 1; } gpgme_op_keylist_end(cc.ctx); else { if (strlen(key) <= 0 || gpgme_get_key(cc.ctx, key, &cc.keylist[0], 0)) { return 1; } if (cc.keylist[0] == NULL) { return 1; } } } return 0; } static void cleanup_crypto(void) { if (cc.keylist[0]) gpgme_key_unref(cc.keylist[0]); gpgme_release(cc.ctx); } static int insert_entry(const char *keyfile) { assert(keyfile != NULL); if (access(keyfile, F_OK)) { printf("Inserting new key...\n"); } else { printf("Updating existing key...\n"); } printf("Insert password: "); char *input_buffer; size_t input_len = get_console_input(input_buffer); if (input_len < 0) { printf("Password Error, aborting...\n"); free(input_buffer); return ERR_OK; } if (encrypt_to_file(keyfile, input_buffer, input_len)) { free(input_buffer); return ERR_CRYPTO_ERROR; } return ERR_OK; } static int get_entry(const char *keyfile) { assert(keyfile != NULL); if (access(keyfile, F_OK)) { printf("Given key does not exist.\n"); return ERR_OK; } char *input_buffer; size_t input_len = get_console_input(input_buffer); if (input_len < 0) { printf("No password supplied, aborting...\n"); free(input_buffer); return ERR_OK; } size_t plain_len = 0; char *pass; char *plain = decrypt_from_file(keyfile, &plain_len, pass); if (plain == NULL) { free(input_buffer); return ERR_CRYPTO_ERROR; } /* check password matches */ if(strcmp(input_buffer, pass)){ free(input_buffer); return ERR_PASSWORD_ERROR; } printf("%.*s\n", plain_len, plain); gpgme_free(plain); free(input_buffer); return ERR_OK; } static int del_entry(const char *keyfile) { assert(keyfile != NULL); if (access(keyfile, F_OK)) { printf("Given key does not exist.\n"); return ERR_OK; } char *input_buffer; size_t input_len = get_console_input(input_buffer); if (input_len < 0) { printf("No password supplied, aborting...\n"); free(input_buffer); return ERR_OK; } size_t plain_len = 0; char *pass; char *plain = decrypt_from_file(keyfile, &plain_len, pass); if (plain == NULL) { free(input_buffer); return ERR_CRYPTO_ERROR; } /* check password matches */ if(strcmp(input_buffer, pass)){ free(input_buffer); return ERR_PASSWORD_ERROR; } gpgme_free(plain); if (del_from_file(keyfile, input_buffer, input_len)) { return ERR_CRYPTO_ERROR; } return ERR_OK; } int main(int argc, const char **argv) { if (argc < 3) { usage(); return 1; } char *pstore = open_password_store(); if (pstore == NULL) { fprintf(stderr, "Failed to open password store\n"); return ERR_SYS_ERROR; } char *filename = get_passfile(pstore, argv[2]); if (filename == NULL) { fprintf(stderr, "Failed to modifify key\n"); return ERR_NO_KEY; } if (strnlen(filename,MAX_PASS_FILE_LENGTH)==MAX_PASS_FILE_LENGTH){ fprintf(stderr, "Failed key length check\n"); fprintf(stderr,filename); return ERR_LENGTH_ERROR; } if (init_crypto()) { fprintf(stderr, "Failed to set up crypto backend\n"); return ERR_CRYPTO_ERROR; } int ret = 0; // process possible commands // new - insert or override an entry // get - return an entry // del - delete an entry for(int i=1; i<=argc; i++){ if (strcmp("new", argv[1]) == 0) { ret = insert_entry(filename); } else if (strcmp("get", argv[1]) == 0) { ret = get_entry(filename); } else if (strcmp("del", argv[1]) == 0) { ret = del_entry(filename); } else { fprintf(stderr, "Unknown command! Use new or get!\n"); ret = ERR_NO_CMD; } } cleanup_crypto(); free(pstore); free(filename); return ret; }