Closes #15 (Thanks mwinstead3790), various fixes

This commit is contained in:
CodeKevin
2015-03-14 23:01:21 -04:00
parent b4dd54d36a
commit f2484c519a
7 changed files with 111 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
from flask import render_template, request, redirect, abort, jsonify, url_for, session
from CTFd.utils import sha512, is_safe_url, authed, admins_only, is_admin, unix_time, unix_time_millis, get_config, set_config, get_digitalocean, sendmail
from CTFd.utils import sha512, is_safe_url, authed, admins_only, is_admin, unix_time, unix_time_millis, get_config, set_config, get_digitalocean, sendmail, rmdir
from CTFd.models import db, Teams, Solves, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config
from itsdangerous import TimedSerializer, BadTimeSignature
from werkzeug.utils import secure_filename
@@ -23,7 +23,10 @@ def init_admin(app):
admin = Teams.query.filter_by(name=request.form['name'], admin=True).first()
if admin and bcrypt_sha256.verify(request.form['password'], admin.password):
session.regenerate() # NO SESSION FIXATION FOR YOU
try:
session.regenerate() # NO SESSION FIXATION FOR YOU
except:
pass # TODO: Some session objects dont implement regenerate :(
session['username'] = admin.name
session['id'] = admin.id
session['admin'] = True
@@ -445,7 +448,6 @@ def init_admin(app):
@app.route('/admin/chal/new', methods=['POST'])
def admin_create_chal():
files = request.files.getlist('files[]')
# Create challenge
@@ -466,20 +468,39 @@ def init_admin(app):
md5hash = hashlib.md5(filename).hexdigest()
if not os.path.exists(os.path.join(app.config['UPLOAD_FOLDER'], md5hash)):
os.makedirs(os.path.join(app.config['UPLOAD_FOLDER'], md5hash))
if not os.path.exists(os.path.join(os.path.normpath(app.config['UPLOAD_FOLDER']), md5hash)):
os.makedirs(os.path.join(os.path.normpath(app.config['UPLOAD_FOLDER']), md5hash))
f.save(os.path.join(app.config['UPLOAD_FOLDER'], md5hash, filename))
db_f = Files(chal.id, os.path.join(app.config['UPLOAD_FOLDER'], md5hash, filename))
f.save(os.path.join(os.path.normpath(app.config['UPLOAD_FOLDER']), md5hash, filename))
db_f = Files(chal.id, os.path.join(os.path.normpath(app.config['UPLOAD_FOLDER']), md5hash, filename))
db.session.add(db_f)
db.session.commit()
db.session.close()
return redirect('/admin/chals')
@app.route('/admin/chal/delete', methods=['POST'])
def admin_delete_chal():
challenge = Challenges.query.filter_by(id=request.form['id']).first()
if challenge:
WrongKeys.query.filter_by(chal=challenge.id).delete()
Solves.query.filter_by(chalid=challenge.id).delete()
Keys.query.filter_by(chal=challenge.id).delete()
files = Files.query.filter_by(chal=challenge.id).all()
print files
Files.query.filter_by(chal=challenge.id).delete()
for file in files:
folder = os.path.dirname(file.location)
rmdir(folder)
Tags.query.filter_by(chal=challenge.id).delete()
Challenges.query.filter_by(id=challenge.id).delete()
db.session.commit()
db.session.close()
return '1'
@app.route('/admin/chal/update', methods=['POST'])
def admin_update_chal():
challenge=Challenges.query.filter_by(id=request.form['id']).first()
challenge = Challenges.query.filter_by(id=request.form['id']).first()
challenge.name = request.form['name']
challenge.description = request.form['desc']
challenge.value = request.form['value']

View File

@@ -105,7 +105,10 @@ Did you initiate a password reset?
# team = Teams.query.filter_by(name=request.form['name'], password=sha512(request.form['password'])).first()
team = Teams.query.filter_by(name=request.form['name']).first()
if team and bcrypt_sha256.verify(request.form['password'], team.password):
# session.regenerate() # NO SESSION FIXATION FOR YOU
try:
session.regenerate() # NO SESSION FIXATION FOR YOU
except:
pass # TODO: Some session objects don't implement regenerate :(
session['username'] = team.name
session['id'] = team.id
session['admin'] = team.admin

View File

@@ -6,7 +6,7 @@ SESSION_TYPE = "filesystem"
SESSION_FILE_DIR = "/tmp/flask_session"
SESSION_COOKIE_HTTPONLY = True
HOST = ".ctfd.io"
UPLOAD_FOLDER = 'static/uploads'
UPLOAD_FOLDER = os.path.normpath('static/uploads')
##### EMAIL #####
CTF_NAME = ''

View File

@@ -12,6 +12,8 @@ import time
import datetime
import hashlib
import digitalocean
import shutil
def init_utils(app):
app.jinja_env.filters['unix_time'] = unix_time
@@ -26,9 +28,11 @@ def pages():
pages = Pages.query.filter(Pages.route!="index").all()
return pages
def authed():
return bool(session.get('id', False))
def is_setup():
setup = Config.query.filter_by(key='setup').first()
if setup:
@@ -36,6 +40,7 @@ def is_setup():
else:
return False
def is_admin():
if authed():
return session['admin']
@@ -50,6 +55,7 @@ def can_register():
else:
return True
def admins_only(f):
@wraps(f)
def decorated_function(*args, **kwargs):
@@ -89,6 +95,7 @@ def ctftime():
return False
def can_view_challenges():
config = Config.query.filter_by(key="view_challenges_unregistered").first()
if config:
@@ -96,24 +103,30 @@ def can_view_challenges():
else:
return authed()
def unix_time(dt):
epoch = datetime.datetime.utcfromtimestamp(0)
delta = dt - epoch
return int(delta.total_seconds())
def unix_time_millis(dt):
return unix_time(dt) * 1000
def long2ip(ip_int):
return inet_ntoa(pack('!I', ip_int))
def ip2long(ip):
return unpack('!I', inet_aton(ip))[0]
def get_kpm(teamid): # keys per minute
one_min_ago = datetime.datetime.utcnow() + datetime.timedelta(minutes=-1)
return len(db.session.query(WrongKeys).filter(WrongKeys.team == teamid, WrongKeys.date >= one_min_ago).all())
def get_config(key):
config = Config.query.filter_by(key=key).first()
if config:
@@ -121,6 +134,7 @@ def get_config(key):
else:
return None
def set_config(key, value):
config = Config.query.filter_by(key=key).first()
if config:
@@ -137,6 +151,7 @@ def mailserver():
return True
return False
def sendmail(addr, text):
try:
msg = Message("Message from {0}".format(app.config['CTF_NAME']), sender = app.config['ADMINS'][0], recipients = [addr])
@@ -146,14 +161,20 @@ def sendmail(addr, text):
except:
return False
def rmdir(dir):
shutil.rmtree(dir, ignore_errors=True)
def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc
def sha512(string):
return hashlib.sha512(string).hexdigest()
def get_digitalocean():
token = get_config('do_api_key')
if token:

View File

@@ -5,7 +5,7 @@ Flask-SQLAlchemy==2.0
Flask-Session==0.1.1
SQLAlchemy==0.9.8
passlib==1.6.2
py-bcrypt==0.4
bcrypt
six==1.8.0
itsdangerous
python-digitalocean

View File

@@ -4,14 +4,14 @@ function loadchal(id) {
obj = $.grep(challenges['game'], function (e) {
return e.id == id;
})[0]
$('#update-challenge .chal-name').val(obj.name)
$('#update-challenge .chal-desc').html(obj.description)
$('#update-challenge .chal-value').val(obj.value)
$('#update-challenge .chal-category').val(obj.category)
$('#update-challenge .chal-id').val(obj.id)
$('#update-challenge .chal-delete').attr({
'href': '/admin/chal/close/' + (id + 1)
})
$('.chal-name').val(obj.name);
$('.chal-desc').html(obj.description);
$('.chal-value').val(obj.value);
$('.chal-category').val(obj.category);
$('.chal-id').val(obj.id);
//$('#update-challenge .chal-delete').attr({
// 'href': '/admin/chal/close/' + (id + 1)
//})
$('#update-challenge').foundation('reveal', 'open');
}
@@ -27,14 +27,14 @@ function submitkey(chal, key) {
function loadkeys(chal){
$.get('/admin/keys/' + chal, function(data){
$('#keys-chal').val(chal)
$('#keys-chal').val(chal);
keys = $.parseJSON(JSON.stringify(data));
keys = keys['keys']
$('#current-keys').empty()
keys = keys['keys'];
$('#current-keys').empty();
for(x=0; x<keys.length; x++){
$('#current-keys').append($("<input class='current-key' type='text'>").val(keys[x].key))
$('#current-keys').append('<input type="radio" name="key_type['+x+']" value="0">Static')
$('#current-keys').append('<input type="radio" name="key_type['+x+']" value="1">Regex')
$('#current-keys').append($("<input class='current-key' type='text'>").val(keys[x].key));
$('#current-keys').append('<input type="radio" name="key_type['+x+']" value="0">Static');
$('#current-keys').append('<input type="radio" name="key_type['+x+']" value="1">Regex');
$('#current-keys input[name="key_type['+x+']"][value="'+keys[x].type+'"]').prop('checked',true);
}
});
@@ -76,6 +76,10 @@ function deletetag(tagid){
$.post('/admin/tags/'+tagid+'/delete', {'nonce': $('#nonce').val()});
}
function deletechal(chalid){
$.post('/admin/chal/delete', {'nonce':$('#nonce').val(), 'id':chalid});
}
function updatetags(){
tags = [];
chal = $('#tags-chal').val()
@@ -115,6 +119,7 @@ function deletefile(chal, file, elem){
function loadchals(){
$('#challenges').empty();
$.post("/admin/chals", {
'nonce': $('#nonce').val()
}, function (data) {
@@ -169,6 +174,20 @@ $('#submit-tags').click(function (e) {
updatetags()
});
$('#delete-chal > form').submit(function(e){
e.preventDefault();
$.post('/admin/chal/delete', $(this).serialize(), function(data){
console.log(data)
if (data){
loadchals();
$('#delete-chal').foundation('reveal', 'close');
}
else {
alert('There was an error');
}
})
});
$(".tag-insert").keyup(function (e) {
if (e.keyCode == 13) {
tag = $('.tag-insert').val()
@@ -184,14 +203,14 @@ $(".tag-insert").keyup(function (e) {
$('.create-category').click(function (e) {
$('#new-category').foundation('reveal', 'open');
});
count = 1
count = 1;
$('#create-key').click(function (e) {
$('#current-keys').append("<input class='current-key' type='text' placeholder='Blank Key'>");
$('#current-keys').append('<input type="radio" name="key_type['+count+']" value="0">Static');
$('#current-keys').append('<input type="radio" name="key_type['+count+']" value="1">Regex');
count++
count++;
});
$(function(){
loadchals()
loadchals();
})

View File

@@ -15,7 +15,7 @@
<h3>New Challenge</h3>
<input type='text' name='name' placeholder='Name'><br/>
<textarea name='desc' placeholder='Description'></textarea><br/>
<input type='text' name='value' placeholder='Value'><br/>
<input type='number' name='value' placeholder='Value'><br/>
<input type='text' name='key' placeholder='Key'><br/>
<input type="radio" name="key_type[0]" value="0">Static
<input type="radio" name="key_type[0]" value="1">Regex
@@ -33,7 +33,7 @@
<input name='nonce' type='hidden' value="{{ nonce }}">
<input type='text' name='name' placeholder='Name'><br/>
<textarea class="textbox" name='desc' placeholder='Description'></textarea><br/>
<input type='text' name='value' placeholder='Value'><br/>
<input type='number' name='value' placeholder='Value'><br/>
<input id="new-chal-category" type="hidden" name="category">
<input type='text' name='key' placeholder='Key'><br/>
@@ -49,6 +49,21 @@
</form>
</div>
<div id="delete-chal" class="reveal-modal" data-reveal>
<h2 class="text-center">Delete Challenge</h2>
<form method="POST" action="/admin/chal/delete">
<input type="hidden" name="nonce" value="{{ nonce }}">
<input type="hidden" name="id" class="chal-id">
<div class="small-6 small-centered text-center columns">
<p>Are you sure you want to delete this challenge?</p>
<p>Solves, wrong keys, files, tags will all be deleted.</p>
<button type="button" class="button alert radius" onclick="$('#delete-chal').foundation('reveal', 'close');">No</button>
<button type="submit" id="delete-user" class="button success radius">Yes</button>
</div>
</form>
<a class="close-reveal-modal">&#215;</a>
</div>
<div id="update-keys" class="reveal-modal" data-reveal>
<form method="POST" action="/admin/keys">
<h3>Keys</h3>
@@ -100,7 +115,7 @@
<input name='nonce' type='hidden' value="{{ nonce }}">
<input class="chal-name" type='text' name='name' placeholder='Name'><br/>
<textarea class="chal-desc textbox" name='desc' placeholder='Description'></textarea><br/>
<input class="chal-value" type='text' name='value' placeholder='Value'><br/>
<input class="chal-value" type='number' name='value' placeholder='Value'><br/>
<select class="chal-category" name="category">
<option>-</option>
</select>
@@ -109,6 +124,7 @@
<a href="#" data-reveal-id="update-tags" class="secondary button">Tags</a>
<a href="#" data-reveal-id="update-files" class="secondary button">Files</a>
<a href="#" data-reveal-id="update-keys" class="secondary button">Keys</a>
<a href="#" data-reveal-id="delete-chal" class="secondary alert button">Delete</a>
<button type='submit'>Update</button>
<a class="close-reveal-modal">&#215;</a>
</form>