Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

212 lines (183 sloc) 6.88 KB
from CTFd.plugins import register_plugin_assets_directory
from CTFd.plugins.flags import get_flag_class
from CTFd.models import (
db,
Solves,
Fails,
Flags,
Challenges,
ChallengeFiles,
Tags,
Hints,
)
from CTFd.utils.user import get_ip
from CTFd.utils.uploads import delete_file
from flask import Blueprint
class BaseChallenge(object):
id = None
name = None
templates = {}
scripts = {}
class CTFdStandardChallenge(BaseChallenge):
id = "standard" # Unique identifier used to register challenges
name = "standard" # Name of a challenge type
templates = { # Templates used for each aspect of challenge editing & viewing
"create": "/plugins/challenges/assets/create.html",
"update": "/plugins/challenges/assets/update.html",
"view": "/plugins/challenges/assets/view.html",
}
scripts = { # Scripts that are loaded when a template is loaded
"create": "/plugins/challenges/assets/create.js",
"update": "/plugins/challenges/assets/update.js",
"view": "/plugins/challenges/assets/view.js",
}
# Route at which files are accessible. This must be registered using register_plugin_assets_directory()
route = "/plugins/challenges/assets/"
# Blueprint used to access the static_folder directory.
blueprint = Blueprint(
"standard", __name__, template_folder="templates", static_folder="assets"
)
@staticmethod
def create(request):
"""
This method is used to process the challenge creation request.
:param request:
:return:
"""
data = request.form or request.get_json()
challenge = Challenges(**data)
db.session.add(challenge)
db.session.commit()
return challenge
@staticmethod
def read(challenge):
"""
This method is in used to access the data of a challenge in a format processable by the front end.
:param challenge:
:return: Challenge object, data dictionary to be returned to the user
"""
data = {
"id": challenge.id,
"name": challenge.name,
"value": challenge.value,
"description": challenge.description,
"category": challenge.category,
"state": challenge.state,
"max_attempts": challenge.max_attempts,
"type": challenge.type,
"type_data": {
"id": CTFdStandardChallenge.id,
"name": CTFdStandardChallenge.name,
"templates": CTFdStandardChallenge.templates,
"scripts": CTFdStandardChallenge.scripts,
},
}
return data
@staticmethod
def update(challenge, request):
"""
This method is used to update the information associated with a challenge. This should be kept strictly to the
Challenges table and any child tables.
:param challenge:
:param request:
:return:
"""
data = request.form or request.get_json()
for attr, value in data.items():
setattr(challenge, attr, value)
db.session.commit()
return challenge
@staticmethod
def delete(challenge):
"""
This method is used to delete the resources used by a challenge.
:param challenge:
:return:
"""
Fails.query.filter_by(challenge_id=challenge.id).delete()
Solves.query.filter_by(challenge_id=challenge.id).delete()
Flags.query.filter_by(challenge_id=challenge.id).delete()
files = ChallengeFiles.query.filter_by(challenge_id=challenge.id).all()
for f in files:
delete_file(f.id)
ChallengeFiles.query.filter_by(challenge_id=challenge.id).delete()
Tags.query.filter_by(challenge_id=challenge.id).delete()
Hints.query.filter_by(challenge_id=challenge.id).delete()
Challenges.query.filter_by(id=challenge.id).delete()
db.session.commit()
@staticmethod
def attempt(challenge, request):
"""
This method is used to check whether a given input is right or wrong. It does not make any changes and should
return a boolean for correctness and a string to be shown to the user. It is also in charge of parsing the
user's input from the request itself.
:param challenge: The Challenge object from the database
:param request: The request the user submitted
:return: (boolean, string)
"""
data = request.form or request.get_json()
submission = data["submission"].strip()
flags = Flags.query.filter_by(challenge_id=challenge.id).all()
for flag in flags:
if get_flag_class(flag.type).compare(flag, submission):
return True, "Correct"
return False, "Incorrect"
@staticmethod
def solve(user, team, challenge, request):
"""
This method is used to insert Solves into the database in order to mark a challenge as solved.
:param team: The Team object from the database
:param chal: The Challenge object from the database
:param request: The request the user submitted
:return:
"""
data = request.form or request.get_json()
submission = data["submission"].strip()
solve = Solves(
user_id=user.id,
team_id=team.id if team else None,
challenge_id=challenge.id,
ip=get_ip(req=request),
provided=submission,
)
db.session.add(solve)
db.session.commit()
db.session.close()
@staticmethod
def fail(user, team, challenge, request):
"""
This method is used to insert Fails into the database in order to mark an answer incorrect.
:param team: The Team object from the database
:param chal: The Challenge object from the database
:param request: The request the user submitted
:return:
"""
data = request.form or request.get_json()
submission = data["submission"].strip()
wrong = Fails(
user_id=user.id,
team_id=team.id if team else None,
challenge_id=challenge.id,
ip=get_ip(request),
provided=submission,
)
db.session.add(wrong)
db.session.commit()
db.session.close()
def get_chal_class(class_id):
"""
Utility function used to get the corresponding class from a class ID.
:param class_id: String representing the class ID
:return: Challenge class
"""
cls = CHALLENGE_CLASSES.get(class_id)
if cls is None:
raise KeyError
return cls
"""
Global dictionary used to hold all the Challenge Type classes used by CTFd. Insert into this dictionary to register
your Challenge Type.
"""
CHALLENGE_CLASSES = {"standard": CTFdStandardChallenge}
def load(app):
register_plugin_assets_directory(app, base_path="/plugins/challenges/assets/")
You can’t perform that action at this time.