Exemplo n.º 1
0
    def setUp(self):
        self.password = '******'
        app.ensure_admin_password(True, password=self.password)
        app.app.config['WTF_CSRF_ENABLED'] = False
        self.work_dir = tempfile.mkdtemp()
        beeswarm.shared.zmq_context = zmq.Context()
        fd, self.db_file = tempfile.mkstemp()
        os.close(fd)
        connection_string = 'sqlite:///{0}'.format(self.db_file)
        os.remove(self.db_file)
        database.setup_db(connection_string)
        self.config_actor = ConfigActor(
            os.path.join(os.path.dirname(__file__), 'beeswarmcfg.json.test'),
            self.work_dir)
        self.config_actor.start()

        # seed database with test data
        session = database.get_session()
        session.add_all([Client(), Honeypot()])
        session.commit()

        # startup session database
        self.database_actor = DatabaseActor(999, delay_seconds=2)
        self.database_actor.start()

        self.app = app.app.test_client()
        app.connect_sockets()
Exemplo n.º 2
0
    def populate_bait(self, honeypot_first):
        honeypot_id = 1
        client_id = 2
        honeypot = Honeypot(id=honeypot_id)
        client = Client(id=client_id)

        db_session = database_setup.get_session()
        db_session.add(honeypot)
        db_session.add(client)
        db_session.commit()

        drone_data_socket = beeswarm.shared.zmq_context.socket(zmq.PUB)
        drone_data_socket.bind(SocketNames.DRONE_DATA.value)

        fd, config_file = tempfile.mkstemp()
        os.close(fd)
        os.remove(config_file)
        # persistence actor needs to communicate with on config REQ/REP socket
        config_actor = ConfigActor(config_file, '')
        config_actor.start()

        # startup session database
        database_actor = DatabaseActor(999, delay_seconds=2)
        database_actor.start()
        gevent.sleep(1)

        BaitSession.client_id = client_id

        honeypot_session = HoneypotSession(source_ip='192.168.100.22', source_port=52311, protocol='pop3', users={},
                                           destination_port=110)
        honeypot_session.add_auth_attempt('plaintext', True, username='******', password='******')
        honeypot_session.honeypot_id = honeypot_id

        bait_session = BaitSession('pop3', '1234', 110, honeypot_id)
        bait_session.add_auth_attempt('plaintext', True, username='******', password='******')
        bait_session.honeypot_id = honeypot_id
        bait_session.did_connect = bait_session.did_login = bait_session.alldone = bait_session.did_complete = True

        if honeypot_first:
            drone_data_socket.send('{0} {1} {2}'.format(Messages.SESSION_HONEYPOT.value, honeypot_id,
                                                        json.dumps(honeypot_session.to_dict(), default=json_default,
                                                        ensure_ascii=False)))
            drone_data_socket.send('{0} {1} {2}'.format(Messages.SESSION_CLIENT.value, client_id,
                                                        json.dumps(bait_session.to_dict(), default=json_default,
                                                        ensure_ascii=False)))
        else:
            drone_data_socket.send('{0} {1} {2}'.format(Messages.SESSION_CLIENT.value, client_id,
                                                        json.dumps(bait_session.to_dict(), default=json_default,
                                                        ensure_ascii=False)))
            drone_data_socket.send('{0} {1} {2}'.format(Messages.SESSION_HONEYPOT.value, honeypot_id,
                                                        json.dumps(honeypot_session.to_dict(), default=json_default,
                                                        ensure_ascii=False)))


        # some time for the session actor to work
        gevent.sleep(2)
        config_actor.stop()
        database_actor.stop()
        if os.path.isfile(config_file):
            os.remove(config_file)
Exemplo n.º 3
0
    def test_matching_quick_succession(self):
        """
        Tests that attack sessions coming in quick succession are classified correctly.
        This test relates to issue #218
        """

        honeypot_id = 1
        honeypot = Honeypot(id=honeypot_id)

        db_session = database_setup.get_session()
        db_session.add(honeypot)
        db_session.commit()

        drone_data_socket = beeswarm.shared.zmq_context.socket(zmq.PUB)
        drone_data_socket.bind(SocketNames.DRONE_DATA.value)

        # startup session database
        database_actor = DatabaseActor(999, delay_seconds=2)
        database_actor.start()
        gevent.sleep(1)

        for x in xrange(0, 100):
            honeypot_session = HoneypotSession(source_ip='192.168.100.22',
                                               source_port=52311,
                                               protocol='pop3',
                                               users={},
                                               destination_port=110)
            honeypot_session.add_auth_attempt('plaintext',
                                              True,
                                              username='******',
                                              password='******')
            honeypot_session.honeypot_id = honeypot_id
            drone_data_socket.send('{0} {1} {2}'.format(
                Messages.SESSION_HONEYPOT.value, honeypot_id,
                json.dumps(honeypot_session.to_dict(),
                           default=json_default,
                           ensure_ascii=False)))

        gevent.sleep(1)
        database_actor_request_socket = beeswarm.shared.zmq_context.socket(
            zmq.REQ)
        database_actor_request_socket.connect(
            SocketNames.DATABASE_REQUESTS.value)
        sessions = send_zmq_request_socket(
            database_actor_request_socket,
            '{0}'.format(Messages.GET_SESSIONS_ALL.value))

        for session in sessions:
            self.assertEqual(session['classification'], 'Bruteforce')

        self.assertEqual(len(sessions), 100)
Exemplo n.º 4
0
    def setUp(self):
        self.password = '******'
        app.ensure_admin_password(True, password=self.password)
        app.app.config['WTF_CSRF_ENABLED'] = False
        self.work_dir = tempfile.mkdtemp()
        beeswarm.shared.zmq_context = zmq.Context()
        fd, self.db_file = tempfile.mkstemp()
        os.close(fd)
        connection_string = 'sqlite:///{0}'.format(self.db_file)
        os.remove(self.db_file)
        database.setup_db(connection_string)
        self.config_actor = ConfigActor(os.path.join(os.path.dirname(__file__), 'beeswarmcfg.json.test'), self.work_dir)
        self.config_actor.start()

        # seed database with test data
        session = database.get_session()
        session.add_all([Client(), Honeypot()])
        session.commit()

        # startup session database
        self.database_actor = DatabaseActor(999, delay_seconds=2)
        self.database_actor.start()

        self.app = app.app.test_client()
        app.connect_sockets()
Exemplo n.º 5
0
    def test_matching_quick_succession(self):
        """
        Tests that attack sessions coming in quick succession are classified correctly.
        This test relates to issue #218
        """

        honeypot_id = 1
        honeypot = Honeypot(id=honeypot_id)

        db_session = database_setup.get_session()
        db_session.add(honeypot)
        db_session.commit()

        drone_data_socket = beeswarm.shared.zmq_context.socket(zmq.PUB)
        drone_data_socket.bind(SocketNames.DRONE_DATA.value)

        # startup session database
        database_actor = DatabaseActor(999, delay_seconds=2)
        database_actor.start()
        gevent.sleep(1)

        for x in xrange(0, 100):
            honeypot_session = HoneypotSession(source_ip='192.168.100.22', source_port=52311, protocol='pop3', users={},
                                               destination_port=110)
            honeypot_session.add_auth_attempt('plaintext', True, username='******', password='******')
            honeypot_session.honeypot_id = honeypot_id
            drone_data_socket.send('{0} {1} {2}'.format(Messages.SESSION_HONEYPOT.value, honeypot_id,
                                                            json.dumps(honeypot_session.to_dict(), default=json_default,
                                                            ensure_ascii=False)))

        gevent.sleep(1)
        database_actor_request_socket = beeswarm.shared.zmq_context.socket(zmq.REQ)
        database_actor_request_socket.connect(SocketNames.DATABASE_REQUESTS.value)
        sessions = send_zmq_request_socket(database_actor_request_socket, '{0}'.format(Messages.GET_SESSIONS_ALL.value))

        for session in sessions:
            self.assertEqual(session['classification'], 'Bruteforce')

        self.assertEqual(len(sessions), 100)
Exemplo n.º 6
0
class WebAppTests(unittest.TestCase):
    def setUp(self):
        self.password = '******'
        app.ensure_admin_password(True, password=self.password)
        app.app.config['WTF_CSRF_ENABLED'] = False
        self.work_dir = tempfile.mkdtemp()
        beeswarm.shared.zmq_context = zmq.Context()
        self.db_file = tempfile.mkstemp()[1]
        connection_string = 'sqlite:///{0}'.format(self.db_file)
        os.remove(self.db_file)
        database.setup_db(connection_string)
        self.config_actor = ConfigActor(os.path.join(os.path.dirname(__file__), 'beeswarmcfg.json.test'), self.work_dir)
        self.config_actor.start()

        # seed database with test data
        session = database.get_session()
        session.add_all([Client(), Honeypot()])
        session.commit()

        # startup session database
        self.database_actor = DatabaseActor(999, delay_seconds=2)
        self.database_actor.start()

        self.app = app.app.test_client()
        app.connect_sockets()

    def tearDown(self):
        self.database_actor.stop()
        self.config_actor.stop()
        shutil.rmtree(self.work_dir)
        if os.path.isfile(self.db_file):
            os.remove(self.db_file)

    # TODO: All these posts should be moved to ZMQ tests
    # def test_basic_client_post(self):
    # """
    # Tests if a bait_session dict can be posted without exceptions.
    # """
    # self.login(self.client_id, self.client_password)
    #     data_dict = {
    #         'id': str(uuid.uuid4()),
    #         'client_id': self.client_id,
    #         'honeypot_id': self.honeypot_id,
    #         'protocol': 'pop3',
    #         'destination_ip': '127.0.0.1',
    #         'destination_port': '110',
    #         'source_ip': '123.123.123.123',
    #         'source_port': 12345,
    #         'timestamp': datetime.utcnow().isoformat(),
    #         'did_connect': True,
    #         'did_login': True,
    #         'did_complete': True,
    #         'protocol_data': None,
    #         'login_attempts': [{'id': str(uuid.uuid4()), 'username': '******', 'password': '******', 'successful': True,
    #                             'timestamp': datetime.utcnow().isoformat()}]
    #     }
    #     r = self.app.post('/ws/client_data', data=json.dumps(data_dict), content_type='application/json')
    #     self.assertEquals(r.status, '200 OK')
    #
    # def test_basic_unsuccessful_client_post(self):
    #     """
    #     Tests if an error is returned when data is posted without ID values.
    #     """
    #
    #     self.login(self.client_id, self.client_password)
    #
    #     #missing id's
    #     data_dict = {
    #         'protocol': 'pop3',
    #         'username': '******',
    #         'password': '******',
    #         'server_host': '127.0.0.1',
    #         'server_port': '110',
    #         'source_ip': '123.123.123.123',
    #         'source_port': 12345,
    #         'timestamp': datetime.utcnow().isoformat(),
    #         'did_connect': True,
    #         'did_login': True,
    #         'did_complete': True,
    #         'protocol_data': None
    #     }
    #
    #     r = self.app.post('/ws/client_data', data=json.dumps(data_dict), content_type='application/json')
    #     self.assertEquals(r.status, '500 INTERNAL SERVER ERROR')
    #
    # def test_basic_honeypot_post(self):
    #     """
    #     Tests if a session dict can be posted without exceptions.
    #     """
    #
    #     self.login(self.honeypot_id, self.honeypot_password)
    #
    #     data_dict = {
    #         'id': 'ba9fdb3d-0efb-4764-9a6b-d9b86eccda96',
    #         'honeypot_id': self.honeypot_id,
    #         'destination_ip': '192.168.1.1',
    #         'destination_port': 8023,
    #         'protocol': 'telnet',
    #         'source_ip': '127.0.0.1',
    #         'timestamp': '2013-05-07T22:21:19.453828',
    #         'source_port': 61175,
    #         'login_attempts': [
    #             {'username': '******', 'timestamp': '2013-05-07T22:21:20.846805', 'password': '******',
    #              'id': '027bd968-f8ea-4a69-8d4c-6cf21476ca10', 'successful': False},
    #             {'username': '******', 'timestamp': '2013-05-07T22:21:21.150571', 'password': '******',
    #              'id': '603f40a4-e7eb-442d-9fde-0cd3ba707af7', 'successful': False}, ],
    #         'transcript': [
    #             {'timestamp': '2013-05-07T22:21:20.846805', 'direction': 'in', 'data': 'whoami\r\n'},
    #             {'timestamp': '2013-05-07T22:21:21.136800', 'direction': 'out', 'data': 'james_brown\r\n$:~'}]
    #     }
    #
    #     r = self.app.post('/ws/honeypot_data', data=json.dumps(data_dict), content_type='application/json')
    #     self.assertEquals(r.status, '200 OK')
    #
    # def test_basic_unsuccessful_honeypot_post(self):
    #     """
    #     Tests if an error is returned when data is posted without ID values.
    #     """
    #
    #     self.login(self.honeypot_id, self.honeypot_password)
    #
    #     #missing id
    #     data_dict = {
    #         'honeypot_id': self.honeypot_id,
    #         'destination_ip': '192.168.1.1',
    #         'destination_port': 8023,
    #         'protocol': 'telnet',
    #         'source_ip': '127.0.0.1',
    #         'timestamp': '2013-05-07T22:21:19.453828',
    #         'source_port': 61175,
    #         'login_attempts': [
    #             {'username': '******', 'timestamp': '2013-05-07T22:21:20.846805', 'password': '******',
    #              'id': '027bd968-f8ea-4a69-8d4c-6cf21476ca10', 'successful': False},
    #             {'username': '******', 'timestamp': '2013-05-07T22:21:21.150571', 'password': '******',
    #              'id': '603f40a4-e7eb-442d-9fde-0cd3ba707af7', 'successful': False}, ],
    #         'transcript': [
    #             {'timestamp': '2013-05-07T22:21:20.846805', 'direction': 'in', 'data': 'whoami\r\n'},
    #             {'timestamp': '2013-05-07T22:21:21.136800', 'direction': 'out', 'data': 'james_brown\r\n$:~'}
    #         ]
    #     }
    #     r = self.app.post('/ws/honeypot_data', data=json.dumps(data_dict), content_type='application/json')
    #     self.assertEquals(r.status, '500 INTERNAL SERVER ERROR')
    #
    # def test_new_client(self):
    #     """
    #     Tests if a new Client configuration can be posted successfully
    #     """
    #
    #     post_data = {
    #         'http_enabled': True,
    #         'http_server': '127.0.0.1',
    #         'http_port': 80,
    #         'http_active_range': '13:40 - 16:30',
    #         'http_sleep_interval': 0,
    #         'http_activation_probability': 1,
    #         'http_login': '******',
    #         'http_password': '******',
    #
    #         'https_enabled': True,
    #         'https_server': '127.0.0.1',
    #         'https_port': 80,
    #         'https_active_range': '13:40 - 16:30',
    #         'https_sleep_interval': 0,
    #         'https_activation_probability': 1,
    #         'https_login': '******',
    #         'https_password': '******',
    #
    #         'pop3s_enabled': True,
    #         'pop3s_server': '127.0.0.1',
    #         'pop3s_port': 80,
    #         'pop3s_active_range': '13:40 - 16:30',
    #         'pop3s_sleep_interval': 0,
    #         'pop3s_activation_probability': 1,
    #         'pop3s_login': '******',
    #         'pop3s_password': '******',
    #
    #         'ssh_enabled': True,
    #         'ssh_server': '127.0.0.1',
    #         'ssh_port': 80,
    #         'ssh_active_range': '13:40 - 16:30',
    #         'ssh_sleep_interval': 0,
    #         'ssh_activation_probability': 1,
    #         'ssh_login': '******',
    #         'ssh_password': '******',
    #
    #         'ftp_enabled': True,
    #         'ftp_server': '127.0.0.1',
    #         'ftp_port': 80,
    #         'ftp_active_range': '13:40 - 16:30',
    #         'ftp_sleep_interval': 0,
    #         'ftp_activation_probability': 1,
    #         'ftp_login': '******',
    #         'ftp_password': '******',
    #
    #         'pop3_enabled': True,
    #         'pop3_server': '127.0.0.1',
    #         'pop3_port': 110,
    #         'pop3_active_range': '13:40 - 16:30',
    #         'pop3_sleep_interval': 0,
    #         'pop3_activation_probability': 1,
    #         'pop3_login': '******',
    #         'pop3_password': '******',
    #
    #         'smtp_enabled': True,
    #         'smtp_server': '127.0.0.1',
    #         'smtp_port': 25,
    #         'smtp_active_range': '13:40 - 16:30',
    #         'smtp_sleep_interval': 0,
    #         'smtp_activation_probability': 1,
    #         'smtp_login': '******',
    #         'smtp_password': '******',
    #
    #         'vnc_enabled': True,
    #         'vnc_server': '127.0.0.1',
    #         'vnc_port': 5900,
    #         'vnc_active_range': '13:40 - 16:30',
    #         'vnc_sleep_interval': 0,
    #         'vnc_activation_probability': 1,
    #         'vnc_login': '******',
    #         'vnc_password': '******',
    #
    #         'telnet_enabled': True,
    #         'telnet_server': '127.0.0.1',
    #         'telnet_port': 23,
    #         'telnet_active_range': '13:40 - 16:30',
    #         'telnet_sleep_interval': 0,
    #         'telnet_activation_probability': 1,
    #         'telnet_login': '******',
    #         'telnet_password': '******',
    #     }
    #     self.login('test', 'test')
    #     resp = self.app.post('/ws/client', data=post_data)
    #     self.assertTrue(200, resp.status_code)
    #     self.logout()
    #
    # def test_new_honeypot(self):
    #     """
    #     Tests whether new Honeypot configuration can be posted successfully.
    #     """
    #     post_data = {
    #         'http_enabled': True,
    #         'http_port': 80,
    #         'http_banner': 'Microsoft-IIS/5.0',
    #
    #         'https_enabled': False,
    #         'https_port': 443,
    #         'https_banner': 'Microsoft-IIS/5.0',
    #
    #         'ftp_enabled': False,
    #         'ftp_port': 21,
    #         'ftp_max_attempts': 3,
    #         'ftp_banner': 'Microsoft FTP Server',
    #
    #         'smtp_enabled': False,
    #         'smtp_port': 25,
    #         'smtp_banner': 'Microsoft ESMTP MAIL service ready',
    #
    #         'vnc_enabled': False,
    #         'vnc_port': 5900,
    #
    #         'telnet_enabled': False,
    #         'telnet_port': 23,
    #         'telnet_max_attempts': 3,
    #
    #         'pop3_enabled': False,
    #         'pop3_port': 110,
    #         'pop3_max_attempts': 3,
    #
    #         'pop3s_enabled': False,
    #         'pop3s_port': 110,
    #         'pop3s_max_attempts': 3,
    #
    #         'ssh_enabled': False,
    #         'ssh_port': 22,
    #         'ssh_key': 'server.key'
    #     }
    #     self.login('test', 'test')
    #     resp = self.app.post('/ws/honeypot', data=post_data)
    #     self.assertTrue(200, resp.status_code)
    #     self.logout()
    #
    # def test_new_honeypot_config(self):
    #     """ Tests if a Honeypot config is being returned correctly """
    #
    #     resp = self.app.get('/ws/honeypot/config/' + self.honeypot_id)
    #     self.assertEquals(resp.data, 'test_honeypot_config')
    #
    # def test_new_client_config(self):
    #     """ Tests if a Client config is being returned correctly """
    #
    #     resp = self.app.get('/ws/client/config/' + self.client_id)
    #     self.assertEquals(resp.data, 'test_client_config')

    def test_data_sessions_all(self):
        """ Tests if all sessions are returned properly"""

        self.login('test', self.password)
        self.populate_sessions()
        resp = self.app.get('/data/sessions/all')
        table_data = json.loads(resp.data)
        self.assertEquals(len(table_data), 4)
        self.logout()

    def test_data_sessions_honeybees(self):
        """ Tests if bait_sessions are returned properly """

        self.login('test', self.password)
        self.populate_honeybees()
        resp = self.app.get('/data/sessions/bait_sessions')
        table_data = json.loads(resp.data)
        self.assertEquals(len(table_data), 3)
        self.logout()

    def test_data_sessions_attacks(self):
        """ Tests if attacks are returned properly """

        self.login('test', self.password)
        self.populate_sessions()
        resp = self.app.get('/data/sessions/attacks')
        table_data = json.loads(resp.data)
        self.assertEquals(len(table_data), 4)
        self.logout()

    def test_data_transcripts(self):
        """ Tests that if given a session ID we can extract the relevant transcripts"""
        db_session = database.get_session()
        self.login('test', self.password)
        session_id = str(uuid.uuid4())

        timestamp = datetime.utcnow()

        db_session.add(Transcript(timestamp=timestamp, direction='outgoing', data='whoami', session_id=session_id))
        db_session.add(Transcript(timestamp=timestamp, direction='outgoing', data='root\r\n', session_id=session_id))
        db_session.commit()
        resp = self.app.get('/data/session/{0}/transcript'.format(session_id))
        data = json.loads(resp.data)
        string_timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')
        expected_result = [{u'direction': u'outgoing', u'data': u'whoami', u'time': u'{0}'.format(string_timestamp)},
                           {u'direction': u'outgoing', u'data': u'root\r\n', u'time': u'{0}'.format(string_timestamp)}]
        self.assertDictEqual(sorted(data)[0], sorted(expected_result)[0])

    def test_login_logout(self):
        """ Tests basic login/logout """

        self.login('admin', self.password)
        self.logout()

    def test_get_baitusers(self):
        """ Tests GET on the '/ws/bait_users' route."""
        self.login('admin', self.password)
        self.populate_bait_users()
        resp = self.app.get('/ws/bait_users')
        table_data = json.loads(resp.data)
        self.assertEquals(len(table_data), 2)
        self.logout()

    def test_add_baituser(self):
        """ Tests POST on the '/ws/bait_users/add' route."""
        self.login('admin', self.password)
        data = [
            {'username': '******', 'password': '******'},
            {'username': '******', 'password': '******'},
            {'username': '******', 'password': '******'}
        ]

        self.app.post('/ws/bait_users/add', data=json.dumps(data), follow_redirects=True)

        resp = self.app.get('/ws/bait_users')
        bait_users = json.loads(resp.data)
        # 22 in bootstrapping, 3 just added
        self.assertEquals(len(bait_users), 22 + 3)
        self.logout()

    def populate_clients(self):
        """ Populates the database with 4 clients """

        db_session = database.get_session()
        self.clients = []
        for i in xrange(4):  # We add 4 here, but one is added in the setup method
            f = Client()
            self.clients.append(f.id)
            db_session.add(f)
        db_session.commit()

    def populate_honeypots(self):
        """ Populates the database with 4 honeypots """

        db_session = database.get_session()
        self.honeypots = []
        for i in xrange(4):  # We add 4 here, but one is added in the setup method
            h = Honeypot()
            self.honeypots.append(h.id)
            db_session.add(h)
        db_session.commit()

    def populate_bait_users(self):
        """ Populates the database with 2 bait users """
        db_session = database.get_session()
        db_session.query(BaitUser).delete()
        self.clients = []
        for c in [('userA', 'passA'), ('userB', 'passB')]:  # We add 4 here, but one is added in the setup method
            bait_user = BaitUser(username=c[0], password=c[1])
            db_session.add(bait_user)
        db_session.commit()

    def login(self, username, password):
        """ Logs into the web-app """

        data = {
            'username': username,
            'password': password
        }
        return self.app.post('/login', data=data, follow_redirects=True)

    def populate_honeybees(self):
        """ Populates the database with 3 Honeybees """

        db_session = database.get_session()
        for i in xrange(3):
            h = BaitSession(
                id=str(uuid.uuid4()),
                timestamp=datetime.utcnow(),
                received=datetime.utcnow(),
                protocol='ssh',
                destination_ip='1.2.3.4',
                destination_port=1234,
                source_ip='4.3.2.1',
                source_port=4321,
                did_connect=True,
                did_login=False,
                did_complete=True
            )
            a = Authentication(id=str(uuid.uuid4()), username='******', password='******',
                               successful=False,
                               timestamp=datetime.utcnow())
            h.authentication.append(a)
            db_session.add(h)

        db_session.commit()

    def populate_sessions(self):
        """ Populates the database with 3 Sessions """

        db_session = database.get_session()
        for i in xrange(4):
            s = Session(
                id=str(uuid.uuid4()),
                timestamp=datetime.utcnow(),
                received=datetime.utcnow(),
                protocol='telnet',
                destination_ip='123.123.123.123',
                destination_port=1234,
                source_ip='12.12.12.12',
                source_port=12345,
                classification_id='asd'
            )
            a = Authentication(id=str(uuid.uuid4()), username='******', password='******',
                               successful=False,
                               timestamp=datetime.utcnow())
            s.authentication.append(a)
            db_session.add(s)

        db_session.commit()

    def logout(self):
        return self.app.get('/logout', follow_redirects=True)
Exemplo n.º 7
0
    def __init__(self, work_dir, config, **kwargs):
        """
            Main class for the Web-Interface. It takes care of setting up
            the database, managing the users, etc.

        :param work_dir: The working directory (usually the current working directory).
        :param config_arg: Beeswarm configuration dictionary, None if not configuration was supplied.
        """
        customize = kwargs['customize']
        reset_password = kwargs['reset_password']
        if 'clear_db' in kwargs:
            clear_sessions = kwargs['clear_db']
        else:
            clear_sessions = True

        if 'server_hostname' in kwargs:
            server_hostname = kwargs['server_hostname']
        else:
            server_hostname = None

        max_sessions = kwargs['max_sessions']
        start_webui = kwargs['start_webui']

        self.work_dir = work_dir
        self.config_file = os.path.join(work_dir, 'beeswarmcfg.json')

        if config is None:
            self.prepare_environment(work_dir,
                                     customize,
                                     server_hostname=server_hostname)
            with open(os.path.join(work_dir, self.config_file),
                      'r') as config_file:
                self.config = json.load(config_file, object_hook=asciify)
        else:
            self.config = config
        # list of all self-running (actor) objects that receive or send
        # messages on one or more zmq queues
        self.actors = []
        self.greenlets = []

        proxy_greenlet = gevent.spawn(self.message_proxy, work_dir)
        self.greenlets.append(proxy_greenlet)
        config_actor = ConfigActor(self.config_file, work_dir)
        config_actor.start()
        self.actors.append(config_actor)
        self.greenlets.append(config_actor)

        # make path in sqlite connection string absolute
        connection_string = self.config['sql']['connection_string']
        if connection_string.startswith('sqlite:///'):
            _, relative_path = os.path.split(connection_string)
            connection_string = 'sqlite:///{0}'.format(
                os.path.join(self.work_dir, relative_path))
        database_setup.setup_db(connection_string)
        database_actor = DatabaseActor(max_sessions, clear_sessions)
        database_actor.start()
        self.actors.append(database_actor)
        self.greenlets.append(database_actor)

        for g in self.greenlets:
            g.link_exception(self.on_exception)

        gevent.sleep()

        self.started = False

        if start_webui:
            from beeswarm.server.webapp import app

            self.app = app.app
            self.app.config['CERT_PATH'] = self.config['ssl']['certpath']
            app.ensure_admin_password(reset_password)
        else:
            self.app = None
Exemplo n.º 8
0
    def populate_bait(self, honeypot_first):
        honeypot_id = 1
        client_id = 2
        honeypot = Honeypot(id=honeypot_id)
        client = Client(id=client_id)

        db_session = database_setup.get_session()
        db_session.add(honeypot)
        db_session.add(client)
        db_session.commit()

        drone_data_socket = beeswarm.shared.zmq_context.socket(zmq.PUB)
        drone_data_socket.bind(SocketNames.DRONE_DATA.value)

        config_file = tempfile.mkstemp()[1]
        os.remove(config_file)
        # persistence actor needs to communicate with on config REQ/REP socket
        config_actor = ConfigActor(config_file, '')
        config_actor.start()

        # startup session database
        database_actor = DatabaseActor(999, delay_seconds=2)
        database_actor.start()
        gevent.sleep(1)

        BaitSession.client_id = client_id

        honeypot_session = HoneypotSession(source_ip='192.168.100.22',
                                           source_port=52311,
                                           protocol='pop3',
                                           users={},
                                           destination_port=110)
        honeypot_session.add_auth_attempt('plaintext',
                                          True,
                                          username='******',
                                          password='******')
        honeypot_session.honeypot_id = honeypot_id

        bait_session = BaitSession('pop3', '1234', 110, honeypot_id)
        bait_session.add_auth_attempt('plaintext',
                                      True,
                                      username='******',
                                      password='******')
        bait_session.honeypot_id = honeypot_id
        bait_session.did_connect = bait_session.did_login = bait_session.alldone = bait_session.did_complete = True

        if honeypot_first:
            drone_data_socket.send('{0} {1} {2}'.format(
                Messages.SESSION_HONEYPOT.value, honeypot_id,
                json.dumps(honeypot_session.to_dict(),
                           default=json_default,
                           ensure_ascii=False)))
            drone_data_socket.send('{0} {1} {2}'.format(
                Messages.SESSION_CLIENT.value, client_id,
                json.dumps(bait_session.to_dict(),
                           default=json_default,
                           ensure_ascii=False)))
        else:
            drone_data_socket.send('{0} {1} {2}'.format(
                Messages.SESSION_CLIENT.value, client_id,
                json.dumps(bait_session.to_dict(),
                           default=json_default,
                           ensure_ascii=False)))
            drone_data_socket.send('{0} {1} {2}'.format(
                Messages.SESSION_HONEYPOT.value, honeypot_id,
                json.dumps(honeypot_session.to_dict(),
                           default=json_default,
                           ensure_ascii=False)))

        # some time for the session actor to work
        gevent.sleep(2)
        config_actor.stop()
        database_actor.stop()
        if os.path.isfile(config_file):
            os.remove(config_file)
Exemplo n.º 9
0
class WebAppTests(unittest.TestCase):
    def setUp(self):
        self.password = '******'
        app.ensure_admin_password(True, password=self.password)
        app.app.config['WTF_CSRF_ENABLED'] = False
        self.work_dir = tempfile.mkdtemp()
        beeswarm.shared.zmq_context = zmq.Context()
        fd, self.db_file = tempfile.mkstemp()
        os.close(fd)
        connection_string = 'sqlite:///{0}'.format(self.db_file)
        os.remove(self.db_file)
        database.setup_db(connection_string)
        self.config_actor = ConfigActor(
            os.path.join(os.path.dirname(__file__), 'beeswarmcfg.json.test'),
            self.work_dir)
        self.config_actor.start()

        # seed database with test data
        session = database.get_session()
        session.add_all([Client(), Honeypot()])
        session.commit()

        # startup session database
        self.database_actor = DatabaseActor(999, delay_seconds=2)
        self.database_actor.start()

        self.app = app.app.test_client()
        app.connect_sockets()

    def tearDown(self):
        self.database_actor.stop()
        self.database_actor = None
        self.config_actor.stop()
        self.config_actor = None
        shutil.rmtree(self.work_dir)
        if os.path.isfile(self.db_file):
            os.remove(self.db_file)

    # TODO: All these posts should be moved to ZMQ tests
    # def test_basic_client_post(self):
    # """
    # Tests if a bait_session dict can be posted without exceptions.
    # """
    # self.login(self.client_id, self.client_password)
    #     data_dict = {
    #         'id': str(uuid.uuid4()),
    #         'client_id': self.client_id,
    #         'honeypot_id': self.honeypot_id,
    #         'protocol': 'pop3',
    #         'destination_ip': '127.0.0.1',
    #         'destination_port': '110',
    #         'source_ip': '123.123.123.123',
    #         'source_port': 12345,
    #         'timestamp': datetime.utcnow().isoformat(),
    #         'did_connect': True,
    #         'did_login': True,
    #         'did_complete': True,
    #         'protocol_data': None,
    #         'login_attempts': [{'id': str(uuid.uuid4()), 'username': '******', 'password': '******', 'successful': True,
    #                             'timestamp': datetime.utcnow().isoformat()}]
    #     }
    #     r = self.app.post('/ws/client_data', data=json.dumps(data_dict), content_type='application/json')
    #     self.assertEquals(r.status, '200 OK')
    #
    # def test_basic_unsuccessful_client_post(self):
    #     """
    #     Tests if an error is returned when data is posted without ID values.
    #     """
    #
    #     self.login(self.client_id, self.client_password)
    #
    #     #missing id's
    #     data_dict = {
    #         'protocol': 'pop3',
    #         'username': '******',
    #         'password': '******',
    #         'server_host': '127.0.0.1',
    #         'server_port': '110',
    #         'source_ip': '123.123.123.123',
    #         'source_port': 12345,
    #         'timestamp': datetime.utcnow().isoformat(),
    #         'did_connect': True,
    #         'did_login': True,
    #         'did_complete': True,
    #         'protocol_data': None
    #     }
    #
    #     r = self.app.post('/ws/client_data', data=json.dumps(data_dict), content_type='application/json')
    #     self.assertEquals(r.status, '500 INTERNAL SERVER ERROR')
    #
    # def test_basic_honeypot_post(self):
    #     """
    #     Tests if a session dict can be posted without exceptions.
    #     """
    #
    #     self.login(self.honeypot_id, self.honeypot_password)
    #
    #     data_dict = {
    #         'id': 'ba9fdb3d-0efb-4764-9a6b-d9b86eccda96',
    #         'honeypot_id': self.honeypot_id,
    #         'destination_ip': '192.168.1.1',
    #         'destination_port': 8023,
    #         'protocol': 'telnet',
    #         'source_ip': '127.0.0.1',
    #         'timestamp': '2013-05-07T22:21:19.453828',
    #         'source_port': 61175,
    #         'login_attempts': [
    #             {'username': '******', 'timestamp': '2013-05-07T22:21:20.846805', 'password': '******',
    #              'id': '027bd968-f8ea-4a69-8d4c-6cf21476ca10', 'successful': False},
    #             {'username': '******', 'timestamp': '2013-05-07T22:21:21.150571', 'password': '******',
    #              'id': '603f40a4-e7eb-442d-9fde-0cd3ba707af7', 'successful': False}, ],
    #         'transcript': [
    #             {'timestamp': '2013-05-07T22:21:20.846805', 'direction': 'in', 'data': 'whoami\r\n'},
    #             {'timestamp': '2013-05-07T22:21:21.136800', 'direction': 'out', 'data': 'james_brown\r\n$:~'}]
    #     }
    #
    #     r = self.app.post('/ws/honeypot_data', data=json.dumps(data_dict), content_type='application/json')
    #     self.assertEquals(r.status, '200 OK')
    #
    # def test_basic_unsuccessful_honeypot_post(self):
    #     """
    #     Tests if an error is returned when data is posted without ID values.
    #     """
    #
    #     self.login(self.honeypot_id, self.honeypot_password)
    #
    #     #missing id
    #     data_dict = {
    #         'honeypot_id': self.honeypot_id,
    #         'destination_ip': '192.168.1.1',
    #         'destination_port': 8023,
    #         'protocol': 'telnet',
    #         'source_ip': '127.0.0.1',
    #         'timestamp': '2013-05-07T22:21:19.453828',
    #         'source_port': 61175,
    #         'login_attempts': [
    #             {'username': '******', 'timestamp': '2013-05-07T22:21:20.846805', 'password': '******',
    #              'id': '027bd968-f8ea-4a69-8d4c-6cf21476ca10', 'successful': False},
    #             {'username': '******', 'timestamp': '2013-05-07T22:21:21.150571', 'password': '******',
    #              'id': '603f40a4-e7eb-442d-9fde-0cd3ba707af7', 'successful': False}, ],
    #         'transcript': [
    #             {'timestamp': '2013-05-07T22:21:20.846805', 'direction': 'in', 'data': 'whoami\r\n'},
    #             {'timestamp': '2013-05-07T22:21:21.136800', 'direction': 'out', 'data': 'james_brown\r\n$:~'}
    #         ]
    #     }
    #     r = self.app.post('/ws/honeypot_data', data=json.dumps(data_dict), content_type='application/json')
    #     self.assertEquals(r.status, '500 INTERNAL SERVER ERROR')
    #
    # def test_new_client(self):
    #     """
    #     Tests if a new Client configuration can be posted successfully
    #     """
    #
    #     post_data = {
    #         'http_enabled': True,
    #         'http_server': '127.0.0.1',
    #         'http_port': 80,
    #         'http_active_range': '13:40 - 16:30',
    #         'http_sleep_interval': 0,
    #         'http_activation_probability': 1,
    #         'http_login': '******',
    #         'http_password': '******',
    #
    #         'https_enabled': True,
    #         'https_server': '127.0.0.1',
    #         'https_port': 80,
    #         'https_active_range': '13:40 - 16:30',
    #         'https_sleep_interval': 0,
    #         'https_activation_probability': 1,
    #         'https_login': '******',
    #         'https_password': '******',
    #
    #         'pop3s_enabled': True,
    #         'pop3s_server': '127.0.0.1',
    #         'pop3s_port': 80,
    #         'pop3s_active_range': '13:40 - 16:30',
    #         'pop3s_sleep_interval': 0,
    #         'pop3s_activation_probability': 1,
    #         'pop3s_login': '******',
    #         'pop3s_password': '******',
    #
    #         'ssh_enabled': True,
    #         'ssh_server': '127.0.0.1',
    #         'ssh_port': 80,
    #         'ssh_active_range': '13:40 - 16:30',
    #         'ssh_sleep_interval': 0,
    #         'ssh_activation_probability': 1,
    #         'ssh_login': '******',
    #         'ssh_password': '******',
    #
    #         'ftp_enabled': True,
    #         'ftp_server': '127.0.0.1',
    #         'ftp_port': 80,
    #         'ftp_active_range': '13:40 - 16:30',
    #         'ftp_sleep_interval': 0,
    #         'ftp_activation_probability': 1,
    #         'ftp_login': '******',
    #         'ftp_password': '******',
    #
    #         'pop3_enabled': True,
    #         'pop3_server': '127.0.0.1',
    #         'pop3_port': 110,
    #         'pop3_active_range': '13:40 - 16:30',
    #         'pop3_sleep_interval': 0,
    #         'pop3_activation_probability': 1,
    #         'pop3_login': '******',
    #         'pop3_password': '******',
    #
    #         'smtp_enabled': True,
    #         'smtp_server': '127.0.0.1',
    #         'smtp_port': 25,
    #         'smtp_active_range': '13:40 - 16:30',
    #         'smtp_sleep_interval': 0,
    #         'smtp_activation_probability': 1,
    #         'smtp_login': '******',
    #         'smtp_password': '******',
    #
    #         'vnc_enabled': True,
    #         'vnc_server': '127.0.0.1',
    #         'vnc_port': 5900,
    #         'vnc_active_range': '13:40 - 16:30',
    #         'vnc_sleep_interval': 0,
    #         'vnc_activation_probability': 1,
    #         'vnc_login': '******',
    #         'vnc_password': '******',
    #
    #         'telnet_enabled': True,
    #         'telnet_server': '127.0.0.1',
    #         'telnet_port': 23,
    #         'telnet_active_range': '13:40 - 16:30',
    #         'telnet_sleep_interval': 0,
    #         'telnet_activation_probability': 1,
    #         'telnet_login': '******',
    #         'telnet_password': '******',
    #     }
    #     self.login('test', 'test')
    #     resp = self.app.post('/ws/client', data=post_data)
    #     self.assertTrue(200, resp.status_code)
    #     self.logout()
    #
    # def test_new_honeypot(self):
    #     """
    #     Tests whether new Honeypot configuration can be posted successfully.
    #     """
    #     post_data = {
    #         'http_enabled': True,
    #         'http_port': 80,
    #         'http_banner': 'Microsoft-IIS/5.0',
    #
    #         'https_enabled': False,
    #         'https_port': 443,
    #         'https_banner': 'Microsoft-IIS/5.0',
    #
    #         'ftp_enabled': False,
    #         'ftp_port': 21,
    #         'ftp_max_attempts': 3,
    #         'ftp_banner': 'Microsoft FTP Server',
    #
    #         'smtp_enabled': False,
    #         'smtp_port': 25,
    #         'smtp_banner': 'Microsoft ESMTP MAIL service ready',
    #
    #         'vnc_enabled': False,
    #         'vnc_port': 5900,
    #
    #         'telnet_enabled': False,
    #         'telnet_port': 23,
    #         'telnet_max_attempts': 3,
    #
    #         'pop3_enabled': False,
    #         'pop3_port': 110,
    #         'pop3_max_attempts': 3,
    #
    #         'pop3s_enabled': False,
    #         'pop3s_port': 110,
    #         'pop3s_max_attempts': 3,
    #
    #         'ssh_enabled': False,
    #         'ssh_port': 22,
    #         'ssh_key': 'server.key'
    #     }
    #     self.login('test', 'test')
    #     resp = self.app.post('/ws/honeypot', data=post_data)
    #     self.assertTrue(200, resp.status_code)
    #     self.logout()
    #
    # def test_new_honeypot_config(self):
    #     """ Tests if a Honeypot config is being returned correctly """
    #
    #     resp = self.app.get('/ws/honeypot/config/' + self.honeypot_id)
    #     self.assertEquals(resp.data, 'test_honeypot_config')
    #
    # def test_new_client_config(self):
    #     """ Tests if a Client config is being returned correctly """
    #
    #     resp = self.app.get('/ws/client/config/' + self.client_id)
    #     self.assertEquals(resp.data, 'test_client_config')

    def test_data_sessions_all(self):
        """ Tests if all sessions are returned properly"""

        self.login('test', self.password)
        self.populate_sessions()
        resp = self.app.get('/data/sessions/all')
        table_data = json.loads(resp.data)
        self.assertEquals(len(table_data), 4)
        self.logout()

    def test_data_sessions_honeybees(self):
        """ Tests if bait_sessions are returned properly """

        self.login('test', self.password)
        self.populate_honeybees()
        resp = self.app.get('/data/sessions/bait_sessions')
        table_data = json.loads(resp.data)
        self.assertEquals(len(table_data), 3)
        self.logout()

    def test_data_sessions_attacks(self):
        """ Tests if attacks are returned properly """

        self.login('test', self.password)
        self.populate_sessions()
        resp = self.app.get('/data/sessions/attacks')
        table_data = json.loads(resp.data)
        self.assertEquals(len(table_data), 4)
        self.logout()

    def test_data_transcripts(self):
        """ Tests that if given a session ID we can extract the relevant transcripts"""
        db_session = database.get_session()
        self.login('test', self.password)
        session_id = str(uuid.uuid4())

        timestamp = datetime.utcnow()

        db_session.add(
            Transcript(timestamp=timestamp,
                       direction='outgoing',
                       data='whoami',
                       session_id=session_id))
        db_session.add(
            Transcript(timestamp=timestamp,
                       direction='outgoing',
                       data='root\r\n',
                       session_id=session_id))
        db_session.commit()
        resp = self.app.get('/data/session/{0}/transcript'.format(session_id))
        data = json.loads(resp.data)
        string_timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')
        expected_result = [{
            u'direction': u'outgoing',
            u'data': u'whoami',
            u'time': u'{0}'.format(string_timestamp)
        }, {
            u'direction': u'outgoing',
            u'data': u'root\r\n',
            u'time': u'{0}'.format(string_timestamp)
        }]
        self.assertDictEqual(sorted(data)[0], sorted(expected_result)[0])

    def test_login_logout(self):
        """ Tests basic login/logout """

        self.login('admin', self.password)
        self.logout()

    def test_get_baitusers(self):
        """ Tests GET on the '/ws/bait_users' route."""
        self.login('admin', self.password)
        self.populate_bait_users()
        resp = self.app.get('/ws/bait_users')
        table_data = json.loads(resp.data)
        self.assertEquals(len(table_data), 2)
        self.logout()

    def test_add_baituser(self):
        """ Tests POST on the '/ws/bait_users/add' route."""
        self.login('admin', self.password)
        data = [{
            'username': '******',
            'password': '******'
        }, {
            'username': '******',
            'password': '******'
        }, {
            'username': '******',
            'password': '******'
        }]

        self.app.post('/ws/bait_users/add',
                      data=json.dumps(data),
                      follow_redirects=True)

        resp = self.app.get('/ws/bait_users')
        bait_users = json.loads(resp.data)
        # 22 in bootstrapping, 3 just added
        self.assertEquals(len(bait_users), 22 + 3)
        self.logout()

    def populate_clients(self):
        """ Populates the database with 4 clients """

        db_session = database.get_session()
        self.clients = []
        for i in xrange(
                4):  # We add 4 here, but one is added in the setup method
            f = Client()
            self.clients.append(f.id)
            db_session.add(f)
        db_session.commit()

    def populate_honeypots(self):
        """ Populates the database with 4 honeypots """

        db_session = database.get_session()
        self.honeypots = []
        for i in xrange(
                4):  # We add 4 here, but one is added in the setup method
            h = Honeypot()
            self.honeypots.append(h.id)
            db_session.add(h)
        db_session.commit()

    def populate_bait_users(self):
        """ Populates the database with 2 bait users """
        db_session = database.get_session()
        db_session.query(BaitUser).delete()
        self.clients = []
        for c in [('userA', 'passA'), ('userB', 'passB')
                  ]:  # We add 4 here, but one is added in the setup method
            bait_user = BaitUser(username=c[0], password=c[1])
            db_session.add(bait_user)
        db_session.commit()

    def login(self, username, password):
        """ Logs into the web-app """

        data = {'username': username, 'password': password}
        return self.app.post('/login', data=data, follow_redirects=True)

    def populate_honeybees(self):
        """ Populates the database with 3 Honeybees """

        db_session = database.get_session()
        for i in xrange(3):
            h = BaitSession(id=str(uuid.uuid4()),
                            timestamp=datetime.utcnow(),
                            received=datetime.utcnow(),
                            protocol='ssh',
                            destination_ip='1.2.3.4',
                            destination_port=1234,
                            source_ip='4.3.2.1',
                            source_port=4321,
                            did_connect=True,
                            did_login=False,
                            did_complete=True)
            a = Authentication(id=str(uuid.uuid4()),
                               username='******',
                               password='******',
                               successful=False,
                               timestamp=datetime.utcnow())
            h.authentication.append(a)
            db_session.add(h)

        db_session.commit()

    def populate_sessions(self):
        """ Populates the database with 3 Sessions """
        db_session = database.get_session()
        for i in xrange(4):
            s = Session(id=str(uuid.uuid4()),
                        timestamp=datetime.utcnow(),
                        received=datetime.utcnow(),
                        protocol='telnet',
                        destination_ip='123.123.123.123',
                        destination_port=1234,
                        source_ip='12.12.12.12',
                        source_port=12345,
                        classification_id='asd')
            a = Authentication(id=str(uuid.uuid4()),
                               username='******',
                               password='******',
                               successful=False,
                               timestamp=datetime.utcnow())
            s.authentication.append(a)
            db_session.add(s)

        db_session.commit()

    def logout(self):
        return self.app.get('/logout', follow_redirects=True)
Exemplo n.º 10
0
    def __init__(self, work_dir, config, **kwargs):
        """
            Main class for the Web-Interface. It takes care of setting up
            the database, managing the users, etc.

        :param work_dir: The working directory (usually the current working directory).
        :param config_arg: Beeswarm configuration dictionary, None if not configuration was supplied.
        """
        customize = kwargs['customize']
        reset_password = kwargs['reset_password']
        if 'clear_db' in kwargs:
            clear_sessions = kwargs['clear_db']
        else:
            clear_sessions = True

        if 'server_hostname' in kwargs:
            server_hostname = kwargs['server_hostname']
        else:
            server_hostname = None

        max_sessions = kwargs['max_sessions']
        start_webui = kwargs['start_webui']

        self.work_dir = work_dir
        self.config_file = os.path.join(work_dir, 'beeswarmcfg.json')

        if config is None:
            self.prepare_environment(work_dir, customize, server_hostname=server_hostname)
            with open(os.path.join(work_dir, self.config_file), 'r') as config_file:
                self.config = json.load(config_file, object_hook=asciify)
        else:
            self.config = config
        # list of all self-running (actor) objects that receive or send
        # messages on one or more zmq queues
        self.actors = []
        self.greenlets = []

        proxy_greenlet = gevent.spawn(self.message_proxy, work_dir)
        self.greenlets.append(proxy_greenlet)
        config_actor = ConfigActor(self.config_file, work_dir)
        config_actor.start()
        self.actors.append(config_actor)
        self.greenlets.append(config_actor)

        # make path in sqlite connection string absolute
        connection_string = self.config['sql']['connection_string']
        if connection_string.startswith('sqlite:///'):
            _, relative_path = os.path.split(connection_string)
            connection_string = 'sqlite:///{0}'.format(os.path.join(self.work_dir, relative_path))
        database_setup.setup_db(connection_string)
        database_actor = DatabaseActor(max_sessions, clear_sessions)
        database_actor.start()
        self.actors.append(database_actor)
        self.greenlets.append(database_actor)

        for g in self.greenlets:
            g.link_exception(self.on_exception)

        gevent.sleep()

        self.started = False

        if start_webui:
            from beeswarm.server.webapp import app

            self.app = app.app
            self.app.config['CERT_PATH'] = self.config['ssl']['certpath']
            app.ensure_admin_password(reset_password)
        else:
            self.app = None