feat: Add PersonalFlag flag with automatic cheat reporting
This commit is contained in:
26
__init__.py
26
__init__.py
@@ -1,12 +1,14 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from CTFd.models import db
|
from CTFd.plugins import register_admin_plugin_menu_bar, register_plugin_assets_directory
|
||||||
from CTFd.plugins import register_admin_plugin_menu_bar
|
|
||||||
from CTFd.plugins.migrations import upgrade
|
from CTFd.plugins.migrations import upgrade
|
||||||
|
from CTFd.plugins.flags import FLAG_CLASSES
|
||||||
from CTFd.utils.decorators import admins_only
|
from CTFd.utils.decorators import admins_only
|
||||||
|
from CTFd.models import Flags, db
|
||||||
from flask import Blueprint, render_template, request
|
from flask import Blueprint, render_template, request
|
||||||
|
|
||||||
from .models import CheaterTeams
|
from .models import CheaterTeams
|
||||||
|
from .flags import PersonalFlag
|
||||||
|
|
||||||
PLUGIN_PATH = os.path.dirname(__file__)
|
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()
|
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"])
|
@bp.route("/admin/cheaters", methods=["GET"])
|
||||||
@admins_only
|
@admins_only
|
||||||
def show_cheaters():
|
def show_cheaters():
|
||||||
@@ -33,6 +52,9 @@ def load(app):
|
|||||||
app.db.create_all()
|
app.db.create_all()
|
||||||
upgrade(plugin_name="cheaters")
|
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")
|
register_admin_plugin_menu_bar(title="Cheaters", route="/admin/cheaters")
|
||||||
|
|
||||||
app.register_blueprint(bp)
|
app.register_blueprint(bp)
|
||||||
|
|||||||
11
assets/create.html
Normal file
11
assets/create.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<label>
|
||||||
|
Personal<br>
|
||||||
|
<small>Enter personal flag data</small>
|
||||||
|
</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="content" value="{{ content }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="user id" value="{{ data }}">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="type" value="personal">
|
||||||
16
assets/edit.html
Normal file
16
assets/edit.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<label>
|
||||||
|
Personal<br>
|
||||||
|
<small>Enter personal flag data</small>
|
||||||
|
</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="content" value="{{ content }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="user id" value="{{ data }}">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="type" value="personal">
|
||||||
|
<input type="hidden" name="id" value="{{ id }}">
|
||||||
|
<hr>
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-success float-right">Update</button>
|
||||||
|
</div>
|
||||||
39
flags.py
Normal file
39
flags.py
Normal file
@@ -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
|
||||||
@@ -28,7 +28,7 @@ class CheaterTeams(db.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
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.challenge_id = challenge_id
|
||||||
self.cheater_id = cheater_id
|
self.cheater_id = cheater_id
|
||||||
|
|||||||
Reference in New Issue
Block a user