Level 06

About

Threaded stack overwrite fun :) Just another way to clutch at straws ;)

This level introduces the danger of inappropriate (or complete lack of) sharing/locking data across multiple connections/threads.

OptionSetting
Vulnerability TypeStack
Position Independent ExecutableYes
Read only relocationsNo
Non-Executable stackYes
Non-Executable heapYes
Address Space Layout RandomisationYes
Source FortificationYes

Source code

#define THREADED #include "../common/common.c"  // Taken some code from gnu tls documentation, // This example is a very simple echo server which supports X.509 // authentication, using the RSA ciphersuites. // This file has the leading comment of... /* This example code is // placed in the public domain. */ // so there :>  #include <gcrypt.h> #include <gnutls/gnutls.h>  #include <libHX/init.h> #include <libHX/defs.h> #include <libHX/map.h> #include <libHX/string.h>  #define KEYFILE "/opt/fusion/ssl/key.pem" #define CERTFILE "/opt/fusion/ssl/cert.pem" #define CAFILE "/opt/fusion/ssl/ca.pem" #define CRLFILE "/opt/fusion/ssl/crl.pem"  gnutls_certificate_credentials_t x509_cred; gnutls_priority_t priority_cache; static gnutls_session_t initialize_tls_session (void) { gnutls_session_t session; gnutls_init (&session, GNUTLS_SERVER); gnutls_priority_set (session, priority_cache); gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); /*  *request client certificate if any.  */ gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); return session; } struct HXmap *dict; struct data { void *data; size_t length; }; struct data *gather_data(gnutls_session_t session, char *key, size_t length) { unsigned char buffer[length]; int offset, ret; struct data *data; for(offset = 0; offset < length; ) { ret = gnutls_record_recv(session, buffer + offset, (length - offset) > 65535 ? 65535 : (length - offset)); if(ret <= 0) return NULL; offset += ret; } data = malloc(sizeof(struct data)); if(! data) return NULL; data->data = HX_memdup(buffer, length); if(!data->data) { free(data); return NULL; } data->length = length; //printf("gather data: returning %08x, data->length = %d\n", data,  // data->length);  //fflush(stdout);  return data; } #define NOKEY "// No key was specified\n" #define NOTFOUND "// Key was not found\n" #define KEYFOUND "// Key exists\n" #define NOMEM "// Not enough memory to allocate\n" #define UPDATEOK "// Updated successfully\n"  int update_data(gnutls_session_t session, char *key, size_t length) { struct data *data; size_t offset; int ret; data = HXmap_get(dict, key); if(! data) { gnutls_record_send(session, NOTFOUND, strlen(NOTFOUND)); return -1; } if(length > data->length) { void *tmp; tmp = realloc(data->data, length); if(! tmp) { gnutls_record_send(session, NOMEM, strlen(NOMEM)); return -1; } data->data = tmp; } for(offset = 0; offset < length; ) { ret = gnutls_record_recv(session, data->data + offset, (length - offset) > 65535 ? 65535 : (length - offset)); if(ret <= 0) return 0; offset += ret; } gnutls_record_send(session, UPDATEOK, strlen(UPDATEOK)); data->length = length; return 0; } int send_data(gnutls_session_t session, char *key, struct data *data) { int offset, ret; int to_send; char *msg; asprintf(&msg, "// Sending %d bytes\n", data->length); gnutls_record_send(session, msg, strlen(msg)); free(msg); for(offset = 0; offset < data->length; ) { int tosend; tosend = (data->length - offset) > 65535 ? 65535 : (data->length - offset); ret = gnutls_record_send(session, data->data + offset, tosend); if(ret <= 0) return -1; offset += ret; } return 0; } void *free_data(void *ptr) { struct data *data; data = (struct data *)(ptr); //printf("in free data, got %08x\n", (unsigned int)data);  if(data) { if(data->data) { free(data->data); } free(data); } } void new_dict() { struct HXmap_ops mops; if(dict) HXmap_free(dict); memset(&mops, 0, sizeof(mops)); mops.d_free = free_data; dict = HXmap_init5(HXMAPT_HASH, HXMAP_SKEY | HXMAP_CKEY, &mops, 0, sizeof(struct data)); } void *keyval_thread(void *arg) { int fd = (int)arg; int ret; struct data *data; int cont; gnutls_session_t session; session = initialize_tls_session (); gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) fd); ret = gnutls_handshake (session); if (ret < 0) { char *msg; close (fd); gnutls_deinit (session); msg = NULL; asprintf(&msg, "*** Handshake has failed (%s)\n\n", gnutls_strerror(ret)); write(fd, msg, strlen(msg)); close(fd); free(msg); } #define BANNER "// Welcome to KeyValDaemon. Type 'h' for help information\n"  gnutls_record_send(session, BANNER, strlen(BANNER)); cont = 1; while(cont) { char cmdbuf[512], *p; char *args[6], *msg; int argcnt, i; memset(cmdbuf, 0, sizeof(cmdbuf)); ret = gnutls_record_recv(session, cmdbuf, sizeof(cmdbuf)); if(ret <= 0) break; p = strchr(cmdbuf, '\r'); if(p) *p = 0; p = strchr(cmdbuf, '\n'); if(p) *p = 0; memset(args, 0, sizeof(args)); argcnt = HX_split5(cmdbuf, " ", 6, args); #if 0  for(i = 0; i < argcnt; i++) {  asprintf(&msg, "args[%d] = \"%s\"\n", i, args[i]);  gnutls_record_send(session, msg, strlen(msg));  free(msg);  } #endif  switch(args[0][0]) { case 'h': #define HELP \ "// f <key> - find entry and see if it exists\n" \ "// s <key> <bytes> - store an entry with key and <bytes> lenght of data\n" \ "// g <key> - read data from key\n" \ "// d <key> - delete key/data\n" \ "// X - delete all data and restart\n" // XXX, loop over HXmap and display data?  gnutls_record_send(session, HELP, strlen(HELP)); break; case 'd': if(! args[1]) { gnutls_record_send(session, NOKEY, strlen(NOKEY)); } else { void *data; data = HXmap_del(dict, args[1]); if(data) { gnutls_record_send(session, KEYFOUND, strlen(KEYFOUND)); } else { gnutls_record_send(session, NOTFOUND, strlen(NOTFOUND)); } } break; case 's': // set  data = gather_data(session, args[1], atoi(args[2])); if(data != NULL) { #define NEWKEY "// New key added!\n"  printf("args[1] = %08x/%s, data = %08x\n", args[1], args[1], data); HXmap_add(dict, args[1], data); gnutls_record_send(session, NEWKEY, strlen(NEWKEY)); } else { #define ADDERROR "// Unable to add new entry, problem getting data\n"  gnutls_record_send(session, ADDERROR, strlen(ADDERROR)); } break; case 'u': // update  update_data(session, args[1], atoi(args[2])); break; case 'f': // find  if(! args[1]) { gnutls_record_send(session, NOKEY, strlen(NOKEY)); } else { if(HXmap_find(dict, args[1]) == NULL) { gnutls_record_send(session, NOTFOUND, strlen(NOTFOUND)); } else { gnutls_record_send(session, KEYFOUND, strlen(KEYFOUND)); } } break; case 'g': // get  if(! args[1]) { gnutls_record_send(session, NOKEY, strlen(NOKEY)); } else { if((data = HXmap_get(dict, args[1])) == NULL) { gnutls_record_send(session, NOTFOUND, strlen(NOTFOUND)); } else { send_data(session, args[1], data); } } break; case 'e': cont = 0; break; case 'X': new_dict(); #define NEWDICT "// New dictionary installed\n"  gnutls_record_send(session, NEWDICT, strlen(NEWDICT)); break; default: #define UC "// Unknown Command, please see 'h' for help information\n"  gnutls_record_send(session, UC, strlen(UC)); break; } } #define GB "// Good bye!\n"  gnutls_record_send(session, GB, strlen(GB)); gnutls_bye(session, GNUTLS_SHUT_WR); close(fd); gnutls_deinit(session); return NULL; } #define DH_BITS 512  static gnutls_dh_params_t dh_params; static int generate_dh_params (void) { /*  * Generate Diffie-Hellman parameters - for use with DHE  * kx algorithms. When short bit length is used, it might  * be wise to regenerate parameters.  *  */ gnutls_dh_params_init (&dh_params); gnutls_dh_params_generate2 (dh_params, DH_BITS); return 0; } GCRY_THREAD_OPTION_PTHREAD_IMPL; int main(int argc, char **argv) { int fd, i; HX_init(); gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); gnutls_global_init(); gnutls_certificate_allocate_credentials (&x509_cred); gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE, GNUTLS_X509_FMT_PEM); gnutls_certificate_set_x509_crl_file (x509_cred, CRLFILE, GNUTLS_X509_FMT_PEM); gnutls_certificate_set_x509_key_file (x509_cred, CERTFILE, KEYFILE, GNUTLS_X509_FMT_PEM); generate_dh_params (); gnutls_priority_init (&priority_cache, "NORMAL", NULL); gnutls_certificate_set_dh_params (x509_cred, dh_params); new_dict(); signal(SIGPIPE, SIG_IGN); background_process(NAME, UID, GID); serve_forever_threaded(PORT, keyval_thread); }