Exemple #1
0
def revoke(flag, user):
    """Revoke a flag submission [for a given user]."""
    with r8.db:
        submissions = [
            x[0]
            for x in r8.db.execute("SELECT uid FROM submissions WHERE fid=?", (
                flag, )).fetchall()
        ]
        if not submissions:
            raise click.UsageError(f"No submissions for {flag}.")
        if user:
            if user in submissions:
                r8.db.execute(
                    "DELETE FROM submissions WHERE fid = ? AND uid = ?",
                    (flag, user))
                r8.echo("r8", f"Submission revoked.")
            else:
                raise click.UsageError(f"Error: {user} did not submit {flag}.")
        else:
            click.confirm(
                f"Deleting all {len(submissions)} submission for {flag}. Continue?",
                abort=True)
            r8.db.execute("DELETE FROM submissions WHERE fid = ?", (flag, ))
            r8.echo(
                "r8",
                f"Submissions have been revoked for the following users: {', '.join(submissions)}"
            )
Exemple #2
0
    def load(self):
        r8.echo("r8", "Loading challenges...")
        for entry_point in pkg_resources.iter_entry_points('r8.challenges'):
            entry_point.load()

        for cid in get_challenges():
            self._instances[cid] = self.make_instance(cid)
Exemple #3
0
def submit(flag, user, force):
    """Submit a flag for a user."""
    try:
        cid = r8.util.submit_flag(flag, user, "127.0.0.1", force)
    except ValueError as e:
        raise click.UsageError(str(e))
    else:
        r8.echo("r8", f"Solved {cid} for {user}.")
Exemple #4
0
 async def _stop(self, cid: str) -> None:
     inst = self[cid]
     try:
         await inst.stop()
     except Exception:
         r8.echo(cid, f"Error on stop.", err=True)
         traceback.print_exc()
     else:
         if type(inst).stop is not Challenge.stop:
             r8.echo(cid, "Stopped.")
Exemple #5
0
def update(user, password):
    """Update a user's password."""
    password = util.hash_password(password)
    with r8.db:
        exists = r8.db.execute("SELECT COUNT(*) FROM users WHERE uid = ?",
                               (user, )).fetchone()[0]
        if not exists:
            raise click.UsageError("User does not exist.")
        r8.db.execute("UPDATE users SET password = ? WHERE uid = ?",
                      (password, user))
    r8.echo("r8", f"Password updated.")
Exemple #6
0
async def start():
    global runner
    r8.echo("r8", "Starting server...")
    app = make_app()
    runner = web.AppRunner(app)
    await runner.setup()
    site = web.TCPSite(runner, r8.settings["host"], r8.settings["port"])
    await site.start()
    address = r8.util.format_address(
        (r8.settings["host"], r8.settings["port"]))
    r8.echo("r8", f"Running at {address}.")
    return runner
Exemple #7
0
    async def _start(self, cid: str) -> None:
        inst = self[cid]
        if type(inst).start is not Challenge.start:
            r8.echo(cid, "Starting...")

        try:
            await inst.start()
        except Exception:
            r8.echo(cid, "Error on start.", err=True)
            traceback.print_exc()
            # if start failed, don't bother with stop.
            inst.stop = lambda: asyncio.sleep(0)
Exemple #8
0
def rename(old_name, new_name):
    """Change a team name."""
    with r8.db:
        old_exists = r8.db.execute("SELECT COUNT(*) FROM teams WHERE tid = ?",
                                   (old_name, )).fetchone()[0]
        if not old_exists:
            raise click.UsageError("Old team does not exist.")
        new_exists = r8.db.execute("SELECT COUNT(*) FROM teams WHERE tid = ?",
                                   (new_name, )).fetchone()[0]
        if new_exists:
            raise click.UsageError("New team name does already exist.")
        r8.db.execute("UPDATE teams SET tid = ? WHERE tid = ?",
                      (new_name, old_name))
    r8.echo("r8", f"""Renamed "{old_name}" to "{new_name}".""")
Exemple #9
0
def delete(flag):
    """Delete an unsubmitted flag."""
    with r8.db:
        exists = r8.db.execute("SELECT COUNT(*) FROM flags WHERE fid = ?",
                               (flag, )).fetchone()[0]
        if not exists:
            raise click.UsageError("Flag does not exist.")
        submissions = r8.db.execute(
            "SELECT COUNT(*) FROM submissions WHERE fid = ?",
            (flag, )).fetchone()[0]
        if submissions:
            raise click.UsageError(
                "Cannot delete a flag that is in use. Revoke all submissions first."
            )
        r8.db.execute("DELETE FROM flags WHERE fid = ?", (flag, ))
    r8.echo("r8", f"Successfully deleted {flag}.")
Exemple #10
0
 def wrapper(database, **kwds):
     if echo:
         r8.echo("r8", f"Loading database ({database})...")
     r8.db = sqlite3_connect(database)
     with r8.db:
         r8.settings = {}
         for k, v in r8.db.execute(
                 "SELECT key, value FROM settings").fetchall():
             try:
                 val = json.loads(v)
             except ValueError as e:
                 raise ValueError(
                     f"Setting {k} is not JSON-deserializable: {v!r}"
                 ) from e
             r8.settings[k] = val
     return f(**kwds)
Exemple #11
0
def update(user, password):
    """Update a user's password.

    Note that this change may only be temporary if you reset credentials in `config.sql`.
    """
    password = util.hash_password(password)
    with r8.db:
        exists = r8.db.execute("SELECT COUNT(*) FROM users WHERE uid = ?",
                               (user, )).fetchone()[0]
        if not exists:
            raise click.UsageError("User does not exist.")
        r8.db.execute("UPDATE users SET password = ? WHERE uid = ?",
                      (password, user))
    r8.echo(
        "r8", f"Password updated. "
        f"Note that this change may only be temporary if you reset credentials in `config.sql`."
    )
Exemple #12
0
async def get_updates(user: str, request: web.Request):
    ws = web.WebSocketResponse(heartbeat=25)
    await ws.prepare(request)
    ws_connections.add(ws)
    # r8.echo('scoreboard', 'websocket connection opened')
    try:
        async for msg in ws:
            assert isinstance(msg, aiohttp.WSMessage)
            # this is here for debugging.
            if msg.type == aiohttp.WSMsgType.TEXT:
                if msg.data == 'close':
                    await ws.close()
                else:
                    await ws.send_str(msg.data)
            elif msg.type == aiohttp.WSMsgType.ERROR:
                r8.echo(
                    "scoreboard",
                    f'ws connection closed with exception {ws.exception()}')
    finally:
        ws_connections.remove(ws)
    return ws
Exemple #13
0
async def on_startup(app):
    scoreboards[0].timestamp = r8.settings.get("start", time.time())
    with r8.db:
        submissions = r8.db.execute("""
            SELECT tid, cid, CAST(strftime('%s',timestamp) AS INTEGER) AS timestamp FROM submissions
            NATURAL INNER JOIN flags
            NATURAL INNER JOIN teams
            ORDER BY TIMESTAMP
        """)
        for team, cid, timestamp in submissions:
            if team.startswith("_"):
                continue
            scoreboards.append(scoreboards[-1].solve(team, r8.challenges[cid],
                                                     timestamp))
    if len(scoreboards) > 1:
        scoreboards[0].timestamp = min(scoreboards[0].timestamp,
                                       scoreboards[1].timestamp)
    r8.echo(
        "scoreboard",
        f"Processed {len(scoreboards) - 1} submission(s): {scoreboards[-1]}")
    r8.util.on_submit.connect(on_solve)
Exemple #14
0
def cli(debug) -> None:
    """Run the server."""
    print(cars.best_car())

    loop = asyncio.get_event_loop()

    if debug:
        r8.db.set_trace_callback(lambda msg: _log_sql(msg))
        loop.set_debug(True)

    r8.challenges.load()

    loop.run_until_complete(
        asyncio.gather(server.start(), r8.challenges.start()))
    r8.echo("r8", "Started.")

    if os.name != "nt":
        loop.add_signal_handler(signal.SIGTERM, loop.stop)

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass

    r8.echo("r8", "Shutting down...")
    loop.run_until_complete(
        asyncio.gather(
            r8.challenges.stop(),
            server.stop(),
        ))
    r8.echo("r8", "Shut down.")
    loop.close()
Exemple #15
0
    with r8.db:
        submissions = r8.db.execute("""
            SELECT tid, cid, CAST(strftime('%s',timestamp) AS INTEGER) AS timestamp FROM submissions
            NATURAL INNER JOIN flags
            NATURAL INNER JOIN teams
            ORDER BY TIMESTAMP
        """)
        for team, cid, timestamp in submissions:
            if next := scoreboards[-1].solve(team, r8.challenges[cid],
                                             timestamp):
                if timestamp < scoreboards[0].timestamp:
                    next.timestamp = scoreboards[0].timestamp
                    scoreboards.clear()
                scoreboards.append(next)
    r8.echo(
        "scoreboard",
        f"Processed {len(scoreboards) - 1} submission(s): {scoreboards[-1]}")
    r8.util.on_submit.connect(on_solve)


def on_solve(sender, user, cid):
    team = r8.util.get_team(user)
    if next := scoreboards[-1].solve(team, r8.challenges[cid], time.time()):
        scoreboards.append(next)
    else:
        return

    data = scoreboards[-1].to_json()
    for ws in ws_connections:
        asyncio.create_task(send_task(ws, data))
Exemple #16
0
async def stop():
    r8.echo("r8", "Stopping server...")
    await runner.cleanup()
    r8.echo("r8", "Stopped.")
Exemple #17
0
def init(origin, static_dir, host, port, database) -> None:
    """Initialize database."""
    if os.path.exists(database):
        raise click.UsageError("Database already exists.")
    conn = util.sqlite3_connect(database)
    conn.executescript("""
        CREATE TABLE users (
            uid TEXT PRIMARY KEY NOT NULL,
            password TEXT NOT NULL
        );
        CREATE TABLE challenges (
            cid TEXT PRIMARY KEY NOT NULL,
            team BOOLEAN NOT NULL DEFAULT 0,
            t_start DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            t_stop DATETIME NOT NULL
        );
        CREATE TABLE flags (
            fid TEXT PRIMARY KEY NOT NULL,
            cid TEXT NOT NULL,
            max_submissions INTEGER NOT NULL,
            FOREIGN KEY (cid) REFERENCES challenges(cid)
        );
        CREATE TABLE submissions (
            uid TEXT NOT NULL,
            fid TEXT NOT NULL,
            timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (uid) REFERENCES users(uid),
            FOREIGN KEY (fid) REFERENCES flags(fid),
            PRIMARY KEY (uid, fid)
        );
        CREATE TABLE events (
            time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            ip TEXT NOT NULL,
            type TEXT NOT NULL,
            data TEXT,
            cid TEXT,
            uid TEXT
        );
        CREATE TABLE teams (
            uid TEXT PRIMARY KEY NOT NULL,
            tid TEXT NOT NULL,
            FOREIGN KEY (uid) REFERENCES users(uid)
        );
        CREATE TABLE data (
          cid TEXT NOT NULL,
          key TEXT NOT NULL,
          value TEXT NOT NULL,
          FOREIGN KEY (cid) REFERENCES challenges(cid),
          PRIMARY KEY (cid, key)
        );
        CREATE TABLE settings (
            key TEXT PRIMARY KEY NOT NULL,
            value TEXT NOT NULL
        );
    """)
    conn.executemany("INSERT INTO settings (key, value) VALUES (?,?)",
                     [(k, json.dumps(v)) for k, v in [
                         ("secret", secrets.token_hex(32)),
                         ("static_dir", static_dir),
                         ("origin", origin.rstrip("/")),
                         ("host", host),
                         ("port", port),
                     ]])
    conn.commit()
    r8.echo("r8", f"{database} initialized!")
Exemple #18
0
 def echo(self, message: str, err: bool = False) -> None:
     """Print to console with the challenge's namespace added in front."""
     r8.echo(self.id, message, err)