def send_command(self, topic, command): """ Sends an Open Command and Control (OpenC2) message to the specified DXL service and returns the response. The `Lycan library <https://github.com/oasis-open/openc2-lycan-python>`_ contains the OpenC2 classes (Command, Response, etc.). :param topic: The DXL service topic to send the OpenC2 command to :param command: The `openc2.v10.Command` to send to the DXL service :return: The `openc2.v10.Response` received from the DXL service """ # Create the DXL request message request = Request(topic) # Serialize and encode the OpenC2 command request.payload = command.serialize().encode() # Perform a synchronous DXL request response = self._dxl_sync_request(request) if response.message_type == Message.MESSAGE_TYPE_ERROR: raise Exception(response.error_message) # Return the response response_dict = MessageUtils.json_payload_to_dict(response) return openc2.v10.Response(**response_dict)
def get_endpoint_by_mac(self, mac_address): """ Get information for an endpoint by its MAC address. :param str mac_address: MAC address of the endpoint. :return: The endpoint information, which should look similar to this: .. code-block:: json { "ancEndpoint": [ { "macAddress": "00:11:22:33:44:55", "policyName": "quarantine_policy" } ], "ancStatus": "success" } :rtype: dict :raises Exception: If no policy has been associated with the endpoint. """ request = Request(self._ANC_GET_ENDPOINT_BY_MAC) MessageUtils.dict_to_json_payload(request, {_PARAM_MAC: mac_address}) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def retrieve_policy_by_name(self, policy_name): """ Retrieve information for an ANC policy by name. :param str policy_name: Name of the policy. :return: The policy information, which should look similar to this: .. code-block:: json { "ancStatus": "success", "ancpolicy": [ { "action": [ "Quarantine" ], "name": "quarantine_policy" } ] } :rtype: dict :raises Exception: If the policy name has not been defined. """ request = Request(self._ANC_RETRIEVE_POLICY_BY_NAME) MessageUtils.dict_to_json_payload(request, {_PARAM_POLICY_NAME: policy_name}) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def retrieve_all_policies(self): """ Retrieve information for all ANC policies. :return: The policy information, which should look similar to this: .. code-block:: json { "ancStatus": "success", "ancpolicy": [ { "action": [ "ShutDown" ], "name": "shutdown_policy" }, { "action": [ "Quarantine" ], "name": "quarantine_policy" } ] } :rtype: dict """ request = Request(self._ANC_RETRIEVE_ALL_POLICIES) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def apply_endpoint_policy_by_mac(self, mac_address, policy_name): """ Apply a policy to an endpoint by its MAC address. :param str mac_address: MAC address of the endpoint. :param str policy_name: Name of the policy to apply. :return: Results of the attempt to apply the endpoint policy. On a successful application, the results should look similar to this: .. code-block:: json { "ancStatus": "success" } :rtype: dict :raises Exception: If the supplied policy name has already been applied to the endpoint or the policy name has not been defined. """ request = Request(self._ANC_APPLY_ENDPOINT_POLICY_BY_MAC) MessageUtils.dict_to_json_payload(request, { _PARAM_MAC: mac_address, _PARAM_POLICY_NAME: policy_name }) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def ticket(self, msg): print "Filing ticket..." req = Request(SERVICE_TOPIC) req.payload = msg res = client.sync_request(req) if res.message_type != Message.MESSAGE_TYPE_ERROR: print "Create Ticket Number %s" % res
def get_broker_registry(self, client_id=None): """ Queries the broker for the broker registry and replaces the currently stored one with the new results. Notifies all connected web sockets that new broker information is available. """ req = Request(TopologyModule.BROKER_REGISTRY_QUERY_TOPIC) if client_id: req.payload = "{ \"brokerGuid\":\"" + client_id + "\"}" else: req.payload = "{}" # Send the request dxl_response = self.app.dxl_service_client.sync_request(req, 5) if dxl_response.message_type == Message.MESSAGE_TYPE_ERROR: logger.error( "Error response returned from the broker registry: %s", dxl_response.error_message) return {} dxl_response_dict = MessageUtils.json_payload_to_dict(dxl_response) logger.info("Broker registry response: %s", dxl_response_dict) brokers = {} for broker_guid in dxl_response_dict["brokers"]: brokers[broker_guid] = dxl_response_dict["brokers"][broker_guid] self.current_connected_broker = dxl_response.source_broker_id return brokers
def send_mitigation_action_by_ip(self, ip_address, action): """ Send an EPS mitigation action for an endpoint IP address. :param str ip_address: IP address of the endpoint for which the action should be performed. :param dxlciscopxgridclient.constants.EpsAction action: The action to perform. :return: Results of the attempt to send the action. The results for a successful send should look similar to this: .. code-block:: json { "gid": "150", "macInterface": "00:11:22:33:44:55", "mitigationStatus": "complete" } :rtype: dict :raises Exception: If no session has been established for an endpoint which corresponds to the IP address. """ request = Request(self._EPS_SEND_MITIGATION_ACTION_BY_IP) MessageUtils.dict_to_json_payload(request, { _PARAM_IP: ip_address, _PARAM_ACTION: action, "foo": "bar" }) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def send_unregister_service_event(self): """ Send the unregister event for the service. :return: None. """ if not self.client: raise DxlException("Client not defined") with self.lock: # Send the unregister event only if the register event was sent before and TTL has not yet expired. current_time = int(time.time()) last_register_time = self.get_register_time() if last_register_time > 0 and ( current_time - last_register_time) <= (self.ttl * 60): request = Request(destination_topic=_ServiceManager. DXL_SERVICE_UNREGISTER_REQUEST_CHANNEL) request.payload = self.json_unregister_service() response = self.client.sync_request(request, timeout=60) if response.message_type == Message.MESSAGE_TYPE_ERROR: raise DxlException("Unregister service request timed out") else: if last_register_time > 0: # pylint: disable=logging-not-lazy logger.info( "TTL expired, unregister service event omitted for " + self.service_type + " (" + self.instance_id + ")") info = self.service if info: info._notify_unregistration_succeeded()
def send_mitigation_action_by_mac(self, mac_address, action): """ Send an EPS mitigation action for an endpoint MAC address. :param str mac_address: MAC address of the endpoint for which the action should be performed. :param dxlciscopxgridclient.constants.EpsAction action: The action to perform. :return: Results of the attempt to send the action. The results for a successful send should look similar to this: .. code-block:: json { "gid": "150", "macInterface": "00:11:22:33:44:55", "mitigationStatus": "complete" } :rtype: dict """ request = Request(self._EPS_SEND_MITIGATION_ACTION_BY_MAC) MessageUtils.dict_to_json_payload(request, { _PARAM_MAC: mac_address, _PARAM_ACTION: action }) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def send_unregister_service_event(self): """ Send the unregister event for the service. :return: None. """ if not self.client: raise DxlException("Client not defined") # Send the unregister event only if the register event was sent before and TTL has not yet expired. current_time = int(time.time()) last_register_time = self.get_register_time() if last_register_time > 0 and (current_time - last_register_time) <= (self.ttl * 60): request = Request(destination_topic=_ServiceManager. DXL_SERVICE_UNREGISTER_REQUEST_CHANNEL) request.payload = bytes(self.json_unregister_service()) response = self.client.sync_request(request, timeout=60) if response.message_type != Message.MESSAGE_TYPE_ERROR: info = self.get_service() if info: info._notify_unregistration_succeeded() else: raise DxlException("Unregister service request timed out") else: raise DxlException("Unregister service request timed out")
def get_tie_file_reputation(client, md5_hex, sha1_hex): """ Returns a dictionary containing the results of a TIE file reputation request :param client: The DXL client :param md5_hex: The MD5 Hex string for the file :param sha1_hex: The SHA-1 Hex string for the file :return: A dictionary containing the results of a TIE file reputation request """ # Create the request message req = Request(FILE_REP_TOPIC) # Create a dictionary for the payload payload_dict = { "hashes": [{ "type": "md5", "value": base64_from_hex(md5_hex) }, { "type": "sha1", "value": base64_from_hex(sha1_hex) }] } # Set the payload req.payload = json.dumps(payload_dict).encode() # Send the request and wait for a response (synchronous) res = client.sync_request(req) # Return a dictionary corresponding to the response payload if res.message_type != Message.MESSAGE_TYPE_ERROR: return json.loads(res.payload.decode(encoding="UTF-8")) else: raise Exception("Error: " + res.error_message + " (" + str(res.error_code) + ")")
def execute_mar_search_api(client, payload_dict): """ Executes a query against the MAR search api :param client: The DXL client :param payload_dict: The payload :return: A dictionary containing the results of the query """ # Create the request message req = Request(CREATE_SEARCH_TOPIC) # Set the payload req.payload = json.dumps(payload_dict).encode() # Display the request that is going to be sent print "Request:\n" + json.dumps(payload_dict, sort_keys=True, indent=4, separators=(',', ': ')) # Send the request and wait for a response (synchronous) res = client.sync_request(req) # Return a dictionary corresponding to the response payload if res.message_type != Message.MESSAGE_TYPE_ERROR: resp_dict = json.loads(res.payload.decode()) # Display the response print "Response:\n" + json.dumps(resp_dict, sort_keys=True, indent=4, separators=(',', ': ')) if "code" in resp_dict: code = resp_dict['code'] if code < 200 or code >= 300: raise Exception("Error: Received failure response code: " + str(code)) else: raise Exception("Error: unable to find response code") return resp_dict else: raise Exception("Error: " + res.error_message + " (" + str(res.error_code) + ")")
def _invoke_service(self, request_method, request_dict): """ Invokes a request method on the MISP DXL service. :param str request_method: The request method to append to the topic for the request. :param dict request_dict: Dictionary containing request information. :return: Results of the service invocation. :rtype: dict """ if self._misp_service_unique_id: request_service_id = "/{}".format(self._misp_service_unique_id) else: request_service_id = "" # Create the DXL request message. request = Request("{}{}/{}".format(self._SERVICE_TYPE, request_service_id, request_method)) # Set the payload on the request message (Python dictionary to JSON # payload). MessageUtils.dict_to_json_payload(request, request_dict) # Perform a synchronous DXL request. response = self._dxl_sync_request(request) # Convert the JSON payload in the DXL response message to a Python # dictionary and return it. return MessageUtils.json_payload_to_dict(response)
def query_repository(query): # Create query message and send it to the repository req = Request(SERVICE_REPOSITORY_QUERY_TOPIC) qm = QueryMessage(query) req.payload = (qm.to_json()).encode() res = client.sync_request(req) # Parse and return the query results qrm = QueryResultMessage() qrm.parse(res.payload.decode()) return qrm
def main(argv): TOPIC_DESTINATION = '' TYPE_PAYLOAD = '' PAYLOAD = '' help = 'python ' + sys.argv[0] + ' -t <topic destination> -p <payload>' try: opts, args = getopt.getopt(argv, "ht:p:", ["topic=", "payload="]) except getopt.GetoptError: print help sys.exit(1) for opt, arg in opts: if opt == '-h': print help sys.exit(1) elif opt in ("-t", "--topic"): TOPIC_DESTINATION = arg elif opt in ("-p", "--payload"): PAYLOAD = arg if (TOPIC_DESTINATION != '' and PAYLOAD != ''): DXL_MESSAGE['SRC_HOST'] = IP DXL_MESSAGE['PAYLOAD'] = PAYLOAD # Create the client with DxlClient(config) as client: # Connect to the fabric client.connect() # # Invoke the service (send a request) # # Create the request req = Request(TOPIC_DESTINATION) # Populate the request payload req.payload = str(json.dumps(DXL_MESSAGE)).encode() # Send the request and wait for a response (synchronous) res = client.sync_request(req) # Extract information from the response (if an error did not occur) if res.message_type != Message.MESSAGE_TYPE_ERROR: print "result is coming:" print res.payload else: logger.error("Error: " + res.error_message + " (" + str(res.error_code) + ")") else: print help sys.exit(1)
def request_assessment(iam): # Create a report request and send it to the manager req = Request(SERVICE_INITIATE_ASSESSMENT_TOPIC) req.payload = (iam.to_json()).encode() logger.info("Requesting report for %s:", iam.to_s()) res = client.sync_request(req) # Extract and store the transaction id from the acknowledgement message if res.message_type != Message.MESSAGE_TYPE_ERROR: ram = RequestAcknowledgementMessage() ram.parse(res.payload.decode()) logger.info("Application received response: %s", ram.to_s()) transactions.append(ram.transaction_id)
def _invoke_service(self, request_method, request_dict): """ Invokes a request method on the Elasticsearch DXL service. :param str request_method: The request method to append to the topic for the request. :param dict request_dict: Dictionary containing request information. :return: Results of the service invocation. :rtype: dict """ if self._elasticsearch_service_unique_id: request_service_id = "/{}".format( self._elasticsearch_service_unique_id) else: request_service_id = "" # Create the DXL request message. request = Request("{}{}/{}".format( self._SERVICE_TYPE, request_service_id, request_method)) # Set the payload on the request message (Python dictionary to JSON # payload). MessageUtils.dict_to_json_payload(request, request_dict) # Perform a synchronous DXL request. response = self._dxl_client.sync_request(request, timeout=self.response_timeout) if response.message_type == Message.MESSAGE_TYPE_ERROR: try: self._raise_exception_for_error_response( MessageUtils.json_payload_to_dict(response)) except ValueError: # If an appropriate exception cannot be constructed from the # error response data, raise a more generic Exception as a # fallback. raise Exception("Error: {} ({})".format( response.error_message, str(response.error_code))) # Convert the JSON payload in the DXL response message to a Python # dictionary and return it. return MessageUtils.json_payload_to_dict(response)
def lookup_host(self, host): """ Looks up Geolocation information for the specified host/IP :param host: The host/IP to lookup :return: A dictionary (``dict``) containing the details of the Geolocation lookup """ # Create the DXL request message request = Request(self.HOST_LOOKUP_TOPIC) # Set the payload on the request message (Python dictionary to JSON payload) MessageUtils.dict_to_json_payload(request, {self._PARAM_HOST: host}) # Perform a synchronous DXL request response = self._dxl_sync_request(request) # Convert the JSON payload in the DXL response message to a Python dictionary # and return it. return MessageUtils.json_payload_to_dict(response)
def clear_endpoint_policy_by_mac(self, mac_address): """ Clear the policy for an endpoint by its MAC address. :param str mac_address: MAC address of the endpoint. :return: Results of the attempt to clear the endpoint policy. On a successful clear attempt, the results should look similar to this: .. code-block:: json { "ancStatus": "success" } :rtype: dict :raises Exception: If no policy has been associated with the endpoint. """ request = Request(self._ANC_CLEAR_ENDPOINT_POLICY_BY_MAC) MessageUtils.dict_to_json_payload(request, {_PARAM_MAC: mac_address}) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def _refresh_all_services(self): """ Queries the broker for the service list and replaces the currently stored one with the new results. Notifies all connected web sockets that new services are available. """ req = Request(MonitorModule.SERVICE_REGISTRY_QUERY_TOPIC) req.payload = "{}" # Send the request dxl_response = self._dxl_service_client.sync_request(req, 5) dxl_response_dict = MessageUtils.json_payload_to_dict(dxl_response) logger.info("Service registry response: %s", dxl_response_dict) with self._service_dict_lock: self._services = {} for service_guid in dxl_response_dict["services"]: self._services[service_guid] = dxl_response_dict["services"][ service_guid] self.notify_web_sockets()
def send_register_service_request(self): """ Send the registration request for the service. :return: None. """ if not self.client: raise DxlException("Client not defined") req = Request(destination_topic=_ServiceManager.DXL_SERVICE_REGISTER_REQUEST_CHANNEL) req.payload = bytes(self.json_register_service()) response = self.client.sync_request(req, timeout=10) if response.message_type != Message.MESSAGE_TYPE_ERROR: self.update_register_time() info = self.get_service() if info: info._notify_registration_succeeded() else: # TODO: Notify the client with an exception if an error occurred, so that it doesn't wait for timeout logger.error("Error registering service.")
def task_pce(crm, pce_id): logger.info("Tasking PCE: %s", crm.to_s()) # Get content and perform any content conversions # before sending to PCE. If cancellation message # just send that to the PCE. if crm.ids == "": content = "cancel_" + str(crm.transaction_id) else: content = get_content(crm.ids) content = convert_content(content) # Send the collection request to the identified PCE req = Request(SERVICE_PCE_REQUEST_TOPIC + "/" + pce_id) req.payload = content res = client.sync_request(req) if res.message_type != Message.MESSAGE_TYPE_ERROR: # Only send results if not a cancel message if crm.ids != "": rrsm = ReportResultsMessage() rrsm.assessment_results = res.payload.decode() rrsm.transaction_id = crm.transaction_id rrsm.requestor_id = crm.requestor_id rrsm.target_id = lookup_target_id(pce_id) rrsm.collector_id = COLLECTOR_ID rrsm.pcx_id = PCX_ID rrsm.pce_id = pce_id rrsm.timestamp = str(datetime.datetime.now()) # Apply collection parameters cp_rrsm = apply_collection_parameters( rrsm, crm.collection_parameters) store_data(cp_rrsm) # Apply result format and filters and send to the # appropriate application rff_rrsm = apply_format_and_filters(rrsm, crm.result_format_filters) send_collection_results_event(rff_rrsm) return
def _invoke_service(self, req_dict, topic): """ Invokes the DXL Nmap service. :param req_dict: Dictionary containing request information :param topic: The Nmap DXL topic to invoke :return: A dictionary containing the response """ # Create the DXL request message request = Request(topic) # Set the payload on the request message (Python dictionary to JSON # payload) MessageUtils.dict_to_json_payload(request, req_dict) # Perform a synchronous DXL request response = self._dxl_sync_request(request) # Convert the JSON payload in the DXL response message to a Python # dictionary and return it. return MessageUtils.json_payload_to_dict(response)
def _invoke_service(self, topic, payload, other_fields=None): """ Invokes a request method on the File Transfer DXL service. :param str topic: The topic to send the request to. :param payload: The payload to include in the request :param dict other_fields: Other fields to include in the request :return: Results of the service invocation. :rtype: dict """ # Create the DXL request message. request = Request(topic) # Set the full request parameters. request.payload = payload request.other_fields = other_fields # Perform a synchronous DXL request. response = self._dxl_sync_request(request) # Convert the JSON payload in the DXL response message to a Python # dictionary and return it. return MessageUtils.json_payload_to_dict(response)
def sendMessage(self, topic="/dsa/dxl/test/event2", message="Default message"): if not self.isConnected(): raise DxlJythonException(1200, "Not connected to a OpenDXL broker") try: request = Request(topic) # Encode string payload as UTF-8 request.payload = message.encode() # Send Synchronous Request with default timeout and wait for Response logger.info("Requesting '" + message + "' from '" + topic + "'") response = self.client.sync_request(request) dxl_message = JavaDxlMessage() dxl_message.setMessageVersion(response.version) dxl_message.setMessageId(response.message_id) dxl_message.setClientId(response.source_client_id) dxl_message.setBrokerId(response.source_broker_id) dxl_message.setMessageType(response.message_type) dxl_message.setBrokerIdList(response.broker_ids) dxl_message.setClientIdList(response.client_ids) dxl_message.setRequestMessageId(response.request_message_id) # Check that the Response is not an Error Response, then extract if response.message_type != Message.MESSAGE_TYPE_ERROR: dxl_message.setServiceId(response.service_id) dxl_message.setPayload(response.payload.decode()) else: dxl_message.setErrorCode(response.error_code) dxl_message.setErrorMessage(response.error_message) return dxl_message except Exception as e: logger.info("Exception: " + e.message) raise DxlJythonException(1010, "Unable to communicate with a DXL broker")
# Create DXL configuration from file config = DxlClientConfig.create_dxl_config_from_file(CONFIG_FILE) # The ePO unique identifier EPO_UNIQUE_ID = "<specify-ePO-unique-identifier>" # The search text SEARCH_TEXT = "<specify-find-search-text>" # Create the client with DxlClient(config) as client: # Connect to the fabric client.connect() req = Request("/mcafee/service/epo/remote/{0}".format(EPO_UNIQUE_ID)) req.payload = \ json.dumps({ "command": "system.find", "output": "json", "params": {"searchText": SEARCH_TEXT} }).encode(encoding="utf-8") # Send the request res = client.sync_request(req, timeout=30) if res.message_type != Message.MESSAGE_TYPE_ERROR: response_dict = json.loads(res.payload, encoding='utf-8') print json.dumps(response_dict, sort_keys=True, indent=4, separators=(',', ': ')) else: print "Error: {0} ({1}) ".format(res.error_message, str(res.error_code))
logger = logging.getLogger(__name__) # Create DXL configuration from file config = DxlClientConfig.create_dxl_config_from_file(CONFIG_FILE) # Create the client with DxlClient(config) as client: # Connect to the fabric client.connect() logger.info("Connected to DXL fabric.") # Invoke 'host scan' method request_topic = "/opendxl-urlvoid/service/urlvapi/host/scan" req = Request(request_topic) MessageUtils.dict_to_json_payload(req, {"host": "027.ru"}) res = client.sync_request(req, timeout=60) if res.message_type != Message.MESSAGE_TYPE_ERROR: payload = MessageUtils.decode_payload(res) xml = xml.dom.minidom.parseString(payload) print("Response for URLVoid host scan:") print( xml.toprettyxml(indent=' ', newl='', encoding="UTF-8").decode("UTF-8")) else: print("Error invoking service with topic '{0}': {1} ({2})".format( request_topic, res.error_message, res.error_code))
logger = logging.getLogger(__name__) # Create DXL configuration from file config = DxlClientConfig.create_dxl_config_from_file(CONFIG_FILE) # Create the client with DxlClient(config) as client: # Connect to the fabric client.connect() logger.info("Connected to DXL fabric.") # Create the new event request request_topic = "/opendxl-misp/service/misp-api/new_event" new_event_request = Request(request_topic) # Set the payload for the new event request MessageUtils.dict_to_json_payload( new_event_request, { "distribution": 3, "info": "OpenDXL MISP new event example", "analysis": 1, "threat_level_id": 3 }) # Send the new event request new_event_response = client.sync_request(new_event_request, timeout=30) if new_event_response.message_type != Message.MESSAGE_TYPE_ERROR: # Display results for the new event request
client.connect() # Prompt user for input to publish DXL Requests while True: print(" Press 1 to send a Synchronous Request") print(" Press 2 to send an Asynchronous Request") print(" Press 9 to quit") option = prompt(" Enter value: ").strip() # Option: Synchronous DXL Request if option == "1": # Create the Request logger.info( "Service Invoker - Creating Synchronous Request for topic %s", SERVICE_TOPIC) request = Request(SERVICE_TOPIC) # Encode string payload as UTF-8 request.payload = \ ("Sample Synchronous Request Payload - Request ID: " + str(request.message_id)).encode() # Send Synchronous Request with default timeout and wait for # Response logger.info( "Service Invoker - Sending Synchronous Request to %s", SERVICE_TOPIC) response = client.sync_request(request) # Check that the Response is not an Error Response, then extract if response.message_type != Message.MESSAGE_TYPE_ERROR: