Development Guide¶
A guide to help with ACE development.
Collectors¶
Parsing Data¶
ACE has a couple built in classes that can aid you in parsing and transforming data into a format that can be used to submit an alert to your collector.
RegexObservableParserGroup
The RegexObservableParserGroup
can be used to add various regular
expressions
``` {.sourceCode .python} from saq.constants import ( F_URL, F_IPV4, F_IPV4_CONVERSATION, F_IPV4_FULL_CONVERSATION, DIRECTIVE_CRAWL, ) from saq.util import RegexObservableParserGroup
sample_log = "dst_ip: 10.0.0.2, port: 8080, src_ip: 10.0.0.1, port: 5555, url: https://hello.local/malicious/payload.exe\n"
Tags you add here will be applied to all observables you find unless¶
you override the tags value at the individual parser being added.¶
parser_group = RegexObservableParserGroup(tags=['my_custom_parser'])
This will capture ALL matching strings. If any are duplicates,¶
they will be filtered out when you generate the observables.¶
parser_group.add(r'ip: ([0-9.]+)', F_IPV4)
You can also add multiple capture groups and determine in which¶
Order the items are extracted.¶
For example, the source IP comes second in our test string, but the¶
F_IPV4_CONVERSATION is in this format: src-ip_dst-ip.¶
Note the capture groups are in reverse order to accomodate:¶
parser_group.add(r'ip: ([0-9.]+).*ip: ([0-9.]+)', F_IPV4_CONVERSATION, capture_groups=[2, 1])
You can also change the delimiter for how all the capture groups¶
are joined together. For example, the F_IPV4_FULL_CONVERSATION¶
is delimited by colons.¶
parser_group.add( r'dst_ip: ([0-9.]+), port: ([0-9]+), src_ip: ([0-9.]+), port: ([0-9]+)', F_IPV4_FULL_CONVERSATION, delimiter=':', capture_groups=[3, 4, 1, 2] )
You can also add directives to control analysis/actions taken on your¶
observable.¶
parser_group.add(r'url: ([^\n]+)', F_URL, directives=[DIRECTIVE_CRAWL])
Once you've added your parsers, you can parse the data:
``` {.sourceCode .python}
parser_group.parse_content(sample_log)
Then you can access the observables:
``` {.sourceCode .python} observables = parser_group.observables
The extractions from this example would create the following list of
dictionairies which are in an appropriate format to be submitted to the
collector:
``` {.sourceCode .python}
[
{
"type": "ipv4",
"value": "10.0.0.2",
"tags": [
"my_custom_parser"
],
"directives": []
},
{
"type": "ipv4",
"value": "10.0.0.1",
"tags": [
"my_custom_parser"
],
"directives": []
},
{
"type": "ipv4_conversation",
"value": "10.0.0.1_10.0.0.2",
"tags": [
"my_custom_parser"
],
"directives": []
},
{
"type": "ipv4_full_conversation",
"value": "10.0.0.1:5555:10.0.0.2:8080",
"tags": [
"my_custom_parser"
],
"directives": []
},
{
"type": "url",
"value": "https://hello.local/malicious/payload.exe",
"tags": [
"my_custom_parser"
],
"directives": [
"crawl"
]
}
]
What if you've created your own observable type? Or maybe you want to change the way the parser groups work.
You can make subclass the saq.util.RegexObservableParser
, override the
parsing logic, and then pass it into the parser group:
``` {.sourceCode .python} from saq.constants import F_CUSTOM_TYPE from saq.util import RegexObservableParserGroup, RegexObservableParser
class MyParser(RegexObservableParser):
def init(self, args, kwargs):
super().init(args, **kwargs)
# override the RegexObservableParser.parse()
method
def parse(self, text):
# My custom parsing logic
pass
parser_group = RegexObservableParserGroup()
parser_group.add(r'ip: ([0-9.]+)', override_class=MyParser)
parser_group.parse_content(my_log)
When you're ready to submit to the collector:
``` {.sourceCode .python}
observables = parser_group.observables
from saq.collectors import Submission
from saq.constanants import ANALYSIS_MODE_CORRELATION
submission = Submission(
description="My custom alert",
analysis_mode=ANALYSIS_MODE_CORRELATION,
tool = 'my custom tool',
tool_instance = 'my custom tool instance',
type = 'custom_type',
event_time = 'datetime_from_alert',
details = [],
observables = observables,
tags=[],
files=[],
)