mirror of https://github.com/dirtbags/moth.git
Mostly cosmetic changes
This commit is contained in:
parent
f82d78bd6a
commit
da11d73276
5
Makefile
5
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
|
||||
|
|
17
config.py
17
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',
|
||||
|
|
17
ctf.css
17
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: ";
|
||||
}
|
||||
|
||||
|
|
10
flagd.py
10
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)
|
||||
|
|
8
game.py
8
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
|
||||
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
|
@ -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
|
||||
|
||||
|
|
58
intro.html
58
intro.html
|
@ -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>
|
||||
|
|
29
points.py
29
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
|
||||
|
|
2
run.ctfd
2
run.ctfd
|
@ -1,4 +1,4 @@
|
|||
#! /bin/sh
|
||||
|
||||
/usr/lib/ctf/ctfd.py 2>&1 | logger -t ctfd
|
||||
exec /usr/lib/ctf/ctfd.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('''<?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>')
|
||||
|
|
25
teams.py
25
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]
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue