Esempio n. 1
0
    def log_and_create_flag(
        self,
        ip: r8.util.THasIP,
        user: Optional[str] = None,
        *,
        max_submissions: int = 1,
        flag: Optional[str] = None,
        challenge: Optional[str] = None,
    ) -> str:
        """
        Create a new flag that can be redeemed for this challenge and log its creation.

        If the challenge is currently inactive, `__flag__{challenge inactive}` will be returned instead.

        If flag creation should not be logged (e.g. because it's done by the challenge
        automatically on startup), use :func:`r8.util.create_flag` directly.

        Args:
            ip: IP address which caused this flag to be created. Used for logging only.
            user: User who caused this flag to be created. Used for logging only.
            challenge: If given, override the challenge for which this flag is valid.
        """
        if not self.active:
            self.log(ip, "flag-inactive", uid=user)
            return "__flag__{challenge inactive}"

        if not challenge:
            challenge = self.id

        flag = r8.util.create_flag(challenge, max_submissions, flag)
        r8.log(ip, "flag-create", flag, uid=user, cid=challenge)
        return flag
Esempio n. 2
0
 def log(self,
         ip: r8.util.THasIP,
         type: str,
         data: Optional[str] = None,
         *,
         uid: Optional[str] = None) -> None:
     """
     Log an event for the current challenge.
     See :func:`r8.log`.
     """
     r8.log(ip, type, data, uid=uid, cid=self.id)
Esempio n. 3
0
async def get_challenges(user: str, request: web.Request):
    """Get the current challenge state."""
    r8.log(request,
           "get-challenges",
           request.headers.get("User-Agent"),
           uid=user)
    challenges = await r8.util.get_challenges(user)
    return web.json_response({
        "user": user,
        "team": r8.util.get_team(user),
        "challenges": challenges,
    })
Esempio n. 4
0
File: auth.py Progetto: mhils/r8
async def register(request: web.Request):
    """very very simply self-registration functionality that abuses teams for nicknames."""
    if not r8.settings.get("register", False):
        return web.HTTPForbidden()

    logindata = await request.json()
    try:
        user = logindata["username"]
        password = logindata["password"]
        nickname = logindata["nickname"]
    except KeyError:
        r8.log(request, "register-invalid", "incomplete request")
        return web.HTTPBadRequest(reason="All fields are required.")
    with r8.db:
        user_exists = r8.db.execute("SELECT 1 FROM users WHERE uid = ?",
                                    (user, )).fetchone()
        team_exists = r8.db.execute("SELECT 1 FROM teams WHERE tid = ?",
                                    (nickname, )).fetchone()
    if user_exists:
        r8.log(request, "register-invalid", "username exists")
        return web.HTTPBadRequest(
            reason="There already exists an account with this email.")
    if team_exists:
        r8.log(request, "register-invalid", "team exists")
        return web.HTTPBadRequest(
            reason="There already exists a team with that name.")
    with r8.db:
        r8.db.execute("INSERT INTO users(uid, password) VALUES (?,?)",
                      (user, r8.util.hash_password(password)))
        r8.db.execute("INSERT INTO teams(uid, tid) VALUES (?,?)",
                      (user, nickname))
    r8.log(request, "register-success", uid=user)
    return await login(request)
Esempio n. 5
0
async def handle_challenge_request(user: str, request: web.Request):
    try:
        inst = r8.challenges[request.match_info["cid"]]
    except KeyError:
        return web.HTTPBadRequest(reason="Unknown challenge.")

    if request.method == "GET":
        resp = await inst.handle_get_request(user, request)
        if isinstance(resp, str):
            resp = web.json_response({"message": resp})
    else:
        path = (request.match_info["path"] + " ").lstrip()
        text = await request.text()
        if request.content_type == 'application/x-www-form-urlencoded':
            text = urllib.parse.unquote(text)
        data = path + text
        # We want this to appear before any challenge-specific logging...
        rowid = r8.log(request, "handle-request", data, uid=user, cid=inst.id)
        try:
            resp = await inst.handle_post_request(user, request)
            if isinstance(resp, str):
                resp = web.json_response({"message": resp})
            with r8.db:
                r8.db.execute(
                    """UPDATE events SET data = ? WHERE ROWID = ?""",
                    (f"{data} -> {resp.status} {resp.reason}", rowid))
        except Exception as e:
            with r8.db:
                r8.db.execute("""UPDATE events SET data = ? WHERE ROWID = ?""",
                              (f"{data} -> {e}", rowid))
            raise

    return resp
Esempio n. 6
0
async def login(request: web.Request):
    logindata = await request.json()
    try:
        user = logindata["username"]
        password = logindata["password"]
    except KeyError:
        r8.log(request, "login-invalid")
        return web.HTTPBadRequest(reason="username or password missing.")
    with r8.db:
        ok = r8.db.execute(
            "SELECT password FROM users WHERE uid = ?",
            (user,)
        ).fetchone()
    try:
        if not ok:
            raise ValueError()
        r8.util.verify_hash(ok[0], password)
        r8.log(request, "login-success", uid=user)
        token = r8.util.auth_sign.sign(user.encode()).decode()
        is_secure = not r8.settings["origin"].startswith("http://")
        resp = web.json_response({})
        resp.set_cookie(
            "token", token,
            path="/",
            max_age=31536000,
            samesite="strict",
            httponly=True,
            secure=is_secure,
        )
        return resp
    except (argon2.exceptions.VerificationError, ValueError):
        r8.log(request, "login-fail", user, uid=user if ok else None)
        return web.HTTPUnauthorized(
            reason="Invalid credentials."
        )
Esempio n. 7
0
    async def log_request(request: web.Request, handler):
        if isinstance(challenge.log_web_requests, bool):
            should_log = challenge.log_web_requests
        else:
            should_log = challenge.log_web_requests(request)
        if not should_log:
            return await handler(request)

        req_str = f"{request.method} {request.path_qs}"
        # We want this to appear before any challenge-specific logging...
        rowid = r8.log(request, "handle-request", req_str, cid=challenge.id)

        resp_str: str = ""
        try:
            resp = await handler(request)
            resp_str = f"{resp.status} {resp.reason}"
        except Exception as e:
            resp_str = f"{e}"
            raise
        else:
            return resp
        finally:
            req_text: str = ""
            try:
                if request._post is not None or request.content_type in (
                        "application/x-www-form-urlencoded",
                        "multipart/form-data"):
                    req_data = await request.post()
                    req_text = "&".join(f"{k}={v}"
                                        for k, v in req_data.items())
                else:
                    req_text = await request.text()
                req_text = req_text[:1024]
            except Exception as e:
                req_text = req_text or f"{e}"
            req_str = f"{req_str} {req_text}".rstrip()
            with r8.db:
                r8.db.execute("""UPDATE events SET data = ? WHERE ROWID = ?""",
                              (f"{req_str} -> {resp_str}", rowid))
Esempio n. 8
0
def submit_flag(flag: str, user: str, ip: THasIP, force: bool = False) -> str:
    """
    Returns:
        the challenge id
    Raises:
        ValueError, if there is an input error.
    """
    with r8.db:
        user_exists = r8.db.execute(
            """
          SELECT 1 FROM users
          WHERE uid = ?
        """, (user, )).fetchone()
        if not user_exists:
            r8.log(ip, "flag-err-unknown", flag)
            raise ValueError("Unknown user.")

        flag, cid = (r8.db.execute(
            """
          SELECT fid, cid FROM flags
          NATURAL INNER JOIN challenges
          WHERE fid = ? OR fid = ?
        """, (flag, correct_flag(flag))).fetchone() or [flag, None])
        if not cid:
            r8.log(ip, "flag-err-unknown", flag, uid=user)
            raise ValueError("Unknown Flag ¯\\_(ツ)_/¯")

        is_active = r8.db.execute(
            """
          SELECT 1 FROM challenges
          WHERE cid = ? 
          AND datetime('now') BETWEEN t_start AND t_stop
        """, (cid, )).fetchone()
        if not is_active and not force:
            r8.log(ip, "flag-err-inactive", flag, uid=user, cid=cid)
            raise ValueError("Challenge is not active.")

        is_already_submitted = r8.db.execute(
            """
          SELECT COUNT(*) FROM submissions 
          NATURAL INNER JOIN flags
          NATURAL INNER JOIN challenges
          WHERE cid = ? AND (
          uid = ? OR
          challenges.team = 1 AND submissions.uid IN (SELECT uid FROM teams WHERE tid = (SELECT tid FROM teams WHERE uid = ?))
          )
        """, (cid, user, user)).fetchone()[0]
        if is_already_submitted:
            r8.log(ip, "flag-err-solved", flag, uid=user, cid=cid)
            raise ValueError("Challenge already solved.")

        is_oversubscribed = r8.db.execute(
            """
          SELECT 1 FROM flags
          WHERE fid = ?
          AND (SELECT COUNT(*) FROM submissions WHERE flags.fid = submissions.fid) >= max_submissions
        """, (flag, )).fetchone()
        if is_oversubscribed and not force:
            r8.log(ip, "flag-err-used", flag, uid=user, cid=cid)
            raise ValueError("Flag already used too often.")

        r8.log(ip, "flag-submit", flag, uid=user, cid=cid)
        r8.db.execute(
            """
          INSERT INTO submissions (uid, fid) VALUES (?, ?)
        """, (user, flag))
        on_submit.send(user=user, cid=cid)
    return cid