aboutsummaryrefslogtreecommitdiffstats
path: root/checker/src/checker.py
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2021-06-24 19:34:08 +0200
committerLouis Burda <quent.burda@gmail.com>2021-06-24 19:34:08 +0200
commitd1d4462f5661e0d15176375ec297b3c59d0896c3 (patch)
treef29c860dea6b4364b579a17a0f1eca0ef5069cf1 /checker/src/checker.py
parent7cc88b34e67b3d35ca10bcbf8b393dbc2713b63e (diff)
downloadenowars5-service-stldoctor-d1d4462f5661e0d15176375ec297b3c59d0896c3.tar.gz
enowars5-service-stldoctor-d1d4462f5661e0d15176375ec297b3c59d0896c3.zip
add more havocs to test stl parsing
Diffstat (limited to 'checker/src/checker.py')
-rw-r--r--checker/src/checker.py105
1 files changed, 77 insertions, 28 deletions
diff --git a/checker/src/checker.py b/checker/src/checker.py
index 69c9084..ebe3b8e 100644
--- a/checker/src/checker.py
+++ b/checker/src/checker.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
from enochecker import BaseChecker, BrokenServiceException, EnoException, run
from enochecker.utils import SimpleSocket, assert_equals, assert_in
-import os, random, string, struct, subprocess, logging, selectors, time, socket
+import math, os, random, string, struct, subprocess, logging, selectors, time, socket
import numpy as np
logging.getLogger("faker").setLevel(logging.WARNING)
@@ -28,16 +28,18 @@ signal.signal(signal.SIGALRM, handler)
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
+ facet normal 0 0 1.0
+ outer loop
+ vertex 1 0 0
+ vertex 1 1 0
+ vertex 0 1 0
+ endloop
+ endfacet
+endsolid test\xff
"""
+generic_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz0123456789-+.!"
+
def ensure_bytes(v):
if type(v) == bytes:
return v
@@ -52,7 +54,7 @@ class STLDoctorChecker(BaseChecker):
flag_variants = 2
noise_variants = 2
- havoc_variants = 4
+ havoc_variants = 8
exploit_variants = 2
prompt = b"$ "
@@ -71,9 +73,8 @@ class STLDoctorChecker(BaseChecker):
def fakeid(self):
fake = Faker(["en_US"])
- allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz0123456789-+.!"
- 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)])
+ idstr = "".join([c for c in fake.name().replace(' ','') if c in generic_alphabet][:12]).ljust(10, '.')
+ idstr += "".join([rand.choice(generic_alphabet) for i in range(8)])
return idstr
def havocid(self):
@@ -110,34 +111,46 @@ class STLDoctorChecker(BaseChecker):
def reverse_hash(self, hashstr):
return subprocess.check_output([os.getenv("REVHASH_PATH"), hashstr])[:-1]
- def genfile_ascii(self, solidname):
+ def genfile_ascii(self, solidname, malformed=False):
solidname = ensure_bytes(solidname)
+ randchoice = rand.randint(0,2)
if len(solidname) != 0:
content = b"solid " + solidname + b"\n"
else:
content = b"solid\n"
facet_count = rand.randint(4, 30)
+ indent = b" " * rand.randint(1, 4)
for fi in range(facet_count):
- content += b"facet normal "
+ if malformed and randchoice == 0: # malformed by wrong keyword
+ content += indent * 1 + b"facet nornal "
+ else:
+ content += indent * 1 + b"facet normal "
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"
- content += b"outer loop\n"
+ if malformed and randchoice == 1: # malformed wrong keyword case
+ content += indent * 2 + b"outer lOop\n"
+ else:
+ content += indent * 2 + b"outer loop\n"
for i in range(3):
- content += b"vertex " + " ".join([f"{v:.2f}" for v in vs[i]]).encode() + b"\n"
- content += b"endloop\n"
- content += b"endfacet\n"
- if solidname != b"":
- content += b"endsolid " + solidname + b"\n"
+ content += indent * 3 + b"vertex " + " ".join([f"{v:.2f}" for v in vs[i]]).encode() + b"\n"
+ content += indent * 2 + b"endloop\n"
+ content += indent + b"endfacet\n"
+ if malformed and randchoice == 2:
+ content += b"" # malformed since no endsolid
else:
- content += b"endsolid\n"
+ if solidname != b"":
+ content += b"endsolid " + solidname + b"\n"
+ else:
+ content += b"endsolid\n"
return content
- def genfile_bin(self, solidname):
+ def genfile_bin(self, solidname, malformed=False):
solidname = ensure_bytes(solidname)
+ randchoice = rand.randint(0, 3)
if len(solidname) > 78:
raise EnoException("Solidname to embed in header is larger than header itself")
@@ -146,23 +159,36 @@ class STLDoctorChecker(BaseChecker):
else:
content = b"#" + b"\x00" * 79
facet_count = rand.randint(4, 30)
- content += struct.pack("<I", facet_count)
+ if malformed and randchoice == 0: # malform by specifying more facets than are in the file
+ content += struct.pack("<I", facet_count + rand.randint(3, 7))
+ else:
+ content += struct.pack("<I", facet_count)
for fi in range(facet_count):
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]))
+ if malformed and randchoice == 2: # malform by setting invalid float in norm
+ norm[rand.randint(0,2)] = math.inf
+ elif malformed and randchoice == 3: # same malformation, but in vec
+ vs[rand.randint(0,2)][rand.randint(0,2)] = math.inf
for i in range(3):
content += struct.pack("<f", norm[i])
for k in range(3):
for i in range(3):
content += struct.pack("<f", vs[k][i])
content += b"\x00\x00"
+ if malformed and randchoice == 1: # malform by adding extra data to the end of the file
+ content += bytes([rand.randint(0, 255) for i in range(30)])
return content
- def genfile(self, filetype, solidname):
+ def genfile(self, filetype, solidname, malformed=False):
if filetype == "ascii":
- return self.genfile_ascii(solidname)
+ return self.genfile_ascii(solidname, malformed=malformed)
elif filetype == "bin":
- return self.genfile_bin(solidname)
+ return self.genfile_bin(solidname, malformed=malformed)
+ elif filetype == "garbage-tiny":
+ return ("".join([rand.choice(generic_alphabet) for i in range(rand.randint(3, 8))])).encode()
+ elif filetype == "garbage":
+ return ("".join([rand.choice(generic_alphabet) for i in range(rand.randint(100, 300))])).encode()
else:
raise EnoException("Invalid file type supplied");
@@ -258,6 +284,22 @@ class STLDoctorChecker(BaseChecker):
self.check_listed(conn, modelid)
self.closeconn(conn)
+ def malformed_upload(self, filetype):
+ conn = self.openconn()
+ solidname = self.fakeid()
+ modelname = self.fakeid()
+ contents = self.genfile(filetype, solidname, malformed = True)
+ conn.write("upload\n")
+ conn.write(f"{len(contents)}\n")
+ conn.write(contents)
+ conn.write(modelname + "\n")
+ if filetype == "garbage-tiny":
+ conn.recvuntil("ERR: File too small")
+ else:
+ conn.recvuntil("ERR:")
+ conn.recvuntil(self.prompt)
+ self.closeconn(conn)
+
def putflag(self): # type: () -> None
if self.variant_id == 0:
conn = self.openconn()
@@ -337,6 +379,14 @@ class STLDoctorChecker(BaseChecker):
self.havoc_upload('ascii', True)
elif self.variant_id == 3:
self.havoc_upload('bin', True)
+ elif self.variant_id == 4:
+ self.malformed_upload('ascii')
+ elif self.variant_id == 5:
+ self.malformed_upload('bin')
+ elif self.variant_id == 6:
+ self.malformed_upload('garbage')
+ elif self.variant_id == 7:
+ self.malformed_upload('garbage-tiny')
else:
raise EnoException(f"Invalid havoc variant ({self.variant_id}) provided")
@@ -372,7 +422,6 @@ class STLDoctorChecker(BaseChecker):
raise BrokenServiceException("Download size is not a valid integer")
resp += conn.recvn(size)
resp += conn.recvuntil("? ")
- self.debug(resp)
found = self.search_flag_bytes(resp)
if found is not None or i == len(filelist) - 1:
break