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