Skip to content
This repository was archived by the owner on Nov 3, 2021. It is now read-only.

Commit c8a9d94

Browse files
authored
Merge pull request #1473 from mozilla/ldap_bruteforce_alert
Add LDAP bruteforce alert
2 parents 1e7d2fa + ac62582 commit c8a9d94

3 files changed

Lines changed: 151 additions & 0 deletions

File tree

alerts/ldap_bruteforce.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[options]
2+
threshold_count = 1
3+
search_depth_min = 60
4+
host_exclusions = foo.example.com,bar.example.com

alerts/ldap_bruteforce.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env python
2+
3+
# This Source Code Form is subject to the terms of the Mozilla Public
4+
# License, v. 2.0. If a copy of the MPL was not distributed with this
5+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
# Copyright (c) 2014 Mozilla Corporation
7+
8+
9+
from lib.alerttask import AlertTask
10+
from mozdef_util.query_models import SearchQuery, TermMatch
11+
12+
13+
class AlertLdapBruteforce(AlertTask):
14+
def main(self):
15+
self.parse_config('ldap_bruteforce.conf', ['threshold_count', 'search_depth_min', 'host_exclusions'])
16+
search_query = SearchQuery(minutes=int(self.config.search_depth_min))
17+
search_query.add_must_not(TermMatch('details.user', ''))
18+
search_query.add_must([
19+
TermMatch('category', 'ldap'),
20+
TermMatch('details.response.error', 'LDAP_INVALID_CREDENTIALS'),
21+
])
22+
23+
for host_exclusion in self.config.host_exclusions.split(","):
24+
search_query.add_must_not([TermMatch("details.server", host_exclusion)])
25+
26+
self.filtersManual(search_query)
27+
self.searchEventsAggregated('details.user', samplesLimit=10)
28+
self.walkAggregations(threshold=int(self.config.threshold_count))
29+
30+
def onAggregation(self, aggreg):
31+
category = 'ldap'
32+
tags = ['ldap']
33+
severity = 'WARNING'
34+
client_list = set()
35+
36+
for event in aggreg['allevents']:
37+
client_list.add(event['_source']['details']['client'])
38+
39+
summary = 'LDAP Bruteforce Attack in Progress against user ({0}) from the following source ip(s): {1}'.format(
40+
aggreg['value'],
41+
", ".join(sorted(client_list)[:10])
42+
)
43+
if len(client_list) >= 10:
44+
summary += '...'
45+
46+
return self.createAlertDict(summary, category, tags, aggreg['events'], severity)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# This Source Code Form is subject to the terms of the Mozilla Public
2+
# License, v. 2.0. If a copy of the MPL was not distributed with this
3+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
# Copyright (c) 2017 Mozilla Corporation
5+
from .positive_alert_test_case import PositiveAlertTestCase
6+
from .negative_alert_test_case import NegativeAlertTestCase
7+
8+
from .alert_test_suite import AlertTestSuite
9+
10+
11+
class TestAlertLdapBruteforce(AlertTestSuite):
12+
alert_filename = "ldap_bruteforce"
13+
# This event is the default positive event that will cause the
14+
# alert to trigger
15+
default_event = {
16+
"_source": {
17+
"category": "ldap",
18+
"details": {
19+
"client": "1.2.3.4",
20+
"requests": [
21+
{
22+
'verb': 'BIND',
23+
'details': [
24+
'method=128'
25+
'dn="mail=jsmith@example.com,o=com,dc=example"',
26+
]
27+
}
28+
],
29+
"server": "ldap.example.com",
30+
"user": "jsmith@example.com",
31+
"response": {
32+
"error": 'LDAP_INVALID_CREDENTIALS',
33+
}
34+
}
35+
}
36+
}
37+
38+
# This alert is the expected result from running this task
39+
default_alert = {
40+
"category": "ldap",
41+
"tags": ["ldap"],
42+
"severity": "WARNING",
43+
"summary": "LDAP Bruteforce Attack in Progress against user (jsmith@example.com) from the following source ip(s): 1.2.3.4",
44+
}
45+
46+
# This alert is the expected result from this task against multiple matching events
47+
default_alert_aggregated = AlertTestSuite.copy(default_alert)
48+
default_alert_aggregated[
49+
"summary"
50+
] = "LDAP Bruteforce Attack in Progress against user (jsmith@example.com) from the following source ip(s): 1.2.3.4"
51+
52+
test_cases = []
53+
54+
test_cases.append(
55+
PositiveAlertTestCase(
56+
description="Positive test with default events and default alert expected",
57+
events=AlertTestSuite.create_events(default_event, 10),
58+
expected_alert=default_alert,
59+
)
60+
)
61+
62+
test_cases.append(
63+
PositiveAlertTestCase(
64+
description="Positive test with default events and default alert expected - dedup",
65+
events=AlertTestSuite.create_events(default_event, 2),
66+
expected_alert=default_alert,
67+
)
68+
)
69+
70+
events = AlertTestSuite.create_events(default_event, 10)
71+
for event in events:
72+
event["_source"]["details"]["response"]["error"] = "LDAP_SUCCESS"
73+
test_cases.append(
74+
NegativeAlertTestCase(
75+
description="Negative test with default negative event", events=events
76+
)
77+
)
78+
79+
events = AlertTestSuite.create_events(default_event, 10)
80+
for event in events:
81+
event["_source"]["category"] = "bad"
82+
test_cases.append(
83+
NegativeAlertTestCase(
84+
description="Negative test case with events with incorrect category",
85+
events=events,
86+
)
87+
)
88+
89+
events = AlertTestSuite.create_events(default_event, 10)
90+
for event in events:
91+
event["_source"][
92+
"utctimestamp"
93+
] = AlertTestSuite.subtract_from_timestamp_lambda({"minutes": 241})
94+
event["_source"][
95+
"receivedtimestamp"
96+
] = AlertTestSuite.subtract_from_timestamp_lambda({"minutes": 241})
97+
test_cases.append(
98+
NegativeAlertTestCase(
99+
description="Negative test case with old timestamp", events=events
100+
)
101+
)

0 commit comments

Comments
 (0)