import os import re import socket import sys class FlagException(Exception): pass class EOFException(Exception): pass class IllegalFlagException(FlagException): pass class InvalidTeamException(FlagException): pass class Action: def __init__(self, actions): self.actions = [] for regexp, fun in actions: self.actions.append((re.compile(regexp), fun)) def Handle(self, flag, line): for regexp, fun in self.actions: regres = regexp.match(line) if regres != None: fun(flag, regres.groups()) return True return False class FlagActions: def __init__(self): self.r = {} # successfully reported flags self.i = [] # invalid flags self.e = [] # expired flags self.a = [] # already reported flags self.b = [] # flags not accepted because our own service is broken self.op = -1 # offensive points actions = [ ("Sorry, you do not have that service running yourself, or your version of the service is broken.", self.broken), ("Team ID ([0-9]+) invalid!", self.invalid), ("Flag \"([^\"]+)\" does not exist!", self.illegal), ("""You successfully reported a flag for service ([A-Za-z 0-9]+?) from team ([A-Za-z0-9 ]+?).""", self.succ), ("""You now have ([0-9]+) offensive points.""", self.points), ("Sorry, you cannot report a flag twice.", self.twice), ("You cannot report your own flags!", self.own), ("This flag is not valid anymore!", self.expired) ] self.action = Action(actions) def broken(self, flag, foo): self.b.append(flag) def invalid(self, flag, foo): raise InvalidTeamException(foo[0]) def illegal(self, flag, foo): self.i.append(foo[0]) def succ(self, flag, foo): self.r[flag] = foo def points(self, flag, foo): self.op = int(foo[0]) def twice(self, flag, foo): self.a.append(flag) def own(self, flag, foo): pass def expired(self, flag, foo): self.e.append(flag) def Handle(self, flag, line): self.action.Handle(flag, line) class LineReader: buf = '' def __init__(self, socket): self.s = socket self.pre = re.compile("^\x1B\\[[A-Za-z0-9;]+m.*\x1B\\[[A-Za-z0-9;]+m *$") def readline(self): while True: pos = self.buf.find("\n") if pos != -1: line, self.buf = self.buf[:pos], self.buf[pos + 1:] return False, line if self.pre.match(self.buf) != None: try: return True, self.buf finally: self.buf = '' frag = self.s.recv(8192) if frag == None: raise EOFException() if len(frag) == 0: raise EOFException() frag = frag.replace("\r", "") if len(frag) == 0: buf, self.buf = self.buf, '' return False, buf # EOF self.buf = self.buf + frag class FlagReporter: def __init__(self, host, port, team, debug=False): self.h = host self.p = port self.t = team self.c = False self.d = debug self.fre = re.compile("^[A-Fa-f0-9]{64}$") self.stats = FlagActions() self.count = 0 def Connect(self): if self.c: return self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.connect((self.h, self.p)) self.lr = LineReader(self.s) self.c = True def WaitReady(self): while True: prompt, line = self.lr.readline() if self.d: print "RECV %s" % line if prompt: break if self.stats.Handle('', line): if self.d: print "Handled." if self.d: print "PROMPT" def ReportFlags(self, flags): self.Connect() for flag in flags: self.ReportFlag(flag) def ReportFlag(self, flag): if self.fre.match(flag) == None: raise IllegalFlagException() self.count = self.count + 1 if self.count >= 100: self.c, self.count = False, 1 # Reconnect after 100 flags self.Connect() self.WaitReady() self.s.sendall('reportflag(%d,"%s")\n' % (self.t, flag)) self.lr.readline() prompt, line = self.lr.readline() if prompt: raise FlagError("Illegal response from the flagserver") self.stats.Handle(flag, line) def die(r): sys.stderr.write("%s\n" % r) sys.exit(111) if __name__ == '__main__': try: import sqlite3 db = sqlite3.connect(os.path.join(os.getenv("HOME", '.'), ".ctfflags.db")) c = db.cursor() try: c.execute("create table flags (uid integer primary key, flag varchar(64))") db.commit() except: print "db seems to already exist" try: c.execute("create index idx_flags on flags (flag)") db.commit() except: pass except: print """WARNING: no sqlite3 package found. Cannot filter already reported flags locally.""" db = None def checkit(flag): if db == None: return True c.execute("select uid from flags where flag=?", (flag,)) if len(c.fetchall()) != 0: return False c.execute("insert into flags (flag) values(?)", (flag,)) db.commit() return True try: [host, port, teamnum, flagfile] = sys.argv[1:] [port, teamnum] = [int(s) for s in [port, teamnum]] except: die("USAGE: %s HOST PORT TEAMNUM FLAGFILE") fr = FlagReporter(host, port, teamnum) ff = open(flagfile, 'r') flags = [i.strip() for i in ff.read().split("\n") if len(i.strip()) == 64] ff.close() afs = len(flags) flags = [flag for flag in flags if checkit(flag)] afs = afs - len(flags) fr.ReportFlags(flags) stats = { 'rep': len(fr.stats.r), 'inv': len(fr.stats.i), 'bro': len(fr.stats.b), 'exp': len(fr.stats.e), 'twi': len(fr.stats.a), 'loc': afs } if fr.stats.op == -1: stats['op'] = 'No valid flags, thus cannot determine' else: stats['op'] = str(fr.stats.op) print """======== REPORT ======== Reported flags: %(rep)d Invalid flags: %(inv)d Own service broken: %(bro)d Expired flags: %(exp)d Already reported flags: %(twi)d Locally caught duplicates: %(loc)d Offensive points: %(op)s""" % stats