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 a0bd3d833d916cadd23d17d0b3784e28c729967d
parent aeb66a7b19008fabecbca23a1e21a9d6942ec28a
Author: Louis Burda <quent.burda@gmail.com>
Date:   Thu, 24 Jun 2021 02:52:21 +0200

various fixes made while stress-testing exploit

Diffstat:
Mchecker/src/checker.py | 50++++++++++++++++++++++++++++++++++----------------
Mchecker/src/gunicorn.conf.py | 4+---
Mchecker/src/requirements.txt | 6+++---
Mchecker/test.sh | 30+++++++++++++++++++-----------
Mdo.sh | 2+-
Mservice/entrypoint.sh | 2+-
Aservice/src/.gitignore | 2++
Mservice/src/main.c | 98+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/.gitignore | 4+---
Msrc/main.c | 89++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
10 files changed, 170 insertions(+), 117 deletions(-)

diff --git a/checker/src/checker.py b/checker/src/checker.py @@ -8,6 +8,8 @@ logging.getLogger("faker").setLevel(logging.WARNING) logging.getLogger("pwnlib").setLevel(logging.WARNING) logging.getLogger("_curses").setLevel(logging.CRITICAL) +rand = random.SystemRandom() + from faker import Faker # DEBUGING MEMORY ISSUES# @@ -63,18 +65,20 @@ class STLDoctorChecker(BaseChecker): def closeconn(self, conn): self.debug("Sending exit command") conn.write("exit\n") + # ensure it is a clean exit + conn.recvuntil("bye!") conn.close() def fakeid(self): fake = Faker(["en_US"]) allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz0123456789-+.!" - idstr = "".join([c for c in fake.name().replace(' ','') if c in allowed][:60]).ljust(10, '.') - idstr += "".join([random.choice(allowed) for i in range(5)]) + idstr = "".join([c for c in fake.name().replace(' ','') if c in allowed][:12]).ljust(10, '.') + idstr += "".join([rand.choice(allowed) for i in range(8)]) return idstr def havocid(self): - idlen = random.randint(10, 60) - return "".join([chr(random.randint(32, 127)) for i in range(idlen)]) + idlen = rand.randint(10, 40) + return "".join([chr(rand.randint(32, 127)) for i in range(idlen)]) def do_auth(self, conn, authstr): authstr = ensure_bytes(authstr) @@ -113,10 +117,10 @@ class STLDoctorChecker(BaseChecker): content = b"solid " + solidname + b"\n" else: content = b"solid\n" - facet_count = random.randint(4, 30) + facet_count = rand.randint(4, 30) for fi in range(facet_count): content += b"facet normal " - vs = [[random.random() for i in range(3)] for k in range(3)] + vs = [[rand.random() for i in range(3)] for k in range(3)] norm = np.cross(np.subtract(vs[1], vs[0]), np.subtract(vs[2],vs[0])) norm = norm / np.linalg.norm(norm) content += " ".join([f"{v:.2f}" for v in norm]).encode() + b"\n" @@ -141,10 +145,10 @@ class STLDoctorChecker(BaseChecker): content = b"#" + solidname.ljust(78, b"\x00") + b"\x00" else: content = b"#" + b"\x00" * 79 - facet_count = random.randint(4, 30) + facet_count = rand.randint(4, 30) content += struct.pack("<I", facet_count) for fi in range(facet_count): - vs = [[random.random() for i in range(3)] for k in range(3)] + vs = [[rand.random() for i in range(3)] for k in range(3)] norm = np.cross(np.subtract(vs[1], vs[0]), np.subtract(vs[2],vs[0])) for i in range(3): content += struct.pack("<f", norm[i]) @@ -184,7 +188,7 @@ class STLDoctorChecker(BaseChecker): modelid = line.rsplit(b"!", 1)[0].split(b"with ID ", 1)[1] if modelid == b"": raise Exception except: - raise BrokenServiceException(f"Invalid response during upload of {modelname}") + raise BrokenServiceException(f"Invalid response during upload of {modelname}:\n{line}") # Consume rest of data in this call conn.recvuntil(self.prompt) @@ -202,7 +206,7 @@ class STLDoctorChecker(BaseChecker): conn.write("y\n" if download else "\n") # Wait for end of info box - resp = conn.recvuntil("==================") + resp = conn.recvuntil("================== \n") # Ask for download if desired if download: @@ -352,22 +356,36 @@ class STLDoctorChecker(BaseChecker): filelist = [l.strip().split(b" : ") for l in conn.recvuntil("?").split(b"\n") if b" : " in l] if len(filelist) == 0: raise BrokenServiceException("Failed to list files through search") + index_dict = {fl[1]: fl[0] for fl in filelist} + targets = [fl[1] for fl in filelist] # Use it to enumerate other files and grab contents found = None - for i in range(len(filelist)): - self.debug(b"Retrieving file " + filelist[i][0] + b": " + filelist[i][1]) - conn.write(filelist[i][0] + b"\ny\n") + self.debug("Targets:\n" + "\n".join([' - ' + l.decode('latin1') for l in targets])) + for i,fhash in enumerate(targets): + if index_dict[fhash] == None: + self.debug(b"Skipping now missing file " + fhash) + continue + + # Retrieve current file + self.debug(b"Retrieving file " + fhash + b" at index " + index_dict[fhash]) + conn.write(index_dict[fhash] + b"\ny\n") fileinfo = conn.recvuntil(self.prompt) - self.debug("File contents:\n" + fileinfo.decode("latin1")) + # self.debug("File contents:\n" + fileinfo.decode("latin1")) found = self.search_flag_bytes(fileinfo) - if found is not None or i == len(filelist) - 1: + if found is not None or i == len(targets) - 1: break # Parse evil file again for next iter self.getfile(conn, name, download=False) conn.write("search last\n") - conn.recvuntil("?") + + # Update indicies from new search + filelist = [l.strip().split(b" : ") for l in conn.recvuntil("?").split(b"\n") if b" : " in l] + index_dict = {name : None for name in targets} + for fl in filelist: + index_dict[fl[1]] = fl[0] + self.closeconn(conn) if found is None: diff --git a/checker/src/gunicorn.conf.py b/checker/src/gunicorn.conf.py @@ -1,10 +1,8 @@ import multiprocessing -worker_class = "gevent" +worker_class = "eventlet" workers = multiprocessing.cpu_count() * 2 + 1 bind = "0.0.0.0:3031" timeout = 90 keepalive = 3600 -max_requests = 100 preload_app = True -max_requests_jitter = 30 diff --git a/checker/src/requirements.txt b/checker/src/requirements.txt @@ -3,14 +3,14 @@ chardet==4.0.0 click==7.1.2 dnspython==1.16.0 # enochecker==0.4.2 -# git+https://github.com/enowars/enochecker@e1ce01b510b0d9e05d292a11a24c809bca1c181b -git+https://github.com/Sinitax/enochecker@7fbc1b9ad4eee85343dcdce7e575e95b8e3c481e +# git+https://github.com/enowars/enochecker@37981175f3125bd552c3c351494186fe9ce35e0b +git+https://github.com/Sinitax/enochecker@3bd2e698e9421f4a67e60a2377ac6f40e65b18a7 enochecker-cli==0.7.0 enochecker-core==0.10.0 eventlet==0.30.2 Flask==1.1.2 greenlet==1.0.0 -gunicorn[gevent] +gunicorn==20.1.0 idna==2.10 itsdangerous==1.1.0 Jinja2==2.11.3 diff --git a/checker/test.sh b/checker/test.sh @@ -26,14 +26,15 @@ except: try() { cmd="$1" - tmpfile="/tmp/checker-log-$BASHPID" + pid=$BASHPID + tmpfile="/tmp/checker-log-$pid" [ -e "$tmpfile" ] && rm "$tmpfile" if [ $# -lt 2 ]; then variant=0 else variant=$2 fi - taskid=$$ + taskid=$pid if [ ! -z "$REMOTE" ]; then python3 enoreq.py -j True -A http://localhost:9091 -a $REMOTE \ --flag ENOTESTFLAG123= --flag_regex 'ENO.*=' -i $taskid \ @@ -46,18 +47,18 @@ try() { res="$(cat $tmpfile | grep -a Result: | tail -n1 | grep -a -o OK)" fi if [ "$res" != "OK" ]; then - newfile="fails/err-$(ls fails | wc -l)" + newfile="fails/err-$pid" ( echo "METHOD $@" + echo "RESULT $res" + echo "TASK $taskid" cat "$tmpfile" if [ ! -z "$REMOTE" -a -e "$ENOLOGMESSAGE_PARSER" ]; then - docker-compose logs --tail=400 | grep '"taskId": '$taskid | $ENOLOGMESSAGE_PARSER + docker-compose logs --tail=2000 | grep '"taskId": '$taskid | $ENOLOGMESSAGE_PARSER fi ) > "$newfile" - echo -ne "Executing $cmd with variant $variant.. $res ($newfile)\n" - else - echo -ne "Executing $cmd with variant $variant.. $res\n" fi + echo -ne "Executing $cmd with variant $variant.. $res (TASK: $taskid)\n" } try-all() { @@ -82,16 +83,23 @@ try-all() { try exploit 1 } -if [ $# -ge 2 ]; then +one-of() { + for arg in ${@:2}; do + [ "$1" == "$arg" ] && return 0 + done + return 1 +} + +if one-of "$1" putflag getflag putnoise getnoise havoc exploit; then try $@ elif [ "$1" == "test-exploits" ]; then try exploit 0 try exploit 1 elif [ "$1" == "stress-test" ]; then mkdir -p fails - while [ 1 ]; do - try-all & - sleep 2 + count=${2:-100} + for i in $(seq $count); do + try ${3:-exploit} ${4:-0} & done else try-all diff --git a/do.sh b/do.sh @@ -35,7 +35,7 @@ elif [ "$1" == "cleansrc" ]; then dst="$3" [ -e "$dst" ] && rm -rf "$dst" mkdir -p "$dst" - cp -r "$src"/{*.c,*.h,Makefile,msgs} "$dst" + cp -r "$src"/{*.c,*.h,Makefile,msgs,.gitignore} "$dst" # strip comments find "$dst" | while read path; do diff --git a/service/entrypoint.sh b/service/entrypoint.sh @@ -5,7 +5,7 @@ chown -R service:service "$RESULTDIR" while [ 1 ]; do /cleaner.sh - sleep 200 + sleep 400 done & CMD="socat -T180 -s TCP-LISTEN:9000,nodelay,reuseaddr,fork EXEC:/service/build/stldoctor,raw,pty,echo=0,stderr" diff --git a/service/src/.gitignore b/service/src/.gitignore @@ -0,0 +1,2 @@ +build +vgcore.* diff --git a/service/src/main.c b/service/src/main.c @@ -92,6 +92,13 @@ fail: return FAIL; } +int +access_authorized(const char *file) +{ + return (loggedin && file[0] == '.' && file[1] != '.') + || (!loggedin && file[0] != '.'); +} + void cat_cmd(const char *arg) { @@ -106,12 +113,10 @@ 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; - } + for (i = 0; arg && i < ARRSIZE(commands); i++) { + if (!strcmp(commands[i].name, arg)) { + printf("%s\n", commands[i].desc); + return; } } @@ -124,6 +129,7 @@ help_cmd(const char *arg) void exit_cmd(const char *arg) { + printf("bye!\n"); exit(0); } @@ -170,8 +176,9 @@ cleanup: void search_cmd(const char *arg) { - char *end, *scandir = NULL, *infopath = NULL, *modelpath = NULL; - int i, which, dirstart, ishidden; + char *end, *scandir = NULL, *infopath = NULL, + *modelpath = NULL, **paths = NULL; + int i, which, dirstart, ishidden, pathc, pathcap = 100; const char *hash, *name; struct dirent *de; DIR *d = NULL; @@ -188,51 +195,49 @@ search_cmd(const char *arg) hash = mhash(arg ? arg : ask("Model name: "), -1); } - if (!(d = opendir(resultdir))) return; + if (!(d = opendir(resultdir))) { + printf("Unable to access upload directory!\n"); + return; + } + paths = checkp(malloc(pathcap * sizeof(char*))); dirstart = telldir(d); - for (i = 0; (de = readdir(d));) { - name = de->d_name; - if (loggedin && *name == '.' && !strpfcmp(hash, name + 1) - || !loggedin && *name != '.' && !strpfcmp(hash, name)) { - printf("%i : %s\n", i, de->d_name); - i++; + for (pathc = 0; (de = readdir(d));) { + if (access_authorized(de->d_name) + && !strpfcmp(hash, de->d_name + loggedin)) { + printf("%i : %s\n", pathc, de->d_name); + paths[pathc++] = checkp(strdup(de->d_name)); + if (pathc == pathcap) { + pathcap *= 2; + paths = checkp(realloc(paths, pathcap * sizeof(char*))); + } } } + closedir(d); - if (i == 0) { + if (pathc == 0) { printf("Sorry, couldnt find a matching scan result!\n"); goto cleanup; - } else { - which = strtoul(ask("Which of these results? "), &end, 10); - if (which >= i || which < 0 || *end) { - printf("Invalid index!\n"); - goto cleanup; - } - } - - seekdir(d, dirstart); - for (i = 0; (de = readdir(d));) { - name = de->d_name; - if (loggedin && *name == '.' && !strpfcmp(hash, name + 1) - || !loggedin && *name != '.' && !strpfcmp(hash, name)) { - if (i == which) { - scandir = aprintf("%s/%s", resultdir, de->d_name); - break; - } - i++; - } } - if (!scandir) { - printf("Selected result spontaneously combusted!\n"); + which = strtoul(ask("Which of these results? "), &end, 10); + if (which >= pathc || which < 0 || *end) { + printf("Invalid index!\n"); goto cleanup; } + scandir = aprintf("%s/%s", resultdir, paths[which]); + infopath = aprintf("%s/%s", scandir, "info"); - if (!(f = fopen(infopath, "r"))) goto cleanup; + if (!(f = fopen(infopath, "r"))) { + printf("Selected result is missing!\n"); + goto cleanup; + } free_info(&cached); - if (load_info(&cached, f) != OK) goto cleanup; + if (load_info(&cached, f) != OK) { + printf("Failed to parse info file!\n"); + goto cleanup; + } fclose(f); f = NULL; @@ -240,11 +245,17 @@ search_cmd(const char *arg) if (strchr(ask("Download the model? "), 'y')) { modelpath = aprintf("%s/%s", scandir, "model"); - if (!(f = fopen(modelpath, "r"))) goto cleanup; + if (!(f = fopen(modelpath, "r"))) { + printf("Failed to access file!\n"); + goto cleanup; + } fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); - if (size > MAXFILESIZE) goto cleanup; + if (size > MAXFILESIZE) { + printf("File is too large to send!\n"); + goto cleanup; + } printf("Here you go.. (%liB)\n", size); while ((i = getc(f)) != EOF) putc(i, stdout); @@ -254,10 +265,13 @@ search_cmd(const char *arg) cleanup: if (f) fclose(f); - closedir(d); + free(scandir); free(infopath); free(modelpath); + + for (i = 0; i < pathc; i++) free(paths[i]); + free(paths); } void diff --git a/src/.gitignore b/src/.gitignore @@ -1,4 +1,2 @@ -stldoctor -*.o +build vgcore.* -safe_* diff --git a/src/main.c b/src/main.c @@ -92,6 +92,13 @@ fail: return FAIL; } +int +access_authorized(const char *file) +{ + return (loggedin && file[0] == '.' && file[1] != '.') + || (!loggedin && file[0] != '.'); +} + void cat_cmd(const char *arg) { @@ -122,6 +129,7 @@ help_cmd(const char *arg) void exit_cmd(const char *arg) { + printf("bye!\n"); exit(0); } @@ -168,8 +176,9 @@ cleanup: void search_cmd(const char *arg) { - char *end, *scandir = NULL, *infopath = NULL, *modelpath = NULL; - int i, which, dirstart, ishidden; + char *end, *scandir = NULL, *infopath = NULL, + *modelpath = NULL, **paths = NULL; + int i, which, dirstart, ishidden, pathc, pathcap = 100; const char *hash, *name; struct dirent *de; DIR *d = NULL; @@ -186,52 +195,49 @@ search_cmd(const char *arg) hash = mhash(arg ? arg : ask("Model name: "), -1); } - if (!(d = opendir(resultdir))) return; + if (!(d = opendir(resultdir))) { + fprintf(stderr, "Unable to access upload directory!\n"); + return; + } + paths = checkp(malloc(pathcap * sizeof(char*))); dirstart = telldir(d); - for (i = 0; (de = readdir(d));) { - name = de->d_name; - if (loggedin && *name == '.' && !strpfcmp(hash, name + 1) - || !loggedin && *name != '.' && !strpfcmp(hash, name)) { - printf("%i : %s\n", i, de->d_name); - i++; + for (pathc = 0; (de = readdir(d));) { + if (access_authorized(de->d_name) + && !strpfcmp(hash, de->d_name + loggedin)) { + printf("%i : %s\n", pathc, de->d_name); + paths[pathc++] = checkp(strdup(de->d_name)); + if (pathc == pathcap) { + pathcap *= 2; + paths = checkp(realloc(paths, pathcap * sizeof(char*))); + } } } + closedir(d); - if (i == 0) { + if (pathc == 0) { fprintf(stderr, "Sorry, couldnt find a matching scan result!\n"); goto cleanup; - } else { - which = strtoul(ask("Which of these results? "), &end, 10); - if (which >= i || which < 0 || *end) { - fprintf(stderr, "Invalid index!\n"); - goto cleanup; - } } - seekdir(d, dirstart); - for (i = 0; (de = readdir(d));) { - name = de->d_name; - if (loggedin && *name == '.' && !strpfcmp(hash, name + 1) - || !loggedin && *name != '.' && !strpfcmp(hash, name)) { - if (i == which) { - scandir = aprintf("%s/%s", resultdir, de->d_name); - break; - } - i++; - } - } - - /* file got cleaned up during race condition by background task */ - if (!scandir) { - fprintf(stderr, "Selected result spontaneously combusted!\n"); + which = strtoul(ask("Which of these results? "), &end, 10); + if (which >= pathc || which < 0 || *end) { + fprintf(stderr, "Invalid index!\n"); goto cleanup; } + scandir = aprintf("%s/%s", resultdir, paths[which]); + infopath = aprintf("%s/%s", scandir, "info"); - if (!(f = fopen(infopath, "r"))) goto cleanup; + if (!(f = fopen(infopath, "r"))) { + fprintf(stderr, "Selected result is missing!\n"); + goto cleanup; + } free_info(&cached); - if (load_info(&cached, f) != OK) goto cleanup; + if (load_info(&cached, f) != OK) { + fprintf(stderr, "Failed to parse info file!\n"); + goto cleanup; + } fclose(f); f = NULL; @@ -239,11 +245,17 @@ search_cmd(const char *arg) if (strchr(ask("Download the model? "), 'y')) { modelpath = aprintf("%s/%s", scandir, "model"); - if (!(f = fopen(modelpath, "r"))) goto cleanup; + if (!(f = fopen(modelpath, "r"))) { + fprintf(stderr, "Failed to access file!\n"); + goto cleanup; + } fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); - if (size > MAXFILESIZE) goto cleanup; + if (size > MAXFILESIZE) { + fprintf(stderr, "File is too large to send!\n"); + goto cleanup; + } printf("Here you go.. (%liB)\n", size); while ((i = getc(f)) != EOF) putc(i, stdout); @@ -253,10 +265,13 @@ search_cmd(const char *arg) cleanup: if (f) fclose(f); - closedir(d); + free(scandir); free(infopath); free(modelpath); + + for (i = 0; i < pathc; i++) free(paths[i]); + free(paths); } void