Skip to content

Commit

Permalink
Merge pull request #8 from D0g3-Lab/dev
Browse files Browse the repository at this point in the history
v1.1.0 Release
  • Loading branch information
0akarma authored Dec 28, 2019
2 parents 7eb45d3 + 09f7701 commit 3ec31d7
Show file tree
Hide file tree
Showing 382 changed files with 46,522 additions and 262 deletions.
60 changes: 60 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,63 @@
1.1.0 / 2019-12-28
=================

* Offer random-ports option in `CTFd-Glowworm` (#7)
* Add more error tips when using `CTFd-Glowworm` plugin
* Fix Error when `docker_max_container_count` is None
* Fix error when `start_time` is not defined
* Support using existing image to start env for `CTFd-Glowworm` plugin
* Fix error when clicking `Expired this instance` button
* Modified messgage with different mode in owl-containers
* Fix bugs when `challenge_name` != `envname`
* Fix bugs when using `Teams` mode in `CTFd-Glowworm` plugins
* Update plugins README

1.0.9 / 2019-12-27
=================

* Seperate schedule-task into plugin-owned
* Uniform network name standard for `CTF_Owl` challenge templates
* Set regular alive time to 3600s for `CTFd_Owl` challenge containers

1.0.8 / 2019-12-26
=================

* Add error tips when using `CTFd-Owl` plugin

1.0.8 / 2019-12-25
=================

* Change `H1ve-theme` index content into admin page setting with route `index`

1.0.7 / 2019-12-24
=================

* Fix scheduled job repeat working
* Add targets and attack-logs message for view.html in `CTFd-Glowworm`
* Add README for `CTFd-Glowworm` plugin
* Uniform network name standard

1.0.6 / 2019-12-23
=================

* Change py-redis to 3.0 to adapt flask_apscheduler
* Fix bugs in `CTFd-Glowworm` plugin
* Add two icons and change some codes to adapt `CTFd-Glowworm` plugin
* Change Dockerfile to an exist base image
* Fix bugs when running with `CTFd-Glowworm` plugin
* Fix jsonify decimal type data error

1.0.5 / 2019-12-21
=================

* Adapt `single-nginx` mode
* Change `.yml` to v3.5 to set exact network name

1.0.3 / 2019-12-19
=================

* Add `CTFd-Glowworm` plugin basic functions

1.0.2 / 2019-12-17
=================

Expand Down
220 changes: 189 additions & 31 deletions CTFd/plugins/ctfd-matrix-scoreboard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
from CTFd import utils, scoreboard, challenges
from CTFd.plugins import override_template
from CTFd.models import db, Teams, Users, Solves, Awards, Challenges
from sqlalchemy.sql.expression import union_all
from CTFd.cache import cache
from CTFd.utils import get_config
from CTFd.utils.modes import get_model
from CTFd.utils.dates import unix_time_to_utc
from CTFd.utils.decorators.visibility import check_score_visibility
from sqlalchemy.sql import or_
from werkzeug.routing import Rule
Expand Down Expand Up @@ -31,31 +36,35 @@ def matrix():
def scores():
return jsonify(get_standings())
def get_standings():
standings = scoreboard.get_standings()
standings = glowworm_get_standings()
# TODO faster lookup here
jstandings = []
# print(standings)
print(standings)
for team in standings:
mode = utils.get_config("user_mode")
if mode == "teams":
teamid = Users.query.filter_by(id=team[0]).first_or_404().team_id
solves = db.session.query(Solves.challenge_id.label('chalid'), Solves.date.label('date')).filter(
teamid = team[0]
basic_solves = db.session.query(Solves.challenge_id.label('chalid'), Solves.date.label('date')).filter(
Solves.team_id == teamid)
else:
teamid = team[0]
solves = db.session.query(Solves.challenge_id.label('chalid'), Solves.date.label('date')).filter(
basic_solves = db.session.query(Solves.challenge_id.label('chalid'), Solves.date.label('date')).filter(
Solves.user_id == teamid)

freeze = utils.get_config('freeze')
if freeze:
freeze = utils.unix_time_to_utc(freeze)
if teamid != session.get('id'):
solves = solves.filter(Solves.date < freeze)
solves = solves.all()
basic_solves = basic_solves.filter(Solves.date < freeze)
basic_solves = basic_solves.all()

jsolves = []
score = 0
for solve in solves:
score = 0 + team[3]
# basic challenge
# 1 first blood
# 2 second blood
# 3 third blood
for solve in basic_solves:
cvalue = Challenges.query.filter_by(id=solve.chalid).first().value
top = Solves.query.filter_by(challenge_id=solve.chalid, type='correct').order_by(Solves.date.asc()).all()
if(solve.date == top[0].date):
Expand All @@ -71,11 +80,30 @@ def get_standings():
solve = str(solve.chalid) + "-0"
score = score + int(cvalue * 1)
jsolves.append(solve)
# ada challenge
# 4 safe
# 5 hacked
try:
from CTFd.plugins.ctfd_glowworm.models import GlowwormAttacks, ADAChallenge
from CTFd.plugins.ctfd_glowworm.extensions import get_round
all_challenges = ADAChallenge.query.all()
for challenge in all_challenges:
envname = challenge.dirname.split('/')[1]
log = GlowwormAttacks.query.filter_by(round=get_round(), victim_id=teamid, envname=envname).first()
if log == None:
solve = str(challenge.id) + "-4"
pass
elif envname == log.envname:
solve = str(challenge.id) + "-5"
pass
jsolves.append(solve)
except Exception as e:
print(e)

if mode == "teams":
jstandings.append({'userid':"", 'teamid':team[0], 'score':score, 'name':team[2],'solves':jsolves})
jstandings.append({'userid':"", 'teamid':team[0], 'score':str(score), 'name':team[2],'solves':jsolves})
else:
jstandings.append({'userid':team[0], 'teamid':"", 'score':score, 'name':team[2],'solves':jsolves})
jstandings.append({'userid':team[0], 'teamid':"", 'score':str(score), 'name':team[2],'solves':jsolves})
# 重新按分数排序
jstandings.sort(key=lambda x: x["score"], reverse=True)
db.session.close()
Expand Down Expand Up @@ -114,28 +142,158 @@ def get_challenges():
return []


def scoreboard_view():
if utils.get_config('view_scoreboard_if_authed') and not utils.authed():
return redirect(url_for('auth.login', next=request.path))
if utils.hide_scores():
return render_template('scoreboard.html',
errors=['Scores are currently hidden'])
standings = get_standings()
return render_template('scoreboard.html', teams=standings,
score_frozen=utils.is_scoreboard_frozen(), challenges=get_challenges())

def scores():
json = {'standings': []}
if utils.get_config('view_scoreboard_if_authed') and not utils.authed():
return redirect(url_for('auth.login', next=request.path))
if utils.hide_scores():
return jsonify(json)

standings = get_standings()
@cache.memoize(timeout=60)
def glowworm_get_standings(count=None, admin=False):
"""
Get standings as a list of tuples containing account_id, name, and score e.g. [(account_id, team_name, score)].
Ties are broken by who reached a given score first based on the solve ID. Two users can have the same score but one
user will have a solve ID that is before the others. That user will be considered the tie-winner.
Challenges & Awards with a value of zero are filtered out of the calculations to avoid incorrect tie breaks.
"""
Model = get_model()

basic_scores = (
db.session.query(
Solves.account_id.label("account_id"),
db.func.sum(0).label("score"),
db.func.max(Solves.id).label("id"),
db.func.max(Solves.date).label("date"),
)
.join(Challenges)
.filter(Challenges.value != 0)
.group_by(Solves.account_id)
)

awards = (
db.session.query(
Awards.account_id.label("account_id"),
db.func.sum(Awards.value).label("score"),
db.func.max(Awards.id).label("id"),
db.func.max(Awards.date).label("date"),
)
.filter(Awards.value != 0)
.group_by(Awards.account_id)
)

try:
from CTFd.plugins.ctfd_glowworm.models import GlowwormAttackLog, ADAChallenge

attack_scores = (
db.session.query(
GlowwormAttackLog.account_id.label("account_id"),
db.func.sum(Challenges.value).label("score"),
db.func.max(GlowwormAttackLog.id).label("id"),
db.func.max(GlowwormAttackLog.date).label("date"),
)
.join(Challenges)
.filter(Challenges.value != 0)
.group_by(GlowwormAttackLog.account_id)
)

from CTFd.plugins.ctfd_glowworm.models import GlowwormCheckLog
check_scores = (
db.session.query(
GlowwormCheckLog.account_id.label("account_id"),
(0-db.func.sum(ADAChallenge.check_value)).label("score"),
db.func.max(GlowwormCheckLog.id).label("id"),
db.func.max(GlowwormCheckLog.date).label("date"),
)
.join(ADAChallenge)
.filter(Challenges.value != 0)
.group_by(GlowwormCheckLog.account_id)
)
from CTFd.plugins.ctfd_glowworm.models import GlowwormInitLog
init_scores = (
db.session.query(
GlowwormInitLog.account_id.label("account_id"),
(0 - db.func.sum(ADAChallenge.check_value)).label("score"),
db.func.max(GlowwormInitLog.id).label("id"),
db.func.max(GlowwormInitLog.date).label("date"),
)
.join(ADAChallenge)
.filter(Challenges.value != 0)
.group_by(GlowwormInitLog.account_id)
)

"""
Filter out solves and awards that are before a specific time point.
"""
freeze = get_config("freeze")
if not admin and freeze:
basic_scores = basic_scores.filter(Solves.date < unix_time_to_utc(freeze))
awards = awards.filter(Awards.date < unix_time_to_utc(freeze))
attack_scores = attack_scores.filter(GlowwormAttackLog.date < unix_time_to_utc(freeze))
check_scores = check_scores.filter(GlowwormCheckLog.date < unix_time_to_utc(freeze))
init_scores = init_scores.filter(GlowwormInitLog.date < unix_time_to_utc(freeze))

"""
Combine awards and solves with a union. They should have the same amount of columns
"""
results = union_all(basic_scores, awards, attack_scores, check_scores, init_scores).alias("results")
except Exception as e:
print(e)
results = union_all(basic_scores, awards).alias("results")

"""
Sum each of the results by the team id to get their score.
"""
sumscores = (
db.session.query(
results.columns.account_id,
db.func.sum(results.columns.score).label("score"),
db.func.max(results.columns.id).label("id"),
db.func.max(results.columns.date).label("date"),
)
.group_by(results.columns.account_id)
.subquery()
)

"""
Admins can see scores for all users but the public cannot see banned users.
Filters out banned users.
Properly resolves value ties by ID.
Different databases treat time precision differently so resolve by the row ID instead.
"""
if admin:
standings_query = (
db.session.query(
Model.id.label("account_id"),
Model.oauth_id.label("oauth_id"),
Model.name.label("name"),
Model.hidden,
Model.banned,
sumscores.columns.score,
)
.join(sumscores, Model.id == sumscores.columns.account_id)
.order_by(sumscores.columns.score.desc(), sumscores.columns.id)
)
else:
standings_query = (
db.session.query(
Model.id.label("account_id"),
Model.oauth_id.label("oauth_id"),
Model.name.label("name"),
sumscores.columns.score,
)
.join(sumscores, Model.id == sumscores.columns.account_id)
.filter(Model.banned == False, Model.hidden == False)
.order_by(sumscores.columns.score.desc(), sumscores.columns.id)
)

"""
Only select a certain amount of users if asked.
"""
if count is None:
standings = standings_query.all()
else:
standings = standings_query.limit(count).all()

for i, x in enumerate(standings):
json['standings'].append({'pos': i + 1, 'id': x['name'], 'team': x['name'],
'score': int(x['score']), 'solves':x['solves']})
return jsonify(json)
return standings

app.register_blueprint(matrix_blueprint)
Loading

0 comments on commit 3ec31d7

Please sign in to comment.