161 lines
5.6 KiB
Python
161 lines
5.6 KiB
Python
from pathlib import Path
|
|
import asyncio
|
|
import random
|
|
import string
|
|
import time
|
|
|
|
# run cargo build or cargo build --release before this
|
|
|
|
OK = 101
|
|
CORRUPT = 102
|
|
MUMBLE = 103
|
|
DOWN = 104
|
|
CHECKER_ERROR = 110
|
|
|
|
|
|
RELEASE = True
|
|
EXPECTED_CHECK_CODE = OK
|
|
EXPECTED_PUT_CODE = OK
|
|
EXPECTED_GET_CODE = OK
|
|
CHECKS_PER_SECOND = 5
|
|
CHECKS_PER_REPORT = 100
|
|
MAX_PARALLEL_CHECKS = CHECKS_PER_SECOND * 3
|
|
VULNS = 1
|
|
TIMEOUT = 30
|
|
HOST = "127.0.0.1"
|
|
|
|
assert VULNS == 1, "multiple vulns not supported."
|
|
|
|
check_period = 1 / CHECKS_PER_SECOND
|
|
|
|
if RELEASE:
|
|
checker_binary_path = Path("../target/release/checker")
|
|
else:
|
|
checker_binary_path = Path("../target/debug/checker")
|
|
|
|
detailed_log_filepath = Path("detailed.log")
|
|
|
|
def returncode_to_string(returncode):
|
|
if returncode == 101:
|
|
return "OK"
|
|
elif returncode == 102:
|
|
return "CORRUPT"
|
|
elif returncode == 103:
|
|
return "MUMBLE"
|
|
elif returncode == 104:
|
|
return "DOWN"
|
|
elif returncode == 110:
|
|
return "CHECKER_ERROR"
|
|
else:
|
|
return "UNEXPECTED"
|
|
|
|
def get_random_flag():
|
|
return "S" + "".join(random.choices(string.ascii_uppercase + string.digits, k=30)) + "="
|
|
|
|
output_lock = asyncio.locks.Lock()
|
|
async def write_to_detailed_log(entry, *, also_print=False):
|
|
async with output_lock:
|
|
with open(detailed_log_filepath, "a") as f:
|
|
f.write(entry + "\n")
|
|
if also_print:
|
|
print(entry)
|
|
|
|
assert checker_binary_path.exists(), "checker binary doesn't exist. run cargo build or cargo build --release before this."
|
|
|
|
async def do_all_checks_once():
|
|
flag = get_random_flag()
|
|
|
|
check_process = await asyncio.create_subprocess_exec(
|
|
checker_binary_path, "check", HOST,
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE
|
|
)
|
|
try:
|
|
stdout_data, stderr_data = await asyncio.wait_for(check_process.communicate(), timeout=TIMEOUT)
|
|
except asyncio.TimeoutError:
|
|
stdout_data, stderr_data = await check_process.stdout.read(), await check_process.stderr.read()
|
|
await write_to_detailed_log(
|
|
f"CHECK({flag}): Stuck for {TIMEOUT} seconds. Make sure the checker code has it's own timeouts. stdout={stdout_data} stderr={stderr_data}",
|
|
also_print=True
|
|
)
|
|
check_process.kill()
|
|
else:
|
|
returncode = check_process.returncode
|
|
await write_to_detailed_log(
|
|
f"CHECK({flag}): {returncode_to_string(returncode)} ({returncode}) stdout={stdout_data} stderr={stderr_data}",
|
|
also_print=(EXPECTED_CHECK_CODE is not None and returncode != EXPECTED_CHECK_CODE)
|
|
)
|
|
|
|
put_process = await asyncio.create_subprocess_exec(
|
|
checker_binary_path, "put", HOST, "", flag, "1",
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE
|
|
)
|
|
try:
|
|
stdout_data, stderr_data = await asyncio.wait_for(put_process.communicate(), timeout=TIMEOUT)
|
|
except asyncio.TimeoutError:
|
|
stdout_data, stderr_data = await put_process.stdout.read(), await put_process.stderr.read()
|
|
await write_to_detailed_log(
|
|
f"PUT({flag}): Stuck for {TIMEOUT} seconds. Make sure the checker code has it's own timeouts. stdout={stdout_data} stderr={stderr_data}",
|
|
also_print=True
|
|
)
|
|
put_process.kill()
|
|
failed = True
|
|
else:
|
|
returncode = put_process.returncode
|
|
await write_to_detailed_log(
|
|
f"PUT({flag}): {returncode_to_string(returncode)} ({returncode}) stdout(public)={stdout_data} stderr(private)={stderr_data}",
|
|
also_print=(EXPECTED_PUT_CODE is not None and returncode != EXPECTED_PUT_CODE)
|
|
)
|
|
flag_id = stderr_data
|
|
failed = returncode != OK
|
|
|
|
if failed:
|
|
await write_to_detailed_log(
|
|
f"GET({flag}): SKIPPED since PUT failed.",
|
|
also_print=(EXPECTED_GET_CODE is not None)
|
|
)
|
|
return
|
|
|
|
get_process = await asyncio.create_subprocess_exec(
|
|
checker_binary_path, "get", HOST, flag_id, flag, "1",
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE
|
|
)
|
|
try:
|
|
stdout_data, stderr_data = await asyncio.wait_for(get_process.communicate(), timeout=TIMEOUT)
|
|
except asyncio.TimeoutError:
|
|
stdout_data, stderr_data = await get_process.stdout.read(), await get_process.stderr.read()
|
|
await write_to_detailed_log(
|
|
f"GET({flag}): Stuck for {TIMEOUT} seconds. Make sure the checker code has it's own timeouts. stdout={stdout_data} stderr={stderr_data}",
|
|
also_print=True
|
|
)
|
|
get_process.kill()
|
|
else:
|
|
returncode = get_process.returncode
|
|
await write_to_detailed_log(
|
|
f"GET({flag}): {returncode_to_string(returncode)} ({returncode}) stdout(public)={stdout_data} stderr(private)={stderr_data}",
|
|
also_print=(EXPECTED_GET_CODE is not None and returncode != EXPECTED_GET_CODE)
|
|
)
|
|
|
|
|
|
async def main():
|
|
pending_tasks = []
|
|
checks_done = 0
|
|
while True:
|
|
if len(pending_tasks) > 0 and pending_tasks[0].done() and (checks_done + 1) % CHECKS_PER_REPORT == 0:
|
|
print(f"Already done {checks_done + 1} checks. {len(pending_tasks) - 1} running in parallel.")
|
|
while len(pending_tasks) > 0 and pending_tasks[0].done():
|
|
pending_tasks.pop(0)
|
|
checks_done += 1
|
|
if len(pending_tasks) <= MAX_PARALLEL_CHECKS:
|
|
pending_tasks.append(asyncio.create_task(asyncio.wait_for(do_all_checks_once(), timeout=TIMEOUT * 3 + 10)))
|
|
else:
|
|
print(f"Throttling checks since there are at least {MAX_PARALLEL_CHECKS} running")
|
|
await asyncio.sleep(check_period)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|
|
|