Mostly cosmetic changes

This commit is contained in:
Neale Pickett 2009-10-01 12:17:03 -06:00
parent f82d78bd6a
commit da11d73276
12 changed files with 158 additions and 59 deletions

View File

@ -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

View File

@ -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',

11
ctf.css
View File

@ -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 {
@ -34,13 +34,19 @@ code, pre, .readme {
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: ";
}

View File

@ -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)
f = open(os.path.join(flags_dir, self.cat), 'w')
if team:
f.write(team)
def found_terminator(self):
data = b''.join(self.inbuf)

View File

@ -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

BIN
grunge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -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

View File

@ -10,7 +10,7 @@
<h1>Introduction</h1>
<p>
Welcome to Capture The Flag, blah blah blah.
Welcome to Capture The Flag.
</p>
<h2>What This Is</h2>
@ -35,7 +35,9 @@
<h3>Important Rules</h3>
<ul>
<li>The contest network is 10.<i>x</i>.<i>x</i>.<i>x</i>. <b>Do
not attack machines outside the contest network</b>.</li>
not attack machines outside the contest network</b>. All
federal, state, and school laws still apply to the outside
network.</li>
<li>If the "outside network" requires you to plug into a different
switch, do not connect any machine that has been on the contest
network.</li>
@ -52,10 +54,49 @@
<li>If IRC is up, you should use it to communicate with the
contest staff. Staff will have operator status in #ctf.</li>
<li>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.</li>
expected to demonstrate the problem and explain what you think
is the correct behavior.</li>
</ul>
<h2>Scoring</h2>
<p>
The contest is made up of multiple <em>categories</em>. 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.
</p>
<p>
There are two kinds of categories: <em>flags</em>,
and <em>puzzles</em>.
</p>
<h3>Flags</h3>
<p>
Flag categories are challenges with a notion of a <em>winner</em>
or <em>service availability</em>. 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.
</p>
<h3>Puzzles</h3>
<p>
Most of the categories come in the form of
multiple <em>puzzles</em>: 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.
</p>
<h2>Hints</h2>
<p>
@ -63,15 +104,16 @@
points, though. For puzzles, you will lose ¼ of the points for
that puzzle <em>even if you never solve the puzzle</em>. 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. <em>It's a hacking contest.</em>
cost. You can try to bribe or otherwise fanagle information out
of us or other contestants. <em>It's a hacking contest.</em>
</p>
<h2>About Us</h2>
<p>
We are the <a href="http://dirtbags.net/">dirtbags</a>. You might
be better at this than we are, but you're not running the contest.
We are the <a href="http://dirtbags.net/">dirtbags</a>. People
pay us money to do the sorts of things you'll be doing in this
contest.
</p>
</body>

View File

@ -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

View File

@ -1,4 +1,4 @@
#! /bin/sh
/usr/lib/ctf/ctfd.py 2>&1 | logger -t ctfd
exec /usr/lib/ctf/ctfd.py

View File

@ -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('''<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>CTF Scoreboard</title>
<title>Scoreboard</title>
<link rel="stylesheet" href="%sctf.css" type="text/css" />
</head>
<body>
<h1>Scoreboard</h1>
''' % config.base_url)
print('<table>')
print('<table class="scoreboard">')
print('<tr>')
print('<th>Overall</th>')
for cat, score in categories:
print('<th>%s (%d)</th>' % (cat, score))
print('<th>')
print(' %s (%d)' % (cat, score))
try:
fn = os.path.join(flags_dir, cat)
team = open(fn).read() or house_team
print(' <br/>')
print(' <!-- flag: %s --> flag: <span style="color: #%s">%s</span>'
% (cat, teams.color(team), team))
except IOError:
pass
print('</th>')
print('</tr>')
print('<tr>')
print('<td><ol>')
totals = []
for team in s.teams:
total = s.team_points(team)
totals.append((total, team))
for total, team in sorted(totals, reverse=True):
print('<li><span style="color: #%s;">%s (%0.3f)</span></li>'
% (teams.color(team), team, total))
print('</ol></td>')
for cat, total in categories:
print('<td style="height: 400px;">')
scores = sorted([(s.team_points_in_cat(cat, team), team) for team in teams])
print('<td>')
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('<div style="height: %f%%; overflow: hidden; background: #%s; color: black;">' % (float(score * 100)/total, color))
print('<!-- category: %s --> %s: %d' % (cat, team, score))
print('</div>')

View File

@ -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]