def config_subscriber(self): ctx = zmq.Context() subscriber_config = ctx.socket(zmq.SUB) subscriber_config.connect('ipc://configPublisher') subscriber_config.setsockopt(zmq.SUBSCRIBE, '') send_zmq_request('ipc://configCommands', Messages.PUBLISH_CONFIG) while True: poller = zmq.Poller() poller.register(subscriber_config, zmq.POLLIN) while True: socks = dict(poller.poll()) if subscriber_config in socks and socks[subscriber_config] == zmq.POLLIN: topic, msg = subscriber_config.recv().split(' ', 1) self.config = json.loads(msg) self.first_cfg_received.set() logger.debug('Config received')
def drone_key(key): global drone_keys if key not in drone_keys: logger.warn('Attempt to add new drone, but using wrong key from: {0}'.format(request.remote_addr)) abort(401) else: drone_id = str(uuid.uuid4()) server_zmq_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_port']) server_zmq_command_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_command_port']) result = send_zmq_request('ipc://configCommands', '{0} {1}'.format(Messages.GEN_ZMQ_KEYS, drone_id)) zmq_public = result['public_key'] zmq_private = result['private_key'] db_session = database_setup.get_session() # this could be done even simpler by only using the http api to provide the ZMQ keys # everything else could be done using zmq drone_config = { 'general': { 'mode': '', 'id': drone_id, 'fetch_ip': False }, 'beeswarm_server': { 'enabled': True, 'zmq_url': server_zmq_url, 'zmq_command_url' : server_zmq_command_url, 'zmq_server_public': config['network']['zmq_server_public_key'], 'zmq_own_public': zmq_public, 'zmq_own_private': zmq_private, } } config_json = json.dumps(drone_config, indent=4) drone = Drone(id=drone_id, configuration=config_json) db_session.add(drone) db_session.commit() logger.debug('Generated drone config for {0} on request from {1}'.format(drone_id, request.remote_addr)) return config_json
def create_client(): form = NewClientConfigForm() client_id = str(uuid.uuid4()) if form.validate_on_submit(): with open(app.config['CERT_PATH']) as cert: cert_str = cert.read() server_zmq_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_port']) result = send_zmq_request('ipc://configCommands', '{0} {1}'.format(Messages.GEN_ZMQ_KEYS, client_id)) zmq_public = result['public_key'] zmq_private = result['private_key'] client_config = { 'general': { 'mode': 'client', 'client_id': client_id, 'honeypot_id': None }, 'public_ip': { 'fetch_ip': True }, 'bait_sessions': { 'http': { 'enabled': form.http_enabled.data, 'server': form.http_server.data, 'port': form.http_port.data, 'timing': { 'active_range': form.http_active_range.data, 'sleep_interval': form.http_sleep_interval.data, 'activation_probability': form.http_activation_probability.data }, 'username': form.http_login.data, 'password': form.http_password.data }, 'ftp': { 'enabled': form.ftp_enabled.data, 'server': form.ftp_server.data, 'port': form.ftp_port.data, 'timing': { 'active_range': form.ftp_active_range.data, 'sleep_interval': form.ftp_sleep_interval.data, 'activation_probability': form.ftp_activation_probability.data }, 'username': form.ftp_login.data, 'password': form.ftp_password.data }, 'https': { 'enabled': form.https_enabled.data, 'server': form.https_server.data, 'port': form.https_port.data, 'timing': { 'active_range': form.https_active_range.data, 'sleep_interval': form.https_sleep_interval.data, 'activation_probability': form.https_activation_probability.data }, 'username': form.https_login.data, 'password': form.https_password.data }, 'pop3': { 'enabled': form.pop3_enabled.data, 'server': form.pop3_server.data, 'port': form.pop3_port.data, 'timing': { 'active_range': form.pop3_active_range.data, 'sleep_interval': form.pop3_sleep_interval.data, 'activation_probability': form.pop3_activation_probability.data }, 'username': form.pop3_login.data, 'password': form.pop3_password.data }, 'ssh': { 'enabled': form.ssh_enabled.data, 'server': form.ssh_server.data, 'port': form.ssh_port.data, 'timing': { 'active_range': form.ssh_active_range.data, 'sleep_interval': form.ssh_sleep_interval.data, 'activation_probability': form.ssh_activation_probability.data }, 'username': form.ssh_login.data, 'password': form.ssh_password.data }, 'pop3s': { 'enabled': form.pop3s_enabled.data, 'server': form.pop3s_server.data, 'port': form.pop3s_port.data, 'timing': { 'active_range': form.pop3s_active_range.data, 'sleep_interval': form.pop3s_sleep_interval.data, 'activation_probability': form.pop3s_activation_probability.data }, 'username': form.pop3s_login.data, 'password': form.pop3s_password.data }, 'smtp': { 'enabled': form.smtp_enabled.data, 'server': form.smtp_server.data, 'port': form.smtp_port.data, 'timing': { 'active_range': form.smtp_active_range.data, 'sleep_interval': form.smtp_sleep_interval.data, 'activation_probability': form.smtp_activation_probability.data }, 'username': form.smtp_login.data, 'local_hostname': form.smtp_local_hostname.data, 'password': form.smtp_password.data }, 'vnc': { 'enabled': form.vnc_enabled.data, 'server': form.vnc_server.data, 'port': form.vnc_port.data, 'timing': { 'active_range': form.vnc_active_range.data, 'sleep_interval': form.vnc_sleep_interval.data, 'activation_probability': form.vnc_activation_probability.data }, 'username': form.vnc_login.data, 'password': form.vnc_password.data }, 'telnet': { 'enabled': form.telnet_enabled.data, 'server': form.telnet_server.data, 'port': form.telnet_port.data, 'timing': { 'active_range': form.telnet_active_range.data, 'sleep_interval': form.telnet_sleep_interval.data, 'activation_probability': form.telnet_activation_probability.data }, 'username': form.telnet_login.data, 'password': form.telnet_password.data } }, 'beeswarm_server': { 'enabled': True, 'zmq_url' : server_zmq_url, 'zmq_server_public': config['network']['zmq_server_public_key'], 'zmq_own_public': zmq_public, 'zmq_own_private': zmq_private, }, } config_json = json.dumps(client_config, indent=4) db_session = database_setup.get_session() f = Client(id=client_id, configuration=config_json) db_session.add(f) db_session.commit() return render_template('finish-config.html', mode_name='Client', user=current_user) return render_template('create-client.html', form=form, mode_name='Client', user=current_user)
def configure_drone(id): db_session = database_setup.get_session() drone = db_session.query(Drone).filter(Drone.id == id).one() if drone.discriminator == 'honeypot': if drone.configuration is not None: config_obj = DictWrapper(json.loads(drone.configuration)) else: # virgin drone config_obj = None form = NewHoneypotConfigForm(obj=config_obj) if not form.validate_on_submit(): return render_template('create-honeypot.html', form=form, mode_name='Honeypot', user=current_user) else: server_zmq_command_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_command_port']) server_zmq_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_port']) # TODO: Check if key pair exists result = send_zmq_request('ipc://configCommands', '{0} {1}'.format(Messages.GEN_ZMQ_KEYS, str(drone.id))) zmq_public = result['public_key'] zmq_private = result['private_key'] drone_config = { 'general': { 'name': form.general__name.data, 'mode': 'honeypot', 'id': drone.id, 'ip': '192.168.1.1', 'fetch_ip': False }, 'beeswarm_server': { 'enabled': True, 'zmq_url' : server_zmq_url, 'zmq_server_public': config['network']['zmq_server_public_key'], 'zmq_own_public': zmq_public, 'zmq_own_private': zmq_private, 'zmq_command_url': server_zmq_command_url, }, 'log_syslog': { 'enabled': False, 'socket': '/dev/log' }, 'certificate_info': { 'common_name': form.certificate_info__common_name.data, 'country': form.certificate_info__country.data, 'state': form.certificate_info__state.data, 'locality': form.certificate_info__locality.data, 'organization': form.certificate_info__organization.data, 'organization_unit': form.certificate_info__organization_unit.data }, 'capabilities': { 'ftp': { 'enabled': form.capabilities__ftp__enabled.data, 'port': form.capabilities__ftp__port.data, 'max_attempts': form.capabilities__ftp__max_attempts.data, 'banner': form.capabilities__ftp__banner.data, 'syst_type': form.capabilities__ftp__syst_type.data }, 'telnet': { 'enabled': form.capabilities__telnet__enabled.data, 'port': form.capabilities__telnet__port.data, 'max_attempts': form.capabilities__telnet__max_attempts.data }, 'pop3': { 'enabled': form.capabilities__pop3__enabled.data, 'port': form.capabilities__pop3__port.data, 'max_attempts': form.capabilities__pop3__max_attempts.data, }, 'pop3s': { 'enabled': form.capabilities__pop3s__enabled.data, 'port': form.capabilities__pop3s__port.data, 'max_attempts': form.capabilities__pop3s__max_attempts.data, }, 'ssh': { 'enabled': form.capabilities__ssh__enabled.data, 'port': form.capabilities__ssh__port.data, }, 'http': { 'enabled': form.capabilities__http__enabled.data, 'port': form.capabilities__http__port.data, 'banner': form.capabilities__http__banner.data }, 'https': { 'enabled': form.capabilities__https__enabled.data, 'port': form.capabilities__https__port.data, 'banner': form.capabilities__https__banner.data }, 'smtp': { 'enabled': form.capabilities__smtp__enabled.data, 'port': form.capabilities__smtp__port.data, 'banner': form.capabilities__smtp__banner.data }, 'vnc': { 'enabled': form.capabilities__vnc__enabled.data, 'port': form.capabilities__vnc__port.data } }, 'users': {}, 'timecheck': { 'enabled': True, 'poll': 5, 'ntp_pool': 'pool.ntp.org' }, } config_json = json.dumps(drone_config, indent=4) drone.name = form.general__name.data drone.configuration = config_json db_session.add(drone) db_session.commit() # everything good, push config to drone if it is listening send_zmq_push('ipc://droneCommandReceiver', '{0} {1} {2}'.format(drone.id, Messages.CONFIG, config_json)) return render_template('finish-config.html', drone_id=drone.id, user=current_user) elif drone.discriminator == 'client': form = NewClientConfigForm() if not form.validate_on_submit(): return render_template('create-client.html', form=form, mode_name='Honeypot', user=current_user) else: server_zmq_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_port']) server_zmq_command_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_command_port']) # TODO: Check if key pair exists result = send_zmq_request('ipc://configCommands', '{0} {1}'.format(Messages.GEN_ZMQ_KEYS, str(drone.id))) zmq_public = result['public_key'] zmq_private = result['private_key'] drone_config = { 'general': { 'mode': 'client', 'id': drone.id, 'honeypot_id': None }, 'public_ip': { 'fetch_ip': True }, 'bait_sessions': { 'http': { 'enabled': form.http_enabled.data, 'server': form.http_server.data, 'port': form.http_port.data, 'timing': { 'active_range': form.http_active_range.data, 'sleep_interval': form.http_sleep_interval.data, 'activation_probability': form.http_activation_probability.data }, 'username': form.http_login.data, 'password': form.http_password.data }, 'ftp': { 'enabled': form.ftp_enabled.data, 'server': form.ftp_server.data, 'port': form.ftp_port.data, 'timing': { 'active_range': form.ftp_active_range.data, 'sleep_interval': form.ftp_sleep_interval.data, 'activation_probability': form.ftp_activation_probability.data }, 'username': form.ftp_login.data, 'password': form.ftp_password.data }, 'https': { 'enabled': form.https_enabled.data, 'server': form.https_server.data, 'port': form.https_port.data, 'timing': { 'active_range': form.https_active_range.data, 'sleep_interval': form.https_sleep_interval.data, 'activation_probability': form.https_activation_probability.data }, 'username': form.https_login.data, 'password': form.https_password.data }, 'pop3': { 'enabled': form.pop3_enabled.data, 'server': form.pop3_server.data, 'port': form.pop3_port.data, 'timing': { 'active_range': form.pop3_active_range.data, 'sleep_interval': form.pop3_sleep_interval.data, 'activation_probability': form.pop3_activation_probability.data }, 'username': form.pop3_login.data, 'password': form.pop3_password.data }, 'ssh': { 'enabled': form.ssh_enabled.data, 'server': form.ssh_server.data, 'port': form.ssh_port.data, 'timing': { 'active_range': form.ssh_active_range.data, 'sleep_interval': form.ssh_sleep_interval.data, 'activation_probability': form.ssh_activation_probability.data }, 'username': form.ssh_login.data, 'password': form.ssh_password.data }, 'pop3s': { 'enabled': form.pop3s_enabled.data, 'server': form.pop3s_server.data, 'port': form.pop3s_port.data, 'timing': { 'active_range': form.pop3s_active_range.data, 'sleep_interval': form.pop3s_sleep_interval.data, 'activation_probability': form.pop3s_activation_probability.data }, 'username': form.pop3s_login.data, 'password': form.pop3s_password.data }, 'smtp': { 'enabled': form.smtp_enabled.data, 'server': form.smtp_server.data, 'port': form.smtp_port.data, 'timing': { 'active_range': form.smtp_active_range.data, 'sleep_interval': form.smtp_sleep_interval.data, 'activation_probability': form.smtp_activation_probability.data }, 'username': form.smtp_login.data, 'local_hostname': form.smtp_local_hostname.data, 'password': form.smtp_password.data }, 'vnc': { 'enabled': form.vnc_enabled.data, 'server': form.vnc_server.data, 'port': form.vnc_port.data, 'timing': { 'active_range': form.vnc_active_range.data, 'sleep_interval': form.vnc_sleep_interval.data, 'activation_probability': form.vnc_activation_probability.data }, 'username': form.vnc_login.data, 'password': form.vnc_password.data }, 'telnet': { 'enabled': form.telnet_enabled.data, 'server': form.telnet_server.data, 'port': form.telnet_port.data, 'timing': { 'active_range': form.telnet_active_range.data, 'sleep_interval': form.telnet_sleep_interval.data, 'activation_probability': form.telnet_activation_probability.data }, 'username': form.telnet_login.data, 'password': form.telnet_password.data } }, 'beeswarm_server': { 'enabled': True, 'zmq_url' : server_zmq_url, 'zmq_server_public': config['network']['zmq_server_public_key'], 'zmq_own_public': zmq_public, 'zmq_own_private': zmq_private, 'zmq_command_url': server_zmq_command_url, }, } config_json = json.dumps(drone_config, indent=4) drone.configuration = config_json db_session.add(drone) db_session.commit() # everything good, push config to drone if it is listening send_zmq_push('ipc://droneCommandReceiver', '{0} {1} {2}'.format(drone.id, Messages.CONFIG, config_json)) return render_template('finish-config.html', drone_id=drone.id, user=current_user) elif drone.discriminator is None: return render_template('drone_mode.html', drone_id=drone.id, user=current_user) else: assert(drone is None) abort(404, 'Drone with that id could not be found.')
def message_proxy(self, work_dir): """ drone_data_inboud is for data comming from drones drone_data_outbound is for commands to the drones, topic must either be a drone ID or all for sending a broadcast message to all drones """ public_keys_dir = os.path.join(work_dir, 'certificates', 'public_keys') secret_keys_dir = os.path.join(work_dir, 'certificates', 'private_keys') # start and configure auth worker auth = IOLoopAuthenticator() auth.start() auth.allow('127.0.0.1') auth.configure_curve(domain='*', location=public_keys_dir) # external interfaces for communicating with drones server_secret_file = os.path.join(secret_keys_dir, 'beeswarm_server.pri') server_public, server_secret = load_certificate(server_secret_file) drone_data_inbound = beeswarm.shared.zmq_context.socket(zmq.PULL) drone_data_inbound.curve_secretkey = server_secret drone_data_inbound.curve_publickey = server_public drone_data_inbound.curve_server = True drone_data_inbound.bind('tcp://*:{0}'.format( self.config['network']['zmq_port'])) drone_data_outbound = beeswarm.shared.zmq_context.socket(zmq.PUB) drone_data_outbound.curve_secretkey = server_secret drone_data_outbound.curve_publickey = server_public drone_data_outbound.curve_server = True drone_data_outbound.bind('tcp://*:{0}'.format( self.config['network']['zmq_command_port'])) # internal interfaces # all inbound session data from drones will be replayed in this socket sessionPublisher = beeswarm.shared.zmq_context.socket(zmq.PUB) sessionPublisher.bind('inproc://sessionPublisher') # all commands received on this will be published on the external interface drone_command_receiver = beeswarm.shared.zmq_context.socket(zmq.PULL) drone_command_receiver.bind('inproc://droneCommandReceiver') poller = zmq.Poller() poller.register(drone_data_inbound, zmq.POLLIN) poller.register(drone_command_receiver, zmq.POLLIN) while True: # .recv() gives no context switch - why not? using poller with timeout instead socks = dict(poller.poll(100)) gevent.sleep() if drone_command_receiver in socks and socks[ drone_command_receiver] == zmq.POLLIN: data = drone_command_receiver.recv() drone_id, _ = data.split(' ', 1) logger.debug("Sending drone command to: {0}".format(drone_id)) # pub socket takes care of filtering drone_data_outbound.send(data) elif drone_data_inbound in socks and socks[ drone_data_inbound] == zmq.POLLIN: split_data = drone_data_inbound.recv().split(' ', 2) if len(split_data) == 3: topic, drone_id, data = split_data else: data = None topic, drone_id, = split_data logger.debug("Received {0} message from {1}.".format( topic, drone_id)) db_session = database_setup.get_session() drone = db_session.query(Drone).filter( Drone.id == drone_id).one() drone.last_activity = datetime.now() db_session.add(drone) db_session.commit() if topic == Messages.SESSION_HONEYPOT or topic == Messages.SESSION_CLIENT: sessionPublisher.send('{0} {1}'.format(topic, data)) elif topic == Messages.KEY or topic == Messages.CERT: # for now we just store the fingerprint # in the future it might be relevant to store the entire public key and private key # for forensic purposes if topic == Messages.CERT: cert = data.split(' ', 1)[1] digest = generate_cert_digest(cert) logging.debug( 'Storing public key digest: {0} for drone {1}.'. format(digest, drone_id)) db_session = database_setup.get_session() drone = db_session.query(Drone).filter( Drone.id == drone_id).one() drone.cert_digest = digest db_session.add(drone) db_session.commit() elif topic == Messages.PING: pass elif topic == Messages.IP: ip_address = data logging.debug('Drone {0} reported ip: {1}'.format( drone_id, ip_address)) db_session = database_setup.get_session() drone = db_session.query(Drone).filter( Drone.id == drone_id).one() if drone.ip_address != ip_address: drone.ip_address = ip_address db_session.add(drone) db_session.commit() send_zmq_request( 'inproc://configCommands', '{0} {1}'.format(Messages.DRONE_CONFIG_CHANGED, drone_id)) # drone want it's config transmitted elif topic == Messages.DRONE_CONFIG: config_dict = send_zmq_request( 'inproc://configCommands', '{0} {1}'.format(Messages.DRONE_CONFIG, drone_id)) drone_data_outbound.send('{0} {1} {2}'.format( drone_id, Messages.CONFIG, json.dumps(config_dict))) else: logger.warn( 'Message with unknown topic received: {0}'.format( topic))
def message_proxy(self, work_dir): """ drone_data_inboud is for data comming from drones drone_data_outbound is for commands to the drones, topic must either be a drone ID or all for sending a broadcast message to all drones """ public_keys_dir = os.path.join(work_dir, 'certificates', 'public_keys') secret_keys_dir = os.path.join(work_dir, 'certificates', 'private_keys') # start and configure auth worker auth = IOLoopAuthenticator() auth.start() auth.allow('127.0.0.1') auth.configure_curve(domain='*', location=public_keys_dir) # external interfaces for communicating with drones server_secret_file = os.path.join(secret_keys_dir, 'beeswarm_server.pri') server_public, server_secret = load_certificate(server_secret_file) drone_data_inbound = beeswarm.shared.zmq_context.socket(zmq.PULL) drone_data_inbound.curve_secretkey = server_secret drone_data_inbound.curve_publickey = server_public drone_data_inbound.curve_server = True drone_data_inbound.bind('tcp://*:{0}'.format(self.config['network']['zmq_port'])) drone_data_outbound = beeswarm.shared.zmq_context.socket(zmq.PUB) drone_data_outbound.curve_secretkey = server_secret drone_data_outbound.curve_publickey = server_public drone_data_outbound.curve_server = True drone_data_outbound.bind('tcp://*:{0}'.format(self.config['network']['zmq_command_port'])) # internal interfaces # all inbound session data from drones will be replayed in this socket sessionPublisher = beeswarm.shared.zmq_context.socket(zmq.PUB) sessionPublisher.bind('inproc://sessionPublisher') # all commands received on this will be published on the external interface drone_command_receiver = beeswarm.shared.zmq_context.socket(zmq.PULL) drone_command_receiver.bind('inproc://droneCommandReceiver') poller = zmq.Poller() poller.register(drone_data_inbound, zmq.POLLIN) poller.register(drone_command_receiver, zmq.POLLIN) while True: # .recv() gives no context switch - why not? using poller with timeout instead socks = dict(poller.poll(100)) gevent.sleep() if drone_command_receiver in socks and socks[drone_command_receiver] == zmq.POLLIN: data = drone_command_receiver.recv() drone_id, _ = data.split(' ', 1) logger.debug("Sending drone command to: {0}".format(drone_id)) # pub socket takes care of filtering drone_data_outbound.send(data) elif drone_data_inbound in socks and socks[drone_data_inbound] == zmq.POLLIN: split_data = drone_data_inbound.recv().split(' ', 2) if len(split_data) == 3: topic, drone_id, data = split_data else: data = None topic, drone_id, = split_data logger.debug("Received {0} message from {1}.".format(topic, drone_id)) db_session = database_setup.get_session() drone = db_session.query(Drone).filter(Drone.id == drone_id).one() drone.last_activity = datetime.now() db_session.add(drone) db_session.commit() if topic == Messages.SESSION_HONEYPOT or topic == Messages.SESSION_CLIENT: sessionPublisher.send('{0} {1}'.format(topic, data)) elif topic == Messages.KEY or topic == Messages.CERT: # for now we just store the fingerprint # in the future it might be relevant to store the entire public key and private key # for forensic purposes if topic == Messages.CERT: cert = data.split(' ', 1)[1] digest = generate_cert_digest(cert) logging.debug('Storing public key digest: {0} for drone {1}.'.format(digest, drone_id)) db_session = database_setup.get_session() drone = db_session.query(Drone).filter(Drone.id == drone_id).one() drone.cert_digest = digest db_session.add(drone) db_session.commit() elif topic == Messages.PING: pass elif topic == Messages.IP: ip_address = data logging.debug('Drone {0} reported ip: {1}'.format(drone_id, ip_address)) db_session = database_setup.get_session() drone = db_session.query(Drone).filter(Drone.id == drone_id).one() if drone.ip_address != ip_address: drone.ip_address = ip_address db_session.add(drone) db_session.commit() send_zmq_request('inproc://configCommands', '{0} {1}'.format(Messages.DRONE_CONFIG_CHANGED, drone_id)) # drone want it's config transmitted elif topic == Messages.DRONE_CONFIG: config_dict = send_zmq_request('inproc://configCommands', '{0} {1}'.format(Messages.DRONE_CONFIG, drone_id)) drone_data_outbound.send('{0} {1} {2}'.format(drone_id, Messages.CONFIG, json.dumps(config_dict))) else: logger.warn('Message with unknown topic received: {0}'.format(topic))