From da11d73276785f8923d60addc791f7b6e5e0cf2b Mon Sep 17 00:00:00 2001
From: Neale Pickett
Date: Thu, 1 Oct 2009 12:17:03 -0600
Subject: [PATCH] Mostly cosmetic changes
---
Makefile | 5 ++++-
config.py | 17 ++++++++++++---
ctf.css | 17 ++++++++++-----
flagd.py | 10 +++++++--
game.py | 8 ++++---
grunge.png | Bin 0 -> 5893 bytes
histogram.py | 6 +++---
intro.html | 58 +++++++++++++++++++++++++++++++++++++++++++-------
points.py | 29 +++++++++----------------
run.ctfd | 2 +-
scoreboard.py | 40 ++++++++++++++++++++++++++--------
teams.py | 25 +++++++++++++++++-----
12 files changed, 158 insertions(+), 59 deletions(-)
create mode 100644 grunge.png
diff --git a/Makefile b/Makefile
index 084fd70..1957826 100644
--- a/Makefile
+++ b/Makefile
@@ -22,13 +22,16 @@ target: $(PYC)
$(INSTALL) ctfd.py $(CTFDIR)
$(INSTALL) -d $(WWWDIR)
- $(INSTALL) index.html intro.html ctf.css $(WWWDIR)
+ $(INSTALL) index.html intro.html ctf.css grunge.png $(WWWDIR)
$(FAKE) ln -s /var/lib/ctf/histogram.png $(WWWDIR)
$(INSTALL) register.cgi scoreboard.cgi puzzler.cgi $(WWWDIR)
$(INSTALL) -d $(DESTDIR)/var/service/ctf
$(INSTALL) run.ctfd $(DESTDIR)/var/service/ctf/run
+ $(INSTALL) -d $(DESTDIR)/var/service/ctf/log
+ $(INSTALL) run.log.ctfd $(DESTDIR)/var/service/ctf/log/run
+
rm -rf $(WWWDIR)/puzzler
$(INSTALL) -d $(WWWDIR)/puzzler
./mkpuzzles.py --htmldir=$(WWWDIR)/puzzler --keyfile=$(CTFDIR)/puzzler.keys
diff --git a/config.py b/config.py
index e7bbeda..3e6fd44 100755
--- a/config.py
+++ b/config.py
@@ -2,17 +2,25 @@
import os
+team_colors = ['F0888A', '88BDF0', '00782B', '999900', 'EF9C00',
+ 'F4B5B7', 'E2EFFB', '89CA9D', 'FAF519', 'FFE7BB',
+ 'BA88F0', '8DCFF4', 'BEDFC4', 'FFFAB2', 'D7D7D7',
+ 'C5B9D7', '006189', '8DCB41', 'FFCC00', '898989']
+
if 'home' in os.environ.get('SCRIPT_FILENAME', ''):
# We're a CGI running out of someone's home directory
config = {'global':
{'data_dir': '.',
'base_url': '.',
'css_url': 'ctf.css',
- 'diasbled_dir': 'disabled'
+ 'diasbled_dir': 'disabled',
+ 'flags_dir': 'flags',
+ 'house_team': 'dirtbags',
+ 'passwd': 'passwd',
+ 'team_colors': team_colors,
},
'puzzler':
{'dir': 'puzzles',
- 'ignore_dir': 'puzzler.ignore',
'cgi_url': 'puzzler.cgi',
'base_url': 'puzzler',
'keys_file': 'puzzler.keys',
@@ -25,10 +33,13 @@ else:
'base_url': '/',
'css_url': '/ctf.css',
'disabled_dir': '/var/lib/ctf/disabled',
+ 'flags_dir': '/var/lib/ctf/flags',
+ 'house_team': 'dirtbags',
+ 'passwd': '/var/lib/ctf/passwd',
+ 'team_colors': team_colors,
},
'puzzler':
{'dir': '/usr/lib/www/puzzler',
- 'ignore_dir': '/var/lib/ctf/puzzler.ignore',
'cgi_url': '/puzzler.cgi',
'base_url': '/puzzler',
'keys_file': '/usr/lib/ctf/puzzler.keys',
diff --git a/ctf.css b/ctf.css
index fa04941..37bd7fe 100644
--- a/ctf.css
+++ b/ctf.css
@@ -1,7 +1,7 @@
/**** document ****/
html {
- background: #222 url(http://dirtbags.net/images/grunge.png) no-repeat;
+ background: #222 url(grunge.png) repeat-x;
}
body {
@@ -29,18 +29,24 @@ a:hover {
}
code, pre, .readme {
- color: #fff;
- background-color: #555;
- margin: 1em;
+ color: #fff;
+ background-color: #555;
+ margin: 1em;
}
+th, td {
+ vertical-align: top;
+}
+
+.scoreboard td {
+ height: 400px;
+}
/**** heading ****/
h1:first-child {
text-transform: lowercase;
font-size: 1.6em;
- letter-spacing: -0.1em;
background-color: #222;
opacity: 0.9;
padding: 3px;
@@ -50,6 +56,7 @@ h1:first-child {
h1:first-child:before {
color: #fff;
+ letter-spacing: -0.1em;
content: "Capture The Flag: ";
}
diff --git a/flagd.py b/flagd.py
index 862afc7..2d1362a 100755
--- a/flagd.py
+++ b/flagd.py
@@ -7,15 +7,19 @@ import functools
import time
import hmac
import optparse
+import os
import points
import pointscli
import teams
+import config
import traceback
key = b'My First Shared Secret (tm)'
def hexdigest(data):
return hmac.new(key, data).hexdigest()
+flags_dir = config.get('global', 'flags_dir')
+
class Submitter(asyncore.dispatcher):
def __init__(self, host='localhost', port=6667):
asyncore.dispatcher.__init__(self)
@@ -110,8 +114,10 @@ class FlagServer(asynchat.async_chat):
def set_flag(self, team):
self.flag = team
- if self.cat:
- self.submitter.set_flag(self.cat, team)
+ self.submitter.set_flag(self.cat, team)
+ f = open(os.path.join(flags_dir, self.cat), 'w')
+ if team:
+ f.write(team)
def found_terminator(self):
data = b''.join(self.inbuf)
diff --git a/game.py b/game.py
index 5104226..9dcd251 100755
--- a/game.py
+++ b/game.py
@@ -59,9 +59,11 @@ class Flagger(asynchat.async_chat):
asynchat.async_chat.handle_error(self)
def set_flag(self, team):
- if not team:
- team = 'dirtbags'
- self.push(team.encode('utf-8') + b'\n')
+ if team:
+ eteam = team.encode('utf-8')
+ else:
+ eteam = b''
+ self.push(eteam + b'\n')
self.flag = team
diff --git a/grunge.png b/grunge.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b98730b0354a8305ea9eb33e64d65ab7de8a393
GIT binary patch
literal 5893
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
literal 0
HcmV?d00001
diff --git a/histogram.py b/histogram.py
index 434ac68..65309d0 100755
--- a/histogram.py
+++ b/histogram.py
@@ -4,6 +4,7 @@ import points
import time
import os
import tempfile
+import teams
import config
pngout = config.datafile('histogram.png')
@@ -16,8 +17,7 @@ def main(s=None):
s = points.Storage()
plotparts = []
- teams = s.teams()
- teamcolors = points.colors(teams)
+ teams = s.teams
catscores = {}
for cat in s.categories():
@@ -27,7 +27,7 @@ def main(s=None):
fn = scoresfile.name
i = 2
for team in teams:
- plotparts.append('"%s" using 1:%d with lines linewidth 2 linetype rgb "#%s"' % (fn, i, teamcolors[team]))
+ plotparts.append('"%s" using 1:%d with lines linewidth 2 linetype rgb "#%s"' % (fn, i, teams.color(team)))
scores[team] = 0
i += 1
diff --git a/intro.html b/intro.html
index 0eea942..f1220cb 100644
--- a/intro.html
+++ b/intro.html
@@ -10,7 +10,7 @@
Introduction
- Welcome to Capture The Flag, blah blah blah.
+ Welcome to Capture The Flag.
What This Is
@@ -35,7 +35,9 @@
Important Rules
The contest network is 10.x .x .x . Do
- not attack machines outside the contest network .
+ not attack machines outside the contest network. All
+ federal, state, and school laws still apply to the outside
+ network.
If the "outside network" requires you to plug into a different
switch, do not connect any machine that has been on the contest
network.
@@ -52,10 +54,49 @@
If IRC is up, you should use it to communicate with the
contest staff. Staff will have operator status in #ctf.
If you think something is wrong with the game, you are
- expected to demonstrate the problem and explain why you think
- it's a problem.
+ expected to demonstrate the problem and explain what you think
+ is the correct behavior.
+ Scoring
+
+
+ The contest is made up of multiple categories . Each
+ category is worth one point toward the total score; the percentage
+ of the total points held by your team is the percentage of one
+ point your team has for that category. The team that has 30% of
+ the points in each of five categories has 1.5 points, whereas the
+ team that has 80% of the points in only one category has 0.8
+ points.
+
+
+
+ There are two kinds of categories: flags ,
+ and puzzles .
+
+
+ Flags
+
+
+ Flag categories are challenges with a notion of a winner
+ or service availability . In these categories, the
+ flag-holder (the winner, or each team with a running service)
+ makes 1 point per minute for as long as they hold the flag. If
+ there is a single flag-holder, and the flag changes hands, a point
+ is awarded to the new winner at the moment the flag moves.
+
+
+ Puzzles
+
+
+ Most of the categories come in the form of
+ multiple puzzles : for each puzzle presented, a key
+ (answer) must be found to recieve the amount of points that puzzle
+ is worth. Any team may answer any puzzle question at any time. A
+ new puzzle is revealed when a team correctly answers the
+ highest-valued puzzle in that category.
+
+
Hints
@@ -63,15 +104,16 @@
points, though. For puzzles, you will lose ΒΌ of the points for
that puzzle even if you never solve the puzzle . For
other events, the staff member will decide how many points it will
- cost. You can try to bribe us or otherwise fanagle information
- out of us, or other contestants. It's a hacking contest.
+ cost. You can try to bribe or otherwise fanagle information out
+ of us or other contestants. It's a hacking contest.
About Us
- We are the dirtbags . You might
- be better at this than we are, but you're not running the contest.
+ We are the dirtbags . People
+ pay us money to do the sorts of things you'll be doing in this
+ contest.
Scoreboard
''' % config.base_url)
- print('')
+ print('')
print('')
+ print('Overall ')
for cat, score in categories:
- print('%s (%d) ' % (cat, score))
+ print('')
+ print(' %s (%d)' % (cat, score))
+ try:
+ fn = os.path.join(flags_dir, cat)
+ team = open(fn).read() or house_team
+ print(' ')
+ print(' flag: %s '
+ % (cat, teams.color(team), team))
+ except IOError:
+ pass
+ print(' ')
print(' ')
print('')
+ print('')
+ totals = []
+ for team in s.teams:
+ total = s.team_points(team)
+ totals.append((total, team))
+ for total, team in sorted(totals, reverse=True):
+ print('%s (%0.3f) '
+ % (teams.color(team), team, total))
+ print(' ')
for cat, total in categories:
- print('')
- scores = sorted([(s.team_points_in_cat(cat, team), team) for team in teams])
+ print(' ')
+ scores = sorted([(s.team_points_in_cat(cat, team), team) for team in s.teams])
for score, team in scores:
- color = teamcolors[team]
+ color = teams.color(team)
print('' % (float(score * 100)/total, color))
print(' %s: %d' % (cat, team, score))
print('
')
diff --git a/teams.py b/teams.py
index a7be5de..4cba5ca 100755
--- a/teams.py
+++ b/teams.py
@@ -2,12 +2,13 @@
import fcntl
import time
+import config
import os
from urllib.parse import quote, unquote
-house = 'dirtbags'
-
-passwdfn = '/var/lib/ctf/passwd'
+house = config.get('global', 'house_team')
+passwdfn = config.get('global', 'passwd')
+team_colors = config.get('global', 'team_colors')
teams = {}
built = 0
@@ -25,7 +26,9 @@ def build_teams():
for line in f:
line = line.strip()
team, passwd = [unquote(v) for v in line.strip().split('\t')]
- teams[team] = passwd
+ color = team_colors.pop(0)
+ team_colors.append(color)
+ teams[team] = (passwd, color)
except IOError:
pass
built = time.time()
@@ -35,7 +38,7 @@ def validate(team):
def chkpasswd(team, passwd):
validate(team)
- if teams.get(team) == passwd:
+ if teams.get(team, [None, None])[0] == passwd:
return True
else:
return False
@@ -51,3 +54,15 @@ def add(team, passwd):
fcntl.lockf(f, fcntl.LOCK_EX)
f.seek(0, 2)
f.write('%s\t%s\n' % (quote(team), quote(passwd)))
+
+def color(team):
+ t = teams.get(team)
+ if not t:
+ validate(team)
+ t = teams.get(team)
+ if not t:
+ return '888888'
+ return t[1]
+
+
+
diff --git a/points.py b/points.py
index b818691..2f419c9 100755
--- a/points.py
+++ b/points.py
@@ -93,7 +93,7 @@ class Storage:
def __init__(self, fn=None):
if not fn:
fn = config.datafile('scores.dat')
- self.points_by_team = {}
+ self.teams = set()
self.points_by_cat = {}
self.points_by_cat_team = {}
self.log = []
@@ -128,7 +128,7 @@ class Storage:
def add(self, req, write=True):
when, cat, team, score = req
- incdict(self.points_by_team, team, score)
+ self.teams.add(team)
incdict(self.points_by_cat, cat, score)
incdict(self.points_by_cat_team, (cat, team), score)
self.log.append(req)
@@ -146,31 +146,23 @@ class Storage:
def categories(self):
return sorted(self.points_by_cat)
- def teams(self):
- return sorted(self.points_by_team)
+ def get_teams(self):
+ return sorted(self.teams)
def cat_points(self, cat):
return self.points_by_cat.get(cat, 0)
def team_points(self, team):
- return self.points_by_team.get(team, 0)
+ points = 0
+ for cat, tot in self.points_by_cat.items():
+ team_points = self.team_points_in_cat(cat, team)
+ points += team_points / float(tot)
+ return points
def team_points_in_cat(self, cat, team):
return self.points_by_cat_team.get((cat, team), 0)
-##
-## Colors
-##
-def colors(teams):
- colors = ['F0888A', '88BDF0', '00782B', '999900', 'EF9C00',
- 'F4B5B7', 'E2EFFB', '89CA9D', 'FAF519', 'FFE7BB',
- 'BA88F0', '8DCFF4', 'BEDFC4', 'FFFAB2', 'D7D7D7',
- 'C5B9D7', '006189', '8DCB41', 'FFCC00', '898989']
- return dict(zip(teams, colors))
-
-
-
##
## Testing
@@ -198,9 +190,8 @@ def test():
s.add((now, 'cat1', 'zebras', 20))
s.add((now, 'cat1', 'aardvarks', 10))
s.add((now, 'merf', 'aardvarks', 50))
- assert s.teams() == ['aardvarks', 'zebras']
+ assert s.get_teams() == ['aardvarks', 'zebras']
assert s.categories() == ['cat1', 'merf']
- assert s.team_points('aardvarks') == 60
assert s.cat_points('cat1') == 30
assert s.team_points_in_cat('cat1', 'aardvarks') == 10
assert s.team_points_in_cat('merf', 'zebras') == 0
diff --git a/run.ctfd b/run.ctfd
index fa143a3..c55f5b3 100755
--- a/run.ctfd
+++ b/run.ctfd
@@ -1,4 +1,4 @@
#! /bin/sh
-/usr/lib/ctf/ctfd.py 2>&1 | logger -t ctfd
+exec /usr/lib/ctf/ctfd.py
diff --git a/scoreboard.py b/scoreboard.py
index e6936d4..f09684f 100755
--- a/scoreboard.py
+++ b/scoreboard.py
@@ -1,42 +1,64 @@
#!/usr/bin/env python3
import cgitb; cgitb.enable()
+import os
import config
+import teams
import points
+flags_dir = config.get('global', 'flags_dir')
+house_team = config.get('global', 'house_team')
+
def main():
s = points.Storage()
- teams = s.teams()
categories = [(cat, s.cat_points(cat)) for cat in s.categories()]
- teamcolors = points.colors(teams)
print('Content-type: text/html')
print()
-
print('''
-
CTF Scoreboard
+
Scoreboard