aboutsummaryrefslogtreecommitdiffstats
path: root/service
diff options
context:
space:
mode:
Diffstat (limited to 'service')
-rw-r--r--service/do.sh49
-rw-r--r--service/patches/flagstore1.diff15
-rw-r--r--service/src/.gitignore1
-rw-r--r--service/src/Makefile15
-rw-r--r--service/src/main.c168
-rw-r--r--service/src/util.c59
-rw-r--r--service/src/util.h4
-rw-r--r--service/tests/test.sh93
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