diff --git a/CTFd/admin.py b/CTFd/admin.py index 5ec457b8..89ad7588 100644 --- a/CTFd/admin.py +++ b/CTFd/admin.py @@ -71,6 +71,7 @@ def init_admin(app): ctf_name = set_config("ctf_name", request.form.get('ctf_name', None)) mg_api_key = set_config("mg_api_key", request.form.get('mg_api_key', None)) do_api_key = set_config("do_api_key", request.form.get('do_api_key', None)) + max_tries = set_config("max_tries", request.form.get('max_tries', None)) db_start = Config.query.filter_by(key='start').first() db_start.value = start @@ -96,6 +97,11 @@ def init_admin(app): if not do_api_key: set_config('do_api_key', None) + max_tries = get_config('max_tries') + if not max_tries: + set_config('max_tries', 0) + max_tries = 0 + start = get_config('start') if not start: set_config('start', None) @@ -120,6 +126,7 @@ def init_admin(app): db.session.close() return render_template('admin/config.html', ctf_name=ctf_name, start=start, end=end, + max_tries=max_tries, view_challenges_unregistered=view_challenges_unregistered, prevent_registration=prevent_registration, do_api_key=do_api_key, mg_api_key=mg_api_key, prevent_name_change=prevent_name_change) diff --git a/CTFd/challenges.py b/CTFd/challenges.py index 3c9ed2ee..d22abef6 100644 --- a/CTFd/challenges.py +++ b/CTFd/challenges.py @@ -1,6 +1,6 @@ from flask import current_app as app, render_template, request, redirect, abort, jsonify, json as json_mod, url_for, session -from CTFd.utils import ctftime, authed, unix_time, get_kpm, can_view_challenges, is_admin +from CTFd.utils import ctftime, authed, unix_time, get_kpm, can_view_challenges, is_admin, get_config from CTFd.models import db, Challenges, Files, Solves, WrongKeys, Keys import time @@ -58,7 +58,17 @@ def init_challenges(app): db.session.close() json = {'solves':[]} for x in solves: - json['solves'].append({'id':x.id, 'chal':x.chal.name, 'chalid':x.chalid,'team':x.teamid, 'value': x.chal.value, 'category':x.chal.category, 'time':unix_time(x.date)}) + json['solves'].append({ 'chal':x.chal.name, 'chalid':x.chalid,'team':x.teamid, 'value': x.chal.value, 'category':x.chal.category, 'time':unix_time(x.date)}) + return jsonify(json) + + @app.route('/maxattempts') + def attempts(): + chals = Challenges.query.add_columns('id').all() + json = {'maxattempts':[]} + for chal, chalid in chals: + fails = WrongKeys.query.filter_by(team=session['id'], chal=chalid).count() + if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0: + json['maxattempts'].append({'chalid':chalid}) return jsonify(json) @app.route('/fails/', methods=['GET']) @@ -82,9 +92,12 @@ def init_challenges(app): if not ctftime(): return redirect('/') if authed(): + fails = WrongKeys.query.filter_by(team=session['id'],chal=chalid).count() logger = logging.getLogger('keys') data = (time.strftime("%m/%d/%Y %X"), session['username'].encode('utf-8'), request.form['key'].encode('utf-8'), get_kpm(session['id'])) print "[{0}] {1} submitted {2} with kpm {3}".format(*data) + if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0: + return "4" #too many tries on this challenge if get_kpm(session['id']) > 10: wrong = WrongKeys(session['id'], chalid, request.form['key']) db.session.add(wrong) diff --git a/CTFd/views.py b/CTFd/views.py index 0ad7b00c..ecfb71f1 100644 --- a/CTFd/views.py +++ b/CTFd/views.py @@ -62,6 +62,10 @@ def init_views(app): html = request.form['html'] page = Pages('index', html) + #max attempts per challenge + max_tries = Config("max_tries",0) + + ## Start time start = Config('start', None) end = Config('end', None) @@ -77,6 +81,7 @@ def init_views(app): db.session.add(ctf_name) db.session.add(admin) db.session.add(page) + db.session.add(max_tries) db.session.add(start) db.session.add(end) db.session.add(view_challenges_unregistered) diff --git a/static/js/chalboard.js b/static/js/chalboard.js index 19fa28ae..90287e0c 100644 --- a/static/js/chalboard.js +++ b/static/js/chalboard.js @@ -105,6 +105,12 @@ function submitkey(chal, key, nonce) { $('#submit-key').css('background-color', '#e18728') $('#submit-key').prop('disabled', true) } + else if (data == 4){ // too many incorrect solves + $('#submit-key').text('Too many attempts.') + $('#submit-key').css('background-color', 'red') + $('#submit-key').prop('disabled', true) + } + marktoomanyattempts() marksolves() updatesolves() setTimeout(function(){ @@ -129,6 +135,20 @@ function marksolves() { }); } +function marktoomanyattempts() { + $.get('/maxattempts', function (data) { + maxattempts = $.parseJSON(JSON.stringify(data)); + for (var i = maxattempts['maxattempts'].length - 1; i >= 0; i--) { + id = maxattempts['maxattempts'][i].chalid + $('#challenges button[value="' + id + '"]').addClass('secondary') + $('#challenges button[value="' + id + '"]').css('background-color', '#FF9999') + }; + if (window.location.hash.length > 0){ + loadchalbyname(window.location.hash.substring(1)) + } + }); +} + function updatesolves(){ $.get('/chals/solves', function (data) { solves = $.parseJSON(JSON.stringify(data)); @@ -177,6 +197,7 @@ function loadchals() { $('#' + challenges['game'][i].category.replace(/ /g,"-")).append($('')); }; updatesolves() + marktoomanyattempts() marksolves() $('#challenges button').click(function (e) { diff --git a/templates/admin/config.html b/templates/admin/config.html index ddbbb10e..f5d6665d 100644 --- a/templates/admin/config.html +++ b/templates/admin/config.html @@ -12,6 +12,11 @@ +
+ + +
+