Introduction
Users cannot edit the intrusion rule directly, but can update the rule's overrideState property via the intrusion policy. The parent intrusion policy id and version properties must be included in the JSON text when updating a rule.
Threat license entitlement must be enabled before intrusion rules can be configured.
To Configure an Intrusion Rule
Step 1
Send an intrusion policy GET request to obtain the policy.
In this example, the request URL includes a filter parameter so that only the policy with name="Security Over Connectivity" is returned.
Copycurl -H 'Accept: application/json' -H "Authorization: Bearer <ACCESS_TOKEN_GOES_HERE>" "https://ftd.example/api/fdm/latest/policy/intrusionpolicies?filter=name:Security%20Over%20Connectivity"
Copydef get_intrusion_policy(host, port, access_token, policy_name):
"""
Requires Python v3.0 or greater and requests lib.
Send an intrusion policy GET request.
:param host: ftd host address
:param port: ftd port
:param access_token: OAUTH token for device access
:param policy_name: URL encoded name of the policy to retrieve
:return: intrusion policy object
"""
headers = {
"Accept": "application/json",
"Authorization": "Bearer {}".format(access_token)
}
intrusion_policy = None
intrusion_policy_url = 'api/fdm/latest/policy/intrusionpolicies?filter=name:{}'.format(policy_name)
response = requests.get(
'https://{host}:{port}/{url}'.format(host=host, port=port, url=intrusion_policy_url),
verify=False, headers=headers)
if response.status_code != 200:
print("Failed GET intrusion policy response {} {}".format(response.status_code, response.json()))
else:
intrusion_policy = response.json().get('items')[0]
print('Intrusion policy found: {}'.format(str(intrusion_policy)))
return intrusion_policy
Copy{
"items" : [ {
"version" : "oq7siofnpzswb",
"name" : "Security Over Connectivity",
"description" : "Security Over Connectivity Layer",
"rules" : {
"links" : {
"self" : "https://ftd.example/api/fdm/latest/policy/intrusionpolicies/cea80e1c-d662-11e9-8607-6fbc78ab2e13/intrusionrules"
}
},
"id" : "cea80e1c-d662-11e9-8607-6fbc78ab2e13",
"type" : "intrusionpolicy",
"links" : {
"self" : "https://ftd.example/api/fdm/latest/policy/intrusionpolicies/cea80e1c-d662-11e9-8607-6fbc78ab2e13"
}
} ],
"paging" : {
"prev" : [ ],
"next" : [ ],
"limit" : 10,
"offset" : 0,
"count" : 1,
"pages" : 0
}
}
Step 2
Send an intrusion rule GET request to obtain an intrusion rule. Append the id from the response in Step 1 to the request URL.
In this example, the request URL includes filter parameters so that only the rule with gid=1 and sid=37244 properties is returned.
Copycurl -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer <ACCESS_TOKEN_GOES_HERE>" -d @json.txt "https://ftd.example/api/fdm/latest/policy/intrusionpolicies/cea80e1c-d662-11e9-8607-6fbc78ab2e13/intrusionrules?filter=gid:1;sid:37244"
Copydef get_intrusion_rule(host, port, access_token, policy_id, gid, sid):
"""
Requires Python v3.0 or greater and requests lib.
Send an intrusion rule GET request.
:param host: ftd host address
:param port: ftd port
:param access_token: OAUTH token for device access
:param policy_id: unique identifier for intrusion policy that owns the rule
:param gid: intrusion rule group identifier
:param sid: intrusion rule signature identifier
:return: intrusion rule object
"""
headers = {
"Accept": "application/json",
"Authorization": "Bearer {}".format(access_token)
}
intrusion_rule = None
intrusion_rule_url = 'api/fdm/latest/policy/intrusionpolicies/{}/intrusionrules?filter=gid:{};sid:{}'.format(
policy_id, gid, sid)
response = requests.get(
'https://{host}:{port}/{url}'.format(host=host, port=port, url=intrusion_rule_url),
verify=False, headers=headers)
if response.status_code != 200:
print("Failed GET intrusion rule response {} {}".format(response.status_code, response.json()))
else:
intrusion_rule = response.json().get('items')[0]
print('Intrusion rule found: {}'.format(str(intrusion_rule)))
return intrusion_rule
Copy{
"items" : [ {
"version" : "ltkde3mqsaxjx",
"name" : "1:37244",
"gid" : 1,
"sid" : 37244,
"revision" : 3,
"msg" : "INDICATOR-COMPROMISE download of a Office document with embedded PowerShell",
"ruleData" : "tcp $EXTERNAL_NET $FILE_DATA_PORTS -> $HOME_NET any (msg:\"INDICATOR-COMPROMISE download of a Office document with embedded PowerShell\"; flow:to_client,established; flowbits:isset,file.doc; file_data; content:\"powershell.exe\"; fast_pattern:only; metadata:policy max-detect-ips drop, policy security-ips alert, service ftp-data, service http, service imap, service pop3; reference:url,attack.mitre.org/techniques/T1086; classtype:trojan-activity; sid:37244; rev:3; gid:1;)",
"defaultState" : "ALERT",
"overrideState" : null,
"id" : "fb57850b-b491-11e5-ad13-a89d216baee6",
"type" : "intrusionrule",
"links" : {
"self" : "https://ftd.example/api/fdm/latest/policy/intrusionpolicies/cea80e1c-d662-11e9-8607-6fbc78ab2e13/intrusionrules/fb57850b-b491-11e5-ad13-a89d216baee6"
}
} ],
"paging" : {
"prev" : [ ],
"next" : [ ],
"limit" : 10,
"offset" : 0,
"count" : 1,
"pages" : 0
}
}
Step 3
Send an intrusion rule PUT request to update the rule action from ALERT to DROP. Include the intrusion policy id from the response in Step 1 in the request URL.
Note carefully in the JSON text that the intrusionpolicyruleupdate id and version values are from the intrusion policy response, and the ruleConfigs id value is from the intrusion rule response.
Copycurl -X PUT -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer <ACCESS_TOKEN_GOES_HERE>" -d @json.txt https://ftd.example/api/fdm/latest/policy/intrusionpolicies/cea80e1c-d662-11e9-8607-6fbc78ab2e13/ruleupdates
Copydef update_intrusion_rule(host, port, access_token, policy_id, rule_update):
"""
Requires Python v3.0 or greater and requests lib.
Update an intrusion rule
:param host: ftd host address
:param port: ftd port
:param access_token: OAUTH token for device access
:param policy_id: unique identifier for intrusion policy that owns the rule
:param rule_update: intrusion rule update object
:return: True if successful, otherwise False
"""
result = True
headers = {
"Accept": "application/json",
"Authorization": "Bearer {}".format(access_token)
}
intrusion_rule_url = 'api/fdm/latest/policy/intrusionpolicies/{}/ruleupdates'.format(policy_id)
response = requests.put(
'https://{host}:{port}/{url}'.format(host=host, port=port, url=intrusion_rule_url),
data=json.dumps(rule_update), verify=False, headers=headers)
if response.status_code != 200 and response.status_code != 204:
print("Failed PUT intrusion rule response {} {}".format(response.status_code, response.json()))
result = False
elif response.status_code == 200:
print(response.json())
return result
Copy{
"version" : "oq7siofnpzswb",
"id" : "cea80e1c-d662-11e9-8607-6fbc78ab2e13",
"ruleConfigs" : [{
"id" : "fb57850b-b491-11e5-ad13-a89d216baee6",
"state" : "DROP"
}],
"type" : "intrusionpolicyruleupdate"
}
Step 4
Send an intrusion rule GET request to confirm the updated intrusion rule.
In this example, the request URL includes filter parameters so that only the rule with gid=1 and sid=37244 properties is returned.
Copycurl -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer <ACCESS_TOKEN_GOES_HERE>" -d @json.txt "https://ftd.example/api/fdm/latest/policy/intrusionpolicies/cea80e1c-d662-11e9-8607-6fbc78ab2e13/intrusionrules?filter=gid:1;sid:37244"
Copydef get_intrusion_rule(host, port, access_token, policy_id, gid, sid):
"""
Requires Python v3.0 or greater and requests lib.
Send an intrusion rule GET request.
:param host: ftd host address
:param port: ftd port
:param access_token: OAUTH token for device access
:param policy_id: unique identifier for intrusion policy that owns the rule
:param gid: intrusion rule group identifier
:param sid: intrusion rule signature identifier
:return: intrusion rule object
"""
headers = {
"Accept": "application/json",
"Authorization": "Bearer {}".format(access_token)
}
intrusion_rule = None
intrusion_rule_url = 'api/fdm/latest/policy/intrusionpolicies/{}/intrusionrules?filter=gid:{};sid:{}'.format(
policy_id, gid, sid)
response = requests.get(
'https://{host}:{port}/{url}'.format(host=host, port=port, url=intrusion_rule_url),
verify=False, headers=headers)
if response.status_code != 200:
print("Failed GET intrusion rule response {} {}".format(response.status_code, response.json()))
else:
intrusion_rule = response.json().get('items')[0]
print('Intrusion rule found: {}'.format(str(intrusion_rule)))
return intrusion_rule
Copy{
"items" : [ {
"version" : "ltkde3mqsaxjx",
"name" : "1:37244",
"gid" : 1,
"sid" : 37244,
"revision" : 3,
"msg" : "INDICATOR-COMPROMISE download of a Office document with embedded PowerShell",
"ruleData" : "tcp $EXTERNAL_NET $FILE_DATA_PORTS -> $HOME_NET any (msg:\"INDICATOR-COMPROMISE download of a Office document with embedded PowerShell\"; flow:to_client,established; flowbits:isset,file.doc; file_data; content:\"powershell.exe\"; fast_pattern:only; metadata:policy max-detect-ips drop, policy security-ips alert, service ftp-data, service http, service imap, service pop3; reference:url,attack.mitre.org/techniques/T1086; classtype:trojan-activity; sid:37244; rev:3; gid:1;)",
"defaultState" : "ALERT",
"overrideState" : "DROP",
"id" : "fb57850b-b491-11e5-ad13-a89d216baee6",
"type" : "intrusionrule",
"links" : {
"self" : "https://ftd.example/api/fdm/latest/policy/intrusionpolicies/cea80e1c-d662-11e9-8607-6fbc78ab2e13/intrusionrules/fb57850b-b491-11e5-ad13-a89d216baee6"
}
} ],
"paging" : {
"prev" : [ ],
"next" : [ ],
"limit" : 10,
"offset" : 0,
"count" : 1,
"pages" : 0
}
}
Step 5
Perform a deployment to commmit the intrusion policy changes.
Complete Intrusion Rule Update Script
Combining all of the Python functions from the previous steps
Copy'''
Copyright (c) 2019 Cisco and/or its affiliates.
This software is licensed to you under the terms of the Cisco Sample
Code License, Version 1.1 (the "License"). A copy of the License
can be found in the LICENSE.TXT file of this software or at
https://developer.cisco.com/site/license/cisco-sample-code-license/
All use of the material herein must be in accordance with the terms of
the License. All rights not expressly granted by the License are
reserved. Unless required by applicable law or agreed to separately in
writing, software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied.
'''
import json
import requests
def get_access_token(host, port, user, passwd):
"""
Requires Python v3.0 or greater and requests lib.
Login to FTD device and obtain an access token. The access token is required so that the user can
connect to the device to send REST API requests.
:param host: ftd host address
:param port: ftd port
:param user: login user name
:param passwd: login password
:return: OAUTH access token
"""
access_token = None
requests.packages.urllib3.disable_warnings()
payload = '{{"grant_type": "password", "username": "{}", "password": "{}"}}'.format(user, passwd)
auth_headers = {"Content-Type": "application/json", "Accept": "application/json"}
try:
response = requests.post("https://{}:{}/api/fdm/latest/fdm/token".format(host, port),
data=payload, verify=False, headers=auth_headers)
if response.status_code == 200:
access_token = response.json().get('access_token')
print("Login successful, access_token obtained {}".format(access_token))
else:
print("Login failed {} {}".format(response.status_code, response.json()))
except Exception as e:
print("Exception in POST access token request: {}".format(str(e)))
return access_token
def get_intrusion_policy(host, port, access_token, policy_name):
"""
Requires Python v3.0 or greater and requests lib.
Send an intrusion policy GET request.
:param host: ftd host address
:param port: ftd port
:param access_token: OAUTH token for device access
:param policy_name: URL encoded name of the policy to retrieve
:return: intrusion policy object
"""
headers = {
"Accept": "application/json",
"Authorization": "Bearer {}".format(access_token)
}
intrusion_policy = None
intrusion_policy_url = 'api/fdm/latest/policy/intrusionpolicies?filter=name:{}'.format(policy_name)
response = requests.get(
'https://{host}:{port}/{url}'.format(host=host, port=port, url=intrusion_policy_url),
verify=False, headers=headers)
if response.status_code != 200:
print("Failed GET intrusion policy response {} {}".format(response.status_code, response.json()))
else:
intrusion_policy = response.json().get('items')[0]
print('Intrusion policy found: {}'.format(str(intrusion_policy)))
return intrusion_policy
def get_intrusion_rule(host, port, access_token, policy_id, gid, sid):
"""
Requires Python v3.0 or greater and requests lib.
Send an intrusion rule GET request.
:param host: ftd host address
:param port: ftd port
:param access_token: OAUTH token for device access
:param policy_id: unique identifier for intrusion policy that owns the rule
:param gid: intrusion rule group identifier
:param sid: intrusion rule signature identifier
:return: intrusion rule object
"""
headers = {
"Accept": "application/json",
"Authorization": "Bearer {}".format(access_token)
}
intrusion_rule = None
intrusion_rule_url = 'api/fdm/latest/policy/intrusionpolicies/{}/intrusionrules?filter=gid:{};sid:{}'.format(
policy_id, gid, sid)
response = requests.get(
'https://{host}:{port}/{url}'.format(host=host, port=port, url=intrusion_rule_url),
verify=False, headers=headers)
if response.status_code != 200:
print("Failed GET intrusion rule response {} {}".format(response.status_code, response.json()))
else:
intrusion_rule = response.json().get('items')[0]
print('Intrusion rule found: {}'.format(str(intrusion_rule)))
return intrusion_rule
def update_intrusion_rule(host, port, access_token, policy_id, rule_update):
"""
Requires Python v3.0 or greater and requests lib.
Update an intrusion rule
:param host: ftd host address
:param port: ftd port
:param access_token: OAUTH token for device access
:param policy_id: unique identifier for intrusion policy that owns the rule
:param rule_update: intrusion rule update object
:return: True if successful, otherwise False
"""
result = True
headers = {
"Accept": "application/json",
"Authorization": "Bearer {}".format(access_token)
}
intrusion_rule_url = 'api/fdm/latest/policy/intrusionpolicies/{}/ruleupdates'.format(policy_id)
response = requests.put(
'https://{host}:{port}/{url}'.format(host=host, port=port, url=intrusion_rule_url),
data=json.dumps(rule_update), verify=False, headers=headers)
if response.status_code != 200 and response.status_code != 204:
print("Failed PUT intrusion rule response {} {}".format(response.status_code, response.json()))
result = False
elif response.status_code == 200:
print(response.json())
return result
def main():
"""
End to end example of code that updates an intrusion rule.
Requires Python v3.0 or greater and the reqeusts library.
You must update the values for host, port, user, and password to connect to your device.
"""
host = 'ftd.example'
port = '443'
user = 'admin'
passwd = 'Admin123'
access_token = get_access_token(host, port, user, passwd)
if not access_token:
print("Unable to obtain an access token. Did you remember to set host, port, user, and password?")
return
intrusion_policy = get_intrusion_policy(host, port, access_token, 'Security%20Over%20Connectivity')
if not intrusion_policy:
print('Unable to get intrusion policy')
return
gid = '1'
sid = '37244'
intrusion_rule = get_intrusion_rule(host, port, access_token, intrusion_policy['id'], gid, sid)
if not intrusion_rule:
print('Unable to get intrusion rule')
return
rule_update = {
'version': intrusion_policy['version'],
'id': intrusion_policy['id'],
'ruleConfigs': [{
'id': intrusion_rule['id'],
'state': 'DROP'
}],
'type': 'intrusionpolicyruleupdate'
}
result = update_intrusion_rule(host, port, access_token, intrusion_policy['id'], rule_update)
if not result:
print('Unable to update intrusion rule')
return
intrusion_rule = get_intrusion_rule(host, port, access_token, intrusion_policy['id'], gid, sid)
if not intrusion_rule:
print('Unable to get intrusion rule')
return
print('rule action is {}'.format(intrusion_rule['overrideState']))
if __name__ == '__main__':
main()
CopyLogin successful, access_token obtained eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzA4MTY1MjIsInN1YiI6ImFkbWluIiwianRpIjoiNGJjMTlmYjMtZWM1MC0xMWU5LWEwYzItNGQwZDQ1Yjk3OWE2IiwibmJmIjoxNTcwODE2NTIyLCJleHAiOjE1NzA4MTgzMjIsInJlZnJlc2hUb2tlbkV4cGlyZXNBdCI6MTU3MDgxODkyMjQwMywidG9rZW5UeXBlIjoiSldUX0FjY2VzcyIsInVzZXJVdWlkIjoiMjcxYmNkOTEtYWNjYy0xMWU5LWIxOTUtNDlmNzk4YTg1NTk2IiwidXNlclJvbGUiOiJST0xFX0FETUlOIiwib3JpZ2luIjoicGFzc3dvcmQiLCJ1c2VybmFtZSI6ImFkbWluIn0.n5PgQ2zxuBSd2-SCfaap0eT1l5MOHZXzWTteSS-fHBg
Intrusion policy found: {'name': 'Security Over Connectivity', 'type': 'intrusionpolicy', 'links': {'self': 'https://ftd.example/api/fdm/latest/policy/intrusionpolicies/9cc5d466-accb-11e9-9a9e-77482da9acbd'}, 'version': 'mlga7bl45a7na', 'rules': {'links': {'self': 'https://ftd.example/api/fdm/latest/policy/intrusionpolicies/9cc5d466-accb-11e9-9a9e-77482da9acbd/intrusionrules'}}, 'id': '9cc5d466-accb-11e9-9a9e-77482da9acbd', 'description': 'Security Over Connectivity Layer'}
Intrusion rule found: {'type': 'intrusionrule', 'sid': 37244, 'links': {'self': 'https://ftd.example/api/fdm/latest/policy/intrusionpolicies/9cc5d466-accb-11e9-9a9e-77482da9acbd/intrusionrules/fb57850b-b491-11e5-ad13-a89d216baee6'}, 'revision': 3, 'id': 'fb57850b-b491-11e5-ad13-a89d216baee6', 'version': 'okzsvt2qtnouz', 'overrideState': 'DROP', 'name': '1:37244', 'gid': 1, 'ruleData': 'tcp $EXTERNAL_NET $FILE_DATA_PORTS -> $HOME_NET any (msg:"INDICATOR-COMPROMISE download of a Office document with embedded PowerShell"; flow:to_client,established; flowbits:isset,file.doc; file_data; content:"powershell.exe"; fast_pattern:only; metadata:policy max-detect-ips drop, policy security-ips alert, service ftp-data, service http, service imap, service pop3; reference:url,attack.mitre.org/techniques/T1086; classtype:trojan-activity; sid:37244; rev:3; gid:1;)', 'msg': 'INDICATOR-COMPROMISE download of a Office document with embedded PowerShell', 'defaultState': 'ALERT'}
Intrusion rule found: {'type': 'intrusionrule', 'sid': 37244, 'links': {'self': 'https://ftd.example/api/fdm/latest/policy/intrusionpolicies/9cc5d466-accb-11e9-9a9e-77482da9acbd/intrusionrules/fb57850b-b491-11e5-ad13-a89d216baee6'}, 'revision': 3, 'id': 'fb57850b-b491-11e5-ad13-a89d216baee6', 'version': 'okzsvt2qtnouz', 'overrideState': 'DROP', 'name': '1:37244', 'gid': 1, 'ruleData': 'tcp $EXTERNAL_NET $FILE_DATA_PORTS -> $HOME_NET any (msg:"INDICATOR-COMPROMISE download of a Office document with embedded PowerShell"; flow:to_client,established; flowbits:isset,file.doc; file_data; content:"powershell.exe"; fast_pattern:only; metadata:policy max-detect-ips drop, policy security-ips alert, service ftp-data, service http, service imap, service pop3; reference:url,attack.mitre.org/techniques/T1086; classtype:trojan-activity; sid:37244; rev:3; gid:1;)', 'msg': 'INDICATOR-COMPROMISE download of a Office document with embedded PowerShell', 'defaultState': 'ALERT'}
rule action is DROP