enowars5-service-stldoctor

STL-Analyzing A/D Service for ENOWARS5 in 2021
git clone https://git.sinitax.com/sinitax/enowars5-service-stldoctor
Log | Files | Refs | README | LICENSE | sfeed.txt

commit 2a8416eaa85af6348fe34859859a7fb39db2003d
parent 65a1a51121278e54e40e2a04ae096053d5a3c47d
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 10 May 2021 15:05:25 +0200

save scan info and model, added vuln in load info, small tweaks

create result directory with serialized info struct and model file after successful scan, dont modify the stl file contents during parsing, EOF getc vuln to truncate loaded hash added in load_info

Diffstat:
Mservice/src/.gitignore | 1+
Mservice/src/msgs/welcome | 2+-
Mservice/src/printdoc.c | 91++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mservice/src/stlfile.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mservice/src/stlfile.h | 5++---
Mservice/src/test.sh | 11+++++++++++
Aservice/src/tests/evil1.stl | 10++++++++++
Mservice/src/util.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mservice/src/util.h | 8++++++++
9 files changed, 221 insertions(+), 81 deletions(-)

diff --git a/service/src/.gitignore b/service/src/.gitignore @@ -1,3 +1,4 @@ printdoc +scans *.o vgcore.* diff --git a/service/src/msgs/welcome b/service/src/msgs/welcome @@ -1,2 +1,2 @@ Welcome to PrintDoc! -Upload an stl file and we'll analyze it! +Submit a stl file and we'll analyze it! diff --git a/service/src/printdoc.c b/service/src/printdoc.c @@ -3,6 +3,9 @@ #include <stdarg.h> #include <unistd.h> #include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <time.h> #include "stlfile.h" #include "util.h" @@ -12,9 +15,6 @@ struct command { void (*func)(char *); }; -const char* ask(const char *fmtstr, ...); -void dump(const char *filepath); - void search_cmd(char *arg); void submit_cmd(char *arg); void list_cmd(char *arg); @@ -30,39 +30,6 @@ struct command commands[] = { struct parseinfo lastrun; const char *resultdir; -const char* -ask(const char *fmtstr, ...) -{ - static char linebuf[256]; - va_list ap; - int fail; - - va_start(ap, fmtstr); - vprintf(fmtstr, ap); - va_end(ap); - - fail = !fgets(linebuf, sizeof(linebuf), stdin); - - if (!fail) linebuf[strlen(linebuf) - 1] = '\0'; - - return fail ? "" : linebuf; -} - -void -dump(const char *filename) -{ - char buf[256]; - FILE *f; - int nb; - - if (!(f = fopen(filename, "r"))) return; - - while ((nb = fread(buf, 1, sizeof(buf), f))) - printf("%.*s\n", nb, buf); - - fclose(f); -} - void search_cmd(char *arg) { @@ -82,14 +49,14 @@ search_cmd(char *arg) i = 0; while ((de = readdir(d))) { - if (de->d_type != DT_DIR && !strncmp(hash, de->d_name, strlen(hash))) { + if (de->d_type != DT_DIR && !strpfcmp(hash, de->d_name)) { printf("%i : %s\n", i, de->d_name); i++; } } if (i == 0) { - fprintf(stderr, "Sorry, couldnt find any matching scan result!\n"); + fprintf(stderr, "Sorry, couldnt find a matching scan result!\n"); goto cleanup; } else { which = strtoul(ask("Which of these results?\n"), &end, 10); @@ -100,7 +67,7 @@ search_cmd(char *arg) } while ((de = readdir(d))) { - if (de->d_type != DT_DIR && !strncmp(hash, de->d_name, strlen(hash))) { + if (de->d_type != DT_DIR && !strpfcmp(hash, de->d_name)) { if (i == which) { resultfile = aprintf("scans/%s/info", strdup(de->d_name)); break; @@ -117,6 +84,44 @@ cleanup: closedir(d); } +int +save_submission(char *stldata, int stlsize) +{ + DIR *d; + FILE *f; + char *dirpath = NULL, *infopath = NULL, *modelpath = NULL; + + dirpath = aprintf("%s/%s-%i", resultdir, lastrun.hash, time(NULL)); + if (mkdir(dirpath, S_IRWXU | S_IRWXG | S_IRWXO)) goto fail; + + modelpath = aprintf("%s/%s", dirpath, "model"); + if (!(f = fopen(modelpath, "w+"))) goto fail; + if (fwrite(stldata, 1, stlsize, f) != stlsize) goto fail; + fclose(f); + + infopath = aprintf("%s/%s", dirpath, "info"); + if (!(f = fopen(infopath, "w+"))) goto fail; + if (save_info(&lastrun, f) != OK) goto fail; + fclose(f); + + free(dirpath); + free(modelpath); + free(infopath); + + return OK; + +fail: + remove(infopath); + remove(modelpath); + remove(dirpath); + + free(dirpath); + free(modelpath); + free(infopath); + + return FAIL; +} + void submit_cmd(char *arg) { @@ -139,10 +144,10 @@ submit_cmd(char *arg) lastrun.valid = parse_file(&lastrun, contents, len); if (lastrun.valid) { - print_info(&lastrun); - save_info(&lastrun, resultdir); - printf("Your file was saved with ID %s!\n", lastrun.hash); - printf("You may retrieve your results via the 'search' function\n"); + if (save_submission(contents, len) != OK) + fprintf(stderr, "Failed to save your submission!\n"); + else + printf(">> Your file was saved with ID %s!\n", lastrun.hash); } free(contents); diff --git a/service/src/stlfile.c b/service/src/stlfile.c @@ -68,16 +68,16 @@ skip_set(char *p, const char *set) } char* -consume_arg(char **start) +consume_arg(char **start, char **end) { char *p, *tmp; p = skip_set(*start, wsset); for (; !strchr(wsset, *p); p++); if (!*p) return NULL; - *p = '\0'; tmp = *start; *start = p + 1; + *end = p; return tmp; } @@ -107,7 +107,7 @@ consume_keyword(char **start) int parse_file_ascii(struct parseinfo *info, char *buf, size_t len) { - char *bp, *arg, *prev, *tmp; + char *bp, *arg, *prev, *tmp, *end; struct stack states; float farg; int i, kw; @@ -117,6 +117,8 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) info->type = TYPE_ASCII; info->loopcount = 0; + memset(info->header, 0, 80); + for (i = 0; i < 3; i++) { info->bbmin[i] = INFINITY; info->bbmax[i] = -INFINITY; @@ -130,8 +132,8 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) if (stack_ind(&states, KW_SOLID_BEGIN) != -1) PARSE_FAIL("Multiple nested solids!\n"); tmp = bp; - if (!consume_keyword(&bp) && (arg = consume_arg(&bp))) { - strncpy(info->extra, arg, sizeof(info->extra)); + if (!consume_keyword(&bp) && (arg = consume_arg(&bp, &end))) { + info->modelname = strndup(arg, end - arg); } else { bp = tmp; } @@ -140,9 +142,9 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) if ((kw = stack_pop(&states)) != STATE_SOLID) PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); tmp = bp; - if (*info->extra && !consume_keyword(&bp) - && (arg = consume_arg(&bp))) { - if (strncmp(info->extra, arg, sizeof(info->extra))) + if (info->modelname && !consume_keyword(&bp) + && (arg = consume_arg(&bp, &end))) { + if (strncmp(info->modelname, arg, end - arg)) PARSE_FAIL("Solid end/begin names do not match!\n"); } else { bp = tmp; @@ -163,10 +165,10 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) if (stack_ind(&states, KW_LOOP_BEGIN) != -1) PARSE_FAIL("Multiple nested facets!\n"); for (i = 0; i < 3; i++) { - if (!(arg = consume_arg(&bp))) + if (!(arg = consume_arg(&bp, &end))) PARSE_FAIL("Facet with less than 3 args!\n"); farg = strtof(arg, &tmp); - if (*tmp) + if (!strchr(wsset, *tmp)) PARSE_FAIL("Facet with invalid arg '%s'!\n", arg); } break; @@ -176,10 +178,10 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) break; case KW_VERTEX: for (i = 0; i < 3; i++) { - if (!(arg = consume_arg(&bp))) + if (!(arg = consume_arg(&bp, &end))) PARSE_FAIL("Vertex with less than 3 args!\n"); farg = strtof(arg, &tmp); - if (*tmp) + if (!strchr(wsset, *tmp)) PARSE_FAIL("Vertex with invalid arg '%s'!\n", arg); info->bbmin[i] = MIN(info->bbmin[i], farg); info->bbmax[i] = MAX(info->bbmax[i], farg); @@ -215,7 +217,7 @@ parse_file_bin(struct parseinfo *info, char *buf, size_t len) if (len < 84) PARSE_FAIL("Truncated data! (header missing)\n"); - memcpy(info->extra, buf, 80); + memcpy(info->header, buf, 80); bp = buf + 80; info->loopcount = le32toh(*(uint32_t*)bp); @@ -249,33 +251,79 @@ int parse_file(struct parseinfo *info, char *buf, size_t len) { int status; + const char *tmp; char *bp; if (info->valid) free_info(info); - /* check bin vs ascii */ for (bp = buf; strchr(wsset, *bp); bp++); + /* check bin vs ascii with first keyword */ status = !strncmp("solid ", bp, 6) ? parse_file_ascii(info, buf, len) : parse_file_bin(info, buf, len); if (status == FAIL) return FAIL; - /* create model hash */ - strncpy(info->hash, mhash(info->extra, sizeof(info->extra)), MHASHLEN); + if (!info->modelname) { + tmp = ask("Please enter your model name:\n"); + if (strlen(tmp) < 4) { + fprintf(stderr, "Model name is too short!\n"); + return FAIL; + } + info->modelname = checkp(strdup(tmp)); + } + + info->hash = checkp(strdup(mhash(info->modelname, -1))); return OK; } int -save_info(struct parseinfo *info, const char *resultdir) +save_info(struct parseinfo *info, FILE *f) { - /* <HASH>: - * - info : binary file info - * - model : original stl file - */ + size_t nwrote = 0; + int i; - return FAIL; + nwrote += fwrite(&info->type, sizeof(int), 1, f); + nwrote += fwrite(&info->loopcount, sizeof(int), 1, f); + + for (i = 0; i < 3; i++) { + nwrote += fwrite(&info->bbmin[i], sizeof(float), 1, f); + nwrote += fwrite(&info->bbmax[i], sizeof(float), 1, f); + } + + nwrote += fwrite(info->header, 80, 1, f); + + if (nwrote != 9) return FAIL; + + fputstr(f, info->modelname); + fputstr(f, info->hash); + + return OK; +} + +int +load_info(struct parseinfo *info, FILE *f) +{ + size_t nread = 0; + int i; + + nread += fread(&info->type, sizeof(int), 1, f); + nread += fread(&info->loopcount, sizeof(int), 1, f); + + for (i = 0; i < 3; i++) { + nread += fwrite(&info->bbmin[i], sizeof(float), 1, f); + nread += fwrite(&info->bbmax[i], sizeof(float), 1, f); + } + + nread += fread(info->header, 80, 1, f); + + if (nread != 9) return FAIL; + + freadstr(f, &info->modelname); + freadstr(f, &info->hash); + + return OK; } void @@ -285,16 +333,16 @@ print_info(struct parseinfo *info) #define FILTERCHAR(c) ((c) >= 32 ? (c) : ' ') - if (info->type == TYPE_ASCII) { - printf("Modelname: %s\n", info->extra); - } else { - printf("Model Header:\n"); + printf("Name: %s\n", info->modelname); + + if (info->type == TYPE_BIN) { + printf("Header:\n"); for (i = 0; i < 80; i += k) { for (k = 0; k < MIN(80 - i, 20); k++) - printf(" %02x", (uint8_t) info->extra[i+k]); + printf(" %02x", (uint8_t) info->header[i+k]); printf(" | "); for (k = 0; k < MIN(80 - i, 20); k++) - printf("%c", FILTERCHAR(info->extra[i+k])); + printf("%c", FILTERCHAR(info->header[i+k])); printf("\n"); } } @@ -310,7 +358,7 @@ print_info(struct parseinfo *info) void free_info(struct parseinfo *info) { - NULLFREE(info->stlpath); - NULLFREE(info->infopath); + NULLFREE(info->hash); + NULLFREE(info->modelname); info->valid = 0; } diff --git a/service/src/stlfile.h b/service/src/stlfile.h @@ -38,15 +38,14 @@ struct stack { }; struct parseinfo { - char extra[80], hash[MHASHLEN+1]; + char header[80], *hash, *modelname; unsigned int loopcount; float bbmin[3], bbmax[3]; int type, valid; - char *infopath, *stlpath; }; int parse_file(struct parseinfo *info, char *buf, size_t len); -int save_info(struct parseinfo *info, const char *resultdir); +int save_info(struct parseinfo *info, FILE *f); void print_info(struct parseinfo *info); void free_info(struct parseinfo *info); diff --git a/service/src/test.sh b/service/src/test.sh @@ -24,6 +24,8 @@ checkleaks() { fi } +export RESULTDIR="scans" + announce "Trying ASCII STL" ( echo "submit" @@ -36,5 +38,14 @@ announce "Trying BIN STL" echo "submit" cat tests/sample-binary.stl | wc -c cat tests/sample-binary.stl + echo "testname" +) | checkleaks + + +announce "Testing Proof-Of-Concept" +( + echo "submit" + cat tests/evil1.stl | wc -c + cat tests/evil1.stl ) | checkleaks diff --git a/service/src/tests/evil1.stl b/service/src/tests/evil1.stl @@ -0,0 +1,9 @@ +solid test˙ + facet normal 0 0 1.0 + outer loop + vertex 1 0 0 + vertex 1 1 0 + vertex 0 1 0 + endloop + endfacet +endsolid +\ No newline at end of file diff --git a/service/src/util.c b/service/src/util.c @@ -29,14 +29,14 @@ aprintf(const char *fmtstr, ...) va_copy(cpy, ap); va_start(cpy, fmtstr); - nb = vsnprintf(NULL, 0, fmtstr, ap); + nb = vsnprintf(NULL, 0, fmtstr, cpy); va_end(cpy); - if (nb <= 0) die("encountered invalid fmtstr!\n"); + if (nb <= 0) die("Invalid fmtstr!\n"); + str = checkp(malloc(nb+1)); - str = checkp(malloc(nb)); va_start(ap, fmtstr); - nb = vsnprintf(str, nb, fmtstr, ap); + nb = vsnprintf(str, nb+1, fmtstr, ap); va_end(ap); return str; @@ -71,4 +71,62 @@ mhash(const char *filename, int len) return buf; } +void +freadstr(FILE *f, char **dst) +{ + size_t start, len; + char c; + + start = ftell(f); + while ((c = fgetc(f)) != EOF && c) len++; + + *dst = calloc(len + 1, 1); + fread(*dst, len, 1, f); +} + +void +fputstr(FILE *f, char *s) +{ + fprintf(f, "%s", s); + fputc(0, f); +} + +const char* +ask(const char *fmtstr, ...) +{ + static char linebuf[256]; + va_list ap; + int fail; + + va_start(ap, fmtstr); + vprintf(fmtstr, ap); + va_end(ap); + + fail = !fgets(linebuf, sizeof(linebuf), stdin); + + if (!fail) linebuf[strlen(linebuf) - 1] = '\0'; + + return fail ? "" : linebuf; +} + +void +dump(const char *filename) +{ + char buf[256]; + FILE *f; + int nb; + + if (!(f = fopen(filename, "r"))) return; + + while ((nb = fread(buf, 1, sizeof(buf), f))) + printf("%.*s\n", nb, buf); + + fclose(f); +} + +int +strpfcmp(const char *prefix, const char *str) +{ + return strncmp(prefix, str, strlen(prefix)); +} diff --git a/service/src/util.h b/service/src/util.h @@ -19,6 +19,14 @@ enum { FAIL = 0, OK = 1 }; void* checkp(void *p); void* die(const char *fmtstr, ...); char* aprintf(const char *fmtstr, ...); + const char* mhash(const char *filename, int len); +void freadstr(FILE *f, char **dst); +void fputstr(FILE *f, char *s); + +const char* ask(const char *fmtstr, ...); +void dump(const char *filepath); +int strpfcmp(const char *prefix, const char *str); + #endif /* UTIL_H */