About

This process runs chrooted in it's home directory, can you break out of it?

As a side note, it's always interesting to watch how things fail, and what you can do with that :)

Vulnerability Type Stack
Position Independent Executable Yes
Read only relocations No
Non-Executable stack Yes
Non-Executable heap Yes
Address Space Layout Randomisation Yes
Source Fortification No

Source code

  1#include "../common/common.c"  
  2#include <tcr/threaded_cr.h>
  3#include "crypto_box.h"
  4
  5/*
  6 * Design:
  7 *
  8 * One stdin-reader
  9 * Many decryption processes
 10 * Many command parsers / executors
 11 * Many encryptor processes
 12 * One stdout-writer
 13 */
 14
 15
 16struct buffer {
 17  unsigned char *data;
 18  size_t len;
 19  CIRCLEQ_ENTRY(buffer) ll;
 20};
 21
 22#define free_buf(x) \
 23  do { \
 24    if((x)) { \
 25      if((x)->data) free(x->data); \
 26      memset((x), 0xdf, sizeof(struct buffer)); \
 27      free((x)); \
 28      (x) = NULL; \
 29    } \
 30  } while(0)
 31
 32unsigned char public_key[crypto_box_PUBLICKEYBYTES];
 33unsigned char secret_key[crypto_box_SECRETKEYBYTES];
 34
 35static struct tc_waitq decryption_waitq;
 36static CIRCLEQ_HEAD(, buffer) decryption_queue;
 37static spinlock_t decryption_sl;
 38
 39static struct tc_waitq process_waitq;
 40static CIRCLEQ_HEAD(, buffer) process_queue;
 41static spinlock_t process_sl;
 42
 43static struct tc_waitq encryption_waitq;
 44static CIRCLEQ_HEAD(, buffer) encryption_queue;
 45static spinlock_t encryption_sl;
 46
 47static struct tc_waitq output_waitq;
 48static CIRCLEQ_HEAD(, buffer) output_queue;
 49static spinlock_t output_sl;
 50
 51int devurandom_fd = -1;
 52
 53
 54ssize_t tc_read(struct tc_fd *t, void *buf, ssize_t buflen)
 55{
 56  ssize_t ret, len = buflen;
 57  enum tc_rv rv;
 58
 59  while (1) {
 60    ret = read(tc_fd(t), buf, len);
 61    if (ret > 0) {
 62      buf += ret;
 63      len -= ret;
 64      if (len == 0)
 65        return buflen;
 66    } else if (ret < 0) {
 67      if (errno != EAGAIN)
 68        return buflen - len ? buflen - len : ret;
 69    } else /* ret == 0 */
 70      return buflen - len;
 71
 72    rv = tc_wait_fd_prio(EPOLLIN, t);
 73    if (rv != RV_OK) {
 74      errno = EINTR;
 75      return -1;
 76    }
 77  }
 78}
 79
 80ssize_t tc_write(struct tc_fd *t, void *buf, ssize_t buflen)
 81{
 82  ssize_t ret, len = buflen;
 83  enum tc_rv rv;
 84
 85  while (1) {
 86    ret = write(tc_fd(t), buf, len);
 87    if (ret > 0) {
 88      buf += ret;
 89      len -= ret;
 90      if (len == 0)
 91        return buflen;
 92    } else if (ret < 0) {
 93      if (errno != EAGAIN)
 94        return buflen - len ? buflen - len : ret;
 95    } else /* ret == 0 */
 96      return buflen - len;
 97
 98    rv = tc_wait_fd_prio(EPOLLIN, t);
 99    if (rv != RV_OK) {
100      errno = EINTR;
101      return -1;
102    }
103  }
104}
105
106ssize_t randombytes(void *buf, ssize_t len)
107{
108  struct tc_fd *tcfd;
109  ssize_t ret;
110  ssize_t max;
111
112  if(devurandom_fd == -1) errx(EXIT_FAILURE, "devurandom_fd has not been setup");
113  max = len;
114  while(len) {
115    ret = read(devurandom_fd, buf, len);
116    if(ret == -1) {
117      if(errno == EAGAIN || errno == EINTR) continue;
118      err(EXIT_FAILURE, "randombytes failed");
119    }
120
121    buf += ret;
122    len -= ret;
123  }
124
125  return max;
126}
127
128static void read_stdin(void *arg)
129{
130  struct buffer *buf;
131
132  struct tc_fd *tcfd = (struct tc_fd *)(arg);
133
134  ssize_t rr;
135  size_t ltr;
136  
137  // printf("in read_stdin\n");
138
139  while(1) {
140    /* no clean ups since program will exit once this function returns */
141
142    if(tc_read(tcfd, &ltr, sizeof(ltr)) != sizeof(ltr)) {
143      fprintf(stderr, "unable to tc_read %d bytes\n", sizeof(ltr));
144      break;
145    }
146
147    buf = calloc(sizeof(struct buffer), 1);
148    if(! buf) {
149      fprintf(stderr, "unable to calloc %d bytes\n", sizeof(struct buffer));
150      break;
151    }
152
153    buf->data = malloc(ltr);
154    if(! buf->data) {
155      fprintf(stderr, "unable to malloc %d bytes\n", ltr);
156      break;
157    }
158
159    buf->len = ltr;
160
161    if(tc_read(tcfd, buf->data, ltr) != ltr) {
162      fprintf(stderr, "Unable to tc_read %d bytes\n", ltr);
163      break;
164    }
165
166    /* Insert the buffer into the queue */
167
168    spin_lock(&decryption_sl);
169    CIRCLEQ_INSERT_TAIL(&decryption_queue, buf, ll);
170    spin_unlock(&decryption_sl);
171
172    /* And wake up one of them */
173
174    tc_waitq_wakeup_one(&decryption_waitq);
175  }
176
177
178}
179
180unsigned char peer_remote_pk[crypto_box_PUBLICKEYBYTES];
181
182static void decryption_worker(void *arg)
183{
184  // XXX, exit_all
185  struct buffer *input_buffer;
186  struct buffer *output_buffer;
187
188  size_t tl;
189  unsigned char *tmpout;
190
191  while(1) {
192    input_buffer = output_buffer = NULL;
193    tmpout = NULL;
194
195    // printf("decryption_worker: about to wait\n");
196
197    // XXX, decryption_queue should probably be locked in this case :/
198
199    if(tc_waitq_wait_event(&decryption_waitq, ! CIRCLEQ_EMPTY(&decryption_queue)) != RV_OK) {
200      fprintf(stderr, "decryption_worker: failed to wait\n");
201      break;
202    }
203
204    // printf("decryption_worker: removing from list\n");
205
206    spin_lock(&decryption_sl);
207    input_buffer = CIRCLEQ_FIRST(&decryption_queue);
208    CIRCLEQ_REMOVE(&decryption_queue, input_buffer, ll);
209    spin_unlock(&decryption_sl);
210
211    // printf("decryption_worker: processing\n");
212
213    // XXX, valgrind this :p
214    // XXX, more sanity checks? :p
215
216    // calculate how many bytes will be processed.
217    tl = input_buffer->len - crypto_box_NONCEBYTES;
218
219    tmpout = malloc(tl);
220    if(! tmpout) {
221      fprintf(stderr, "decryption_worker: unable to malloc %d bytes for tmpout, skipping\n", tl);
222      goto failure;
223    }
224
225    output_buffer = calloc(sizeof(struct buffer), 1);
226    if(! output_buffer) {
227      fprintf(stderr, "decryption_worker: unable to calloc new buffer, skipping\n");
228      goto failure;
229    }
230    output_buffer->len = tl - crypto_box_ZEROBYTES;
231    output_buffer->data = malloc(output_buffer->len);
232    if(! output_buffer->data) {
233      fprintf(stderr, "decryption_worker: unable to malloc new data buffer of "
234      "%d bytes, skipping\n", tl - crypto_box_ZEROBYTES);
235      goto failure;
236    }
237
238    // printf("attempting to decrypt with length of %d bytes\n", tl);
239
240    if(crypto_box_open(tmpout, input_buffer->data + crypto_box_NONCEBYTES, tl, input_buffer->data,
241      peer_remote_pk, secret_key) != 0) {
242      fprintf(stderr, "decryption_worker: unable to crypto_box_open :s, skipping\n");
243      goto failure;
244    }
245
246    // printf("decryption_worker: outputting buffer\n");
247
248    memcpy(output_buffer->data, tmpout + crypto_box_ZEROBYTES, output_buffer->len);
249    
250    /* Insert the buffer into the queue */
251
252    spin_lock(&process_sl);
253    CIRCLEQ_INSERT_TAIL(&process_queue, output_buffer, ll);
254    spin_unlock(&process_sl);
255
256    /* And wake up one of them */
257
258    tc_waitq_wakeup_one(&process_waitq);
259
260    // printf("decryption_worker: finished, signaled process worker\n");
261
262    goto alright;
263failure:
264    free_buf(output_buffer);
265alright:
266    free_buf(input_buffer);
267    if(tmpout) free(tmpout);
268  }
269}
270
271static int sanity_check_name(char *s)
272{
273  char buf[256];
274
275  int error;
276   error = 0;
277
278  error |= (!! strstr(s, "ssh"));
279  error |= (!! strstr(s, "/."));
280
281  memset(buf, 0, sizeof(buf));
282  if((realpath(s, buf) == NULL) && errno != ENOENT) error |= 1;
283
284  error |= (buf[0] == '.');
285
286  return error;
287}
288
289#define set_str_buffer(tag, buf, string) \
290do { \
291  size_t *t; \
292  buf->len = sizeof(size_t) + strlen(string); \
293  buf->data = malloc(buf->len + 1); \
294  if(!buf->data) { \
295    fprintf(stderr, "process_worker (set_str_buffer): failure to malloc buffer->data\n"); \
296    goto failure; \
297  } \
298  t = (size_t *)(buf->data); \
299  t[0] = tag; \
300  strcpy(buf->data + 4, string); \
301} while(0)
302
303
304
305static void process_worker(void *arg)
306{
307  struct buffer *input_buffer;
308  struct buffer *output_buffer;
309  size_t *tag;
310  int ret;
311  mode_t mode;
312  unsigned char *f, *g, *h;
313  int fd;
314  char msg[64];
315  size_t offset;
316  int len;
317  struct tc_fd *tcfd;
318
319
320  while(1) {
321    input_buffer = NULL;
322
323    if(tc_waitq_wait_event(&process_waitq, ! CIRCLEQ_EMPTY(&process_queue)) != RV_OK) {
324      fprintf(stderr, "process_worker: failed to wait\n");
325      break;
326    }
327
328    spin_lock(&process_sl);
329    input_buffer = CIRCLEQ_FIRST(&process_queue);
330    CIRCLEQ_REMOVE(&process_queue, input_buffer, ll);
331    spin_unlock(&process_sl);
332
333    // printf("process_worker: got one!\n");
334
335    output_buffer = calloc(sizeof(struct buffer), 1);
336    if(! output_buffer) {
337      fprintf(stderr, "process_worker: can't allocate output buffer :(\n");
338      goto append_output;
339    }
340
341    tag = ((size_t *)(input_buffer->data))[0];
342
343    if(input_buffer->len < 4) {
344      set_str_buffer(-1, output_buffer, "input packet length specified");
345      goto append_output;
346    }
347
348    // printf("process_worker: handling %02x .. input is %s\n", input_buffer->data[4],
349    // input_buffer->data + 4);
350    
351    switch(input_buffer->data[4]) {
352      case 'm':
353        // make a directory
354        mode = strtoul(input_buffer->data + 5, (char **) &f, 8);
355        if(f == (input_buffer->data + 5)) {
356          printf("invalid mode\n");
357          set_str_buffer(tag, output_buffer, "invalid mode specified");
358          break;
359        }
360        if(f[0] == '\0') {
361          printf("no filename\n");
362          set_str_buffer(tag, output_buffer, "no filename specified");
363          break;
364        }
365
366        if(sanity_check_name(f) != 0) {
367          printf("filename specification\n");
368          set_str_buffer(tag, output_buffer, "invalid filename specified");
369          break;
370        }
371
372        ret = mkdir(f, mode);
373        set_str_buffer(tag, output_buffer, ret == -1 ? "error in creating directory" : 
374          "successfully created directory");
375
376        break;
377      case 'o':
378        // open specified file, with mode.
379        mode = strtoul(input_buffer->data + 5, (char **) &f, 8);
380        if(f == (input_buffer->data + 5)) {
381          set_str_buffer(tag, output_buffer, "invalid mode specified");
382          break;
383        }
384        if(f[0] == '\0') {
385          printf("input = %s\n", (input_buffer->data + 5));
386          set_str_buffer(tag, output_buffer, "no filename specified");
387          break;
388        }
389
390        if(sanity_check_name(f) != 0) {
391          set_str_buffer(tag, output_buffer, "invalid filename specified");
392          break;
393        }
394
395        fd = open(f, O_CREAT|O_TRUNC|O_WRONLY, mode);
396        if(fd == -1) {
397          set_str_buffer(tag, output_buffer, "failed to open file");
398          break;
399        }
400
401        sprintf(msg, "fd is %d", fd);
402
403        set_str_buffer(tag, output_buffer, msg);
404
405        break;
406
407      case 'w':
408        // write to file, offset, data
409
410        fd = strtoul(input_buffer->data + 5, (char **)&f, 10);
411        if(f == (input_buffer->data + 5)) {
412          set_str_buffer(tag, output_buffer, "invalid fd specified");
413          break;
414        }
415
416        if(f[0] != ',') {
417          set_str_buffer(tag, output_buffer, "invalid protocol");
418          break;
419        }
420
421        f++;  
422
423        offset = strtoul(f, (char **) &g, 10);
424        if(f == g) {
425          set_str_buffer(tag, output_buffer, "no fd specified");
426          break;
427        }
428        len = input_buffer->len - ((unsigned int)(g) - (unsigned int)(input_buffer->data));
429
430        while(len) {
431          size_t ret;
432
433          ret = pwrite(fd, g, len, offset);
434          if(ret == -1) {
435            if(errno == EAGAIN || errno == EINTR) continue;
436            break;
437
438          }
439
440          g += ret;
441          len -= ret;
442        }
443
444        set_str_buffer(tag, output_buffer, len == 0 ? "successfully wrote contents to fd\x00" : 
445          "failed to write contents to fd\x00");
446
447        break;
448
449      case 'c':
450        // close file
451
452        fd = strtoul(input_buffer->data + 5, (char **)&f, 10);
453        if(f == (input_buffer->data + 5)) {
454          set_str_buffer(tag, output_buffer, "no fd specified");
455          break;
456        }
457
458        if(f[0] != '\0') {
459          set_str_buffer(tag, output_buffer, "fd not specified");
460          break;
461        }
462
463        set_str_buffer(tag, output_buffer, "fd closed");
464        close(fd);
465
466        break;
467      default:
468        set_str_buffer(tag, output_buffer, "malformed input");
469        break;
470
471    }
472
473append_output:
474    spin_lock(&encryption_sl);
475    CIRCLEQ_INSERT_TAIL(&encryption_queue, output_buffer, ll);
476    spin_unlock(&encryption_sl);
477    tc_waitq_wakeup_one(&encryption_waitq);
478
479failure:
480    free_buf(input_buffer);
481  }
482}
483
484static void encryption_worker(void *arg)
485{
486  struct buffer *input_buffer;
487  struct buffer *output_buffer;
488  unsigned char *tmp;
489
490  while(1) {
491    tmp = NULL;
492
493    // dequeue, and remove
494    if(tc_waitq_wait_event(&encryption_waitq, ! CIRCLEQ_EMPTY(&encryption_queue)) != RV_OK) {
495      fprintf(stderr, "encryption_worker: failed to wait\n");
496      break;
497    }
498
499    spin_lock(&encryption_sl);
500    input_buffer = CIRCLEQ_FIRST(&encryption_queue);
501    CIRCLEQ_REMOVE(&encryption_queue, input_buffer, ll);
502    spin_unlock(&encryption_sl);
503
504    output_buffer = calloc(sizeof(struct buffer), 1);
505    if(output_buffer == NULL) goto failure;
506    output_buffer->len = input_buffer->len + crypto_box_ZEROBYTES + crypto_box_NONCEBYTES;
507    output_buffer->data = malloc(output_buffer->len);
508    if(output_buffer->data == NULL) goto failure;
509
510    randombytes(output_buffer->data, crypto_box_NONCEBYTES);
511
512    tmp = malloc(input_buffer->len + crypto_box_ZEROBYTES);
513    if(! tmp) goto failure;
514
515
516    // printf("input_buffer is %08x\n", input_buffer);
517    // printf("input_buffer->data = %s, input_buffer->len = %d\n", input_buffer->data, input_buffer->len);
518
519    memset(tmp, 0, crypto_box_ZEROBYTES);
520    memcpy(tmp + crypto_box_ZEROBYTES, input_buffer->data, input_buffer->len);    
521
522    if(crypto_box(output_buffer->data + crypto_box_NONCEBYTES, tmp, crypto_box_ZEROBYTES + input_buffer->len,
523      output_buffer->data, peer_remote_pk, secret_key) != 0) {
524      fprintf(stderr, "crypto_box failed\n");
525      goto failure;
526    }
527
528    spin_lock(&output_sl);
529    CIRCLEQ_INSERT_TAIL(&output_queue, output_buffer, ll);
530    spin_unlock(&output_sl);
531    tc_waitq_wakeup_one(&output_waitq);
532    
533    goto alright;
534failure:
535    free_buf(output_buffer);
536alright:
537    free_buf(input_buffer);
538    if(tmp) { free(tmp); tmp = NULL; }
539  }
540}
541
542static void write_stdout(void *arg)
543{
544  struct tc_fd *tcfd = (struct tc_fd *)(arg);
545  struct buffer *buffer;
546  size_t len;
547
548  while(1) {
549    if(tc_waitq_wait_event(&output_waitq, ! CIRCLEQ_EMPTY(&output_queue)) != RV_OK) {
550      fprintf(stderr, "output_worker: failed to wait\n");
551      break;
552    }
553
554    spin_lock(&output_sl);
555    buffer = CIRCLEQ_FIRST(&output_queue);
556    CIRCLEQ_REMOVE(&output_queue, buffer, ll);
557    spin_unlock(&output_sl);
558
559    len = buffer->len;
560
561    if(tc_write(tcfd, &len, sizeof(size_t)) != sizeof(size_t)) {
562      errx(1, "failed to write");
563      // xxx, return to tc_main
564    }
565
566    if(tc_write(tcfd, buffer->data, buffer->len) != buffer->len) {
567      errx(1, "failed to write data");
568    }
569
570    free_buf(buffer);
571
572  }
573}
574
575static void tc_main(void *arg)
576{
577  struct tc_thread *reader;
578  struct tc_thread_pool decryption_pool;
579  struct tc_thread_pool process_pool;
580  struct tc_thread_pool encryption_pool;
581  struct tc_thread *writer;
582  struct tc_fd *tcfd;
583
584  CIRCLEQ_INIT(&decryption_queue);
585  spin_lock_init(&decryption_sl);
586  tc_waitq_init(&decryption_waitq);
587
588  CIRCLEQ_INIT(&process_queue);
589  spin_lock_init(&process_sl);
590  tc_waitq_init(&process_waitq);
591
592  CIRCLEQ_INIT(&encryption_queue);
593  spin_lock_init(&encryption_sl);
594  tc_waitq_init(&encryption_waitq);
595
596  CIRCLEQ_INIT(&output_queue);
597  spin_lock_init(&output_sl);
598  tc_waitq_init(&output_waitq);
599
600  tcfd = tc_register_fd(fileno(stdin));
601
602  if(write(fileno(stdin), public_key, crypto_box_PUBLICKEYBYTES) != crypto_box_PUBLICKEYBYTES)
603    err(EXIT_FAILURE, "writing public key");
604
605  if(tc_read(tcfd, peer_remote_pk, sizeof(peer_remote_pk)) != sizeof(peer_remote_pk)) {
606    fprintf(stderr, "failed to read public key\n");
607    goto failure;
608  }
609
610  tc_thread_pool_new(&decryption_pool, decryption_worker, NULL, "decryption_worker_%d", 0);
611  tc_thread_pool_new(&process_pool, process_worker, NULL, "process_worker_%d", 0);
612  tc_thread_pool_new(&encryption_pool, encryption_worker, NULL, "encryption_worker_%d", 0);
613
614  if((reader = tc_thread_new(read_stdin, tcfd, "stdin_reader")) == NULL) {
615    errx(1, "tc_thread_new failed to create stdin_reader");
616  }
617
618  if((writer = tc_thread_new(write_stdout, tcfd, "stdout_writer")) == NULL) {
619    errx(1, "tc_thread_new failed to create stdout_writer");
620  }
621
622  tc_thread_wait(reader);
623    
624failure:
625  tc_unregister_fd(tcfd);
626
627  // signal exit
628
629  // printf("waited on read_stdin\n");
630
631}
632
633int main(int argc, char **argv, char **envp)
634{
635  int fd;
636  char *p;
637
638  if(is_restarted_process(argc, argv)) {
639    devurandom_fd = open("/dev/urandom", O_RDONLY);
640
641    crypto_box_keypair(public_key, secret_key);
642
643    if(chroot("/home/level08") == -1) {
644      err(1, "Unable to chroot");
645    }
646
647    if(chdir("/") == -1) {
648      err(1, "chdir(\"/\")");
649    }
650
651    drop_privileges(UID, GID);
652
653    tc_run(tc_main, NULL, "tc_main", sysconf(_SC_NPROCESSORS_ONLN));
654    return 0;    
655  }
656
657  if(daemon(0, 0) == -1) {
658    err(EXIT_FAILURE, "Unable to daemon(0, 0)");
659  }
660
661  fd = serve_forever(PORT);
662  set_io(fd);
663  restart_process(NAME);
664
665}

Discussion