예제 #1
0
 def post(self, *args, **kwargs):
     ''' Clears cookies and session data '''
     if self.session is not None:
         user = self.get_current_user()
         EventManager.instance().deauth(user)
         self.session.delete()
     self.clear_all_cookies()
     self.redirect("/")
예제 #2
0
 def post(self, *args, **kwargs):
     """ Clears cookies and session data """
     if self.session is not None:
         user = self.get_current_user()
         EventManager.instance().deauth(user)
         self.session.delete()
     self.clear_all_cookies()
     self.redirect("/")
예제 #3
0
 def del_user(self):
     """
     Delete user objects in the database, you cannot delete yourself.
     """
     user = User.by_uuid(self.get_argument("uuid", ""))
     if user is not None and user != self.get_current_user():
         logging.info("Deleted User: '******'" % str(user.handle))
         EventManager.instance().deauth(user)
         self.dbsession.delete(user)
         self.dbsession.commit()
         self.event_manager.push_score_update()
         self.redirect("/admin/users")
     else:
         self.render("admin/view/users.html",
                     errors=["User does not exist"])
예제 #4
0
 def __init__(self):
     self.config = options
     self.dbsession = dbsession
     self.cache = MemcachedConnect()
     self.epoch = None  # Date/time of first snapshot
     self._load()
     self.event_manager = EventManager.instance()
예제 #5
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()
예제 #6
0
 def __init__(self):
     self.config = options
     self.dbsession = dbsession
     self.cache = memcache.Client([self.config.memcached], debug=0)
     self.epoch = None  # Date/time of first snapshot
     self._load()
     self.event_manager = EventManager.instance()
예제 #7
0
 def __init__(self):
     self.config = options
     self.dbsession = dbsession
     self.cache = memcache.Client([self.config.memcached], debug=0)
     self.epoch = None  # Date/time of first snapshot
     self._load()
     self.event_manager = EventManager.instance()
예제 #8
0
 def __init__(self):
     self.config = ConfigManager.instance()
     self.dbsession = dbsession
     self.cache = pylibmc.Client([self.config.memcached], binary=True)
     self.epoch = None  # Date/time of first snapshot
     self._load()
     self.event_manager = EventManager.instance()
예제 #9
0
 def initialize(self):
     """ Setup sessions, etc """
     self.session = None
     self.manager = EventManager.instance()
     self.config = ConfigManager.instance()
     session_id = self.get_secure_cookie("session_id")
     if session_id is not None:
         self.conn = pylibmc.Client([self.config.memcached], binary=True)
         self.conn.behaviors["no_block"] = 1  # async I/O
         self.session = self._create_session(session_id)
         self.session.refresh()
예제 #10
0
 def del_team(self):
     """
     Delete team objects in the database.
     """
     team = Team.by_uuid(self.get_argument("uuid", ""))
     for user in team.members:
         if user == self.get_current_user():
             self.render(
                 "admin/view/users.html",
                 errors=["Unable to delete user %s" % user.handle],
             )
             return
         EventManager.instance().deauth(user)
     if team is not None:
         logging.info("Deleted Team: '%s'" % str(team.name))
         self.dbsession.delete(team)
         self.dbsession.commit()
         self.redirect("/admin/users")
     else:
         self.render("admin/view/users.html",
                     errors=["Team does not exist"])
예제 #11
0
class AdminMessageHandler(BaseHandler):

    event_manager = EventManager.instance()
    """ Send a global notification message """
    @restrict_ip_address
    @authenticated
    @authorized(ADMIN_PERMISSION)
    def post(self, *args, **kwargs):
        message = self.get_argument("message", "")
        if len(message) > 0:
            self.event_manager.admin_message(message)
            if self.chatsession:
                self.chatsession.post_message(message)
        self.redirect("/user")
예제 #12
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}
예제 #13
0
class NotifySocketHandler(BaseWebSocketHandler):

    ''' Handles websocket connections '''

    event_manager = EventManager.instance()

    def open(self):
        ''' When we receive a new websocket connect '''
        self.event_manager.add_connection(self)
        if self.session is not None and 'team_id' in self.session:
            logging.debug("Opened new websocket with user id: %s" % (
                self.session['user_id'],
            ))
            self.io_loop.add_callback(self.event_manager.push_user,
                                      self.team_id, self.user_id
                                      )
        else:
            logging.debug("[Web Socket] Opened public notification socket.")

    def on_close(self):
        ''' Lost connection to client '''
        self.event_manager.remove_connection(self)

    @property
    def team_id(self):
        if self.session is not None and 'team_id' in self.session:
            return self.session['team_id']

    @team_id.setter
    def team_id(self, value):
        raise ValueError('Cannot set team_id')

    @property
    def user_id(self):
        if self.session is not None and 'user_id' in self.session:
            return self.session['user_id']

    @user_id.setter
    def user_id(self, value):
        raise ValueError('Cannot set user_id')
예제 #14
0
class BaseHandler(RequestHandler):

    ''' User handlers extend this class '''

    csp = {
        "default-src": set(["'self'"]),
        "script-src": set(["'self'"]),
        "connect-src": set(["'self'"]),
        "frame-src": set(["'self'"]),
        "img-src": set(["'self'"]),
        "media-src": set(["'none'"]),
        "font-src": set(["'self'"]),
        "object-src": set(["'none'"]),
        "style-src": set(["'self'"]),
    }
    _session = None
    dbsession = dbsession
    _memcached = None
    new_events = []
    io_loop = IOLoop.instance()
    event_manager = EventManager.instance()
    config = options  # backward compatability

    def initialize(self):
        ''' Setup sessions, etc '''
        self.add_content_policy('connect-src', self.config.origin)
        # We need this for a few things, and so far as I know it doesn't
        # present too much of a security risk - TODO: no longer require
        # inline styles
        self.add_content_policy('style-src', "'unsafe-inline'")

    def get_current_user(self):
        ''' Get current user object from database '''
        if self.session is not None:
            try:
                return User.by_uuid(self.session['user_uuid'])
            except KeyError:
                logging.exception("Malformed session: %r" % self.session)
            except:
                logging.exception("Failed call to get_current_user()")
        return None

    def start_session(self):
        ''' Starts a new session '''
        self.session = self._create_session()
        flags = {
            'expires': self.session.expires,
            'path': '/',
            'HttpOnly': True
        }
        if self.config.ssl:
            flags['Secure'] = True
        self.set_secure_cookie('session_id', self.session.session_id, **flags)

    def add_content_policy(self, src, policy):
        ''' Add to the existing CSP header '''
        if not src.endswith('-src'):
            src += '-src'
        if src in self.csp:
            self.csp[src].add(policy)
            self._refresh_csp()
        else:
            raise ValueError("Invalid content source")

    def clear_content_policy(self, src):
        ''' Clear a content source in the existing CSP header '''
        if not src.endswith('-src'):
            src += '-src'
        if src in self.csp:
            self.csp[src] = set()
            self._refresh_csp()
        else:
            raise ValueError("Invalid content source")

    def _refresh_csp(self):
        ''' Rebuild the Content-Security-Policy header '''
        _csp = []
        for src, policies in self.csp.iteritems():
            if len(policies):
                _csp.append("%s %s; " % (src, " ".join(policies)))
        csp = ''.join(_csp)
        # Disabled until i can figure out the bug
        # self.set_header("Content-Security-Policy", csp)

    @property
    def memcached(self):
        ''' Connects to Memcached instance '''
        if self._memcached is None:
            self._memcached = memcache.Client([self.config.memcached], debug=0)
        return self._memcached

    def _create_session(self):
        ''' Creates a new session '''
        kwargs = {
            'connection': self.memcached,
            'ip_address': self.request.remote_ip,
        }
        new_session = MemcachedSession(**kwargs)
        new_session.save()
        return new_session

    @property
    def session(self):
        if self._session is None:
            session_id = self.get_secure_cookie('session_id')
            if session_id is not None:
                self._session = self._get_session(session_id)
        return self._session

    @session.setter
    def session(self, new_session):
        self._session = new_session

    def _get_session(self, session_id):
        kwargs = {
            'connection': self.memcached,
            'session_id': session_id,
            'ip_address': self.request.remote_ip,
        }
        old_session = MemcachedSession.load(**kwargs)
        if old_session and not old_session.is_expired():
            old_session.refresh()
            return old_session
        else:
            return None

    def set_default_headers(self):
        '''
        Set security HTTP headers, and add some troll-y version headers
        '''
        self.set_header("Server", "Microsoft-IIS/7.5")
        self.add_header("X-Powered-By", "ASP.NET")
        self.add_header("X-Frame-Options", "DENY")
        self.add_header("X-XSS-Protection", "1; mode=block")
        self.add_header("X-Content-Type-Options", "nosniff")
        self._refresh_csp()
        if self.config.ssl:
            self.add_header("Strict-Transport-Security",
                            'max-age=31536000; includeSubDomains;')

    def write_error(self, status_code, **kwargs):
        ''' Write our custom error pages '''
        if not self.config.debug:
            trace = "".join(traceback.format_exception(*kwargs["exc_info"]))
            logging.error("Request from %s resulted in an error code %d:\n%s" % (
                self.request.remote_ip, status_code, trace
            ))
            if status_code in [403]:
                # This should only get called when the _xsrf check fails,
                # all other '403' cases we just send a redirect to /403
                self.render('public/403.html', locked=False, xsrf=True)
            else:
                # Never tell the user we got a 500
                self.render('public/404.html')
        else:
            # If debug mode is enabled, just call Tornado's write_error()
            super(BaseHandler, self).write_error(status_code, **kwargs)

    def get(self, *args, **kwargs):
        ''' Placeholder, incase child class does not impl this method '''
        self.render("public/404.html")

    def post(self, *args, **kwargs):
        ''' Placeholder, incase child class does not impl this method '''
        self.render("public/404.html")

    def put(self, *args, **kwargs):
        ''' Log odd behavior, this should never get legitimately called '''
        logging.warn(
            "%s attempted to use PUT method" % self.request.remote_ip
        )

    def delete(self, *args, **kwargs):
        ''' Log odd behavior, this should never get legitimately called '''
        logging.warn(
            "%s attempted to use DELETE method" % self.request.remote_ip
        )

    def head(self, *args, **kwargs):
        ''' Ignore it '''
        logging.warn(
            "%s attempted to use HEAD method" % self.request.remote_ip
        )

    def options(self, *args, **kwargs):
        ''' Log odd behavior, this should never get legitimately called '''
        logging.warn(
            "%s attempted to use OPTIONS method" % self.request.remote_ip
        )

    def on_finish(self, *args, **kwargs):
        ''' Called after a response is sent to the client '''
        self.dbsession.close()
예제 #15
0
 def initialize(self):
     ''' Setup sessions '''
     self.manager = EventManager.instance()
예제 #16
0
class BaseHandler(RequestHandler):
    """ User handlers extend this class """

    csp = {
        "default-src": set(["'self'"]),
        "script-src": set(["'self'"]),
        "connect-src": set(["'self'"]),
        "frame-src": set(["'self'"]),
        "img-src": set(["'self'"]),
        "media-src": set(["'none'"]),
        "font-src": set(["'self'"]),
        "object-src": set(["'none'"]),
        "style-src": set(["'self'"]),
    }
    _session = None
    dbsession = dbsession
    chatsession = chatsession
    _memcached = None
    new_events = []
    io_loop = IOLoop.instance()
    event_manager = EventManager.instance()
    config = options  # backward compatability

    def initialize(self):
        """ Setup sessions, etc """
        self.add_content_policy("connect-src", self.config.origin)
        # We need this for a few things, and so far as I know it doesn't
        # present too much of a security risk - TODO: no longer require
        # inline styles
        self.add_content_policy("style-src", "'unsafe-inline'")

    def get_current_user(self):
        """ Get current user object from database """
        if self.session is not None:
            try:
                return User.by_uuid(self.session["user_uuid"])
            except KeyError:
                logging.exception("Malformed session: %r" % self.session)
            except:
                logging.exception("Failed call to get_current_user()")
        return None

    def start_session(self):
        """ Starts a new session """
        self.session = self._create_session()
        flags = {
            "expires": self.session.expires,
            "path": "/",
            "HttpOnly": True
        }
        if self.config.ssl:
            flags["Secure"] = True
        self.set_secure_cookie("session_id", self.session.session_id, **flags)

    def add_content_policy(self, src, policy):
        """ Add to the existing CSP header """
        if not src.endswith("-src"):
            src += "-src"
        if src in self.csp:
            self.csp[src].add(policy)
            self._refresh_csp()
        else:
            raise ValueError("Invalid content source")

    def clear_content_policy(self, src):
        """ Clear a content source in the existing CSP header """
        if not src.endswith("-src"):
            src += "-src"
        if src in self.csp:
            self.csp[src] = set()
            self._refresh_csp()
        else:
            raise ValueError("Invalid content source")

    def _refresh_csp(self):
        """ Rebuild the Content-Security-Policy header """
        _csp = []
        for src, policies in list(self.csp.items()):
            if len(policies):
                _csp.append("%s %s; " % (src, " ".join(policies)))
        csp = "".join(_csp)
        # Disabled until i can figure out the bug
        # self.set_header("Content-Security-Policy", csp)

    @property
    def memcached(self):
        """ Connects to Memcached instance """
        if self._memcached is None:
            self._memcached = MemcachedConnect()
        return self._memcached

    def _create_session(self):
        """ Creates a new session """
        kwargs = {
            "connection": self.memcached,
            "ip_address": self.request.remote_ip
        }
        new_session = MemcachedSession(**kwargs)
        new_session.save()
        return new_session

    def flush_memcached(self):
        if self._memcached is not None:
            self._memcached.flush_all()

    @property
    def session(self):
        if self._session is None:
            session_id = self.get_secure_cookie("session_id")
            if session_id is not None:
                self._session = self._get_session(session_id)
        return self._session

    @session.setter
    def session(self, new_session):
        self._session = new_session

    def _get_session(self, session_id):
        kwargs = {
            "connection": self.memcached,
            "session_id": session_id,
            "ip_address": self.request.remote_ip,
        }
        old_session = MemcachedSession.load(**kwargs)
        if old_session and not old_session.is_expired():
            old_session.refresh()
            return old_session
        else:
            return None

    def set_default_headers(self):
        """
        Set security HTTP headers, and add some troll-y version headers
        """
        self.set_header("Server", "Microsoft-IIS/7.5")
        self.add_header("X-Powered-By", "ASP.NET")
        self.add_header("X-Frame-Options", "DENY")
        self.add_header("X-XSS-Protection", "1; mode=block")
        self.add_header("X-Content-Type-Options", "nosniff")
        self._refresh_csp()
        if self.config.ssl:
            self.add_header("Strict-Transport-Security",
                            "max-age=31536000; includeSubDomains;")

    def write_error(self, status_code, **kwargs):
        """ Write our custom error pages """
        trace = "".join(traceback.format_exception(*kwargs["exc_info"]))
        logging.error("Request from %s resulted in an error code %d:\n%s" %
                      (self.request.remote_ip, status_code, trace))
        if status_code in [403]:
            # This should only get called when the _xsrf check fails,
            # all other '403' cases we just send a redirect to /403
            # self.render('public/403.html', locked=False, xsrf=True)
            self.redirect("/logout")  # just log them out
        else:
            if not self.config.debug:
                # Never tell the user we got a 500
                self.render("public/404.html")
            else:
                # If debug mode is enabled, just call Tornado's write_error()
                super(BaseHandler, self).write_error(status_code, **kwargs)

    def get(self, *args, **kwargs):
        """ Placeholder, incase child class does not impl this method """
        self.render("public/404.html")

    def post(self, *args, **kwargs):
        """ Placeholder, incase child class does not impl this method """
        self.render("public/404.html")

    def put(self, *args, **kwargs):
        """ Log odd behavior, this should never get legitimately called """
        logging.warn("%s attempted to use PUT method" % self.request.remote_ip)

    def delete(self, *args, **kwargs):
        """ Log odd behavior, this should never get legitimately called """
        logging.warn("%s attempted to use DELETE method" %
                     self.request.remote_ip)

    def head(self, *args, **kwargs):
        """ Ignore it """
        logging.warn("%s attempted to use HEAD method" %
                     self.request.remote_ip)

    def options(self, *args, **kwargs):
        """ Log odd behavior, this should never get legitimately called """
        logging.warn("%s attempted to use OPTIONS method" %
                     self.request.remote_ip)

    def on_finish(self, *args, **kwargs):
        """ Called after a response is sent to the client """
        self.dbsession.close()

    def timer(self):
        timer = None
        if self.application.settings["freeze_scoreboard"]:
            timerdiff = self.application.settings[
                "freeze_scoreboard"] - time.time()
            if timerdiff <= 0:
                timerdiff = 0
                if self.application.settings["stop_timer"]:
                    self.application.settings["stop_timer"] = False
                    self.stop_game()
            timer = str(timerdiff)
        return timer

    def start_game(self):
        """ Start the game and any related callbacks """
        if not self.application.settings["game_started"]:
            logging.info("The game is about to begin, good hunting!")
            self.application.settings["game_started"] = True
            self.application.settings["history_callback"].start()
            if self.config.use_bots:
                self.application.settings["score_bots_callback"].start()
            # Fire game start webhook
            send_game_start_webhook()

    def stop_game(self):
        """ Stop the game and all callbacks """
        if self.application.settings["game_started"]:
            logging.info("The game is stopping ...")
            self.application.settings["game_started"] = False
            if self.application.settings["history_callback"]._running:
                self.application.settings["history_callback"].stop()
            if self.application.settings["score_bots_callback"]._running:
                self.application.settings["score_bots_callback"].stop()
            # Fire game stop webhook
            send_game_stop_webhook()

    def get_user_locale(self):
        """
        Get user lang value from config.
        If None is returned, Tornado fall back to get_browser_locale()
        """
        if len(self.config.force_locale) > 0:
            return locale.get(self.config.force_locale)
        else:
            """
            This is a work around as Tornado get_browser_locale() is not returning the closest match.
            https://github.com/tornadoweb/tornado/issues/1858
            https://github.com/moloch--/RootTheBox/issues/367
            """
            codes = self.request.headers.get("Accept-Language")
            if codes:
                for code in codes.split(","):
                    code = code.split(";")[0]
                    for l in locale.get_supported_locales():
                        if code.lower() == l.split("_")[0]:
                            return locale.get(l)
        return None
예제 #17
0
class BaseWebSocketHandler(WebSocketHandler):
    """ Handles websocket connections """

    _session = None
    _memcached = None
    io_loop = IOLoop.instance()
    manager = EventManager.instance()
    config = options  # backward compatability

    def check_origin(self, origin):
        """ Parses the request's origin header """
        try:
            request_origin = urlparse(origin)
            origin = urlparse(self.config.origin)
            logging.debug("Checking request origin '%s' ends with '%s'" %
                          (request_origin, origin))
            return request_origin.netloc.endswith(origin)
        except:
            logging.exception("Failed to parse request origin: %r" % origin)
            return False

    @property
    def memcached(self):
        """ Connects to Memcached instance """
        if self._memcached is None:
            self._memcached = MemcachedConnect()
        return self._memcached

    @property
    def session(self):
        if self._session is None:
            session_id = self.get_secure_cookie("session_id")
            if session_id is not None:
                self._session = self._get_session(session_id)
        return self._session

    @session.setter
    def session(self, new_session):
        self._session = new_session

    def _get_session(self, session_id):
        kwargs = {
            "connection": self.memcached,
            "session_id": session_id,
            "ip_address": self.request.remote_ip,
        }
        old_session = MemcachedSession.load(**kwargs)
        if old_session and not old_session.is_expired():
            old_session.refresh()
            return old_session
        else:
            return None

    def get_current_user(self):
        """ Get current user object from database """
        if self.session is not None:
            try:
                return User.by_handle(self.session["handle"])
            except KeyError:
                logging.exception("Malformed session: %r" % self.session)
            except:
                logging.exception("Failed call to get_current_user()")
        return None

    def open(self):
        pass

    def on_message(self, message):
        pass

    def on_close(self):
        pass
예제 #18
0
 def initialize(self):
     ''' Setup sessions '''
     self.manager = EventManager.instance()
     self.game_history = GameHistory.instance()
예제 #19
0
class BaseWebSocketHandler(WebSocketHandler):
    ''' Handles websocket connections '''

    _session = None
    _memcached = None
    io_loop = IOLoop.instance()
    manager = EventManager.instance()
    config = ConfigManager.instance()

    @property
    def memcached(self):
        ''' Connects to Memcached instance '''
        if self._memcached is None:
            self._memcached = pylibmc.Client([self.config.memcached], binary=True)
            self._memcached.behaviors['no_block'] = 1  # async I/O
        return self._memcached

    @property
    def session(self):
        if self._session is None:
            session_id = self.get_secure_cookie('session_id')
            if session_id is not None:
                self._session = self._get_session(session_id)
        return self._session

    @session.setter
    def session(self, new_session):
        self._session = new_session

    def _get_session(self, session_id):
        kwargs = {
            'connection': self.memcached,
            'session_id': session_id,
            'ip_address': self.request.remote_ip,
        }
        old_session = MemcachedSession.load(**kwargs)
        if old_session and not old_session.is_expired():
            old_session.refresh()
            return old_session
        else:
            return None

    def get_current_user(self):
        ''' Get current user object from database '''
        if self.session is not None:
            try:
                return User.by_handle(self.session['handle'])
            except KeyError:
                logging.exception(
                    "Malformed session: %r" % self.session
                )
            except:
                logging.exception("Failed call to get_current_user()")
        return None

    def open(self):
        pass

    def on_message(self, message):
        pass

    def on_close(self):
        pass
예제 #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()
예제 #21
0
 def initialize(self):
     ''' Setup sessions '''
     self.manager = EventManager.instance()
     self.game_history = GameHistory.instance()
예제 #22
0
    port=config.listen_port,

    # Anti-bruteforce
    automatic_ban=False,
    blacklist_threshold=10,
    blacklisted_ips=[],
    failed_logins={},

    # Special file directories
    source_code_market_dir=path.abspath('files/source_code_market/'),

    # Notifier WebSocket
    ws_connect=config.ws_connect,

    # Event manager
    event_manager=EventManager.instance(),

    # Debug mode
    debug=config.debug,

    # Flag used to start the game
    game_started=False,

    # Callback functions
    score_bots_callback = PeriodicCallback(
        score_bots,
        config.bot_reward_interval,
        io_loop=io_loop
    ),

    history_callback = PeriodicCallback(
예제 #23
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()
예제 #24
0
 def initialize(self):
     ''' Setup sessions '''
     self.manager = EventManager.instance()