diff --git a/__init__.py b/__init__.py
index 04073b4..448ea60 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,12 +1,14 @@
import os
-from CTFd.models import db
-from CTFd.plugins import register_admin_plugin_menu_bar
+from CTFd.plugins import register_admin_plugin_menu_bar, register_plugin_assets_directory
from CTFd.plugins.migrations import upgrade
+from CTFd.plugins.flags import FLAG_CLASSES
from CTFd.utils.decorators import admins_only
+from CTFd.models import Flags, db
from flask import Blueprint, render_template, request
from .models import CheaterTeams
+from .flags import PersonalFlag
PLUGIN_PATH = os.path.dirname(__file__)
@@ -21,6 +23,23 @@ def report_cheater(challenge_id: int, cheater_id: int, helper_id: int, flag_id:
db.session.commit()
+def create_flag_if_missing(challenge_id: int, user_id: int, flag_content: str):
+ flags = Flags.query.filter_by(id=challenge_id, data=user_id).all()
+
+ if len(flags) == 0:
+ new_flag = Flags(
+ challenge_id=challenge_id,
+ type="personal",
+ content=flag_content,
+ data=user_id,
+ )
+ db.session.add(new_flag)
+ db.session.commit()
+ return new_flag.id
+
+ return flags[0].id
+
+
@bp.route("/admin/cheaters", methods=["GET"])
@admins_only
def show_cheaters():
@@ -33,6 +52,9 @@ def load(app):
app.db.create_all()
upgrade(plugin_name="cheaters")
+ FLAG_CLASSES["personal"] = PersonalFlag
+
+ register_plugin_assets_directory(app, base_path="/plugins/ctfd_cheaters/assets/")
register_admin_plugin_menu_bar(title="Cheaters", route="/admin/cheaters")
app.register_blueprint(bp)
diff --git a/assets/create.html b/assets/create.html
new file mode 100644
index 0000000..248b8fe
--- /dev/null
+++ b/assets/create.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/assets/edit.html b/assets/edit.html
new file mode 100644
index 0000000..1cb3f5b
--- /dev/null
+++ b/assets/edit.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/flags.py b/flags.py
new file mode 100644
index 0000000..2da7b94
--- /dev/null
+++ b/flags.py
@@ -0,0 +1,39 @@
+from CTFd.plugins.flags import BaseFlag
+from CTFd.utils.user import get_current_user
+
+from . import report_cheater
+
+
+class PersonalFlag(BaseFlag):
+ name: str = "personal"
+ templates = { # Nunjucks templates used for key editing & viewing
+ "create": "/plugins/ctfd_cheaters/assets/create.html",
+ "update": "/plugins/ctfd_cheaters/assets/edit.html",
+ }
+
+ @staticmethod
+ def compare(chal_key_obj, provided):
+ saved = chal_key_obj.content
+ user_id = chal_key_obj.data
+
+ if len(saved) != len(provided):
+ return False
+ result = 0
+
+ for x, y in zip(saved, provided):
+ result |= ord(x) ^ ord(y)
+
+ if result == 0:
+ # If the flag is correct, we need to check if the team is the one associated with the flag
+ curr_user_id = get_current_user().id
+
+ if int(user_id) == int(curr_user_id):
+ # User is correct
+ return True
+
+ # Caught a cheater!
+ report_cheater(
+ chal_key_obj.challenge_id, curr_user_id, user_id, chal_key_obj.id
+ )
+
+ return False
diff --git a/models.py b/models.py
index 4b60bdc..f7115cf 100644
--- a/models.py
+++ b/models.py
@@ -28,7 +28,7 @@ class CheaterTeams(db.Model):
)
def __init__(
- self, challenge_id: int, cheater_id: int, helper_id: int, flag_id: str
+ self, challenge_id: int, cheater_id: int, helper_id: int, flag_id: int
):
self.challenge_id = challenge_id
self.cheater_id = cheater_id