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;
}