ctf-2011

old assets from capture-the-flag ictf 2011

git clone https://9o.is/git/ctf-2011.git

riskasses.py

(27158B)


      1 #!/usr/bin/env python
      2 import socket
      3 import threading
      4 import SocketServer
      5 from datetime import datetime
      6 import re
      7 import pickle
      8 import time
      9 import json
     10 from termcolor import colored, cprint
     11 from pprint import pprint as pp
     12 import ipaddr
     13 import threading
     14 
     15 class ictf:
     16     # Risk assesment and money laundering library based on 
     17     # http://ictf.cs.ucsb.edu/iCTF_2011_Description.txt
     18     #
     19     # Missing Information, that will be provided at beginning of iCTF2011:
     20     
     21     #  - Regular expression for flags
     22     #flag_re = r'[A-Fa-f0-9]{40}'
     23     flag_re = r'FOO|BAR|BAZ'
     24     
     25     #  - ID of this team
     26     my_id = "A-team"
     27 
     28     #  - Overall risk function
     29     def risk_function(self, R, M, N, Q):
     30         ''' Calculates the risk, using the following input:
     31         R: Risk associated with service (float, probability, <= 1)
     32         M: Amount of money to launder
     33         N: Amount of money that has been laundered through that team by us
     34         Q: Amount of money that has been laundered through that svc by us
     35         
     36         Must return a probability (float smaller/equal 1.0)'''
     37         
     38         return ((R*(M/10))+0.5((N-700)/(300+ abs(N-700) )+1) + 0.5((Q-1500)/(300+ abs(Q-1500)) +1))/3
     39         
     40     
     41     # - Submit launder requests
     42     def _launder_request(self, amount, flag):
     43         '''Must return a tuple with the following format:
     44         (result, points_gained, msg, exploited_team, exploited_svc) with 
     45         state:         "SUCCESS", "BAD_FLAG", "OLD_FLAG", "BAD_LUCK" or 
     46                        "OTHER"
     47         points_gained: amount of points gained
     48         msg:           whatever flugsubmission returned
     49         exploited_team, exploited_svc: if msg contains that information, 
     50                                        include team/svc id, otherwise None'''
     51         
     52         # TODO
     53         return ("OTHER", 0.0, 'not yet implemented', None, None)
     54     
     55     # - Submit betrayal requests
     56     def _betrayal_request(self, flag):
     57         '''Must return a tuple with the following format:
     58         (result, msg, from_team, from_service) with state being:
     59         "SUCCESS", "OLD_FLAG", "BAD_FLAG" or "OTHER"
     60         and msg whatever flugsubmission returned
     61         from_team, from_service if msg contains that information, 
     62         include team/svc id, otherwise None'''
     63         
     64         # TODO
     65         return ("OTHER",'not yet implemented', None, None)
     66     
     67     # THAT'S IT! NOTHING TO DONE BELOW
     68     
     69     #  - State Pusher receive port
     70     recv_port = 55555
     71     
     72     def __init__(self, my_id=my_id):
     73         self.teams = {}
     74         # Format: 
     75         # { "TEAM1_ID": {'money': 123, 
     76         #                'points': 123, 
     77         #                'svc_up': ["SVC1_ID",...], 
     78         #                'svc_comp': ["SVC99_ID",...],
     79         #                'N': {"TEAM2_ID": 4, ...},
     80         #                'Q': {"SVC1_ID": 6, ...}
     81         #                'D': 0.33, 
     82         #                'subnet': '1.2.3.0/24',
     83         #                'challenges_solved': [30, 5391, 305]}
     84         
     85         self.services = {}
     86         # Format: 
     87         # { "SVC1_ID": {'C': 0.33,    # cut in Percentage/100
     88         #               'P': 0.67, # profit in Percentage/100
     89         #               'R': 0.1}    # risk in Probability
     90         
     91         self.transactions = []
     92         # Format:
     93         # [{ 'time_used': datetime.now(), # Time of usage
     94         #    'flag': 'fooBAR',            # String associated with flag
     95         #    'time_stolen': ...,          # As found in unused_flags
     96         #    'from_team': 'TEAM1_ID',     # As found in unused_flags
     97         #    'from_service': 'SVC1_ID',   # As found in new_flag
     98         #    'action': 'laundering',      # "betrayal" or "laundering"
     99         #    'money_given': 12.3,         # if "laundering", otherwise 0.0
    100         #    'points_gained': 12.3,       # if "laundering" and successfull, 
    101         #                                 # otherwise 0.0 
    102         #    'result': "SUCCESS",         # "SUCCESS", "BAD_FLAG", "BAD_LUCK",
    103         #                                 # "OLD_FLAG" or "OTHER"
    104         #    'msg': 'Flag sub. suc.',     # Whatever flagsubmission returned
    105         #  },...]
    106         
    107         self.unused_flags = []
    108         # Format:
    109         # [{ 'flag': 'fooBAR',            # String associated with flag
    110         #    'time_received': ...,        # Time of reception
    111         #    'from_team': 'TEAM1_ID',     # Where did it come from?
    112         #                                 # if unknown, None
    113         #    'from_service': 'SVC1_ID',   # Where did it come from?
    114         #                                 # if unknown, None
    115         #  },...]
    116         
    117         self.all_flags = []
    118         # List of all flags ever put in unused_flags or transactions
    119         
    120         self.my_id = my_id
    121         self.recv_state_socket = None
    122         self.recv_state_thread = None
    123         self.recv_flag_server = None
    124         
    125         self.flag_dissipater_instance = None
    126         
    127         # Do you want every tick to produce a message?
    128         self.show_ticks = True
    129         self.tick_callback = self.print_scorboard
    130 
    131     def get_risks(self, amount, service=None, team=None):
    132         '''Compiles a list of tuples describing the risk associated in 
    133         laundering *amount* money through each team and service, except
    134         our self.
    135         
    136         Returned list of tuples has the following format:
    137         [(team_id, service_id, risk),...]
    138         
    139         If service and team are given, only one risk will be returned.'''
    140         
    141         current_risks = []
    142         
    143         if team and service:
    144             N = 0
    145             if team in self.my_state['N']:
    146                 # The next line may needs to be exchanged by the following
    147                 # t['N'][self.my_id]
    148                 N = self.my_state['N'][team]
    149 
    150             Q = 0
    151             if service in self.my_state['Q']:
    152                 Q = self.my_state['Q'][service]
    153             
    154             return self.risk_function(self.services[service]['R'], amount, N, Q)
    155         
    156         for t_id,t in self.teams.items():
    157             # Ignore myself
    158             if t_id == self.my_id:
    159                 continue
    160             
    161             N = 0
    162             if t_id in self.my_state['N']:
    163                 # The next line may needs to be exchanged by the following
    164                 # t['N'][self.my_id]
    165                 N = self.my_state['N'][t_id]
    166             
    167             for s_id,s in self.services.items():
    168                 Q = 0
    169                 if s_id in self.my_state['Q']:
    170                     Q = self.my_state['Q'][s_id]
    171                 
    172                 current_risks.append(
    173                     (t_id, s_id, self.risk_function(s['R'], amount, N, Q)))
    174         
    175         return current_risks
    176     
    177     def get_payoffs(self, amount, service=None):
    178         '''Gives a quantitative prediction on how good a service would payoff 
    179         using it to launder *amount* money.
    180         
    181         If *defense* is not set, the quotient will be assumed 1.0 (meaning no 
    182         defense penalty)
    183         
    184         Returned list of tuples has the following format:
    185         [(service_id, payoff_prediction),...]
    186         
    187         If service is given, only that payoff will be returned'''
    188         
    189         if service:
    190             s = self.services[service]
    191             after_cut = amount*(1.0-s['C']) # cut is given as percentage
    192             return after_cut*s['P']*self.my_state['D']
    193         
    194         payoffs = []
    195         
    196         for s_id,s in self.services.items():
    197             # Payoff fuction: (amount laundered - cut) * defense% * payoff%
    198             after_cut = amount*(1.0-s['C']) # cut is given as percentage
    199             payoffs.append((s_id, after_cut*s['P']*self.my_state['D']))
    200         
    201         return payoffs
    202     
    203     def parse_pushed_state(self, json_input):
    204         ''' Updates self.teams and self.services
    205         For format of members, see _init__()
    206         and for json see: http://ictf.cs.ucsb.edu/your_pusher.txt
    207 
    208         Be careful to create new teams and services as they appear!'''
    209         for json_line in json_input.split('\n'):
    210             try:
    211                 state = json.loads(json_line)
    212                 assert type(state) == dict, 'Has to be a dict'
    213             except Exception, err:
    214                 if json_input.strip():
    215                     print 'Could not parse json input line:',json_line
    216                     print 'Error:',err
    217                 return
    218 
    219             self.tick = state['tick']
    220 
    221             for s_id,s in state['services'].items():
    222                 # Parse
    223                 s['C'] = s['C']/100.0
    224                 s['P'] = s['P']/100.0
    225                 s['R'] = s['R']/100.0
    226 
    227                 # Create service if necessary
    228                 if s_id not in self.services:
    229                     self.services[s_id] = s
    230                 else:
    231                     # Or just update
    232                     self.services[s_id].update(s)
    233 
    234             for t_id,t in state['teams'].items():
    235                 # Parse
    236                 t['D'] = t['D']/100.0
    237                 
    238                 # Subnet to ipaddr.IPv4Network object
    239                 t['subnet'] = ipaddr.IPv4Network(t['subnet'])
    240                 
    241                 # N, Q, money, points and challenges_solved are fine like
    242                 # they are, nothing to do
    243 
    244                 # Rename services_compromised_last_tick -> svc_up
    245                 t['svc_up'] = t['services_compromised_last_tick']
    246                 del t['services_compromised_last_tick']
    247                 # and services_up_last_tick -> svc_comp
    248                 t['svc_comp'] = t['services_up_last_tick']
    249                 del t['services_up_last_tick']
    250                 
    251                 # Create team if necessary
    252                 if t_id not in self.teams:
    253                     self.teams[t_id] = t
    254                 else:
    255                     # Or just update
    256                     self.teams[t_id].update(t)
    257 
    258             if self.show_ticks:
    259                 print 'New tick:',self.tick
    260                 if self.tick_callback:
    261                     self.tick_callback()
    262     
    263     def get_flag(self, flag, remove=False):
    264         r = filter(lambda x: x['flag']==flag, self.unused_flags)
    265         
    266         if r:
    267             if remove:
    268                 self.unused_flags.remove(r[0])
    269             return r[0]
    270         else:
    271             return None
    272     
    273     def launder(self, amount, flag_data):
    274         '''Tries to launder *amount* money with flag contained in *flag_data*
    275         *flag_data* must have same format as in unused_flags, see __init__()
    276         and should have been poped (removed) from unused_flags.
    277         
    278         Returns (result, points_gained, msg)'''
    279         
    280         assert amount<=self.teams[self.my_id]['money'], 'Insufficient funds.'
    281         
    282         result, points_gained, msg, from_team, from_service = \
    283             self._launder_request(amount, flag_data['flag'])
    284         
    285         # If from_team or from_service were returned on request, use them,
    286         # they must be correct
    287         if from_team:
    288             flag_data['from_team'] = from_team
    289         if from_service:
    290             flag_data['from_service'] = from_service
    291         
    292         flag_data['time_used'] = datetime.now()
    293         flag_data['action'] = 'laundering'
    294         flag_data['money_given'] = amount
    295         flag_data['points_gained'] = points_gained
    296         flag_data['result'] = result
    297         flag_data['msg'] = msg
    298         
    299         # Reduce our left-over money
    300         if result in ["SUCCESS", "BAD_LUCK"]:
    301             self.teams[self.my_id]['money'] -= amount
    302         # Own points are not used for anything, thus need no update
    303         
    304         self.transactions.append(flag_data)
    305         
    306         return (result, points_gained, msg)
    307     
    308     def betray(self, flag_data):
    309         '''Tries to betray others with flag contained in *flag_data*
    310         *flag_data* must have same format as in unused_flags, see __init__()
    311         and should have been poped (removed) from unused_flags.
    312         
    313         Returns (result, msg)'''
    314         
    315         result, msg, from_team, from_service = \
    316             self._betrayal_request(flag_data['flag'])
    317         
    318         # If from_team or from_service were returned on request, use them, 
    319         # they must be correct
    320         if from_team:
    321             flag_data['from_team'] = from_team
    322         if from_service:
    323             flag_data['from_service'] = from_service
    324         
    325         flag_data['time_used'] = datetime.now()
    326         flag_data['action'] = 'betrayal'
    327         flag_data['money_given'] = 0.0
    328         flag_data['points_gained'] = 0.0
    329         flag_data['result'] = result
    330         flag_data['msg'] = msg
    331         
    332         self.transactions.append(flag_data)
    333         
    334         return (result, msg)
    335     
    336     def add_new_flag(self, flag, team, service):
    337         if flag in self.all_flags:
    338             return False
    339         self.all_flags.append(flag)
    340         
    341         flag_data = { 
    342             'flag': flag, 
    343             'time_received': datetime.now(),
    344             'from_team': team,
    345             'from_service': service,
    346         }
    347         
    348         self.unused_flags.append(flag_data)
    349         return True
    350 
    351     @property
    352     def my_state(self):
    353         return self.teams[self.my_id]
    354 
    355     def start_state_receiver(self, host="localhost", port=recv_port):
    356         assert not self.recv_state_socket, 'Server has already been started!'
    357         
    358         class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    359             def handle(self_server):
    360                 try:
    361                     f = self_server.request.makefile("rb")
    362                     while True:
    363                         self.parse_pushed_state(f.readline())
    364                 except:
    365                     return
    366 
    367         class ThreadedTCPServer(SocketServer.ThreadingMixIn, \
    368             SocketServer.TCPServer):
    369             pass
    370         
    371         self.recv_state_socket = ThreadedTCPServer((host, port), \
    372             ThreadedTCPRequestHandler)
    373         ip, port = self.recv_state_socket.server_address
    374 
    375         # Start a thread with the server -- that thread will then start one
    376         # more thread for each request
    377         self.recv_state_thread = threading.Thread( \
    378             target=self.recv_state_socket.serve_forever)
    379         # Exit the server thread when the main thread terminates
    380         self.recv_state_thread.daemon = True
    381         self.recv_state_thread.start()
    382         
    383         return ip, port
    384 
    385     def stop_state_receiver(self):
    386         if self.recv_state_socket:
    387             self.recv_state_socket.shutdown()
    388 
    389     def find_team(self, needle):
    390         if needle in self.teams.keys():
    391             return needle
    392         
    393         try:
    394             ip = ipaddr.IPv4Address(needle)
    395             teams = filter(lambda x: ip in x[1]['subnet'], self.teams.items())
    396             if teams:
    397                 return teams[0][0]
    398         except:
    399             pass
    400         
    401         team_ids = map(lambda x: (x[0], x[1]['subnet'].ip.exploded.split('.')[2]), \
    402             self.teams.items())
    403         teams = filter(lambda x: needle in x[1], team_ids)
    404         if teams:
    405             return teams[0][0]
    406         
    407         return None
    408         
    409     def start_flag_receiver(self, host="localhost", port=0):
    410         assert not self.recv_flag_server, 'Server has already been started!'
    411         self.recv_flag_server = {}
    412 
    413         class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    414             def handle(self_server):
    415                 try:
    416                     f = self_server.request.makefile("rb")
    417                     
    418                     f.write("Which team? Number, IP or name\n")
    419                     team = self.find_team(f.readline().strip())
    420                     if team == None:
    421                         f.write('Unknown team!\n')
    422                         return
    423                     
    424                     f.write("Which service? Choices: "+ \
    425                         str(self.services.keys())+"\n")
    426                     service = f.readline().strip()
    427                     if service not in self.services.keys():
    428                         f.write('Unknown service!\n')
    429                         return
    430                     
    431                     while True:
    432                         recv = f.readline()
    433                         if not recv: break
    434                     
    435                         counter_new, counter_old = 0, 0
    436                         for m in re.finditer(self.flag_re, recv):
    437                             if self.add_new_flag(m.group(0), team, service):
    438                                 counter_new += 1
    439                             else:
    440                                 counter_old += 1
    441                         
    442                         if counter_new or counter_old:
    443                             self_server.request.send(str(counter_new)+ \
    444                             ' new and '+str(counter_old)+' old flags.\n')
    445                         else:
    446                             self_server.request.send('No flags :(\n')
    447                 except Exception, e:
    448                     # Client may have disconnected
    449                     return
    450                 
    451         class ThreadedTCPServer(SocketServer.ThreadingMixIn, \
    452             SocketServer.TCPServer):
    453             pass
    454 
    455         self.recv_flag_server['socket'] = ThreadedTCPServer((host, port), \
    456             ThreadedTCPRequestHandler)
    457         ip, port = self.recv_flag_server['socket'].server_address
    458         self.recv_flag_server['ip'] = ip
    459         self.recv_flag_server['port'] = port
    460 
    461         # Start a thread with the server -- that thread will then start one
    462         # more thread for each request
    463         self.recv_flag_server['thread'] = threading.Thread( \
    464             target=self.recv_flag_server['socket'].serve_forever)
    465         # Exit the server thread when the main thread terminates
    466         self.recv_flag_server['thread'].daemon = True
    467         self.recv_flag_server['thread'].start()
    468 
    469         return ip, port
    470 
    471     def stop_flag_receiver(self):
    472         if self.recv_flag_server:
    473             self.recv_flag_server['socket'].shutdown()
    474             del self.recv_flag_server
    475     
    476     def print_scorboard(self, sort_by='points', reverse=True):
    477         print
    478         print "Scorboard as of tick",str(self.tick)+':'
    479         
    480         teams = sorted(self.teams.items(), key=lambda x: x[1][sort_by], \
    481             reverse=reverse)
    482         cprint('% 20s % 8s % 8s % 7s' % \
    483             ('Team', 'Points', 'Money', 'Chal.'), attrs=['bold'])
    484         for t_id,t in teams:
    485             if t_id == self.my_id:
    486                 cprint('% 20s % 8i % 8i % 7i' % \
    487                     (t_id, t['points'], t['money'], len(t['challenges_solved'])), \
    488                     'blue', 'on_red')
    489             else:
    490                 print '% 20s % 8i % 8i % 7i' % \
    491                     (t_id, t['points'], t['money'], len(t['challenges_solved']))
    492     
    493     def print_risks(self, amount, reverse=False):
    494         print
    495         print "Risks for converion of",amount,"as of tick",str(self.tick)+':'
    496 
    497         risks = sorted(self.get_risks(amount), key=lambda x: x[2], \
    498             reverse=reverse)
    499         cprint('% 20s % 10s % 6s' % \
    500             ('Team', 'Service', 'Risk'), attrs=['bold'])
    501         for t_id,s_id,risk in risks:
    502             have_flags = False
    503             for f in self.unused_flags:
    504                 if f['from_team'] == t_id and f['from_service'] == s_id:
    505                     have_flags = True
    506                     break
    507             
    508             if have_flags:
    509                 cprint('% 20s % 10s % 6.2f' % \
    510                     (t_id, s_id, risk), 'blue', 'on_red')
    511             else:
    512                 print '% 20s % 10s % 6.2f' % (t_id, s_id, risk)
    513     
    514     def print_payoffs(self, amount, reverse=True):
    515         print
    516         print "Payoffs for conversion of",amount,"as of tick",str(self.tick)+':'
    517 
    518         payoffs = sorted(self.get_payoffs(amount), key=lambda x: x[1], \
    519             reverse=reverse)
    520         cprint('% 10s % 8s' % \
    521             ('Service', 'Payoff'), attrs=['bold'])
    522         for s_id,payoff in payoffs:
    523             have_flags = False
    524             for f in self.unused_flags:
    525                 if f['from_service'] == s_id:
    526                     have_flags = True
    527                     break
    528 
    529             if have_flags:
    530                 cprint('% 10s % 8.0f' % \
    531                     (s_id, payoff), 'blue', 'on_red')
    532             else:
    533                 print '% 10s % 8.0f' % (s_id, payoff)
    534     
    535     def print_expectancy(self, amount, cutoff=None):
    536         flags = []
    537         
    538         for f in self.unused_flags:
    539             r = self.get_risks(amount, service=f['from_service'], \
    540                 team=f['from_team'])
    541             p = self.get_payoffs(amount, service=f['from_service'])
    542             
    543             if not cutoff or amount/r*p > cutoff:
    544                 flags.append((f['flag'], r*p))
    545         
    546         flags = sorted(flags, key=lambda x: x[1], reverse=True)
    547         
    548         print 
    549         print 'Payoff expectancy for',amount,'as of tick',str(self.tick)+':'
    550         cprint('% 12s % 40s' % \
    551             ('Exp.Points', 'Flag'), attrs=['bold'])
    552         for flag,payoff in flags:
    553             print '% 12.0f % 40s' % (payoff,flag)
    554     
    555     def print_transactions(self, num=20):
    556         #    'time_used': datetime.now(), # Time of usage
    557         #    'flag': 'fooBAR',            # String associated with flag
    558         #    'time_stolen': ...,          # As found in unused_flags
    559         #    'from_team': 'TEAM1_ID',     # As found in unused_flags
    560         #    'from_service': 'SVC1_ID',   # As found in new_flag
    561         #    'action': 'laundering',      # "betrayal" or "laundering"
    562         #    'money_given': 12.3,         # if "laundering", otherwise 0.0
    563         #    'points_gained': 12.3,       # if "laundering" and successfull, 
    564         #                                 # otherwise 0.0 
    565         #    'result': "SUCCESS",         # "SUCCESS", "BAD_FLAG", "BAD_LUCK",
    566         #                                 # "OLD_FLAG" or "OTHER"
    567         #    'msg': 'Flag sub. suc.'
    568         print
    569         print 'Latest transactions:'
    570         print '% 7s % 20s % 10s % 10s % 8s % 20s' % \
    571             ('Time', 'Team', 'Service', 'Result', 'Points', 'Return Message')
    572         for t in self.transactions[num*-1:]:
    573             time = t['time_used'].time()
    574             print '% 7s % 20s % 10s % 10s % 8i % 20s' % \
    575             (str(time.hour)+':'+str(time.minute), \
    576             t['from_team'], t['from_service'], t['result'], \
    577             t['points_gained'], t['msg'])
    578 
    579     
    580     def __getstate__(self):
    581         '''Needed for pickleability... (sockets can not be pickled)'''
    582         new_dict = self.__dict__
    583         new_dict['recv_flag_server'] = None
    584         new_dict['recv_state_thread'] = None
    585         new_dict['recv_state_socket'] = None
    586         new_dict['tick_callback'] = None
    587         new_dict['flag_dissipater_instance'] = None
    588         
    589         return new_dict
    590     
    591     def backup(self, filename='ictf2011.backup'):
    592         pickle.dump(self, open(filename, 'w'))
    593     
    594     @staticmethod
    595     def load(filename='ictf2011.backup'):
    596         return pickle.load(open(filename))
    597     
    598     def stop_flag_dissipater(self):
    599         if self.flag_dissipater_instance:
    600             self.flag_dissipater_instance.stop = True
    601     
    602     def start_flag_dissipater(self):
    603         if not self.flag_dissipater_instance:
    604             return
    605         
    606         class flag_dissipater(threading.Thread):
    607             stop = False
    608             cutoff = 0.75
    609             
    610             def run(self):
    611                 '''Runs until stop is set to True
    612                 Will "sell" all flags with expectancy over cutoff and use 
    613                 all others to compromise other people services
    614                 TODO'''
    615                 while not stop:
    616                     time.sleep(30)
    617                     # TODO
    618                     print 'foo'
    619         
    620         self.flag_dissipater_instance = flag_dissipater()
    621         self.flag_dissipater_instance.start()
    622     
    623 def client(ip, port, messages):
    624     '''Just for debugging and testing purposes'''
    625     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    626     sock.connect((ip, port))
    627     try:
    628         for message in messages:
    629             sock.send(message)
    630     finally:
    631         sock.close()
    632 
    633 if __name__ == '__main__':
    634     ctf = ictf()
    635     ip, port = ctf.start_state_receiver()
    636     
    637     # Run test data through here!
    638     client(ip, port, '{"services": {"S1": {"P": 58, "C": 3, "R": 10}, "foobar": {"P": 98, "C": 42, "R": 90}, "hogwarts": {"P": 60, "C": 10, "R": 5}}, "tick": 666, "teams": {"A-team": {"subnet": "127.0.0.0/24", "D": 70, "services_compromised_last_tick": ["foobar", "S1"], "money": 395, "N": { "Justice League" : 600 }, "Q": {"S1": 10, "foobar": 10000}, "points": 394, "services_up_last_tick": ["S1", "foobar", "hogwarts"], "challenges_solved": [10, 971, 424242]}, "Justice League": {"subnet": "10.230.230.0/24", "D": 30, "services_compromised_last_tick": ["hogwarts"], "money": 3859, "N": { "foobar" : 6093 }, "Q": {"hogwarts": 1093, "foobar": 10000}, "points": 10293, "services_up_last_tick": ["hogwarts"], "challenges_solved": [30, 5391, 305]}}}'+ \
    639         "\nIf you can read this on your console, that is good :) (test passed)")
    640         
    641     time.sleep(1)
    642     t = ctf.teams['Justice League']
    643     assert t['svc_comp'] == ['hogwarts'], 'Justice League.svc_comp not received'
    644     assert t['svc_up'] == ['hogwarts'], 'Justice League.svc_up not received'
    645     assert t['points'] == 10293, 'Justice League.points not received'
    646     assert t['money'] == 3859, 'Justice League.money not received'
    647     assert t['N']['foobar'] == 6093, 'Justice League.N not received'
    648     
    649     s = ctf.services['foobar']
    650     assert s['C'] == 42/100.0, 'foobar.C not received'
    651     assert s['P'] == 98/100.0, 'foobar.P not received'
    652     assert s['R'] == 90/100.0, 'foobar.R not received'
    653     
    654     ctf.start_flag_receiver()
    655     time.sleep(1)
    656     ip, port = ctf.recv_flag_server['ip'], \
    657         ctf.recv_flag_server['port']
    658     client(ip, port, ["Justice League\n","foobar\n","ABCDEFGHIJKLMNOPQRSTUVWXYZ!\n"])
    659     client(ip, port, ["230\n","S1\n","ABCDEFGHIJKLFOOPQRSTUVWXYZ!\n"])
    660     client(ip, port, ["10.230.230.3\n","hogwarts\n","FOOBARGHIJKLMNOPQRSTUVWXYZ!\n", \
    661         "!!BARFOOBAZ!!\n","BLUBBER!\n","BAR\n"])
    662     time.sleep(1)
    663     assert 'FOO' in ctf.all_flags, 'Flag FOO not recognized'
    664     assert 'BAR' in ctf.all_flags, 'Flag BAR not recognized'
    665     assert 'BAZ' in ctf.all_flags, 'Flag BAZ not recognized'
    666     assert len(ctf.unused_flags) == 3, 'Flags did not make it in unused_flags'
    667     
    668     ctf.stop_flag_receiver()
    669     ctf.stop_state_receiver()
    670     
    671     ctf.backup()
    672     new_ctf = ictf.load()
    673     
    674     assert new_ctf.get_risks(100) == ctf.get_risks(100), 'Dump and reload faulty'
    675     assert new_ctf.get_payoffs(100) == ctf.get_payoffs(100), 'Dump and reload faulty'
    676     del new_ctf
    677     
    678     ctf.print_scorboard()
    679     ctf.print_risks(1000)
    680     ctf.print_payoffs(1000)
    681     ctf.print_expectancy(1000)
    682     
    683     print ctf.launder(100, ctf.get_flag('FOO',remove=True))
    684     assert None == ctf.get_flag('FOO'), 'get_flag(f, remove=True) failed'
    685     assert 'FOO' == ctf.transactions[-1]['flag'], 'Transaction failed'
    686     print ctf.betray(ctf.get_flag('BAR',remove=True))
    687     assert 'BAR' == ctf.transactions[-1]['flag'], 'Transaction failed'
    688     
    689     ctf.print_transactions()
    690     
    691     print 'ok'