forked from michalpurzynski/bro-gramming
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bugzilla_bruteforce.bro
executable file
·98 lines (87 loc) · 4.27 KB
/
bugzilla_bruteforce.bro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
module BugzBruteforcing;
export {
redef enum Notice::Type += {
## Indicates that a host performing HTTP requests leading to
## excessive HTTP auth errors was detected.
HTTP_BugzBruteforcing_Attacker,
## Indicates that a host was seen to respond excessive HTTP
## auth errors. This is tracked by IP address as opposed to
## hostname.
HTTP_BugzBruteforcing_Victim,
};
const ports_int: set[port] = { 80/tcp, 443/tcp } &redef;
redef enum Log::ID += { LOG };
# Let's tag the http item
redef enum HTTP::Tags += {
## HTTP status code 401, describing a HTTP auth error
HTTP_AUTH_ERROR,
## HTTP describing a successful HTTP auth
HTTP_AUTH_SUCCESS,
};
type Info: record {
ts: time &log;
uid: string &log;
id: conn_id &log &optional;
cluster_client_ip: string &log &optional;
status_code: count &log &optional;
host: string &log &optional;
uri: string &log &optional;
username: string &log &optional;
auth_success: bool &log &optional;
};
const auth_errors_threshold: double = 10.0 &redef;
const auth_errors_interval = 15min &redef;
}
event bro_init()
{
Log::create_stream(BugzBruteforcing::LOG, [$columns=Info]);
# HTTP auth errors for requests FROM the same host
local r1: SumStats::Reducer = [$stream="bugz.auth_errors.attacker", $apply=set(SumStats::SUM)];
SumStats::create([$name="bugz-http-errors-attackers",
$epoch=auth_errors_interval,
$reducers=set(r1),
$threshold_val(key: SumStats::Key, result: SumStats::Result) = {
return result["bugz.auth_errors.attacker"]$sum;
},
$threshold=auth_errors_threshold,
$threshold_crossed(key: SumStats::Key, result: SumStats::Result) = {
NOTICE([$note=HTTP_BugzBruteforcing_Attacker,
$msg=fmt("HTTP auth bruteforcing from attacker %s", key$host),
$sub=fmt("%.0f auth failed in %s", result["bugz.auth_errors.attacker"]$sum, auth_errors_interval),
$src=key$host,
$n=to_count(fmt("%.0f", result["bugz.auth_errors.attacker"]$sum))
]);
}]);
# HTTP errors for requests TO the same host
local r2: SumStats::Reducer = [$stream="bugz.auth_errors.victim", $apply=set(SumStats::SUM)];
SumStats::create([$name="bugz-http-errors-victims",
$epoch=auth_errors_interval,
$reducers=set(r2),
$threshold_val(key: SumStats::Key, result: SumStats::Result) = {
return result["bugz.auth_errors.victim"]$sum;
},
$threshold=auth_errors_threshold,
$threshold_crossed(key: SumStats::Key, result: SumStats::Result) = {
NOTICE([$note=HTTP_BugzBruteforcing_Victim,
$msg=fmt("HTTP auth bruteforcing to victim %s", key$str),
$sub=fmt("%s auth failed in %s", result["bugz.auth_errors.victim"]$sum, auth_errors_interval),
$n=to_count(fmt("%.0f", result["bugz.auth_errors.victim"]$sum))
]);
}]);
}
event http_entity_data(c: connection, is_orig: bool, length: count, data: string) {
# if (!Site::is_local_addr(c$id$resp_h))
# return;
if (c$id$resp_p !in ports_int)
return;
if (!c$http?$method || c$http$method != "POST")
return;
if (/(index|show_bug)\.cgi/ !in c$http$uri)
return;
local meta_table: string = sub_bytes(data, 250, 260);
if (/Invalid\ Username\ Or\ Password/ in meta_table) {
SumStats::observe("bugz.auth_errors.attacker", [$host=to_addr(c$http$cluster_client_ip)], SumStats::Observation($num=1));
SumStats::observe("bugz.auth_errors.victim", [$str=c$http$host], SumStats::Observation($num=1));
add c$http$tags[HTTP_AUTH_ERROR];
}
}