Exemplo n.º 1
0
def score_bots():
    ''' Award money for botnets '''
    logging.info("Scoring botnets, please wait ...")
    bot_manager = BotManager.instance()
    for team in Team.all():
        if len(team.members) > 0:
            bots = bot_manager.by_team(team.name)
            reward = 0
            for bot in bots:
                try:
                    reward += options.bot_reward
                    bot.write_message({
                        'opcode':
                        'status',
                        'message':
                        'Collected $%d reward' % options.bot_reward
                    })
                except:
                    logging.info("Bot at %s failed to respond to score ping" %
                                 bot.remote_ip)
            if 0 < len(bots):
                logging.info("%s was awarded $%d for controlling %s bot(s)" % (
                    team.name,
                    reward,
                    len(bots),
                ))
                bot_manager.add_rewards(team.name, options.bot_reward)
                bot_manager.notify_monitors(team.name)
                team.money += reward
                dbsession.add(team)
                dbsession.flush()
    dbsession.commit()
Exemplo n.º 2
0
 def initialize(self):
     self.bot_manager = BotManager.Instance()
     self.team_name = None
     self.uuid = unicode(uuid4())
     self.opcodes = {
         'auth': self.auth,
     }
Exemplo n.º 3
0
def score_bots():
    ''' Award money for botnets '''
    logging.info("Scoring botnets, please wait ...")
    bot_manager = BotManager.instance()
    for team in Team.all():
        bots = bot_manager.by_team(team.name)
        reward = 0
        for bot in bots:
            try:
                reward += options.bot_reward
                bot.write_message({
                    'opcode': 'status',
                    'message': 'Collected $%d reward' % options.bot_reward
                })
            except:
                logging.info(
                    "Bot at %s failed to respond to score ping" % bot.remote_ip
                )
        if 0 < len(bots):
            logging.info("%s was awarded $%d for controlling %s bot(s)" % (
                team.name, reward, len(bots),
            ))
            bot_manager.add_rewards(team.name, options.bot_reward)
            bot_manager.notify_monitors(team.name)
            team.money += reward
            dbsession.add(team)
            dbsession.flush()
    dbsession.commit()
Exemplo n.º 4
0
    def open(self):
        """ Only open sockets from authenticated clients """
        user = self.get_current_user()
        if self.session is not None and ("team_id" in self.session or user.is_admin()):
            logging.debug(
                "[Web Socket] Opened web monitor socket with %s" % user.handle
            )
            self.uuid = unicode(uuid4())
            self.bot_manager = BotManager.instance()

            if user.is_admin():
                self.team_name = user.name
                self.bot_manager.add_monitor(self)
                bots = self.bot_manager.get_all_bots()
            else:
                self.team_name = "".join(user.team.name)
                self.bot_manager.add_monitor(self)
                bots = self.bot_manager.get_bots(self.team_name)
            self.update(bots)
        else:
            logging.debug(
                "[Web Socket] Denied web monitor socket to %s" % self.request.remote_ip
            )
            self.bot_manager = None
            self.close()
Exemplo n.º 5
0
 def initialize(self):
     self.config = ConfigManager.instance()
     self.bot_manager = BotManager.instance()
     self.team_name = None
     if not self.config.use_bots:
         self.close()
     else:
         self.uuid = unicode(uuid4())
         self.opcodes = {"auth": self.auth}
Exemplo n.º 6
0
 def initialize(self):
     self.bot_manager = BotManager.Instance()
     self.event_manager = EventManager.Instance()
     self.team_name = None
     self.team_uuid = None
     self.box_uuid = None
     self.remote_ip = None
     self.uuid = unicode(uuid4())
     self.opcodes = {
         'set_user': self.set_user,
     }
Exemplo n.º 7
0
 def initialize(self):
     self.bot_manager = BotManager.instance()
     self.event_manager = EventManager.instance()
     self.config = ConfigManager.instance()
     self.team_name = None
     self.team_uuid = None
     self.box_uuid = None
     self.remote_ip = None
     self.xid = os.urandom(16).encode("hex")
     if not self.config.use_bots:
         self.close()
     else:
         self.uuid = unicode(uuid4())
         self.opcodes = {"interrogation_response": self.interrogation_response}
Exemplo n.º 8
0
 def open(self):
     """ Only open sockets from authenticated clients """
     if self.session is not None and "team_id" in self.session:
         user = self.get_current_user()
         logging.debug("[Web Socket] Opened web monitor socket with %s" % user.handle)
         self.uuid = unicode(uuid4())
         self.bot_manager = BotManager.instance()
         self.team_name = "".join(user.team.name)
         self.bot_manager.add_monitor(self)
         bots = self.bot_manager.get_bots(self.team_name)
         self.update(bots)
     else:
         logging.debug("[Web Socket] Denied web monitor socket to %s" % self.request.remote_ip)
         self.bot_manager = None
         self.close()
Exemplo n.º 9
0
 def open(self):
     ''' Only open sockets from authenticated clients '''
     if self.session is not None and 'team_id' in self.session:
         user = self.get_current_user()
         logging.debug("[Web Socket] Opened web monitor socket with %s" % user.handle)
         self.uuid = unicode(uuid4())
         self.bot_manager = BotManager.instance()
         self.team_name = ''.join(user.team.name)
         self.bot_manager.add_monitor(self)
         bots = self.bot_manager.get_bots(self.team_name)
         self.update(bots)
     else:
         logging.debug("[Web Socket] Denied web monitor socket to %s" % self.request.remote_ip)
         self.bot_manager = None
         self.close()
Exemplo n.º 10
0
 def __now__(self):
     ''' Returns snapshot object it as a dict '''
     snapshot = Snapshot()
     bot_manager = BotManager.Instance()
     for team in Team.all():
         snapshot_team = SnapshotTeam(team_id=team.id,
                                      money=team.money,
                                      bots=bot_manager.count_by_team(team))
         snapshot_team.game_levels = team.game_levels
         snapshot_team.flags = team.flags
         dbsession.add(snapshot_team)
         dbsession.flush()
         snapshot.teams.append(snapshot_team)
     dbsession.add(snapshot)
     dbsession.flush()
     return snapshot
Exemplo n.º 11
0
 def __now__(self):
     ''' Returns snapshot object it as a dict '''
     snapshot = Snapshot()
     bot_manager = BotManager.instance()
     #self.dbsession = DBSession()
     for team in Team.all():
         if len(team.members) > 0:
             snapshot_team = SnapshotTeam(
                 team_id=team.id,
                 money=team.money,
                 bots=bot_manager.count_by_team(team))
             snapshot_team.game_levels = team.game_levels
             snapshot_team.flags = team.flags
             self.dbsession.add(snapshot_team)
             self.dbsession.flush()
             snapshot.teams.append(snapshot_team)
     self.dbsession.add(snapshot)
     self.dbsession.commit()
     return snapshot
Exemplo n.º 12
0
 def __now__(self):
     ''' Returns snapshot object it as a dict '''
     snapshot = Snapshot()
     bot_manager = BotManager.instance()
     #self.dbsession = DBSession()
     for team in Team.all():
         snapshot_team = SnapshotTeam(
             team_id=team.id,
             money=team.money,
             bots=bot_manager.count_by_team(team)
         )
         snapshot_team.game_levels = team.game_levels
         snapshot_team.flags = team.flags
         self.dbsession.add(snapshot_team)
         self.dbsession.flush()
         snapshot.teams.append(snapshot_team)
     self.dbsession.add(snapshot)
     self.dbsession.commit()
     return snapshot
Exemplo n.º 13
0
 def open(self):
     ''' Only open sockets from authenticated clients '''
     user = self.get_current_user()
     if self.session is not None and ('team_id' in self.session or user.is_admin()):
         logging.debug("[Web Socket] Opened web monitor socket with %s" % user.handle)
         self.uuid = unicode(uuid4())
         self.bot_manager = BotManager.instance()
         
         if user.is_admin():
             self.team_name = user.name
             self.bot_manager.add_monitor(self)
             bots = self.bot_manager.get_all_bots()
         else:
             self.team_name = ''.join(user.team.name)
             self.bot_manager.add_monitor(self)
             bots = self.bot_manager.get_bots(self.team_name)
         self.update(bots)
     else:
         logging.debug("[Web Socket] Denied web monitor socket to %s" % self.request.remote_ip)
         self.bot_manager = None
         self.close()
Exemplo n.º 14
0
def score_bots():
    """ Award money for botnets """
    logging.info("Scoring botnets, please wait ...")
    bot_manager = BotManager.instance()
    config = ConfigManager.instance()
    for team in Team.all():
        bots = bot_manager.by_team(team.name)
        reward = 0
        for bot in bots:
            try:
                reward += config.bot_reward
                bot.write_message({"opcode": "status", "message": "Collected $%d reward" % config.bot_reward})
            except:
                logging.info("Bot at %s failed to respond to score ping" % bot.remote_ip)
        if 0 < len(bots):
            logging.info("%s was awarded $%d for controlling %s bot(s)" % (team.name, reward, len(bots)))
            bot_manager.add_rewards(team.name, config.bot_reward)
            bot_manager.notify_monitors(team.name)
            team.money += reward
            dbsession.add(team)
            dbsession.flush()
    dbsession.commit()
Exemplo n.º 15
0
def score_bots():
    """ Award money for botnets """
    logging.info("Scoring botnets, please wait ...")
    bot_manager = BotManager.instance()
    event_manager = EventManager.instance()
    for team in Team.all():
        if len(team.members) > 0:
            bots = bot_manager.by_team(team.name)
            if 0 < len(bots):
                reward = 0
                for bot in bots:
                    try:
                        reward += options.bot_reward
                        bot.write_message({
                            "opcode":
                            "status",
                            "message":
                            "Collected $%d reward" % options.bot_reward,
                        })
                    except:
                        logging.info(
                            "Bot at %s failed to respond to score ping" %
                            bot.remote_ip)

                message = "%s was awarded $%d for controlling %s bot(s)" % (
                    team.name,
                    reward,
                    len(bots),
                )
                bot_manager.add_rewards(team.name, options.bot_reward)
                bot_manager.notify_monitors(team.name)
                team.money += reward
                dbsession.add(team)
                dbsession.flush()
                event_manager.bot_scored(team, message)
    dbsession.commit()
Exemplo n.º 16
0
 def bot_count(self):
     bot_manager = BotManager.instance()
     return bot_manager.count_by_team_uuid(self.uuid)
Exemplo n.º 17
0
class BotSocketHandler(tornado.websocket.WebSocketHandler):
    """
    *** Rough bot protocol layout ***
    =================================
    1) Bot connects to server
        a) If IP config.whitelist_box_ips is enabled, check
           the datbase for boxes with matching IPs

    2) Server responds with "Interrogation" request
        a) This request includes a random string 'xid'

    3) Bot responds with a "InterrogationResponse", includes
        a) The value of SHA512(SHA512(xid + garbage))
        b) Asserted user handle (reward goes to user.team)
        c) Asserted box name

    4) Server looks up asserted box and user in database, ensures
       they do exist, and the user is not an admin.

    5) Server then computes it's own SHA512(SHA512(xid + box garbage))
        a) Check if the server's value matches the bot's

    6) Check for duplicate bots (one bot per box per team)

    7) Add new bot to botnet

    """

    bot_manager = BotManager.instance()
    event_manager = EventManager.instance()
    config = options
    team_name = None
    team_uuid = None
    box_uuid = None
    remote_ip = None

    def initialize(self):
        try:
            hex_random = os.urandom(16).hex()
        except AttributeError:
            hex_random = encode(os.urandom(16), "hex")
        self.xid = hex_random
        if not self.config.use_bots:
            self.close()
        else:
            self.uuid = str(uuid4())
            self.opcodes = {
                "interrogation_response": self.interrogation_response
            }

    def open(self, *args):
        """ Steps 1 and 2; called when a new bot connects """
        box = Box.by_ip_address(self.request.remote_ip)
        self.remote_ip = self.request.remote_ip
        if box is None and self.config.whitelist_box_ips:
            logging.debug("Rejected bot from '%s' (not a box)" %
                          self.request.remote_ip)
            self.write_message({
                "opcode": "error",
                "message": "Invalid IP address."
            })
            self.close()
        else:
            logging.debug("Interrogating bot on %s" % self.request.remote_ip)
            self.write_message({"opcode": "interrogate", "xid": str(self.xid)})

    def on_message(self, message):
        """ Routes the request to the correct function based on opcode """
        try:
            req = json.loads(message)
            if "opcode" not in req:
                raise ValueError("Missing opcode")
            elif req["opcode"] not in self.opcodes:
                raise ValueError("Invalid opcode in request: %s" %
                                 req["opcode"])
            else:
                self.opcodes[req["opcode"]](req)
        except ValueError as error:
            logging.warning("Invalid json request from bot: %s" % str(error))
            self.close()

    def on_close(self):
        """ Close connection to remote host """
        if self.uuid in self.bot_manager.botnet:
            self.bot_manager.remove_bot(self)
        logging.debug("Closing connection to bot at %s" %
                      self.request.remote_ip)

    def interrogation_response(self, msg):
        """ Steps 3 and 4; validate repsonses """
        logging.debug("Received interrogate response, validating ...")
        response_xid = msg["response_xid"]
        user = User.by_handle(msg["handle"])
        box = Box.by_name(msg["box_name"])
        if self.config.whitelist_box_ips and self.remote_ip not in box.ips:
            self.send_error("Invalid remote IP for this box")
        elif user is None or user.is_admin():
            self.send_error("User does not exist")
        elif box is None:
            self.send_error("Box does not exist")
        elif not self.is_valid_xid(box, response_xid):
            self.send_error("Invalid xid response")
        else:
            self.team_name = user.team.name
            self.team_uuid = user.team.uuid
            self.box_uuid = box.uuid
            self.box_name = box.name
            self.add_to_botnet(user)

    def add_to_botnet(self, user):
        """ Step 6 and 7; Add current web socket to botnet """
        if self.bot_manager.add_bot(self):
            logging.debug("Auth okay, adding '%s' to botnet" % self.uuid)
            count = self.bot_manager.count_by_team(self.team_name)
            self.write_message({
                "opcode":
                "status",
                "message":
                "Added new bot; total number of bots is now %d" % count,
            })
            self.event_manager.bot_added(user, count)
        else:
            logging.debug("Duplicate bot on %s" % self.remote_ip)
            self.send_error("Duplicate bot")

    def is_valid_xid(self, box, response_xid):
        round1 = encode(sha512(encode(self.xid + box.garbage)).hexdigest())
        return response_xid == sha512(round1).hexdigest()

    def ping(self):
        """ Just make sure we can write data to the socket """
        try:
            self.write_message({"opcode": "ping"})
        except:
            logging.exception("Error: while sending ping to bot.")
            self.close()

    def send_error(self, msg):
        """ Send the errors, and close socket """
        self.write_message({"opcode": "error", "message": msg})
        self.close()
Exemplo n.º 18
0
class BotCliMonitorSocketHandler(tornado.websocket.WebSocketHandler):
    """
    Handles the CLI BotMonitor websocket connections, has custom auth.
    TODO: Trash this and use the web api handler, w/ normal session cookie
    """

    config = options
    bot_manager = BotManager.instance()
    team_name = None

    def initialize(self):
        if not self.config.use_bots:
            self.close()
        else:
            self.uuid = str(uuid4())
            self.opcodes = {"auth": self.auth}

    def open(self):
        logging.debug("Opened new monitor socket to %s" %
                      self.request.remote_ip)

    def on_message(self, message):
        """ Parse request """
        try:
            req = json.loads(message)
            if "opcode" not in req:
                raise ValueError("Missing opcode")
            elif req["opcode"] not in self.opcodes:
                raise ValueError("Invalid opcode in request: %s" %
                                 req["opcode"])
            else:
                self.opcodes[req["opcode"]](req)
        except ValueError as error:
            logging.warning("Invalid json request from bot: %s" % str(error))

    def on_close(self):
        """ Close connection to remote host """
        self.bot_manager.remove_monitor(self)
        logging.debug("Closing connection to bot monitor at %s" %
                      self.request.remote_ip)

    def auth(self, req):
        """ Authenticate user """
        try:
            user = User.by_handle(req["handle"])
        except:
            user = None
        if user is None or user.is_admin():
            logging.debug("Monitor socket user does not exist.")
            self.write_message({
                "opcode": "auth_failure",
                "message": "Authentication failure"
            })
            self.close()
        elif user.validate_password(req.get("password", "")):
            logging.debug("Monitor socket successfully authenticated as %s" %
                          user.handle)
            self.team_name = "".join(user.team.name)
            self.bot_manager.add_monitor(self)
            self.write_message({"opcode": "auth_success"})
            bots = self.bot_manager.get_bots(self.team_name)
            self.update(bots)
        else:
            logging.debug("Monitor socket provided invalid password for user")
            self.write_message({
                "opcode": "auth_failure",
                "message": "Authentication failure"
            })
            self.close()

    def update(self, bots):
        """ Called by the observable class """
        self.write_message({"opcode": "update", "bots": bots})

    def ping(self):
        self.write_message({"opcode": "ping"})
Exemplo n.º 19
0
 def bot_count(self):
     bot_manager = BotManager.instance()
     return bot_manager.count_by_team_uuid(self.uuid)
Exemplo n.º 20
0
class BotSocketHandler(tornado.websocket.WebSocketHandler):
    '''
    *** Rough bot protocol layout ***
    =================================
    1) Bot connects to server
        a) If IP config.whitelist_box_ips is enabled, check
           the datbase for boxes with matching IPs

    2) Server responds with "Interrogation" request
        a) This request includes a random string 'xid'

    3) Bot responds with a "InterrogationResponse", includes
        a) The value of SHA512(SHA512(xid + garbage))
        b) Asserted user handle (reward goes to user.team)
        c) Asserted box name

    4) Server looks up asserted box and user in database, ensures
       they do exist, and the user is not an admin.

    5) Server then computes it's own SHA512(SHA512(xid + box garbage))
        a) Check if the server's value matches the bot's

    6) Check for duplicate bots (one bot per box per team)

    7) Add new bot to botnet

    '''

    bot_manager = BotManager.instance()
    event_manager = EventManager.instance()
    config = options
    team_name = None
    team_uuid = None
    box_uuid = None
    remote_ip = None

    def initialize(self):
        self.xid = os.urandom(16).encode('hex')
        if not self.config.use_bots:
            self.close()
        else:
            self.uuid = unicode(uuid4())
            self.opcodes = {
                'interrogation_response': self.interrogation_response,
            }

    def open(self, *args):
        ''' Steps 1 and 2; called when a new bot connects '''
        box = Box.by_ip_address(self.request.remote_ip)
        self.remote_ip = self.request.remote_ip
        if box is None and self.config.whitelist_box_ips:
            logging.debug("Rejected bot from '%s' (not a box)" % self.request.remote_ip)
            self.write_message({
                'opcode': 'error',
                'message': 'Invalid IP address.'
            })
            self.close()
        else:
            logging.debug("Interrogating bot on %s" % self.request.remote_ip)
            self.write_message({'opcode': 'interrogate', 'xid': self.xid})

    def on_message(self, message):
        ''' Routes the request to the correct function based on opcode '''
        try:
            req = json.loads(message)
            if 'opcode' not in req:
                raise ValueError('Missing opcode')
            elif req['opcode'] not in self.opcodes:
                raise ValueError('Invalid opcode in request: %s' % req['opcode'])
            else:
                self.opcodes[req['opcode']](req)
        except ValueError as error:
            logging.warn("Invalid json request from bot: %s" % str(error))
            self.close()

    def on_close(self):
        ''' Close connection to remote host '''
        if self.uuid in self.bot_manager.botnet:
            self.bot_manager.remove_bot(self)
        logging.debug("Closing connection to bot at %s" % self.request.remote_ip)

    def interrogation_response(self, msg):
        ''' Steps 3 and 4; validate repsonses '''
        logging.debug("Recieved interrogate response, validating ...")
        response_xid = msg['rxid']
        user = User.by_handle(msg['handle'])
        box = Box.by_name(msg['box_name'])
        if self.config.whitelist_box_ips and self.remote_ip not in box.ips:
            self.send_error("Invalid remote IP for this box")
        elif user is None or user.has_permission(ADMIN_PERMISSION):
            self.send_error("User does not exist")
        elif box is None:
            self.send_error("Box does not exist")
        elif not self.is_valid_xid(box, response_xid):
            self.send_error("Invalid xid response")
        else:
            self.team_name = user.team.name
            self.team_uuid = user.team.uuid
            self.box_uuid = box.uuid
            self.box_name = box.name
            self.add_to_botnet()

    def add_to_botnet(self):
        ''' Step 6 and 7; Add current web socket to botnet '''
        if self.bot_manager.add_bot(self):
            logging.debug("Auth okay, adding '%s' to botnet" % self.uuid)
            count = self.bot_manager.count_by_team(self.team_name)
            self.write_message({
                'opcode': 'status',
                'message': 'Added new bot; total number of bots is now %d' % count
            })
        else:
            logging.debug("Duplicate bot on %s" % self.remote_ip)
            self.send_error("Duplicate bot")

    def is_valid_xid(self, box, response_xid):
        return response_xid == sha1(self.xid + box.garbage).hexdigest()

    def ping(self):
        ''' Just make sure we can write data to the socket '''
        try:
            self.write_message({'opcode': 'ping'})
        except:
            logging.exception("Error: while sending ping to bot.")
            self.close()

    def send_error(self, msg):
        ''' Send the errors, and close socket '''
        self.write_message({
            'opcode': 'error',
            'message': msg,
        })
        self.close()
Exemplo n.º 21
0
class BotCliMonitorSocketHandler(tornado.websocket.WebSocketHandler):
    '''
    Handles the CLI BotMonitor websocket connections, has custom auth.
    TODO: Trash this and use the web api handler, w/ normal session cookie
    '''

    config = options
    bot_manager = BotManager.instance()
    team_name = None

    def initialize(self):
        if not self.config.use_bots:
            self.close()
        else:
            self.uuid = unicode(uuid4())
            self.opcodes = {
                'auth': self.auth,
            }

    def open(self):
        logging.debug("Opened new monitor socket to %s" % self.request.remote_ip)

    def on_message(self, message):
        ''' Parse request '''
        try:
            req = json.loads(message)
            if 'opcode' not in req:
                raise ValueError('Missing opcode')
            elif req['opcode'] not in self.opcodes:
                raise ValueError('Invalid opcode in request: %s' % req['opcode'])
            else:
                self.opcodes[req['opcode']](req)
        except ValueError as error:
            logging.warn("Invalid json request from bot: %s" % str(error))

    def on_close(self):
        ''' Close connection to remote host '''
        self.bot_manager.remove_monitor(self)
        logging.debug("Closing connection to bot monitor at %s" % self.request.remote_ip)

    def auth(self, req):
        ''' Authenticate user '''
        try:
            user = User.by_handle(req['handle'])
        except:
            user = None
        if user is None or user.is_admin():
            logging.debug("Monitor socket user does not exist.")
            self.write_message({
                'opcode': 'auth_failure',
                'message': 'Authentication failure',
            })
            self.close()
        elif user.validate_password(req.get('password', '')):
            logging.debug("Monitor socket successfully authenticated as %s" % user.handle)
            self.team_name = ''.join(user.team.name)
            self.bot_manager.add_monitor(self)
            self.write_message({'opcode': 'auth_success'})
            bots = self.bot_manager.get_bots(self.team_name)
            self.update(bots)
        else:
            logging.debug("Monitor socket provided invalid password for user")
            self.write_message({
                'opcode': 'auth_failure',
                'message': 'Authentication failure',
            })
            self.close()

    def update(self, bots):
        ''' Called by the observable class '''
        self.write_message({'opcode': 'update', 'bots': bots})

    def ping(self):
        self.write_message({'opcode': 'ping'})
Exemplo n.º 22
0
    def update_gamestate(self, app):
        game_state = {
            "teams": {},
            "levels": {},
            "boxes": {},
            "hint_count": len(Hint.all()),
            "flag_count": len(Flag.all()),
            "box_count": len(Box.all()),
            "level_count": len(GameLevel.all()),
        }
        teams = Team.ranks()
        for team in teams:
            if len(team.members) > 0:
                millis = int(round(time.time() * 1000))
                game_state["teams"][team.name] = {
                    "uuid": team.uuid,
                    "flags": [str(flag) for flag in team.flags],
                    "game_levels": [str(lvl) for lvl in team.game_levels],
                    "members_count": len(team.members),
                    "hints_count": len(team.hints),
                    "bot_count":
                    BotManager.instance().count_by_team(team.name),
                    "money": team.money,
                }

                highlights = {"money": 0, "flag": 0, "bot": 0, "hint": 0}
                for item in highlights:
                    value = team.get_score(item)
                    game_state["teams"][team.name][item] = value
                    game_history = app.settings["scoreboard_history"]
                    if team.name in game_history:
                        prev = game_history[team.name][item]
                        if prev < value:
                            highlights[item] = millis
                        else:
                            highlights[item] = game_history[
                                team.name]["highlights"][item]
                highlights["now"] = millis
                game_state["teams"][team.name]["highlights"] = highlights
                app.settings["scoreboard_history"][
                    team.name] = game_state["teams"].get(team.name)
        for level in GameLevel.all():
            game_state["levels"][level.name] = {
                "type": level.type,
                "number": level.number,
                "teams": {},
                "boxes": {},
                "box_count": len(level.boxes),
                "flag_count": len(level.flags),
            }
            for team in teams:
                game_state["levels"][level.name]["teams"][team.name] = {
                    "lvl_count": len(team.level_flags(level.number)),
                    "lvl_unlock": level in team.game_levels,
                }
            for box in level.boxes:
                game_state["levels"][level.name]["boxes"][box.uuid] = {
                    "name": box.name,
                    "teams": {},
                    "flag_count": len(box.flags),
                }
                for team in teams:
                    game_state["levels"][level.name]["boxes"][
                        box.uuid]["teams"][team.name] = {
                            "box_count": len(team.box_flags(box))
                        }
        app.settings["scoreboard_state"] = game_state