diff options
Diffstat (limited to 'service')
| -rw-r--r-- | service/do.sh | 49 | ||||
| -rw-r--r-- | service/patches/flagstore1.diff | 15 | ||||
| -rw-r--r-- | service/src/.gitignore | 1 | ||||
| -rw-r--r-- | service/src/Makefile | 15 | ||||
| -rw-r--r-- | service/src/main.c | 168 | ||||
| -rw-r--r-- | service/src/util.c | 59 | ||||
| -rw-r--r-- | service/src/util.h | 4 | ||||
| -rw-r--r-- | service/tests/test.sh | 93 |
8 files changed, 277 insertions, 127 deletions
diff --git a/service/do.sh b/service/do.sh index 2dfd82c..9a4823d 100644 --- a/service/do.sh +++ b/service/do.sh @@ -24,23 +24,60 @@ if [ "$1" == "compose" ]; then docker-compose ${@:2} popd elif [ "$1" == "cleansrc" ]; then + if [ $# -lt 3 ]; then + echo "USAGE: do.sh cleansrc <SRC> <DST>" + exit 0 + fi + # copy files src="$2" dst="$3" [ -e "$dst" ] && rm -rf "$dst" - cp -r "$src" "$dst" + mkdir -p "$dst" + cp -r "$src"/{*.c,*.h,Makefile,msgs} "$dst" # strip comments find "$dst" | while read path; do if [ -f "$path" ]; then - sed -i -e 's/^\s*\/\*.*\*\/\s*$//g' "$path" # remove /* */ style comments - sed -i -e 's/\s*\/\*.*\*\/\s*/ /g' "$path" # remove /* */ style comments - sed -i -e 's/\/\/.*//g' "$path" # remove // style comments - sed -i -e ':a;N;$!ba;s/\n{2,}/\n/g' "$path" # collapse multiple newlines + if [ ! -z $(echo "$path" | grep '.[hc]$') ]; then + sed -i -e 's/^\s*\/\*.*\*\/\s*$//g' "$path" # remove /* */ style comments + sed -i -e 's/\s*\/\*.*\*\/\s*/ /g' "$path" # remove /* */ style comments + sed -i -e 's/\/\/.*//g' "$path" # remove // style comments + sed -i -e ':a;N;$!ba;s/\n\{3,\}/\n\n/g' "$path" # collapse multiple newlines + sed -i -e 's/fprintf(\s*stderr\s*,\s*/printf(/g' "$path" # replace fprintf stderr + elif [ "$(basename "$path")" == "Makefile" ]; then + sed -i -e 's/\s*#.*//g' "$path" # remove # style comments + sed -i -e ':a;N;$!ba;s/\n\{3,\}/\n\n/g' "$path" # collapse multiple newlines + fi fi done +elif [ "$1" == "test" ]; then + SRCDIR="$PWD/src" DATADIR="$PWD/data" bash "tests/test.sh" ${@:2} +elif [ "$1" == "make" ]; then + cd src + + make clean + make +elif [ "$1" == "make-safe" ]; then + cd "src" + + make clean + + for f in $(ls | grep '\.[ch]$'); do + cp "$f" "safe_$f" + done + + git apply patches/flagstore1.diff + git apply patches/flagstore2.diff + + PREFIX="safe_" make + + rm safe_* else echo "USAGE: do.sh (compose) [args..]" echo "EXAMPLES:" - echo " do.sh compose up --build # starts the docker container" + echo " do.sh compose up --build # starts the docker container" + echo " do.sh cleansrc <src> <dst> # post-process source files for release" + echo " do.sh make-safe # create patched version of binary" + echo " do.sh test <cmd> # run a test on the binary" fi diff --git a/service/patches/flagstore1.diff b/service/patches/flagstore1.diff deleted file mode 100644 index c307b3e..0000000 --- a/service/patches/flagstore1.diff +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/service/src/util.c b/service/src/util.c ---- a/service/src/util.c -+++ b/service/src/util.c -@@ -75,10 +75,9 @@ void - freadstr(FILE *f, char **dst) - { - size_t start, len; -- char c; - - start = ftell(f); -- for (len = 0; (c = fgetc(f)) != EOF && c; len++); -+ for (len = 0; fgetc(f) > 0; len++); - fseek(f, start, SEEK_SET); - - *dst = checkp(calloc(1, len + 1)); diff --git a/service/src/.gitignore b/service/src/.gitignore index 362a687..edaffd1 100644 --- a/service/src/.gitignore +++ b/service/src/.gitignore @@ -2,3 +2,4 @@ stldoctor scans *.o vgcore.* +safe_* diff --git a/service/src/Makefile b/service/src/Makefile index c46988a..d7732b3 100644 --- a/service/src/Makefile +++ b/service/src/Makefile @@ -1,14 +1,21 @@ CFLAGS = -g -I . +# fortify source code +CFLAGS += -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 +LDFLAGS = -Wl,-z,now -Wl,-z,relro + .PHONY: all clean -all: stldoctor +all: build/stldoctor clean: - rm -f stldoctor *.o + rm -rf build + +build: + mkdir build -%.o: %.c %.h +build/%.o: %.c %.h | build $(CC) -c -o $@ $< $(CFLAGS) $(LDLIBS) -stldoctor: main.c stlfile.o util.o +build/stldoctor: build/$(PREFIX)stlfile.o build/$(PREFIX)util.o $(PREFIX)main.c | build $(CC) -o $@ $^ $(CFLAGS) $(LDLIBS) diff --git a/service/src/main.c b/service/src/main.c index 3d62fc7..959ef58 100644 --- a/service/src/main.c +++ b/service/src/main.c @@ -6,6 +6,7 @@ #include <fcntl.h> #include <sys/stat.h> #include <time.h> +#include <errno.h> #include "stlfile.h" #include "util.h" @@ -14,30 +15,37 @@ struct command { const char *name; - void (*func)(char *); + void (*func)(const char *); + const char *desc; }; int save_submission(struct parseinfo *info, char *data, int len); -void cat_cmd(char *arg); -void list_cmd(char *arg); -void exit_cmd(char *arg); -void echo_cmd(char *arg); -void submit_cmd(char *arg); -void query_cmd(char *arg); +void cat_cmd(const char *arg); +void help_cmd(const char *arg); +void exit_cmd(const char *arg); +void echo_cmd(const char *arg); +void upload_cmd(const char *arg); +void search_cmd(const char *arg); +void list_cmd(const char *arg); +void auth_cmd(const char *arg); + +void cleanexit(); struct command commands[] = { - { "cat", cat_cmd }, - { "help", list_cmd }, - { "exit", exit_cmd }, - { "echo", echo_cmd }, - { "submit", submit_cmd }, - { "query", query_cmd }, + { "cat", cat_cmd, "Cat cmd go prrrrr." }, + { "help", help_cmd, "You already know what this does." }, + { "exit", exit_cmd, "Closes the session." }, + { "echo", echo_cmd, "Repeat after me!" }, + { "upload", upload_cmd, "Upload an STL file to analyze." }, + { "search", search_cmd, "Search for an STL file by model name." }, + { "list", list_cmd, "List your uploaded files." }, + { "auth", auth_cmd, "Login to upload files to a private dir." } }; struct parseinfo cached; -const char *resultdir; -int echo = 0; +char *resultdir; +int echo = 0, loggedin = 0; int save_submission(struct parseinfo *info, char *stldata, int stlsize) @@ -46,7 +54,10 @@ save_submission(struct parseinfo *info, char *stldata, int stlsize) FILE *f = NULL; char *dirpath = NULL, *infopath = NULL, *modelpath = NULL; - dirpath = aprintf("%s/%s-%i", resultdir, info->hash, time(NULL)); + if (loggedin) + dirpath = aprintf("%s/.%s-%i", resultdir, info->hash, time(NULL)); + else + dirpath = aprintf("%s/%s-%i", resultdir, info->hash, time(NULL)); if (mkdir(dirpath, S_IRWXU | S_IRWXG | S_IRWXO)) goto fail; modelpath = aprintf("%s/%s", dirpath, "model"); @@ -82,7 +93,7 @@ fail: } void -cat_cmd(char *arg) +cat_cmd(const char *arg) { if (arg && !strncmp(arg, "flag", 4)) dump("msgs/cat_flag"); @@ -91,10 +102,19 @@ cat_cmd(char *arg) } void -list_cmd(char *arg) +help_cmd(const char *arg) { int i; + if (arg) { + for (i = 0; i < ARRSIZE(commands); i++) { + if (!strcmp(commands[i].name, arg)) { + printf("%s\n", commands[i].desc); + return; + } + } + } + printf("Available commands:\n"); for (i = 0; i < ARRSIZE(commands); i++) printf("%s%s", i ? " " : "", commands[i].name); @@ -102,49 +122,53 @@ list_cmd(char *arg) } void -exit_cmd(char *arg) +exit_cmd(const char *arg) { exit(0); } void -echo_cmd(char *arg) +echo_cmd(const char *arg) { echo ^= 1; printf("Echo is %s\n", echo ? "enabled" : "disabled"); } void -submit_cmd(char *arg) +upload_cmd(const char *arg) { const char *bufp; char *end, *contents; size_t len; - bufp = ask("> How large is your file? "); + bufp = ask("How large is your file? "); len = strtoul(bufp, &end, 10); if (len <= 0 || len >= MAXFILESIZE || *end) { fprintf(stderr, "Invalid file length!\n"); return; } - printf("> Ok! Im listening..\n"); + printf("Ok! Im listening..\n"); contents = checkp(malloc(len + 1)); - fread(contents, 1, len, stdin); + if (fread(contents, 1, len, stdin) != len) { + fprintf(stderr, "Hm, I'm missing some bytes.. try again!\n"); + goto cleanup; + } contents[len] = '\0'; if ((cached.valid = parse_file(&cached, contents, len))) { if (save_submission(&cached, contents, len) != OK) fprintf(stderr, "Failed to save your submission!\n"); else - printf("> Your file was saved with ID %s!\n", cached.hash); + printf("Your file was saved with ID %s!\n", cached.hash); } +cleanup: free(contents); } void -query_cmd(char *arg) +search_cmd(const char *arg) { char *end, *scandir = NULL, *infopath = NULL, *modelpath = NULL; const char *hash; @@ -161,7 +185,7 @@ query_cmd(char *arg) } hash = cached.hash; } else { - hash = mhash(ask("> Model name: "), -1); + hash = mhash(arg ? arg : ask("Model name: "), -1); } if (!(d = opendir(resultdir))) return; @@ -178,7 +202,7 @@ query_cmd(char *arg) fprintf(stderr, "Sorry, couldnt find a matching scan result!\n"); goto cleanup; } else { - which = strtoul(ask("> Which of these results? "), &end, 10); + which = strtoul(ask("Which of these results? "), &end, 10); if (which >= i || which < 0 || *end) { fprintf(stderr, "Invalid index!\n"); goto cleanup; @@ -196,8 +220,9 @@ query_cmd(char *arg) } } + /* file got cleaned up during race condition by background task */ if (!scandir) { - fprintf(stderr, "Unexpected error!\n"); + fprintf(stderr, "Selected result spontaneously combusted!\n"); goto cleanup; } @@ -210,14 +235,14 @@ query_cmd(char *arg) print_info(&cached); - if (strchr(ask("> Download the model? "), 'y')) { + if (strchr(ask("Download the model? "), 'y')) { modelpath = aprintf("%s/%s", scandir, "model"); if (!(f = fopen(modelpath, "r"))) goto cleanup; fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); if (size > MAXFILESIZE) goto cleanup; - printf("> Here you go.. (%liB)\n", size); + printf("Here you go.. (%liB)\n", size); while ((i = getc(f)) != EOF) putc(i, stdout); fclose(f); @@ -233,6 +258,52 @@ cleanup: } void +list_cmd(const char *arg) +{ + DIR *d; + struct dirent *de; + + if (!loggedin) { + fprintf(stderr, "Not logged in!\n"); + return; + } + + if (!(d = opendir(resultdir))) return; + + while ((de = readdir(d))) { + if (*de->d_name == '.' && !strchr(".", de->d_name[1])) { + printf("%s\n", de->d_name); + } + } +} + +void +auth_cmd(const char *arg) +{ + const char *hash; + char *ndir; + + if (loggedin) { + fprintf(stderr, "Already logged in!\n"); + return; + } + + hash = mhash(arg ? arg : ask("Enter a password: "), -1); + ndir = aprintf("%s/.%s", resultdir, hash); + if (mkdir(ndir, S_IRWXU | S_IRWXG | S_IRWXO) && errno != EEXIST) { + fprintf(stderr, "Auth failed!\n"); + return; + } + + printf("Success!\n"); + + free(resultdir); + resultdir = ndir; + loggedin = 1; + cached.valid = 0; +} + +void cleanexit() { printf("see you later!\n"); @@ -242,11 +313,14 @@ cleanexit() int main() { - char linebuf[256], *cp, *arg; - int exit, i; + const char *cmd; + char *cp, *arg; + int exit, i, cmdlen; - if (!(resultdir = getenv("RESULTDIR"))) - resultdir = "scans"; + if (!(resultdir = checkp(strdup(getenv("RESULTDIR"))))) { + fprintf(stderr, "RESULTDIR not defined\n"); + return 1; + } setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); @@ -258,30 +332,24 @@ main() exit = 0; while (!exit) { - memset(linebuf, '\0', sizeof(linebuf)); - - printf("$ "); - exit = !fgets(linebuf, sizeof(linebuf), stdin); - if (exit || !*linebuf) break; - - if (*linebuf == '\n') continue; - if (linebuf[strlen(linebuf) - 1] == '\n') - linebuf[strlen(linebuf) - 1] = '\0'; - - if (echo) printf("%s\n", linebuf); + errno = 0; + cmd = ask("$ "); + if (!*cmd && errno == EBADMSG) break; + if (!*cmd) continue; - cp = strchr(linebuf, ' '); + cp = strchr(cmd, ' '); arg = cp ? cp + 1 : NULL; - if (cp) *cp = 0; + cmdlen = cp ? cp - cmd : strlen(cmd); for (i = 0; i < ARRSIZE(commands); i++) { - if (!strcmp(commands[i].name, linebuf)) { + if (!strncmp(commands[i].name, cmd, cmdlen) + && cmdlen == strlen(commands[i].name)) { commands[i].func(arg); break; } } - if (i == ARRSIZE(commands) && strlen(linebuf) != 0) + if (i == ARRSIZE(commands) && strlen(cmd) != 0) fprintf(stderr, "No such command!\n"); } } diff --git a/service/src/util.c b/service/src/util.c index bf6e872..31a2628 100644 --- a/service/src/util.c +++ b/service/src/util.c @@ -43,45 +43,52 @@ aprintf(const char *fmtstr, ...) } const char* -mhash(const char *filename, int len) +mhash(const char *str, int len) { - static const char *hexalph = "0123456789ABCDEF"; - static char buf[2 * MHASHLEN + 1]; - int i, k; - - if (len == -1) len = strlen(filename); - - for (i = 0; i < MIN(MHASHLEN, len); i++) { - unsigned char v = 0; - for (k = i; k < len; k += MHASHLEN) - v ^= filename[k]; - buf[i*2+0] = hexalph[(v >> 4) & 0x0f]; - buf[i*2+1] = hexalph[(v >> 0) & 0x0f]; - } + static char buf[MHASHLEN + 1]; + int i, k, v; + char c, *bp; - if (i == 0) { - memset(buf, '0', MHASHLEN); - } else if (i < MHASHLEN) { - for (k = 0; k < MHASHLEN; k++) - buf[k] = buf[k % i]; - } + /* VULN #2: BUFFER OVERFLOW */ + /* see documentation/README.md for more details */ + + if (len == -1) len = strlen(str) + 1; - buf[MHASHLEN] = '\0'; + for (v = 0, i = 0; i < len; i++) v += str[i]; + srand(v); + + for (bp = buf, i = 0; i < MHASHLEN / 2; i++) + bp += sprintf(bp, "%02x", str[i % len] ^ (rand() % 256)); return buf; } +int +checkalph(const char *str, const char *alph) +{ + int i; + + for (i = 0; i < strlen(str); i++) + if (!strchr(alph, str[i])) return 0; + + return 1; +} + void freadstr(FILE *f, char **dst) { - size_t start, len; + size_t start, len, tmp; + char c; + + /* VULN #1: BAD CAST */ + /* see documentation/README.md for more details */ start = ftell(f); - for (len = 0; fgetc(f) > 0; len++); + for (len = 0; (c = fgetc(f)) != EOF && c; len++); fseek(f, start, SEEK_SET); *dst = checkp(calloc(1, len + 1)); - fread(*dst, len, 1, f); + tmp = fread(*dst, len, 1, f); fgetc(f); } @@ -111,6 +118,8 @@ ask(const char *fmtstr, ...) if (echo) printf("%s\n", linebuf); } + if (fail) errno = EBADMSG; + return fail ? "" : linebuf; } @@ -123,7 +132,7 @@ dump(const char *filename) if (!(f = fopen(filename, "r"))) return; - while ((nb = fread(buf, 1, sizeof(buf), f))) + while ((nb = fread(buf, 1, sizeof(buf) - 1, f))) printf("%.*s\n", nb, buf); fclose(f); diff --git a/service/src/util.h b/service/src/util.h index 3496461..eaef14f 100644 --- a/service/src/util.h +++ b/service/src/util.h @@ -5,6 +5,7 @@ #include <stdarg.h> #include <string.h> #include <stdio.h> +#include <errno.h> #define ARRSIZE(x) (sizeof(x)/sizeof((x)[0])) #define MIN(x,y) ((x) > (y) ? (y) : (x)) @@ -12,7 +13,7 @@ #define NULLFREE(p) do { free(p); p = NULL; } while (0) -#define MHASHLEN 32 +#define MHASHLEN 40 enum { FAIL = 0, OK = 1 }; @@ -21,6 +22,7 @@ void* die(const char *fmtstr, ...); char* aprintf(const char *fmtstr, ...); const char* mhash(const char *filename, int len); +int checkalph(const char *str, const char *alph); void freadstr(FILE *f, char **dst); void fputstr(FILE *f, char *s); diff --git a/service/tests/test.sh b/service/tests/test.sh index 69e3ea1..ddefa03 100644 --- a/service/tests/test.sh +++ b/service/tests/test.sh @@ -2,13 +2,18 @@ set -e -# RUNTYPE=1 +if [ -z "$SRCDIR" -o -z "$DATADIR" ]; then + echo "Missing either SRCDIR or DATADIR env vars" + exit 1 +fi + +export RESULTDIR="$DATADIR/uploads" +export ECHO_INPUT=1 SCRIPTPATH="$(dirname $(readlink -f "$0"))" -cd "$SCRIPTPATH" +TESTDATA="$SCRIPTPATH/data" -export RESULTDIR="../data/scans" -export ECHO_INPUT=1 +cd "$SRCDIR" announce() { count=$(echo "$1" | wc -c) @@ -25,7 +30,7 @@ print() } checkleaks() { - valgrind --leak-check=full ./stldoctor 2>&1 | tee /tmp/testlog + valgrind --leak-check=full ./build/stldoctor 2>&1 | tee /tmp/testlog if [ -z "$(grep "no leaks are possible" /tmp/testlog)" ]; then echo "Valgrind exited with errors!" exit 1 @@ -38,42 +43,43 @@ connect() { elif [ "$RUNTYPE" == "debug" ]; then checkleaks else - ./stldoctor + ./build/stldoctor fi } +[ ! -z "$RESULTDIR" ] && rm -rf "$RESULTDIR" +mkdir -p "$RESULTDIR" + if [ "$1" == "stl" ]; then announce "Testing ASCII STL Parsing" ( echo "echo" - echo "submit" - cat tests/sample-ascii.stl | wc -c - cat tests/sample-ascii.stl + echo "upload" + cat "$TESTDATA/sample-ascii.stl" | wc -c + cat "$TESTDATA/sample-ascii.stl" echo "ASCII-testname" ) | checkleaks announce "Testing BIN STL Parsing" ( echo "echo" - echo "submit" - cat tests/sample-binary.stl | wc -c - cat tests/sample-binary.stl + echo "upload" + cat "$TESTDATA/sample-binary.stl" | wc -c + cat "$TESTDATA/sample-binary.stl" echo "BIN-testname" ) | checkleaks -elif [ "$1" == "poc" ]; then - - announce "Testing Proof-Of-Concept" +elif [ "$1" == "vuln1" ]; then - [ ! -z "$RESULTDIR" ] && rm -rf "$RESULTDIR"/* + announce "Testing Flagstore 1" echo -e "\n--- Uploading target STL ---\n" 1>&2 ( echo "echo" - echo "submit" - cat tests/flag1.stl | wc -c - cat tests/flag1.stl + echo "upload" + cat "$TESTDATA/flag1.stl" | wc -c + cat "$TESTDATA/flag1.stl" echo "N0TaFL4G" echo "exit" ) | connect @@ -81,9 +87,9 @@ elif [ "$1" == "poc" ]; then echo -e "\n--- Uploading evil STL ---\n" 1>&2 ( echo "echo" - echo "submit" - cat tests/evil1.stl | wc -c - cat tests/evil1.stl + echo "upload" + cat "$TESTDATA/evil1.stl" | wc -c + cat "$TESTDATA/evil1.stl" echo "EV1L" echo "exit" ) | connect @@ -93,27 +99,62 @@ elif [ "$1" == "poc" ]; then echo "echo" # try index 0 - echo "query" + echo "search" echo "EV1L" echo "0" echo "n" - echo "query last" + echo "search last" echo "0" echo "n" # try index 1 - echo "query" + echo "search" echo -e "EV1L" echo "0" echo "n" - echo "query last" + echo "search last" echo "1" echo "n" echo "exit" ) | connect +elif [ "$1" == "vuln2" ]; then + + announce "Testing Flagstore 2" + + echo -e "\n--- Uploading target STL ---\n" 1>&2 + ( + echo "echo" + echo "auth test" + echo "upload" + cat "$TESTDATA/flag1.stl" | wc -c + cat "$TESTDATA/flag1.stl" + echo "N0TaFL4G" + echo "exit" + ) | connect + + echo -e "\n--- Testing Exploit ---\n" 1>&2 + ( + echo "echo" + echo -e "search \xff\xff\xff\xff\xff0000000000000000" + echo "auth" + echo "list" + echo "exit" + ) | connect + +elif [ "$1" == "authupload" ]; then + ( + echo "echo" + + echo "auth test" + echo "upload" + cat "$TESTDATA/sample-ascii.stl" | wc -c + cat "$TESTDATA/sample-ascii.stl" + echo "testname" + echo "list" + ) | connect else connect fi |
