Merge branch 'master' of cfl.lanl.gov:/var/projects/gctf

This commit is contained in:
Alexander Brugh 2009-10-08 11:28:12 -06:00
commit b0815fdb9f
60 changed files with 820 additions and 239 deletions

2
.gitignore vendored
View File

@ -2,7 +2,9 @@
*.pyc
*.dat
*.swp
*.tce
passwd
fake
target/
puzzler/
ctf.tce

View File

@ -17,6 +17,9 @@ all: ctf.tce
target: $(PYC)
$(INSTALL) -d --mode=0755 --owner=100 $(DESTDIR)/var/lib/ctf
$(INSTALL) -d --mode=0755 --owner=100 $(DESTDIR)/var/lib/ctf/survey
$(INSTALL) -d $(DESTDIR)/var/lib/ctf/disabled
touch $(DESTDIR)/var/lib/ctf/disabled/survey
@ -25,6 +28,7 @@ target: $(PYC)
$(INSTALL) -d $(DESTDIR)/usr/sbin
$(INSTALL) ctfd.py $(DESTDIR)/usr/sbin
$(INSTALL) new-contest $(DESTDIR)/usr/sbin
$(INSTALL) -d $(WWWDIR)
$(INSTALL) index.html intro.html ctf.css grunge.png $(WWWDIR)

View File

@ -40,6 +40,7 @@ class Gyopi(irc.Bot):
self._lvl = 0
self._flag.set_flag( self.FLAG_DEFAULT )
self._tokens = []
self._lastAttempt = {}
self._affiliations = {}
self._newPuzzle()
@ -62,7 +63,7 @@ class Gyopi(irc.Bot):
self.last_tb = '%s %s %s' % (t, v, infostr)
print(self.last_tb)
def cmd_join(self, sender, forum, addl):
def cmd_JOIN(self, sender, forum, addl):
"""On join, announce who has the flag."""
if sender.name() in self.nicks:
self._tellFlag(forum)
@ -150,11 +151,11 @@ class Gyopi(irc.Bot):
self._tokens[user].remove(token)
def cmd_privmsg(self, sender, forum, addl):
def cmd_PRIVMSG(self, sender, forum, addl):
text = addl[0]
who = sender.name()
if text.startswith('!'):
parts = text[1:].lower().split(' ', 1)
parts = text[1:].split(' ', 1)
cmd = parts[0]
if len(parts) > 1:
args = parts[1]
@ -179,6 +180,7 @@ class Gyopi(irc.Bot):
elif cmd.startswith('h'):
# Help
forum.msg('Goal: Help me with my math homework, FROM ANOTHER DIMENSION!')
forum.msg('Order of operations is always left to right.')
#forum.msg('Goal: The current winner gets to control the contest music.')
forum.msg('Commands: !help, !flag, !register [TEAM], !solve SOLUTION,!? EQUATION, !ops, !problem, !who')
elif cmd.startswith('prob'):
@ -208,11 +210,15 @@ class Gyopi(irc.Bot):
# self._giveToken(who, sender)
self._saveState()
else:
forum.msg('%s: %s != %s' % (who, attempt, answer))
forum.msg('%s: That is not correct.' % who)
# Test a simple one op command.
elif cmd.startswith('?'):
if not args:
forum.msg('%s: Give me an easier problem, and I\'ll '
'give you the answer.' % who)
return
try:
tokens = badmath.parse(''.join(args))
except (ValueError) as msg:
@ -253,7 +259,7 @@ if __name__ == '__main__':
help='Flag server password')
p.add_option('-d', '--path', dest='path', default='/var/lib/badmath',
help='Path to where we can store state info.')
p.add_option('-c', '--channel', dest='channel', default='+badmath',
p.add_option('-c', '--channel', dest='channel', default='#badmath',
help='Which channel to join')
opts, args = p.parse_args()

Binary file not shown.

View File

@ -10,5 +10,6 @@ dev=16,ino=87557238,mode=40755,uid=0,gid=0,nlink=2,rdev=0
dev=16,ino=87557239,mode=40755,uid=0,gid=0,nlink=2,rdev=0
dev=16,ino=87557240,mode=40755,uid=0,gid=0,nlink=2,rdev=0
dev=16,ino=87573208,mode=100755,uid=0,gid=0,nlink=1,rdev=0
dev=16,ino=87573213,mode=100755,uid=0,gid=0,nlink=1,rdev=0
dev=16,ino=87573285,mode=100755,uid=0,gid=0,nlink=1,rdev=0
dev=16,ino=87573433,mode=100755,uid=0,gid=0,nlink=1,rdev=0
dev=16,ino=87573456,mode=100755,uid=0,gid=0,nlink=1,rdev=0

View File

@ -5,4 +5,4 @@
DATA_PATH=/var/lib/badmath
mkdir -p $DATA_PATH
exec envuidgid ctf python3.0 usr/lib/ctf/badmath/Gyopi.py --data=$DATA_PATH
exec envuidgid ctf python3 /usr/lib/ctf/badmath/Gyopi.py --path=$DATA_PATH

16
ctf.css
View File

@ -77,3 +77,19 @@ p {
margin-bottom: 20px;
color: #f4f4f4;
}
.solved {
text-decoration: line-through;
}
table.pollster {
margin-left: 5em;
}
table.pollster td {
padding: 2px 1em 2px 5px;
}
table.pollster thead {
font-weight: bold;
}

View File

@ -37,6 +37,10 @@ else:
'house_team': 'dirtbags',
'passwd': '/var/lib/ctf/passwd',
'team_colors': team_colors,
'poll_interval': 60,
'poll_timeout': 0.5,
'heartbeat_dir': '/var/lib/pollster',
'poll_dir': '/var/lib/www',
},
'puzzler':
{'dir': '/usr/lib/www/puzzler',
@ -66,3 +70,23 @@ def datafile(filename):
def url(path):
return base_url + path
def start_html(title):
if os.environ.get('GATEWAY_INTERFACE'):
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">
<head>
<title>%s</title>
<link rel="stylesheet" href="%s" type="text/css" />
</head>
<body>
<h1>%s</h1>
''' % (title, css, title))
def end_html():
print('</body></html>')

View File

@ -142,6 +142,8 @@ class FlagServer(asynchat.async_chat):
self.inbuf.append(data)
def set_flag(self, team):
if not self.cat:
return
self.flag = team
self.submitter.set_flag(self.cat, team)
f = open(os.path.join(flags_dir, self.cat), 'w')

View File

@ -27,7 +27,7 @@ cat_re = re.compile(r'^[a-z]+$')
points_re = re.compile(r'^[0-9]+$')
def dbg(*vals):
print('<--: \nContent-type: text/html\n\n--><pre>')
print('<!--: \nContent-type: text/html\n\n--><pre>')
print(*vals)
print('</pre>')
@ -59,8 +59,6 @@ passwd = f.getfirst('w', passwd)
key = f.getfirst('k')
def start_html(title):
if os.environ.get('GATEWAY_INTERFACE'):
print('Content-type: text/html')
if team or passwd:
c = http.cookies.SimpleCookie()
if team:
@ -68,23 +66,9 @@ def start_html(title):
if passwd:
c['passwd'] = passwd
print(c)
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">
<head>
<title>%s</title>
<link rel="stylesheet" href="%s" type="text/css" />
</head>
<body>
<h1>%s</h1>
''' % (title, config.css, title))
def end_html():
print('</body></html>')
config.start_html(title)
end_html = config.end_html
def safe_join(*args):
safe = list(args[:1])
@ -125,7 +109,17 @@ def show_puzzles(cat, cat_dir):
if puzzles:
print('<ul>')
for p in puzzles:
print('<li><a href="%s/%s/%d">%d</a></li>' % (base_url, cat, p, p))
cls = ''
try:
if p in points_by_team[(team, cat)]:
cls = 'solved'
except KeyError:
pass
print('<li><a href="%(base)s/%(cat)s/%(points)d" class="%(class)s">%(points)d</a></li>' %
{'base': base_url,
'cat': cat,
'points': p,
'class': cls})
if p > opened:
break
print('</ul>')

View File

@ -7,6 +7,22 @@ import string
from . import teams
from . import config
def head(title):
return '''<?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>Team Registration</title>
<link rel="stylesheet" href="%s" type="text/css" />
</head>
<body>
<h1>%s</h1>
''' % (config.css, title)
def foot():
return '''</body></html>'''
def main():
print('Content-type: text/html')
print()
@ -17,17 +33,8 @@ def main():
pw = f.getfirst('pw')
confirm_pw = f.getfirst('confirm_pw')
html = string.Template('''<?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>Team Registration</title>
<link rel="stylesheet" href="%s" type="text/css" />
</head>
<body>
<h1>Team Registration</h1>
html = string.Template(config.start_html('Team Registration') +
('''
<p>
Pick a short team name: you'll be typing it a lot.
</p>
@ -50,10 +57,8 @@ def main():
<input type="submit" value="Register" />
</fieldset>
</form>
</body>
</html>
''' % (config.css, config.url('register.cgi')))
</form>''' % config.url('register.cgi')) +
config.end_html())
if not (team and pw and confirm_pw): # If we're starting from the beginning?
html = html.substitute(team_error='',
@ -66,7 +71,9 @@ def main():
pw_match_error='Passwords do not match')
else:
teams.add(team, pw)
html = 'Team registered.'
html = (config.start_html('Team registered') +
('<p>Congratulations, <samp>%s</samp> is now registered. Go <a href="%s">back to the front page</a> and start playing!</p>' % (team, config.url(''))) +
config.end_html())
print(html)

View File

@ -28,9 +28,7 @@ def build_teams():
f = open(passwdfn)
for line in f:
line = line.strip()
team, passwd = [unquote(v) for v in line.strip().split('\t')]
color = team_colors.pop(0)
team_colors.append(color)
team, passwd, color = map(unquote, line.strip().split('\t'))
teams[team] = (passwd, color)
except IOError:
pass
@ -53,10 +51,15 @@ def exists(team):
return team in teams
def add(team, passwd):
build_teams()
color = team_colors[len(teams)%len(team_colors)]
assert team not in teams, "Team already exists."
f = open(passwdfn, 'a')
fcntl.lockf(f, fcntl.LOCK_EX)
f.seek(0, 2)
f.write('%s\t%s\n' % (quote(team), quote(passwd)))
f.write('%s\t%s\t%s\n' % (quote(team), quote(passwd), quote(color)))
def color(team):
t = teams.get(team)
@ -66,6 +69,3 @@ def color(team):
if not t:
return '888888'
return t[1]

View File

@ -4,6 +4,7 @@ import asyncore
import os
import sys
import optparse
import signal
from ctf import pointsd
from ctf import flagd
from ctf import histogram
@ -38,6 +39,7 @@ def main():
pointsrv = pointsd.start()
flagsrv = flagd.start()
signal.signal(signal.SIGCHLD, sigchld)
s = pointsrv.store
slen = 0
while True:

View File

@ -10,7 +10,7 @@
<h1>Welcome</h1>
<ol>
<li><a href="intro.html">Read the introduction</a> to this event</li>
<li><a href="intro.html">Read the introduction and rules</a></li>
<li><a href="register.cgi">Register</a> your team</li>
<li><a href="scoreboard.cgi">View the score board</a></li>
</ol>

18
new-contest Executable file
View File

@ -0,0 +1,18 @@
#! /bin/sh -e
ctime () {
stat -c %z $1 | awk '{ print $1; }'
}
rotate () {
mv $1 $1.$(ctime $1)
}
rotate /var/lib/ctf/puzzler.dat
rotate /var/lib/ctf/scores.dat
rotate /var/lib/ctf/passwd
rm -f /var/lib/ctf/flags/* || true
echo "Things you may want to tweak:"
find /var/lib/ctf/disabled
find /var/lib/kevin/tokens

8
pollster/in.heartbeatd Executable file
View File

@ -0,0 +1,8 @@
#! /bin/sh
case "$REMOTEADDR" in
10.0.0.[2-254])
touch /var/lib/pollster/$REMOTEADDR
;;
esac

249
pollster/pollster.py Executable file
View File

@ -0,0 +1,249 @@
#!/usr/bin/env python3
import os
import re
import sys
import time
import socket
import traceback
import threading
import queue
from ctf import config
from ctf import pointscli
DEBUG = False
POLL_INTERVAL = config.get('poll_interval')
IP_DIR = config.get('heartbeat_dir')
REPORT_PATH = config.get('poll_dir')
SOCK_TIMEOUT = config.get('poll_timeout')
class PointSubmitter(threading.Thread):
''' Pulls point allocations from the queue and submits them. '''
def __init__(self, point_queue):
threading.Thread.__init__(self)
self.point_queue = point_queue
self.sock = pointscli.makesock('localhost')
def run(self):
# loop forever
while(True):
cat, team, score = self.point_queue.get()
if None in [cat, team, score]:
continue
try:
pointscli.submit(cat, team, score, sock=self.sock)
except ValueError:
print('pollster: error submitting score (%s, %s, %d)' % (cat, team, score))
traceback.print_exc()
def socket_poll(ip, port, msg, prot, max_recv=1):
''' Connect via socket to the specified <ip>:<port> using the
specified <prot>, send the specified <msg> and return the
response or None if something went wrong. <max_recvs> specifies
how many times to read from the socket (default to once). '''
# create a socket
try:
sock = socket.socket(socket.AF_INET, prot)
except Exception as e:
print('pollster: create socket failed (%s)' % e)
traceback.print_exc()
return None
sock.settimeout(SOCK_TIMEOUT)
# connect
try:
sock.connect((ip, port))
except socket.timeout as e:
print('pollster: attempt to connect to %s:%d timed out (%s)' % (ip, port, e))
traceback.print_exc()
return None
except Exception as e:
print('pollster: attempt to connect to %s:%d failed (%s)' % (ip, port, e))
traceback.print_exc()
return None
# send something
sock.send(msg)
# get a response
resp = ''
try:
# first read
data = sock.recv(1024)
resp += data.decode('utf-8')
max_recv -= 1
# remaining reads as necessary until timeout or socket closes
while(len(data) > 0 and max_recv > 0):
data = sock.recv(1024)
resp += data.decode('utf-8')
max_recv -= 1
sock.close()
except socket.timeout as e:
print('pollster: timed out waiting for a response from %s:%d (%s)' % (ip, port, e))
traceback.print_exc()
except Exception as e:
print('pollster: receive from %s:%d failed (%s)' % (ip, port, e))
traceback.print_exc()
if len(resp) == 0:
return None
return resp
# PUT POLLS FUNCTIONS HERE
# Each function should take an IP address and return a team name or None
# if (a) the service is not up, (b) it doesn't return a valid team name.
def poll_fingerd(ip):
''' Poll the fingerd service. Returns None or a team name. '''
resp = socket_poll(ip, 79, b'flag\n', socket.SOCK_STREAM)
if resp is None:
return None
return resp.strip('\r\n')
def poll_noted(ip):
''' Poll the noted service. Returns None or a team name. '''
resp = socket_poll(ip, 4000, b'rflag\n', socket.SOCK_STREAM)
if resp is None:
return None
return resp.strip('\r\n')
def poll_catcgi(ip):
''' Poll the cat.cgi web service. Returns None or a team name. '''
request = bytes('GET /cat.cgi/flag HTTP/1.1\r\nHost: %s\r\n\r\n' % ip, 'ascii')
resp = socket_poll(ip, 80, request, socket.SOCK_STREAM, 3)
if resp is None:
return None
content = resp.split('\r\n\r\n')
if len(content) < 3:
return None
content = content[1].split('\r\n')
try:
content_len = int(content[0])
except Exception as e:
return None
if content_len <= 0:
return None
return content[1].strip('\r\n')
def poll_tftpd(ip):
''' Poll the tftp service. Returns None or a team name. '''
resp = socket_poll(ip, 69, b'\x00\x01' + b'flag' + b'\x00' + b'octet' + b'\x00', socket.SOCK_DGRAM)
if resp is None:
return None
if len(resp) <= 5:
return None
resp = resp.split('\n')[0]
return resp[4:].strip('\r\n')
# PUT POLL FUNCTIONS IN HERE OR THEY WONT BE POLLED
POLLS = {
'fingerd' : poll_fingerd,
'noted' : poll_noted,
'catcgi' : poll_catcgi,
'tftpd' : poll_tftpd,
}
ip_re = re.compile('(\d{1,3}\.){3}\d{1,3}')
# start point submitter thread
point_queue = queue.Queue()
t = PointSubmitter(point_queue)
t.start()
# loop forever
while True:
t_start = time.time()
# gather the list of IPs to poll
try:
ips = os.listdir(IP_DIR)
except Exception as e:
print('pollster: could not list dir %s (%s)' % (IP_DIR, e))
traceback.print_exc()
try:
os.remove(REPORT_PATH)
except Exception as e:
pass
try:
out = open(REPORT_PATH, 'w')
except Exception as e:
out = None
pass
if out is not None:
out.write('<html>\n<head>\n')
out.write('<title>Pollster Results</title>\n')
out.write('<link rel="stylesheet" href="ctf.css" type="text/css" media="all" />\n')
out.write('</head><body>\n<h1>Polling Results</h1>\n')
for ip in ips:
# check file name format is ip
if ip_re.match(ip) is None:
continue
# remove the file
try:
os.remove(os.path.join(IP_DIR, ip))
except Exception as e:
print('pollster: could not remove %s' % os.path.join(IP_DIR, ip))
traceback.print_exc()
results = {}
if DEBUG is True:
print('ip: %s' % ip)
if out is not None:
out.write('<h2>%s</h2>\n' % ip)
out.write('<table class="pollster">\n<thead><tr><td>Service Name</td></td>')
out.write('<td>Flag Holder</td></tr></thead>\n')
# perform polls
for service,func in POLLS.items():
team = func(ip)
if team is None:
team = 'dirtbags'
if DEBUG is True:
print('\t%s - %s' % (service, team))
if out is not None:
out.write('<tr><td>%s</td><td>%s</td>\n' % (service, team))
point_queue.put((service, team, 1))
if out is not None:
out.write('</table>\n')
if DEBUG is True:
print('+-----------------------------------------+')
t_end = time.time()
exec_time = int(t_end - t_start)
sleep_time = POLL_INTERVAL - exec_time
if out is not None:
out.write('<p><b>Next poll in: %ds</b></p>\n' % sleep_time)
out.write('</body>\n</html>\n')
out.close()
# sleep until its time to poll again
time.sleep(sleep_time)

3
pollster/run.heartbeatd Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
exec udpsvd 0 9 /usr/sbin/in.heartbeatd

View File

@ -0,0 +1,108 @@
Safe to ex<b>e</b>cute.
<img width="20" height="20" src="data:image;base64,
cjd7PTw9PDw8DQ0NDQ0NDQ8PDA0MDAwMzE5KQnYNDQ1lYmJiYmJiDTk5GRke
HjY2Kg0UFBISEhImJiYNObm9tYEBBQ3tDQ0N7e3t7ejo6A0JCQkJCgoKCh4M
DAwYmZ2VgQAEBRYWFhYFBQUFAQ0NDQwMDAwNDQ0NDQ0NDQ2NiYGBjYmBAQUF
BYWBgQ0ICAgICBgYGBkNDQ2NiYmJCZ2ZBYURFR0dHBwcGAwMDAoKCgoKGhoN
Dw8PD5ufn58LmZ2VAZWRmVFRUQ3FxcXFw8PDw8cNDQ0JCQkJISAgDSWkoKiA
AQUNLQ0NDS0tLS0pKSkNCQkJCVi9ya2tDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQsN
DQ0JCQkJJkojb0AsSGUJYA57AyNQPxEjIyMnJycNHR0dHRwcHBxbQxYWFhYW
FhQUFA0PDw8PCgoKCgkNDQ0LCwsLDg4ODQ8PDw8MDAwMDA0NDQ0NDQ0MDAwN
DQ0NDQ0NDQ0JDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDSMNDQ0NDQ0Np6amDR8fHx9d
XV1dXQ0NDdDQ0NDCwsINPj4+PmLm4uruDQ0NHBwSEhMTEw0NDQ0NDQ0NDS0N
DQ0YGBgYGBgYDQ0NDQ0tLS0tLVIYbjFjBmEIew9oGlk1VCdUMUJCUg1qB2gG
WSpeP38LVAsLZw5sDyF+ET8JCXkMeAsLUhtUC3gMaAFvMHgLbgoKVQpmD21u
MUI2VyVRDmMCZAoKTQFICkkWJCMTExMTERETExINDQ0NDQ0NDAwNDSkpKSk5
OTk5OQ0NDR10HRAQEBINWVlZWVlZWVkFmJyUkpeXl/tuagUCAwMDc+bi6u0P
Dw9a0za1WVG5bGxsbIQwMDAw2IaHh4dOjY1yRyOYnJRrTiazt7+/DQ0N8te7
LioiSg0NDQ3kBPsE+wQoWM3JwamhoaGh5DTLNMv6F0nAIY5qmsqezKRw8/cF
be1uamIzZQ1ljoqCaqVapVquPp3IQaT3Hx8fHx9W1xRndXV1J6wv8Q7xDotL
Pz3CElUOxwSUBJTBSK2OYmrq11fCxs7OeXadgg95efo6Pq7SR0NLtGbHuy4J
AYqaH824U5WQjRgcFBXcH5ZgNYRh4g4Gpzejp6+ISDwlnZ2dnZ0YzbmpKsbK
ojKmogX6KqltffCGhk/OXs6bEvd0mJAT6RmadmoCYubi6uXHOMc48TJn7gta
DF/cMDzU1NTUDVbXFMbX19c/4fMM83793SLdIq+evkG+QciNfVSEPMoL8/HI
Dn1r4tpTpVpO/Hc6yuP0snOKiLF/9gx+42CkqPOt8jv4cfuuJ8KVw5B4eHgN
DVbXFJWEhIQJjq5RrlHcZ0e4R/LbI+IaGJt3e/Z9gmls/AMXoO5t8wx5jmZI
SEhIy8nFnsCfVpUFlcCEYTJg21vPy8NijRkdFf70eQ8PjObiHc1GRcY+wbT5
ofoz8KUsyZpyDQ0NDVbXFA8eHg1dtQP9Av2k/zbOzs7Nzc3NzMzODUQwEHkK
KktrB2IUcR1kRCBBOBhiF2MQeR14eHh4DQ0N8g3yDQ0NDQ3yDfINDQ0NDQ0N
DQ0MDAwMKCgoDQEBAQF5+//3+g0NDTG1sbm9vb0NRcTAyM3Nzc0ZjIiAhoaG
hvJzdwUPDw8PUVFRUVoNDQ0dHR0dCAgIDQ0NDQ0ODg4ObpiclJaWlpaGhoYN
GRkZGQgICAgfDQ0NZefj6/r6+g1t7+vj8fHx8fkNDQ0eHh4eFhYWDfMM85zc
XlpSrfINYmNjY2OTbJNiUNLW3t7e3t7eDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N
DQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ2ZDQkFBQUFBQUFBQWjj4uDNbez
u7u7uw0NDQ0NgRURGRlKCUpwUHg/cSQNLR4wBCocPA4+DjsLPw87GzNhBGAt
ZQRwUGNNeVdhIBgxMTF2NXZMbCViLHlQcENtWXc7GykZKR8vGysfLQVXMlZ2
Pl8rCz4QJAo8ESkAAABKCUpwUHg/cSQNLR4wBCocPA4+DjsLPw87GzNhBGAt
ZQRwUGNNeVdhIBkwMDB3NHdNbSViLHlQcENtWXc7GykZKR8vGysfLQVXMlZ2
Pl8rCz4QJAo8ESgBAQFKCUpwUHg/cSQNLR4wBCocPA4+DjsLPw87GzNhBGAt
ZQRwUGNNeVdhIBkwMDB3NHdNbSViLHlQcENtWXc7GykZKR8vGysfLQVXMlZ2
Pl8rCz4QJAo8ESkAAAAjUClEMFEzMx1ueQt/Hnx8UiFJOnkLfx58fFI7VSFo
GmpqRCpFMVR6TA5Hah5/GBg2Xmwfd3dZPUQqWSBgYE4qUz1OOkhII0QqX3EH
YhBjCmIMDCJFK15wBmN/DGUKZDtJSWcVaAQqTjdZWXcFYGFPP1MnJwlgDmd5
eVcjRj5KSmQCZApjY00/UDRVIWxsQidPEHYEZQhoaEYlUT5MPz8RaR1yAHNz
XTdUJg0jRz5QMVw1VlYjRCtfX3EWeQ0jfRFlZUsvTjpbWyNBMkFBbwxjDmNo
BnJycnJycnJyDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N
DRYWFg0MDAwMDg4ODhqMiICUlZWVhoaGDQ0NDQ0NDQ0NDA0NDQ0NDQ0uLi4N
CgoKCggICAggjIiAqKmpqYmJiQ0NDQ0NDQ0NDQkNDQ0NDQ0NPDw8DQgICAgK
CgoKQoyIgMjJycnl5eUNCQkJCQkJCQkNDQ0NCQkJCT4+Pg0GBgYGBAQEBHCM
iID09fX1lZWVDQgICAgJCQkJDQ0NDR0dHR0iIiINDg4ODgwMDAzYjIiAVFVV
VQsLCw0NDQ0NDQ0NDQwNDQ0NDQ0NSkpKDfIN8p2fn5+frY+Lg7Gzs7O/v78N
CQkJCQkJCQkLDQ0NDw8PD1tbWw3zDPOcnp6ent6Pi4PDwcHB4eHhDQgICAgJ
CQkJDQ0NDQ0NDQ1ubm4NBAQEBAYGBgZmj4uD4+Hh4enp6Q0JCQkJCQkJCQ0N
DQ0FBQUFaWlpDQQEBAQGBgYGbo+Lg+vp6en5+fkNCQkJCQICAgIGDQ0NBQUF
BXBwcA0MDAwMCgoKCnKPi4P7+fn57u7uDQ0NDQ0NDQ0NCQ0NDQ0NDQ19fX0N
DAwMDAoKCgqaj4uDExERESEhIQ0NDQ0NDQ0NDQkNDQ0JCQkJcnJyDQwMDAwK
CgoKyo+Lg0NBQUE9PDwNDQ0NDQ0NDQ0JDQ0NDQ0NDYyMjA0MDAwMCgoKCjaJ
jYW5vb29p6enDQ0NDQ0NDQ0NCQ0NDQ0NDQ2KiooNDAwMDA4ODg5WiY2F3dnZ
2fr6+g0NDQ0NDQ0NDQkNDQ0NDQ0NgoKCDQwMDAwODg4OcomNhfn9/f35+fkN
DQ0NDQ0NDQ0JDQ0NDQ0NDZSUlA0MDAwMDw8PD4+ZnZUVERERGRkZDQ0NDQ0N
DQ0NCQ0NDQ0NDQ2tra0NDAwMDA8PDw+HmZ2VHRkZGREREQ0NDQ0NDQ0NDQkN
DQ0NDQ0NqqqqDQwMDAwPDw8Pn5mdlQUBAQEFBQUNDQ0NDQ0NDQ0JDQ0NDQ0N
DaGhoQ0LCwsLCAgICJyZnZUBBQUFzc3NDQgICAgICAgIDA0NDQUFBQWwsLAN
DAwMDA8PDw9TmJyUyM3NzcnJyQ0NDQ0NDQ0NDQkNDQ0JCQkJs7OzDQwMDAwP
Dw8Pb5iclPTx8fHl5eUNDQ0NDQ0NDQ0JDQ0NCQkJCcrKyg0MDAwMDw8PD3uY
nJTg5eXl6enpDQ0NDQ0NDQ0NCQ0NDQ0NDQ3ExMQNBQUFBQYGBgaGmJyUFBER
ERUVFQ0NDQ0NDQ0NDQkNDQ0NDQ0Nw8PDDQwMDAwMDAwMDA0NDY2IiIiGh4cN
DQ0NDQ0NDQ0MDQ0NDQ0NDRwcHA0ODg4ODg4ODg4NDQ2DhYWFUlJSDQ0NDQ0N
DQ0NDA0NDQ0NDQ0MDAwNDw8PDw8PDw8PDQ0Nxc7Ozq6qqg0WFhYWOjo6Oj4N
DQ0dHR0dFBQUDQ4ODg4ODg4ODg0NDSU1NTUBAwMNDQ0NDQ0NDQ0MDQ0NDQ0N
DQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NGZicBQUFBQUGBgcHBw0NDSWkoKioqKgN
Dg4MDAwMDAxEjIiAgICAgIODgA0NDQ0Nefj89PQNDQ0ODgoKCgoKDdlYXFRU
VFRUVw0ICAgICAg6uLwFBQUFBQYGAAAADQ0NTc/Lw8PDww0ODgkJCQkJCWmP
i4ODg4ODgICIDQ0NDQ1l5+Pr6w0NDQ4OBwcHBwcNdffz+/v7+/v4DQcHBwcH
B5cVEQUFBQUFBgYNDQ0NDQ3NT0tDQ0NDDQ4OAgICAgICPomNhYWFhYWGhosN
DQ0NDVXR1d3dDQ0NDg4AAAAAAA1x9fH5+fn5+foNAgICAgICghYSBQUFBQUG
BhYWFg0NDYURFR0dHR0NDg4fHx8fHx+PmZ2VlZWVlZaWhA0NDQ0NmQ0JAQEN
DQ0ODh0dHR0dDVHEwMjIyMjIyw0ZGRkZGRl57OgFBQUFBQYGExMTDQ0Neezo
4ODg4A0ODhgYGBgYGJiYnJSUlJSUl5eADQ0NDQ0NDQ0NDQ0NDQ4OFhYWFhYN
DQ0NDQ0NDQ0ODRQUFBQUFBQUFA0NDQ0NDg4UFBQNDQ0NDQ0NDQ0NDQ4OFRUU
FBQU8I+Lg4ODg4OBgY0NHBwcHBwcHBwcDQ0NCQn4BxsbGw2NGR0VFRUVFRQN
HR03Nzc3vysvBQUFBQUEBBUVLQ0NDZ0JDQUFBQUNDAweHltbW1snmJyUlJSU
lJWVgw1ERERExFFVXVwNDQ0MDBsbTk5ODQWGgoqKioqKiA0BAWpqampW1dEF
BQUFBQcHCwsaDQ0NDQ0NDQ0NDQ0JCfgHcHBwcPSZnZWVlZWVlJSEDYmJiYkF
kZWdnQ0NDQwMHR2MjIwNcfXx+fn5+fn4DQICnZ2dnQ2ZnQUFBQUFBAQWFr0N
DQ0VkZWdnZ2dDQ8PAwPCwsLCwg0NDQ0NDQ0JCfjyOjo6Oq46PjY2DQ0NHBwP
D97e3g1V0dXd2dnZ2cgNAwPb29vbW8/LBQUFBQUVF+YZ8A0NDXXg5Ozs7OwN
HB4ICP7+/v4qjoqCwMDAwNLS3g0LCgoKCgoKCqAMDAweHh4eCAkJDXX38/v7
+/v76Q0HBxsaGhraWFwFBQUFBRcXGxs4DAwMjBgcFBQUFA0dH+4RJyYmJqaO
ioLQ0NDQwsLODUtKSkrKX1tTUw0NDR0d7BNBQEANZebi6vLy8vLgDQEBVldX
V1dXVw3Q0NDQwsLCwrYMDAyMGBwUFBQUDR0f7hGUlZWV4ZiclJSUlJS0tKIN
nZycnKAkICgoDQ0NHx8SEoSFhQ2NGR0VFRUVFQUP/gGrqqqqKr+7BQUFBQUV
FeQbqgwMDGz5/fX19fUNHBwJCc7Pz89LmJyUlJSUlISEdfI+Pz8/vysvJycN
DQ0dH+4Rzs/PDVHV0dnd3d3dzA0DA+3s7OyYDQkFBQUFBRUVAwP4DAwMDAwM
DAwMDA0tLS0tIiAgIKCZnZWVlZWVhYd28tfV1dXV1dXV1Q0NDS0tLS0tTi9h
DVI1WDdZBnUBbB5qagl7D3wIfWsNI0BAH0ADVxhfAEwFVgJdAgJdUhZCDV8A
TAVWAlINDVINRwRWCUVEF0McQ0MzHS0tbgFsHHAVYQRgTjw8YzxYN2gPYwxv
DmI9WS1CMEMcbBlhYQd1FHkcQ2kccRxlZTplJnJCEE8KRABfAABfUhZCDV8A
RQtPEFJSDVIURgdKD1BIBkIdQkIdQghLXwBFC08QT08QT2kGWT5SPV8+Ug1u
GnUHdCtKP0dHeRxvGzVWVglNFEMCTwZFRRp8DFNlEhJNEnQdcxpFbB5sDXQr
TiBERFINaRp1KkIjTSlhBARbBGgBYwBfbh1oN1E4Vj8/T3gMfz9/OHQ9fzxS
YE5+fiFIJk87DVIhVTRGMjJtMmsCbAVaO0k7WiNSIVU0RjIybTJeZAZlOlkq
XwBpB2QQEE8QcgFyLV55GGoeHnMSexUVUg1hCGoJViVRMH8LVDlYMV8fXxhB
CEoJVmRKenolUjtVPEgXdgR2F3QrTiBERCBBNVRSIVU0RjIybQtiYwoKVQp6
CG0EamQQTy5cLk82aQxjBwdYPVk4TC0tUhVZFlQVWQZJD0sYXQlWAkMBTQhS
Ug1oBmJiPWILYwp+IUAyQCFYB34KaxltbTJ7NGt+Cm4HaTZDMFUxDVINaQh8
HUIxRWweamo1fwlWBGFqA3AEYRNQPF0ufhtoaDdoGGoPZmMKfiFAMkAhWAd+
CmsZbW0ybQpnYgxTIFQ1RzNsMw0=" alt="Santa's helpers binary" />

View File

@ -0,0 +1 @@
It is a lovely day outside

View File

@ -0,0 +1,2 @@
Recovery, while not strictly necessary, may be tremendously helpful.

Binary file not shown.

1
puzzles/compaq/150/key Normal file
View File

@ -0,0 +1 @@
This is our world now... the world of the electron and the switch, the beauty of the baud.

Binary file not shown.

1
puzzles/compaq/350/key Normal file
View File

@ -0,0 +1 @@
Actually, Werner, we're all tickled to here you say that. Frankly, watchin' Donny beat Nazis to death is is the closest we ever get to goin' to the movies.

Binary file not shown.

1
puzzles/compaq/50/key Normal file
View File

@ -0,0 +1 @@
extra special text

Binary file not shown.

1
puzzles/compaq/600/key Normal file
View File

@ -0,0 +1 @@
Now think real hard. You been bird-doggin' this township awhile now. They wouldn't mind a corpse of you. Now, you can luxuriate in a nice jail cell, but if your hand touches metal, I swear by my pretty floral bonnet, I will end you.

View File

@ -1,6 +1,8 @@
#! /usr/bin/env python3
import cgi
import time
import os
f = cgi.FieldStorage()
if f.getfirst('submit'):
@ -8,6 +10,13 @@ if f.getfirst('submit'):
print()
print('Thanks for filling in the survey.')
print()
try:
fn = '/var/lib/ctf/survey/%s.%d.%d.txt' % (time.strftime('%Y-%m-%d'), time.time(), os.getpid())
o = open(fn, 'w')
for k in f.keys():
o.write('%s: %r\n' % (k, f.getlist(k)))
except IOError:
pass
print('The key is:')
print(' quux blorb frotz')
else:

View File

@ -0,0 +1,44 @@
<?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">
<head>
<title>Survey</title>
<link rel="stylesheet" href="%s" type="text/css" />
</head>
<body>
<form method="post" action=",submit.cgi">
<ul>
<li>
Did you have any trouble figuring out how to play?
<select name="getting-started">
<option>A lot of trouble</option>
<option selected="selected">Not much trouble</option>
<option>No trouble</option>
</select>
</li>
<li>
How difficult were the puzzles?
<select name="puzzle-strength">
<option>Too hard</option>
<option selected="selected">About right</option>
<option>Too easy</option>
</select>
</li>
</ul>
<p>
Please use the provided space for any additional comments.
</p>
<textarea name="comments" style="height: 5em; width: 40em;"></textarea>
<p>
Thanks for your feedback! We hope you had fun and learned
something!
</p>
<input type="submit" name="submit" value="Submit survey" />
</form>
</body>
</html>

View File

@ -4,38 +4,7 @@
recieve a key redeemable for <b>ONE MILLION POINTS</b>.
</p>
<fieldset>
<legend>Survey</legend>
<form method="post" action="submit.cgi">
<ul>
<li>
Did you have any trouble figuring out how to play?
<select name="getting-started">
<option>A lot of trouble</option>
<option selected="selected">Not much trouble</option>
<option>No trouble</option>
</select>
</li>
<li>
How difficult were the puzzles?
<select name="puzzle-strength">
<option>Too hard</option>
<option selected="selected">About right</option>
<option>Too easy</option>
</select>
</li>
</ul>
<p>
Please use the provided space for any additional comments.
</p>
<textarea name="comments" style="height: 5em; width: 40em;"></textarea>
<p>
Thanks for your feedback! We hope you had fun and learned
something!
</p>
<input type="submit" name="submit" value="Submit survey" />
</form>
</fieldset>
<object data=",survey.html" type="text/html"
style="width: 100%; height: 40em;">
<a href=",survey.html">Survey</a>
</object>

View File

@ -5,9 +5,9 @@ TARGET = $(CURDIR)/target
FAKE = fakeroot -s $(CURDIR)/fake -i $(CURDIR)/fake
INSTALL = $(FAKE) install
all: 99-pwnables.tce
all: pwnables.tce
99-pwnables.tce: target
pwnables.tce: target
$(FAKE) sh -c 'cd target && tar -czf - --exclude=placeholder --exclude=*~ .' > $@
target:
@ -17,13 +17,5 @@ target:
$(MAKE) -C daemons TARGET=$(TARGET) install
$(INSTALL) -d $(TARGET)/usr/lib/www
$(INSTALL) $(CGI) $(TARGET)/usr/lib/www
$(INSTALL) -D flag $(TARGET)/var/lib/tftp/flag
$(INSTALL) -D flag $(TARGET)/var/lib/notes/flag
$(INSTALL) -D flag $(TARGET)/home/flag/.plan
clean:
rm -rf target
rm -rf target pwnables.tce

View File

@ -1 +0,0 @@
all: in.fingerd

View File

@ -1,38 +0,0 @@
#include <stdio.h>
int
main(int argc, char *argv)
{
char user[256];
char path[512];
char *data;
FILE *f;
size_t count;
int i;
if (NULL == gets(user)) {
return 0;
}
for (data = user; *data; data += 1) {
if ('\r' == *data) {
*data = 0;
}
}
if (0 == user[0]) {
printf("Nobody's home.\n");
return 0;
}
sprintf(path, "/home/%s/.plan", user);
f = fopen(path, "r");
if (NULL == f) {
printf("No such user.\n");
return 0;
}
data = path;
while (count = fread(data, sizeof(*data), 1, f)) {
fwrite(data, count, 1, stdout);
}
return 0;
}

View File

@ -1,3 +0,0 @@
#! /bin/sh
exec tcpsvd 0 79 /usr/sbin/in.fingerd

View File

@ -0,0 +1 @@
dirtbags

View File

@ -0,0 +1 @@
/var/lib/cat/flag

View File

@ -0,0 +1 @@
dirtbags

View File

@ -0,0 +1 @@
dirtbags

View File

@ -0,0 +1 @@
dirtbags

View File

@ -0,0 +1,9 @@
#! /bin/sh
# Busybox netcat doesn't support UDP unless you compile in desktop mode.
# No problem, traceroute can send a UDP packet too.
while true; do
# Apparently traceroute adds 1 to the base port (-p)
traceroute -m 2 -q 1 -p 8 10.0.0.1 2>/dev/null >/dev/null
sleep 10
done

View File

@ -1,3 +0,0 @@
#! /bin/sh
exec udpsvd 0 69 tftpd /var/lib/tftp

3
run.log.ctfd Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
exec logger -t ctfd

View File

@ -3,12 +3,16 @@ INSTALL = $(FAKE) install -o 100
all: tanks.tce
push: tanks.tce
netcat -l -q 0 -p 3333 < tanks.tce
tanks.tce: target
$(FAKE) sh -c 'cd target && tar -czf - .' > $@
target:
$(INSTALL) -d target/var/lib/tanks/
$(INSTALL) -d target/var/lib/tanks/results/
$(INSTALL) -d target/var/lib/tanks/errors/
$(INSTALL) -d target/var/lib/tanks/ai/easy
$(INSTALL) -d target/var/lib/tanks/ai/medium
$(INSTALL) -d target/var/lib/tanks/ai/hard
@ -17,15 +21,16 @@ target:
$(INSTALL) AI/medium/* target/var/lib/tanks/ai/medium/
$(INSTALL) AI/hard/* target/var/lib/tanks/ai/hard/
$(INSTALL) -d target/var/lib/www/tanks/
$(INSTALL) www/* target/var/lib/www/tanks/
$(FAKE) ln -s target/var/lib/tanks/ target/var/lib/www/data
$(INSTALL) -d target/usr/lib/www/tanks/
$(INSTALL) www/* target/usr/lib/www/tanks/
ln -s /var/lib/tanks/results target/usr/lib/www/tanks/results
$(INSTALL) -d target/usr/lib/python2.6/site-packages/tanks/
$(INSTALL) lib/* target/usr/lib/python2.6/site-packages/tanks/
$(INSTALL) -d target/var/service/tanks
$(INSTALL) run target/var/service/tanks/run
$(INSTALL) run run_tanks.py target/var/service/tanks/
$(INSTALL) -d target/var/service/tanks/log/
$(INSTALL) log.run target/var/service/tanks/log/run

View File

@ -48,7 +48,7 @@ def displacePoly(points, disp, limits, coordSequence=False):
maxX, maxY = limits
basePoints = []
for point in points:
x,y = point[0] + disp[0], point[1] + disp[1]
x,y = int(point[0] + disp[0]), int(point[1] + disp[1])
# Check if duplication is needed on each axis
if x > maxX:

View File

@ -9,19 +9,12 @@ from urllib import unquote, quote
from PIL import Image, ImageColor, ImageDraw
try:
from ctf import teams
except:
import sys
path = '/home/pflarr/repos/gctf/'
sys.path.append(path)
from ctf import teams
teams.build_teams()
import Tank
class Pflanzarr:
TEAMS_FILE = '/var/lib/ctf/passwd'
FRAME_DELAY = 15
SPACING = 150
@ -42,12 +35,14 @@ class Pflanzarr:
if not os.path.exists(self._gameDir):
os.mkdir(self._gameDir)
colors = self._getColors()
tmpPlayers = os.listdir(self._playerDir)
players = []
for p in tmpPlayers:
p = unquote(p)
if not (p.startswith('.') or p.endswith('#') or p.endswith('~'))\
and p in teams.teams:
and p in colors:
players.append(p)
AIs = {}
@ -73,7 +68,7 @@ class Pflanzarr:
self._board = (cols*self.SPACING, rows*self.SPACING)
while len(players) < cols*rows:
players.append('#default')
players.append(None)
self._tanks = []
for i in range(cols):
@ -82,13 +77,13 @@ class Pflanzarr:
startY = j*self.SPACING + self.SPACING/2
player = random.choice(players)
players.remove(player)
if player == '#default':
if player == None:
color = '#a0a0a0'
else:
color = '#%s' % teams.teams[player][1]
color = colors[player]
tank = Tank.Tank( player, (startX, startY), color,
self._board, testMode=True)
if player == '#default':
if player == None:
tank.program(random.choice(defaultAIs))
else:
tank.program(AIs[player])
@ -156,10 +151,12 @@ class Pflanzarr:
if tank in kills[tank]:
kills[tank].remove(tank)
self._saveResults(kills)
for tank in self._tanks:
self._outputErrors(tank)
self._makeMovie()
# This needs to go after _makeMovie; the web scripts look for these
# files to see if the game has completed.
self._saveResults(kills)
def _killTanks(self, tanks, reason):
for tank in tanks:
@ -204,22 +201,32 @@ class Pflanzarr:
break
winner = random.choice(winners)
html = ['<html><body>',
html = ['<html>',
'<head><title>Game %d results</title>',
'<link href="/ctf.css" rel="stylesheet" type="text/css">',
'</head>',
'<body>',
'<table><tr><th>Team<th>Kills<th>Cause of Death']
for tank in tanks:
if tank is winner:
rowStyle = 'style="color:red;"'
rowStyle = 'style="font-weight:bold; '\
'background-color:%s"' % tank.color
else:
rowStyle = ''
rowStyle = 'style="background-color:%s"' % tank.color
if tank.name:
name = xml.sax.saxutils.escape(tank.name)
else:
name = '#default'
html.append('<tr %s><td>%s<td>%d<td>%s' %
(rowStyle,
xml.sax.saxutils.escape(tank.name),
name,
len(kills[tank]),
xml.sax.saxutils.escape(tank.deathReason)))
html.append('</table><body></html>')
if winner.name != '#default':
# Write a blank file if the winner is a default tank..
if winner.name != None:
winnerFile.write(tanks[0].name)
winnerFile.close()
@ -243,14 +250,14 @@ class Pflanzarr:
clearFrames = ['rm', '-rf', '%s' % self._imageDir]
print 'Making Movie'
subprocess.call(movieCmd)
# subprocess.call(movieCmd, stderr=open('/dev/null', 'w'),
# stdout=open('/dev/null', 'w'))
# subprocess.call(movieCmd)
subprocess.call(movieCmd, stderr=open('/dev/null', 'w'),
stdout=open('/dev/null', 'w'))
subprocess.call(clearFrames)
def _outputErrors(self, tank):
"""Output errors for each team."""
if tank.name == '#default':
if tank.name == None:
return
if tank._program.errors:
@ -379,6 +386,26 @@ class Pflanzarr:
return defaultAIs
def _getColors(self):
"""Get the team colors from the passwd file. The passwd file location
is set by self.TEAMS_FILE. Returns a dictionary of players->color"""
errorColor = '#ffffff'
try:
file = open(self.TEAMS_FILE)
except:
return {}.fromkeys(players, errorColor)
colors = {}
for line in file:
try:
team, passwd, color = map(unquote, line.split('\t'))
colors[team] = '#%s' % color
except:
colors[team] = errorColor
return colors
def _getGameNum(self):
"""Figure out what game number this is from the past games played."""

View File

@ -75,7 +75,7 @@ class Tank(object):
else:
self._tAngle = tAngle
self._color = color
self.color = color
# You can't fire until fireReady is 0.
self._fireReady = self.FIRE_RATE
@ -466,7 +466,7 @@ class Tank(object):
# The base body rectangle.
for poly in gm.displacePoly(hood, self.pos, self._limits):
d.polygon( poly, fill=self._color )
d.polygon( poly, fill=self.color )
# The treads
for poly in gm.displacePoly(tread1, self.pos, self._limits) + \
@ -475,7 +475,7 @@ class Tank(object):
# The turret circle
for poly in gm.displacePoly(self.body, self.pos, self._limits):
d.ellipse( poly, fill=self._color, outline='black')
d.ellipse( poly, fill=self.color, outline='black')
self._drawLaser(d)
@ -491,7 +491,7 @@ class Tank(object):
if self._fired:
laser = gm.rotatePoly( self.laser, self._angle + self._tAngle )
for poly in gm.displacePoly(laser, self.pos, self._limits):
drawing.polygon(poly, fill=self._color)
drawing.polygon(poly, fill=self.color)
self._fired = False
@ -522,7 +522,7 @@ class Tank(object):
if self._sensorState[i]:
color = '#000000'
else:
color = self._color
color = self.color
r, angle, width, tAttached = self._sensors[i]
r = int(r)

View File

@ -2,5 +2,5 @@
[ -f /var/lib/ctf/disabled/tanks ] && exit 0
envuidgid ctf python2.6 run_tanks.py /var/lib/tanks/ easy 100 &
envuidgid ctf report_score.py
exec envuidgid ctf python2.6 run_tanks.py /var/lib/tanks/ easy 100 2>&1
#envuidgid ctf report_score.py 2>&1

95
tanks/run_tanks.py Normal file → Executable file
View File

@ -1,20 +1,93 @@
#! /usr/bin/python
import asynchat
import asyncore
import optparse
import os
import shutil
import socket
import time
from tanks import Pflanzarr
import sys
T = 60*5
MAX_HIST = 30
HIST_STEP = 100
key = 'tanks:::2bac5e912ff2e1ad559b177eb5aeecca'
try:
while 1:
start = time.time()
p = Pflanzarr(sys.argv[1], sys.argv[2])
p.run(int(sys.argv[3]))
class Flagger(asynchat.async_chat):
"""Use to connect to flagd and submit the current flag holder."""
diff = time.time() - start
if diff - T > 0:
time.sleep( diff - T )
def __init__(self, addr, auth):
asynchat.async_chat.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((addr, 6668))
self.push(auth + '\n')
self.flag = None
except:
print 'Usage: python2.6 run_tanks.py data_dir easy|medium|hard max_turns'
def handle_read(self):
msg = self.recv(4096)
raise ValueError("Flagger died: %r" % msg)
def handle_error(self):
# If we lose the connection to flagd, nobody can score any
# points. Terminate everything.
asyncore.close_all()
asynchat.async_chat.handle_error(self)
def set_flag(self, team):
if team:
eteam = team
else:
eteam = ''
self.push(eteam + '\n')
self.flag = team
def run_tanks(args, turns, flagger):
p = Pflanzarr.Pflanzarr(args[0], args[1])
p.run(turns)
path = os.path.join(args[0], 'results')
files = os.listdir(path)
gameNums = []
for file in files:
try:
gameNums.append( int(file) )
except:
continue
gameNums.sort(reverse=True)
highest = gameNums[0]
for num in gameNums:
if highest - MAX_HIST > num and not (num % HIST_STEP == 0):
shutil.rmtree(os.path.join(path, str(num)))
try:
winner = open('/var/lib/tanks/winner').read().strip()
except:
winner = None
flagger.set_flag(winner)
def main():
parser = optparse.OptionParser('DATA_DIR easy|medium|hard MAX_TURNS')
opts, args = parser.parse_args()
if (len(args) != 3) or (args[1] not in ('easy', 'medium', 'hard')):
parser.error('Wrong number of arguments')
try:
turns = int(args[2])
except:
parser.error('Invalid number of turns')
flagger = Flagger('localhost', key)
lastrun = 0
while True:
asyncore.loop(60, count=1)
now = time.time()
if now - lastrun >= 60:
run_tanks(args, turns, flagger)
lastrun = now
if __name__ == '__main__':
main()

3
tanks/t.py Normal file
View File

@ -0,0 +1,3 @@
import sys
print >> sys.stderr, 'hello'

View File

@ -7,7 +7,7 @@ import os
import sys
try:
from Tanks import Program, setup, conditions, actions, docs
from tanks import Program, setup, conditions, actions, docs
except:
path = os.getcwd().split('/')
path.pop()

View File

@ -1,9 +1,10 @@
#!/usr/bin/python
#!/usr/bin/python3
print """Content-Type: text/html\n\n"""
print """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">\n\n"""
print("""Content-Type: text/html\n\n""")
print("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">\n\n""")
import cgi
import cgitb; cgitb.enable()
import sys
import os
import Config
@ -16,18 +17,17 @@ except:
try:
from ctf import teams
except:
import sys
path = '/home/pflarr/repos/gctf/'
sys.path.append(path)
from ctf import teams
teams.build_teams()
head = open('head.html').read() % "Error Report"
print head
print open('links.html').read()
print(head)
print(open('links.html').read())
def done():
print '</body></html>'
print('</body></html>')
sys.exit(0)
fields = cgi.FieldStorage()
@ -38,25 +38,25 @@ if team and passwd and \
path = os.path.join(Config.DATA_PATH, 'errors', quote(team))
if os.path.isfile(path):
errors = open(path).readlines()
print '<p>Your latest errors:'
print '<div class=errors>'
print('<p>Your latest errors:')
print('<div class=errors>')
if errors:
print '<BR>\n'.join(errors)
print('<BR>\n'.join(errors))
else:
print 'There were no errors.'
print '</div>'
print('There were no errors.')
print('</div>')
else:
print '<p>No error file found.'
print('<p>No error file found.')
done()
if team and team not in teams.teams:
print '<p>Invalid team.'
print('<p>Invalid team.')
if team and team in teams.teams and passwd != teams.teams[team][0]:
print '<p>Invalid password.'
print('<p>Invalid password.')
print '''
print('''
<form action="errors.cgi" method="get">
<fieldset>
<legend>Error report request:</legend>
@ -64,6 +64,6 @@ print '''
Password: <input type="text" name="passwd"><BR>
<button type="get my errors">Submit</button>
</fieldset>
</form>'''
</form>''')
done()

View File

@ -28,22 +28,28 @@ except:
if not games:
print "<p>No games have occurred yet."
gameNums = []
for game in games:
try:
num = int(game)
path = os.path.join( 'results', game, 'results.html')
if os.path.exists( path ):
gameNums.append( int(num) )
else:
continue
gameNums.append( int(game) )
except:
continue
gameNums.sort(reverse=True)
# Don't include games that haven't completed
i = 0
num = str(gameNums[i])
for i in range(len(gameNums)):
path = os.path.join( 'results', str(gameNums[i]), 'results.html') )
if os.path.exists( path ):
break
gameNums = gameNums[i:]
for num in gameNums:
print '<p>%d - ' % num,
print '<a href="results/%d/game.avi">v</a>' % num,
print '<a href="results/%d/results.html">r</a>' % num
print '</body></html>'

View File

@ -1,8 +1,11 @@
#!/usr/bin/python
#!/usr/bin/python3
print("Content-Type: text/html\n\n")
print("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">\n\n""")
import cgi
import cgitb; cgitb.enable()
import os
import sys
import Config
@ -14,21 +17,18 @@ except:
try:
from ctf import teams
except:
import sys
path = '/home/pflarr/repos/gctf/'
sys.path.append(path)
from ctf import teams
teams.build_teams()
print """Content-Type: text/html\n\n"""
print """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">\n\n"""
head = open('head.html').read() % "Submission Results"
print head
print "<H1>Results</H1>"
print open('links.html').read()
print(head)
print("<H1>Results</H1>")
print(open('links.html').read())
def done():
print '</body></html>'
print('</body></html>')
sys.exit(0)
fields = cgi.FieldStorage()
@ -36,21 +36,23 @@ team = fields.getfirst('team', '').strip()
passwd = fields.getfirst('passwd', '').strip()
code = fields.getfirst('code', '')
if not team:
print '<p>No team specified'; done()
print('<p>No team specified'); done()
elif not passwd:
print '<p>No password given'; done()
print('<p>No password given'); done()
elif not code:
print '<p>No program given.'; done()
print('<p>No program given.'); done()
if team not in teams.teams:
print '<p>Team is not registered.'; done()
print('<p>Team is not registered.'); done()
if passwd != teams.teams[team][0]:
print '<p>Invalid password.'; done()
print('<p>Invalid password.'); done()
path = os.path.join(Config.DATA_PATH, 'ai/players', encode(team) )
path = os.path.join(Config.DATA_PATH, 'ai/players', quote(team) )
file = open(path, 'w')
file.write(code)
file.close()
print("<P>Submission Successful")
done()

20
tanksFlagger/Makefile Normal file
View File

@ -0,0 +1,20 @@
FAKE = fakeroot -s fake -i fake
INSTALL = $(FAKE) install -o 100
all: tanksFlagger.tce
push: tanksFlagger.tce
netcat -l -q 0 -p 3333 < tanksFlagger.tce
tanksFlagger.tce: target
$(FAKE) sh -c 'cd target && tar -czf - .' > $@
target:
$(INSTALL) -d target/var/service/tanksFlagger
$(INSTALL) run report_score.py target/var/service/tanksFlagger/
$(INSTALL) -d target/var/service/tanksFlagger/log/
$(INSTALL) log.run target/var/service/tanksFlagger/log/run
clean:
rm -rf target tanksFlagger.tce fake

3
tanksFlagger/log.run Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
exec logger -t tanksFlagger

5
tanksFlagger/run Executable file
View File

@ -0,0 +1,5 @@
#! /bin/sh
[ -f /var/lib/ctf/disabled/tanks ] && exit 0
exec envuidgid ctf python3 report_score.py 2>&1