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

[Security Solutions][Detection Engine] Adds threat matching API and rule type #77395

Merged
merged 30 commits into from
Sep 20, 2020

Conversation

FrankHassanabad
Copy link
Contributor

@FrankHassanabad FrankHassanabad commented Sep 14, 2020

Summary

This is the backend, first iteration of threat matching API and rule type. You see elements using the backend API on the front end but cannot use the UI to add or edit a threshold rule with this PR.

Screen shots of it running in the UI elements that do work:
Screen Shot 2020-09-16 at 10 34 26 AM

Screen Shot 2020-09-16 at 10 34 48 AM

Usage
Since this is only backend API work and does not have the front end add/edit at the moment, you can use the existing UI's (for the most part) to validate the work here through CURL scripts below:

Go to the folder:

/kibana/x-pack/plugins/security_solution/server/lib/detection_engine/scripts

And post a small ECS threat mapping to the index called mock-threat-list:

./create_threat_mapping.sh

Then to post a small number of threats that represent simple port numbers you can run:

./create_threat_data.sh

However, feel free to also manually create them directly in your dev tools like so:

# Posts a threat list item called some-name with an IP but change these out for valid data in your system
PUT mock-threat-list-1/_doc/9999
{
  "@timestamp": "2020-09-09T20:30:45.725Z",
  "host": {
    "name": "some-name",
    "ip": "127.0.0.1"
  }
}
# Posts a destination port number to watch
PUT mock-threat-list-1/_doc/10000
{
  "@timestamp": "2020-09-08T20:30:45.725Z",
  "destination": {
    "port": "443"
  }
}
# Posts a source port number to watch
PUT mock-threat-list-1/_doc/10001
{
  "@timestamp": "2020-09-08T20:30:45.725Z",
  "source": {
    "port": "443"
  }
}

Then you can post a threat match rule:

./post_rule.sh ./rules/queries/query_with_threat_mapping.json
Click here to see Response
{
  "actions": [],
  "author": [],
  "created_at": "2020-09-16T04:25:58.041Z",
  "created_by": "yo",
  "description": "Query with a threat mapping",
  "enabled": true,
  "exceptions_list": [],
  "false_positives": [],
  "from": "now-6m",
  "id": "f4226ab0-6f88-49c3-8f09-84cf5946ee7a",
  "immutable": false,
  "interval": "5m",
  "language": "kuery",
  "max_signals": 100,
  "name": "Query with a threat mapping",
  "output_index": ".siem-signals-hassanabad3-default",
  "query": "*:*",
  "references": [],
  "risk_score": 1,
  "risk_score_mapping": [],
  "rule_id": "threat-mapping",
  "severity": "high",
  "severity_mapping": [],
  "tags": [
    "tag_1",
    "tag_2"
  ],
  "threat": [],
  "threat_index": "mock-threat-list-1",
  "threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ],
  "threat_query": "*:*",
  "throttle": "no_actions",
  "to": "now",
  "type": "threat_match",
  "updated_at": "2020-09-16T04:25:58.051Z",
  "updated_by": "yo",
  "version": 1
}

Structure

You can see the rule structure in the file:

x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_threat_mapping.json
Click here to see JSON
{
  "name": "Query with a threat mapping",
  "description": "Query with a threat mapping",
  "rule_id": "threat-mapping",
  "risk_score": 1,
  "severity": "high",
  "type": "threat_match",
  "query": "*:*",
  "tags": ["tag_1", "tag_2"],
  "threat_index": "mock-threat-list",
  "threat_query": "*:*",
  "threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ]
}

Structural elements that are new:

New type enum called "threat_match"

"type": "threat_match",

New threat_index string which can be set to a single threat index (This might change to an array in the near future before release):

"threat_index": "mock-threat-list"

New threat_query string which can be set any valid query to filter the threat list before executing the rule. This can be undefined, if you are only pushing in filters from the API.

"threat_query": "*:*",

New threat_filters array which can be set to any valid filter like filters. This can be undefined if you are only using the query from the API.

threat_filter": []

New threat_mapping array which can be set to a valid mapping between the threat list and the ECS list. This structure has an inner array called entries which represent a 2 level tree of 1st level OR elements followed by 2nd level AND elements.

For example, if you want to find all threat matches where ECS documents will match against some ${threatList} index where it would be like so:

Click here to see array from the boolean
"threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ]

What that array represents in pseudo boolean logic is:

Click here to see pseduo logic
(host.name: ${threatList.host.name} AND host.ip: ${threatList.host.name}) OR
(destination.ip: ${threatList.destination.ip} AND destination.port: ${threatList.destination.port}) OR
(source.port ${threatList.source.port}) OR
(source.ip ${threatList.source.ip})

Checklist

@FrankHassanabad FrankHassanabad requested review from yctercero and removed request for yctercero September 16, 2020 15:51
@FrankHassanabad FrankHassanabad self-assigned this Sep 16, 2020
@FrankHassanabad FrankHassanabad marked this pull request as ready for review September 16, 2020 16:40
@FrankHassanabad FrankHassanabad requested review from a team as code owners September 16, 2020 16:40
@elasticmachine
Copy link
Contributor

Pinging @elastic/siem (Team:SIEM)

@@ -116,6 +122,10 @@ export const addPrepackagedRulesSchema = t.intersection([
references: DefaultStringArray, // defaults to empty array of strings if not set during decode
note, // defaults to "undefined" if not set during decode
exceptions_list: DefaultListArray, // defaults to empty array if not set during decode
threat_filters, // defaults to "undefined" if not set during decode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: For fields that are arrays, do we want to default them to empty arrays? Or did you choose to default them to "undefined" to be more explicit about like if a rule is not of type "threat_match" these fields should not be there (as opposed to them being there and being [])?

Copy link
Contributor Author

@FrankHassanabad FrankHassanabad Sep 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you answered the question below so I think we're good here 👍

test('You can set a threat query, index, mapping, filters on a pre-packaged rule', () => {
const payload: AddPrepackagedRulesSchema = {
...getAddPrepackagedRulesSchemaMock(),
threat_query: '*:*',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: It might be useful to make a getThreatMatchAddOnMock that includes these threat match values to throw in throughout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added both a getAddPrepackagedThreatRulesSchemaMock and a getAddPrepackagedThreatRulesSchemaDecodedMock here as well as the other schema based tests to keep the boiler platting down as suggested. 👍

So now it looks like all the rest. Thanks for the good advice

@@ -1660,5 +1660,84 @@ describe('create rules schema', () => {
};
expect(message.schema).toEqual(expected);
});

describe('threat_mapping', () => {
test('You can set a threat query, index, mapping, filters on a pre-packaged rule', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be:

Suggested change
test('You can set a threat query, index, mapping, filters on a pre-packaged rule', () => {
test('You can set a threat query, index, mapping, filters when creating rule', () => {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good catch, fixed for the next commit

threat_query: '*:*',
};
const errors = createRuleValidateTypeDependents(schema);
expect(errors).toEqual(['threat_mapping" must have at least one element']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, I see now here why this value defaults to undefined as opposed to empty array given this requirement here.

@@ -107,6 +107,24 @@ export const validateThreshold = (rule: CreateRulesSchema): string[] => {
return [];
};

export const validateThreatMapping = (rule: CreateRulesSchema): string[] => {
let errors: string[] = [];
if (rule.type === 'threat_match') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: saw that I think one of the more recent PRs added the pattern of using a helper function to check rule type (x-pack/plugins/security_solution/common/detection_engine/utils.ts) like on line 98 above - maybe a threat match one can be added.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually yeah I had one in the backend on the server side but it looks like these have moved to the common section so totally agree. I am moving the server side one to the common section and then collapsing code and cleaning this one up. Thanks for the 🦅 👀 on these.

@@ -1791,5 +1791,84 @@ describe('import rules schema', () => {
};
expect(message.schema).toEqual(expected);
});

describe('threat_mapping', () => {
test('You can set a threat query, index, mapping, filters on a pre-packaged rule', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: quick wording fix:

Suggested change
test('You can set a threat query, index, mapping, filters on a pre-packaged rule', () => {
test('You can set a threat query, index, mapping, filters on an imported rule', () => {

@@ -96,6 +97,9 @@ export const getFilter = async ({
case 'query': {
return queryFilter();
}
case 'threat_match': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can case threat_match be added to the first statement on line 93?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! That is a better spot for it.

export const threatMappingEntries = t.array(
t.exact(
t.type({
field: t.string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about making this the non-empty string type? Just remembering with exceptions, we had to update it to that after the fact to avoid some bugs that were happening. Not sure if that's of concern here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I think that is a good idea. Changed the code now to reflect this. You will see it on the next commit.

* Max(timer_array_1) + Max(timer_array_2)
* @param existingTimers String array of existing timers
* @param newTimers String array of new timers.
* @returns String of the new maximum between the two timers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
* @returns String of the new maximum between the two timers
* @returns String array of the new maximum between the two timers

}: GetSortWithTieBreakerOptions): SortWithTieBreaker[] => {
const ascOrDesc = sortOrder ?? 'asc';
if (sortField != null) {
return [{ [sortField]: ascOrDesc, '@timestamp': 'asc' }];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this comment is at all relevant - will threat list rules have timestamp overide? If so, would that matter at all here? Like wanting to use that field over @timestamp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can have an override of a timestamp. That is a good point. I will add this to a small TODO list I am tracking. I will more than likely have to revisit this item in a follow up PR. Appreciate you finding this.

Copy link
Contributor

@yctercero yctercero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really awesome! There might be some more discussion that can happen as to optimizing the DSL structure - that's not an area I'm well versed on, but I don't think that's anything that should hold up the PR going in. The code itself was really easy and nice to read through, appreciate the use of reduce to help with the readability of the various loops.

Thanks so much for taking the time to talk through the code with me. I pulled it down and played around with it some more after our call. Some things that I encountered during and post call testing:

  • when using a slower network, I received the following errors running the threat list rule:
log   [15:35:00.087] [error][plugins][plugins][securitySolution][securitySolution] [-] nextSearchAfter threw an error Error: Request Timeout after 30000ms
server    log   [15:35:00.088] [error][plugins][plugins][securitySolution][securitySolution] [-] search_after and bulk threw an error Error: Request Timeout after 30000ms name: "Query with a threat mapping" id: "21fb7bb1-378d-4f28-acda-b61dcf046899" rule id: "threat-mapping" signals index: ".siem-signals-yt"
server    log   [15:35:00.187] [error][plugins][plugins][securitySolution][securitySolution] Bulk Indexing of signals failed. Check logs for further details. name: "Query with a threat mapping" id: "21fb7bb1-378d-4f28-acda-b61dcf046899" rule id: "threat-mapping" signals index: ".siem-signals-yt
  • We played around with this one together and found that decreasing the threat list MAX_PER_PAGE constant to 1000 allowed the rule to run without errors
  • I also tested that same threat list but with an exceptions list that included 800 exceptions and hit the same timeout error (on regular home network)

I think the above is a matter of tuning, finding out the ideal settings for the user and like you mentioned, maybe allowing the user access to some of the configs to tune themselves if they like. I think your other PR that improves the error handling/bubbling of the detection engine will also improve the user experience if they're to hit such timeouts to guide them through a solution.

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Build metrics

async chunks size

id value diff baseline
securitySolution 10.1MB +508.0B 10.1MB

page load bundle size

id value diff baseline
securitySolution 793.4KB +18.0B 793.3KB

distributable file count

id value diff baseline
default 45950 +11 45939

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@FrankHassanabad FrankHassanabad merged commit 496152e into elastic:master Sep 20, 2020
@FrankHassanabad FrankHassanabad deleted the add-threat-match branch September 20, 2020 20:40
FrankHassanabad added a commit to FrankHassanabad/kibana that referenced this pull request Sep 20, 2020
…ule type (elastic#77395)

## Summary

This is the backend, first iteration of threat matching API and rule type. You see elements using the backend API on the front end but cannot use the UI to add or edit a threshold rule with this PR.

Screen shots of it running in the UI elements that do work:
<img width="1862" alt="Screen Shot 2020-09-16 at 10 34 26 AM" src="https://user-images.githubusercontent.com/1151048/93366465-6e2b9c00-f808-11ea-923b-78e8d0fdfbaa.png">

<img width="1863" alt="Screen Shot 2020-09-16 at 10 34 48 AM" src="https://user-images.githubusercontent.com/1151048/93366476-71268c80-f808-11ea-8247-d2091ff1599a.png"> 

**Usage**
Since this is only backend API work and does not have the front end add/edit at the moment, you can use the existing UI's (for the most part) to validate the work here through CURL scripts below:

Go to the folder:
```ts
/kibana/x-pack/plugins/security_solution/server/lib/detection_engine/scripts
```

And post a small ECS threat mapping to the index called `mock-threat-list`:
```ts
./create_threat_mapping.sh
```

Then to post a small number of threats that represent simple port numbers you can run:
```ts
./create_threat_data.sh
```

However, feel free to also manually create them directly in your dev tools like so:

```ts
# Posts a threat list item called some-name with an IP but change these out for valid data in your system
PUT mock-threat-list-1/_doc/9999
{
  "@timestamp": "2020-09-09T20:30:45.725Z",
  "host": {
    "name": "some-name",
    "ip": "127.0.0.1"
  }
}
```

```ts
# Posts a destination port number to watch
PUT mock-threat-list-1/_doc/10000
{
  "@timestamp": "2020-09-08T20:30:45.725Z",
  "destination": {
    "port": "443"
  }
}
```

```ts
# Posts a source port number to watch
PUT mock-threat-list-1/_doc/10001
{
  "@timestamp": "2020-09-08T20:30:45.725Z",
  "source": {
    "port": "443"
  }
}
```

Then you can post a threat match rule:
```ts
./post_rule.sh ./rules/queries/query_with_threat_mapping.json
```
<details>
 <summary>Click here to see Response</summary>

```ts
{
  "actions": [],
  "author": [],
  "created_at": "2020-09-16T04:25:58.041Z",
  "created_by": "yo",
  "description": "Query with a threat mapping",
  "enabled": true,
  "exceptions_list": [],
  "false_positives": [],
  "from": "now-6m",
  "id": "f4226ab0-6f88-49c3-8f09-84cf5946ee7a",
  "immutable": false,
  "interval": "5m",
  "language": "kuery",
  "max_signals": 100,
  "name": "Query with a threat mapping",
  "output_index": ".siem-signals-hassanabad3-default",
  "query": "*:*",
  "references": [],
  "risk_score": 1,
  "risk_score_mapping": [],
  "rule_id": "threat-mapping",
  "severity": "high",
  "severity_mapping": [],
  "tags": [
    "tag_1",
    "tag_2"
  ],
  "threat": [],
  "threat_index": "mock-threat-list-1",
  "threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ],
  "threat_query": "*:*",
  "throttle": "no_actions",
  "to": "now",
  "type": "threat_match",
  "updated_at": "2020-09-16T04:25:58.051Z",
  "updated_by": "yo",
  "version": 1
}
```
</details>

**Structure**

You can see the rule structure in the file:
```ts
x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_threat_mapping.json
```
<details>
 <summary>Click here to see JSON</summary>

```ts
{
  "name": "Query with a threat mapping",
  "description": "Query with a threat mapping",
  "rule_id": "threat-mapping",
  "risk_score": 1,
  "severity": "high",
  "type": "threat_match",
  "query": "*:*",
  "tags": ["tag_1", "tag_2"],
  "threat_index": "mock-threat-list",
  "threat_query": "*:*",
  "threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ]
}
```

</details>

Structural elements that are new:

New type enum called "threat_match"
```ts
"type": "threat_match",
```

New `threat_index` string which can be set to a single threat index (This might change to an array in the near future before release):
```ts
"threat_index": "mock-threat-list"
```

New `threat_query` string which can be set any valid query to filter the threat list before executing the rule. This can be undefined, if you are only pushing in filters from the API.

```ts
"threat_query": "*:*",
```

New `threat_filters` array which can be set to any valid filter like `filters`. This can be `undefined` if you are only using the query from the API.
```ts
threat_filter": []
```

New `threat_mapping` array which can be set to a valid mapping between the threat list and the ECS list. This structure has an inner array called `entries` which represent a 2 level tree of 1st level OR elements followed by 2nd level AND elements.

For example, if you want to find all threat matches where ECS documents will match against some ${threatList} index where it would be like so:

<details>
 <summary>Click here to see array from the boolean</summary>

```ts
"threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ]
```

</details>

What that array represents in pseudo boolean logic is: 

<details>
 <summary>Click here to see pseduo logic</summary>

```ts
(host.name: ${threatList.host.name} AND host.ip: ${threatList.host.name}) OR
(destination.ip: ${threatList.destination.ip} AND destination.port: ${threatList.destination.port}) OR
(source.port ${threatList.source.port}) OR
(source.ip ${threatList.source.ip})
```

</details>

### Checklist

- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
FrankHassanabad added a commit that referenced this pull request Sep 20, 2020
…ule type (#77395) (#77978)

## Summary

This is the backend, first iteration of threat matching API and rule type. You see elements using the backend API on the front end but cannot use the UI to add or edit a threshold rule with this PR.

Screen shots of it running in the UI elements that do work:
<img width="1862" alt="Screen Shot 2020-09-16 at 10 34 26 AM" src="https://user-images.githubusercontent.com/1151048/93366465-6e2b9c00-f808-11ea-923b-78e8d0fdfbaa.png">

<img width="1863" alt="Screen Shot 2020-09-16 at 10 34 48 AM" src="https://user-images.githubusercontent.com/1151048/93366476-71268c80-f808-11ea-8247-d2091ff1599a.png"> 

**Usage**
Since this is only backend API work and does not have the front end add/edit at the moment, you can use the existing UI's (for the most part) to validate the work here through CURL scripts below:

Go to the folder:
```ts
/kibana/x-pack/plugins/security_solution/server/lib/detection_engine/scripts
```

And post a small ECS threat mapping to the index called `mock-threat-list`:
```ts
./create_threat_mapping.sh
```

Then to post a small number of threats that represent simple port numbers you can run:
```ts
./create_threat_data.sh
```

However, feel free to also manually create them directly in your dev tools like so:

```ts
# Posts a threat list item called some-name with an IP but change these out for valid data in your system
PUT mock-threat-list-1/_doc/9999
{
  "@timestamp": "2020-09-09T20:30:45.725Z",
  "host": {
    "name": "some-name",
    "ip": "127.0.0.1"
  }
}
```

```ts
# Posts a destination port number to watch
PUT mock-threat-list-1/_doc/10000
{
  "@timestamp": "2020-09-08T20:30:45.725Z",
  "destination": {
    "port": "443"
  }
}
```

```ts
# Posts a source port number to watch
PUT mock-threat-list-1/_doc/10001
{
  "@timestamp": "2020-09-08T20:30:45.725Z",
  "source": {
    "port": "443"
  }
}
```

Then you can post a threat match rule:
```ts
./post_rule.sh ./rules/queries/query_with_threat_mapping.json
```
<details>
 <summary>Click here to see Response</summary>

```ts
{
  "actions": [],
  "author": [],
  "created_at": "2020-09-16T04:25:58.041Z",
  "created_by": "yo",
  "description": "Query with a threat mapping",
  "enabled": true,
  "exceptions_list": [],
  "false_positives": [],
  "from": "now-6m",
  "id": "f4226ab0-6f88-49c3-8f09-84cf5946ee7a",
  "immutable": false,
  "interval": "5m",
  "language": "kuery",
  "max_signals": 100,
  "name": "Query with a threat mapping",
  "output_index": ".siem-signals-hassanabad3-default",
  "query": "*:*",
  "references": [],
  "risk_score": 1,
  "risk_score_mapping": [],
  "rule_id": "threat-mapping",
  "severity": "high",
  "severity_mapping": [],
  "tags": [
    "tag_1",
    "tag_2"
  ],
  "threat": [],
  "threat_index": "mock-threat-list-1",
  "threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ],
  "threat_query": "*:*",
  "throttle": "no_actions",
  "to": "now",
  "type": "threat_match",
  "updated_at": "2020-09-16T04:25:58.051Z",
  "updated_by": "yo",
  "version": 1
}
```
</details>

**Structure**

You can see the rule structure in the file:
```ts
x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_threat_mapping.json
```
<details>
 <summary>Click here to see JSON</summary>

```ts
{
  "name": "Query with a threat mapping",
  "description": "Query with a threat mapping",
  "rule_id": "threat-mapping",
  "risk_score": 1,
  "severity": "high",
  "type": "threat_match",
  "query": "*:*",
  "tags": ["tag_1", "tag_2"],
  "threat_index": "mock-threat-list",
  "threat_query": "*:*",
  "threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ]
}
```

</details>

Structural elements that are new:

New type enum called "threat_match"
```ts
"type": "threat_match",
```

New `threat_index` string which can be set to a single threat index (This might change to an array in the near future before release):
```ts
"threat_index": "mock-threat-list"
```

New `threat_query` string which can be set any valid query to filter the threat list before executing the rule. This can be undefined, if you are only pushing in filters from the API.

```ts
"threat_query": "*:*",
```

New `threat_filters` array which can be set to any valid filter like `filters`. This can be `undefined` if you are only using the query from the API.
```ts
threat_filter": []
```

New `threat_mapping` array which can be set to a valid mapping between the threat list and the ECS list. This structure has an inner array called `entries` which represent a 2 level tree of 1st level OR elements followed by 2nd level AND elements.

For example, if you want to find all threat matches where ECS documents will match against some ${threatList} index where it would be like so:

<details>
 <summary>Click here to see array from the boolean</summary>

```ts
"threat_mapping": [
    {
      "entries": [
        {
          "field": "host.name",
          "type": "mapping",
          "value": "host.name"
        },
        {
          "field": "host.ip",
          "type": "mapping",
          "value": "host.ip"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "destination.ip",
          "type": "mapping",
          "value": "destination.ip"
        },
        {
          "field": "destination.port",
          "type": "mapping",
          "value": "destination.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.port",
          "type": "mapping",
          "value": "source.port"
        }
      ]
    },
    {
      "entries": [
        {
          "field": "source.ip",
          "type": "mapping",
          "value": "source.ip"
        }
      ]
    }
  ]
```

</details>

What that array represents in pseudo boolean logic is: 

<details>
 <summary>Click here to see pseduo logic</summary>

```ts
(host.name: ${threatList.host.name} AND host.ip: ${threatList.host.name}) OR
(destination.ip: ${threatList.destination.ip} AND destination.port: ${threatList.destination.port}) OR
(source.port ${threatList.source.port}) OR
(source.ip ${threatList.source.ip})
```

</details>

### Checklist

- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
@MindyRS MindyRS added the Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. label Sep 23, 2021
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-solution (Team: SecuritySolution)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Detection Rules Security Solution rules and Detection Engine release_note:enhancement Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. Team:SIEM v7.10.0 v7.11.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants