Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RT4-CreateTicket #543

Merged
merged 2 commits into from
May 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions responders/RT4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Request Tracker 4 Cortex Responder
Summary: Creates RT tickets from TheHive

Applies To: Case Observables (Artifacts), Alerts, Cases

## Initial Responder Configuration

The following need to be configured under **Organization --> Responders** prior to use:

`server` - **Required** - RT4 base URL, e.g.: https://rt.domain.local

`username` - **Required** - RT4 username for API authentication

`password` - **Required** - RT4 password for user account above

`Queue` - **Required** - Default queue in which to create new tickets (can be overriden by custom tag on observables)

`Owner` - Default owner to assign newly created tickets (Optional - can be overriden by custom tags per observable)

`Status` - Default status to assign newly created tickets (Optional - can be overriden by custom tags per observable)

`custom_field_list` - Colon-separated Name:Value pairs of RT custom fields and values to set across all newly-created tickets (Optional - can be overriden by custom tags per observable) - adding a value of `How Reported:TheHive` would set the custom field named `How Reported` to `TheHive` on all newly created tickets

`tag_to_template_map` - **Required** - Tags to Templates mapping (can be overriden by custom tag on observables). Should be colon-separated tag-to-template values. E.g.

`thehive_cf_rtticket` - Name of a case custom field in TheHive in which RT ticket #s will be saved upon successful case-level Responder run (Optional - TheHive Custom Field should be of type 'String')

`thehive_url` - TheHive Base URL, e.g., https://thehive.domain.local:9000 (Optional - only needed to process Cases)

`thehive_token` - TheHive API token for authentication (Optional - only needed to process Cases)

```

phishing:phishing_generic
spear_phishing:phishing_spear

```

Any observable with a `phishing` tag would be assigned the template named `phishing_generic`. Any observale tagged `spear_phishing` would have its ticket created with a body from the `phishing_spear` template.

## Workflow

1. Set [Initial Responder Configuration](#Initial-Responder-Configuration)
2. [Create Template(s)](#Templates)
3. As new observables arrive, appropriately [tag](#Tags-to-Modify-RT4-Responder-Behavior) them
4. Run the RT4-CreateTicket responder
5. When complete, the ticket(s) should be created and the `thehive_cf_rtticket` custom field on TheHive cases (if present) should be populated with the URL to any created ticket

## Templates

Inside the `./templates` dir of the RT4 responder, you will need to create the templates for subjects and notification bodies that will be used on ticket creation. For the above example on an observable tagged to use the `phishing_generic` template, there should be a file inside ./templates/ called `phishing_generic.j2` (all templates should end in the .j2 extension since it uses Jinja2 templating)

The .j2 files should be formatted like so:

```
{% block Subject %}
[SOC] ** Notification ** Phishing Site Targeting Your Organization
{% endblock %}


{% block Text %}
Greetings,

We have recently discovered a potential phishing site targeting employees at your organization:

Domain(s):
{{ indicator_list }}

On behalf of the SOC,

--
[email protected]
24x7 Watch Desk
https://www.org.local
{% endblock %}

```

The mandatory blocks are `Subject` and `Text` inside which are the respective content for the ticket creation. You may reference any variables inside the template file which exist in the observable/artifact/alert/case for population of other data within the ticket notification (in the above case, ``indicator_list``). Those variables should be inside double curly-braces as is the format for Jinja. Example data available in the [Observable Object Data](#Observable-Object-Data) section.

Inside the jinja2 template, all block names are passed at RT ticket variables with their respective block values upon ticket creation. Therefore, any number of blocks corresponding to RT fields can also be assigned to further customize setting ticket variables at the template level.

*Example*:

`{% block CF_Classification %}Phishing{% endblock %}`

Every ticket created from that template will have the RT custom field CF_Classification set to "Phishing" upon ticket creation.

## Tags to Modify RT4 Responder Behavior

Set any of the following tags to modify behavior of the created ticket:

`rt4_set_requestor:[email protected]` or `contact:[email protected]` - **Required** - This is the only tag that must be present. Without one of these, the ticket won't be created.

`rt4_set_cf_Classification:phishing` - sets the CF.{Classification} = 'phishing' in RT ticket

`rt4_set_cc:[email protected]` - adds [email protected] as Cc on ticket

`rt4_set_admincc:[email protected]` - sets AdminCc of ticket to [email protected]

`rt4_set_owner:[email protected]` - sets Owner of ticket to [email protected] (**must match person in RT or ticket creation will fail**)

`rt4_set_queue:Incident Reports` - sets Queue of ticket created to _Incident Reports_

`rt4_set_subject:This is a test` - overrides the Subject line from the template with _This is a test_

`rt4_set_status:Resolved` - creates the ticket and then sets its status to _Resolved_ (can also use any other ticket status in your RT instance)

`rt4_set_template:phishing_generic` - overrides any default template from tag_to_template_map setting when constructing the body of the notification, in this case instructing the Responder to use the `phishing_generic` template

## Ticket customization order

As already alluded to, there are 4 ways to customize ticket creation options:

1. Global level
- Queue
- Owner
- Status
- Custom Fields
- Template
2. Template level
- All of the above except Template, plus:
- Requestor/Cc/AdminCc
3. Case/Alert level
- All RT options
4. Case artifact/observable level
- All RT options

Greater numbered config options take precedence over smaller ones.

*Example:*

If a tag_to_template map at the Org Responder config in Cortex is set to map tags of `phishing` to the `phishing_generic` template, but a `set_rt4_template:phishing_spear` tag on the observable sets a different template, the observable tag takes precedence.

## Observable Object Data

Observables are a custom dictionary in which their properties are stored. In addition to the ticket properties passed to RT, each observable is also tagged with its case/artifact info which makes available the following info in each observable:

```
"owner": "michael",
"severity": 2,
"_routing": "AWxyhvveZCXO8BqIWSLs",
"flag": false,
"updatedBy": "michael",
"customFields": {
"RTTicket": {
"string": "http://192.168.0.2/Ticket/Display.html?id=141, http://192.168.0.2/Ticket/Display.html?id=142, http://192.168.0.2/Ticket/Display.html?id=143"
}
},
"_type": "case",
"description": "test",
"title": "RT-testing",
"tags": [
"contact:[email protected]",
"rt4:submitted"
],
"createdAt": 1565289544365,
"_parent": null,
"createdBy": "michael",
"caseId": 1,
"tlp": 2,
"metrics": {
"seen_prior": 1
},
"_id": "AWxyhvveZCXO8BqIWSLs",
"id": "AWxyhvveZCXO8BqIWSLs",
"_version": 45,
"startDate": 1565289480000,
"pap": 2,
"status": "Open",
"updatedAt": 1570482005825,
"indicator_list": [
"malicious.baddomain.tld"
]
```
Those properties can all be referenced as variables in the jinja2 template as mentioned in the [Templates section](#Templates).
3 changes: 3 additions & 0 deletions responders/RT4/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Allow imports from this dir
"""
95 changes: 95 additions & 0 deletions responders/RT4/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Config item classes

class RT4ResponderConfig(dict):
"""Define what an RT4 Responder Config should allow and how it can be set (dict
that only takes certain keys).
Format courtesy of: https://stackoverflow.com/a/8187408 and https://stackoverflow.com/a/40631881

Configs should be init'd like so: config = RT4ResponderConfig(1, **data) where 1 = weight/rank and data is a dict of k,v's
Configs should be updated like so: config.update(1, **newdata) where 1 = weight/rank and newdata is a dict of k,v's. In this
case, the newdata would not be entered since its weight is not greater than the existing data.
"""

def __init__(self, weight=None, **kwargs):
self.WEIGHTS = {
'global': 1,
'template': 2,
'case': 3,
'alert': 3,
'case_artifact': 4,
'observable': 4
}
self.allowed_keys = set([
'Queue',
'Status',
'Owner',
'Requestor',
'Cc',
'AdminCc',
'Subject',
'Text',
'Priority',
'InitialPriority',
'FinalPriority',
'TimeEstimated',
'Starts',
'Due',
'Files',
'template',
'indicator_list'
])

# 'normal' dict init, no weight but requires key_to_list_mapping
if 'key_to_list_mapping' in kwargs:
super().__init__(kwargs.get('key_to_list_mapping'))
# RT4 init, be sure we have weights
else:
super().__init__(self)
self.__setitem__(weight, **kwargs)


# override default 'set' method so users can't accidentally set config items without a corresponding weight
def __setitem__(self, weight, **kwargs):
for key, value in kwargs.items():
if key in self.allowed_keys or key.startswith('CF_'):
weight_key = "{}_weight".format(key)
# map string weight to int if needed
if isinstance(weight, str):
weight = self.WEIGHTS[weight]
if weight_key not in self or weight >= self[weight_key]:
# update weight key value with new weight
super().__setitem__(key, value)
super().__setitem__(weight_key, weight)
# if we're not an RT4 setting, don't worry about weights
# e.g., for case/artifact details we store in a config object
else:
super().__setitem__(key, value)

# override default 'update' method to include weighting
def update(self, weight, **kwargs):
self.__setitem__(weight, **kwargs)

# override default 'keys' method to only display keys related to RT4
def keys(self):
for key in super().keys():
if key in self.allowed_keys:
yield key

# override default 'items' method to only iterate items related to RT4
def items(self):
for key in super().keys():
if key in self.allowed_keys:
yield key, self[key]

# function to provide all items
def fullitems(self):
for key in super().keys():
yield key, self[key]

# create custom '__copy__' method. we do this so that copies don't include all the case/artifact details
def __copy__(self):
return self.__class__(**{'key_to_list_mapping': self.items()})

def copy(self):
"Returns a copy of this object."
return self.__copy__()
4 changes: 4 additions & 0 deletions responders/RT4/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defang
jinja2
rt
requests
91 changes: 91 additions & 0 deletions responders/RT4/rt4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
"name": "RT4-CreateTicket",
"version": "1.0",
"author": "Michael Davis, REN-ISAC",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers/tree/master/responders/RT4",
"license": "MIT",
"description": "Cortex Responder to create a ticket in RT4 from TheHive observables or alerts",
"dataTypeList": ["thehive:case_artifact", "thehive:alert", "thehive:case"],
"command": "RT4/rt4.py",
"baseConfig": "RT4",
"configurationItems": [
{
"name": "server",
"description": "RT4 Base URL, e.g., https://rt.domain.local",
"type": "string",
"multi": false,
"required": true
},
{
"name": "username",
"description": "RT4 username for authentication",
"type": "string",
"multi": false,
"required": true
},
{
"name": "password",
"description": "RT4 password for user account",
"type": "string",
"multi": false,
"required": true
},
{
"name": "Queue",
"description": "Default queue in which to create new tickets",
"type": "string",
"multi": false,
"required": true,
"defaultValue": "General"
},
{
"name": "Owner",
"description": "Default owner to assign newly created tickets (optional)",
"type": "string",
"multi": false,
"required": false
},
{
"name": "Status",
"description": "Default ticket status to assign newly created tickets (optional)",
"type": "string",
"multi": false,
"required": false
},
{
"name": "custom_field_list",
"description": "Name:Value of Custom Fields in RT to set on every ticket created (e.g.: 'How Reported:TheHive' sets CF.{How Reported} = TheHive on every new ticket)",
"type": "string",
"multi": true,
"required": false
},
{
"name": "tag_to_template_map",
"description": "Mapping table of tags to templates (e.g.: 'phishing:phish_letter' maps anything tagged as 'phishing' to the 'phish_letter' template)",
"type": "string",
"multi": true,
"required": true
},
{
"name": "thehive_cf_rtticket",
"description": "Name of a case custom field in TheHive in which RT ticket #s will be saved upon successful case-level Responder run (optional)",
"type": "string",
"multi": false,
"required": false
},
{
"name": "thehive_url",
"description": "TheHive Base URL, e.g., https://thehive.domain.local:9000 (optional: only needed to process Cases)",
"type": "string",
"multi": false,
"required": false
},
{
"name": "thehive_token",
"description": "TheHive API token for authentication (optional: only needed to process Cases)",
"type": "string",
"multi": false,
"required": false
}
]
}
Loading