1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18
19 // ps_module.cc author Russ Combs <rucombs@cisco.com>
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "ps_module.h"
26 #include "log/messages.h"
27 #include "main/snort.h"
28
29 #include <cassert>
30
31 using namespace snort;
32
33 //-------------------------------------------------------------------------
34 // port_scan params
35 //-------------------------------------------------------------------------
36
37 // order of protos and scans must match PS_* flags
38 #define protos \
39 "tcp | udp | icmp | ip | all"
40
41 #define scan_types \
42 "portscan | portsweep | decoy_portscan | distributed_portscan | all"
43
44 static const Parameter scan_params[] =
45 {
46 { "scans", Parameter::PT_INT, "0:65535", "100",
47 "scan attempts" },
48
49 { "rejects", Parameter::PT_INT, "0:65535", "15",
50 "scan attempts with negative response" },
51
52 { "nets", Parameter::PT_INT, "0:65535", "25",
53 "number of times address changed from prior attempt" },
54
55 { "ports", Parameter::PT_INT, "0:65535", "25",
56 "number of times port (or proto) changed from prior attempt" },
57
58 { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
59 };
60
61 static const Parameter ps_params[] =
62 {
63 { "memcap", Parameter::PT_INT, "1024:maxSZ", "10485760",
64 "maximum tracker memory in bytes" },
65
66 { "protos", Parameter::PT_MULTI, protos, "all",
67 "choose the protocols to monitor" },
68
69 { "scan_types", Parameter::PT_MULTI, scan_types, "all",
70 "choose type of scans to look for" },
71
72 { "watch_ip", Parameter::PT_STRING, nullptr, nullptr,
73 "list of CIDRs with optional ports to watch" },
74
75 { "ignore_scanners", Parameter::PT_STRING, nullptr, nullptr,
76 "list of CIDRs with optional ports to ignore if the source of scan alerts" },
77
78 { "ignore_scanned", Parameter::PT_STRING, nullptr, nullptr,
79 "list of CIDRs with optional ports to ignore if the destination of scan alerts" },
80
81 { "alert_all", Parameter::PT_BOOL, nullptr, "false",
82 "alert on all events over threshold within window if true; else alert on first only" },
83
84 { "include_midstream", Parameter::PT_BOOL, nullptr, "false",
85 "list of CIDRs with optional ports" },
86
87 { "tcp_ports", Parameter::PT_TABLE, scan_params, nullptr,
88 "TCP port scan configuration (one-to-one)" },
89
90 { "tcp_decoy", Parameter::PT_TABLE, scan_params, nullptr,
91 "TCP decoy scan configuration (one-to-one decoy)" },
92
93 { "tcp_sweep", Parameter::PT_TABLE, scan_params, nullptr,
94 "TCP sweep scan configuration (one-to-many)" },
95
96 { "tcp_dist", Parameter::PT_TABLE, scan_params, nullptr,
97 "TCP distributed scan configuration (many-to-one)" },
98
99 { "udp_ports", Parameter::PT_TABLE, scan_params, nullptr,
100 "UDP port scan configuration (one-to-one)" },
101
102 { "udp_decoy", Parameter::PT_TABLE, scan_params, nullptr,
103 "UDP decoy scan configuration (one-to-one)" },
104
105 { "udp_sweep", Parameter::PT_TABLE, scan_params, nullptr,
106 "UDP sweep scan configuration (one-to-many)" },
107
108 { "udp_dist", Parameter::PT_TABLE, scan_params, nullptr,
109 "UDP distributed scan configuration (many-to-one)" },
110
111 { "ip_proto", Parameter::PT_TABLE, scan_params, nullptr,
112 "IP protocol scan configuration (one-to-one)" },
113
114 { "ip_decoy", Parameter::PT_TABLE, scan_params, nullptr,
115 "IP decoy scan configuration (one-to-one decoy)" },
116
117 { "ip_sweep", Parameter::PT_TABLE, scan_params, nullptr,
118 "ip sweep scan configuration (one-to-many)" },
119
120 { "ip_dist", Parameter::PT_TABLE, scan_params, nullptr,
121 "IP distributed scan configuration (many-to-one)" },
122
123 { "icmp_sweep", Parameter::PT_TABLE, scan_params, nullptr,
124 "ICMP sweep scan configuration (one-to-many)" },
125
126 { "tcp_window", Parameter::PT_INT, "0:max32", "0",
127 "detection interval for all TCP scans" },
128
129 { "udp_window", Parameter::PT_INT, "0:max32", "0",
130 "detection interval for all UDP scans" },
131
132 { "ip_window", Parameter::PT_INT, "0:max32", "0",
133 "detection interval for all IP scans" },
134
135 { "icmp_window", Parameter::PT_INT, "0:max32", "0",
136 "detection interval for all ICMP scans" },
137
138 { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
139 };
140
141 //-------------------------------------------------------------------------
142 // port_scan rules
143 //-------------------------------------------------------------------------
144
145 static const RuleMap port_scan_rules[] =
146 {
147 { PSNG_TCP_PORTSCAN, PSNG_TCP_PORTSCAN_STR },
148 { PSNG_TCP_DECOY_PORTSCAN, PSNG_TCP_DECOY_PORTSCAN_STR },
149 { PSNG_TCP_PORTSWEEP, PSNG_TCP_PORTSWEEP_STR },
150 { PSNG_TCP_DISTRIBUTED_PORTSCAN, PSNG_TCP_DISTRIBUTED_PORTSCAN_STR },
151 { PSNG_TCP_FILTERED_PORTSCAN, PSNG_TCP_FILTERED_PORTSCAN_STR },
152 { PSNG_TCP_FILTERED_DECOY_PORTSCAN, PSNG_TCP_FILTERED_DECOY_PORTSCAN_STR },
153 { PSNG_TCP_PORTSWEEP_FILTERED, PSNG_TCP_PORTSWEEP_FILTERED_STR },
154 { PSNG_TCP_FILTERED_DISTRIBUTED_PORTSCAN,
155 PSNG_TCP_FILTERED_DISTRIBUTED_PORTSCAN_STR },
156
157 { PSNG_IP_PORTSCAN, PSNG_IP_PORTSCAN_STR },
158 { PSNG_IP_DECOY_PORTSCAN, PSNG_IP_DECOY_PORTSCAN_STR },
159 { PSNG_IP_PORTSWEEP, PSNG_IP_PORTSWEEP_STR },
160 { PSNG_IP_DISTRIBUTED_PORTSCAN, PSNG_IP_DISTRIBUTED_PORTSCAN_STR },
161 { PSNG_IP_FILTERED_PORTSCAN, PSNG_IP_FILTERED_PORTSCAN_STR },
162 { PSNG_IP_FILTERED_DECOY_PORTSCAN, PSNG_IP_FILTERED_DECOY_PORTSCAN_STR },
163 { PSNG_IP_PORTSWEEP_FILTERED, PSNG_IP_PORTSWEEP_FILTERED_STR },
164 { PSNG_IP_FILTERED_DISTRIBUTED_PORTSCAN, PSNG_IP_FILTERED_DISTRIBUTED_PORTSCAN_STR },
165
166 { PSNG_UDP_PORTSCAN, PSNG_UDP_PORTSCAN_STR },
167 { PSNG_UDP_DECOY_PORTSCAN, PSNG_UDP_DECOY_PORTSCAN_STR },
168 { PSNG_UDP_PORTSWEEP, PSNG_UDP_PORTSWEEP_STR },
169 { PSNG_UDP_DISTRIBUTED_PORTSCAN, PSNG_UDP_DISTRIBUTED_PORTSCAN_STR },
170 { PSNG_UDP_FILTERED_PORTSCAN, PSNG_UDP_FILTERED_PORTSCAN_STR },
171 { PSNG_UDP_FILTERED_DECOY_PORTSCAN, PSNG_UDP_FILTERED_DECOY_PORTSCAN_STR },
172 { PSNG_UDP_PORTSWEEP_FILTERED, PSNG_UDP_PORTSWEEP_FILTERED_STR },
173 { PSNG_UDP_FILTERED_DISTRIBUTED_PORTSCAN, PSNG_UDP_FILTERED_DISTRIBUTED_PORTSCAN_STR },
174
175 { PSNG_ICMP_PORTSWEEP, PSNG_ICMP_PORTSWEEP_STR },
176 { PSNG_ICMP_PORTSWEEP_FILTERED, PSNG_ICMP_PORTSWEEP_FILTERED_STR },
177
178 { PSNG_OPEN_PORT, PSNG_OPEN_PORT_STR },
179
180 { 0, nullptr }
181 };
182
183 //-------------------------------------------------------------------------
184 // port_scan module
185 //-------------------------------------------------------------------------
186
PortScanModule()187 PortScanModule::PortScanModule() :
188 Module(PS_NAME, PS_HELP, ps_params)
189 {
190 config = nullptr;
191 }
192
~PortScanModule()193 PortScanModule::~PortScanModule()
194 {
195 if ( config )
196 delete config;
197 }
198
get_profile() const199 ProfileStats* PortScanModule::get_profile() const
200 { return &psPerfStats; }
201
get_pegs() const202 const PegInfo* PortScanModule::get_pegs() const
203 { return ps_module_pegs; }
204
get_counts() const205 PegCount* PortScanModule::get_counts() const
206 { return (PegCount*)&spstats; }
207
get_rules() const208 const RuleMap* PortScanModule::get_rules() const
209 { return port_scan_rules; }
210
begin(const char * fqn,int,SnortConfig *)211 bool PortScanModule::begin(const char* fqn, int, SnortConfig*)
212 {
213 if ( !config )
214 config = new PortscanConfig;
215
216 else if ( strcmp(fqn, "port_scan") )
217 return false;
218
219 return true;
220 }
221
222 //-------------------------------------------------------------------------
223 // FIXIT-L ipset_parse() format must be changed to remove comma
224 // separators between tokens which means using something other than
225 // space between CIDR and port. the current format is:
226 // CIDR[ ports][,CIDR[ ports]]*
227 // ports is either a single port or a range (num-num)
228 // note that the current code has a parsing bug:
229 // 3.4.5.6/16 7 works but 3.4.5.6 7 does not.
230 // a possible new format is:
231 // CIDR[#ports][ CIDR[#ports]]*
232 // also note that classic Snort address lists appear to be allowed which
233 // will cause problems with Lua syntax if [[ and ]] are present. must be
234 // [ [ and ] ].
235 // consult RFC 5952 for ideas.
236 //-------------------------------------------------------------------------
set(const char * fqn,Value & v,SnortConfig *)237 bool PortScanModule::set(const char* fqn, Value& v, SnortConfig*)
238 {
239 if ( v.is("memcap") )
240 config->memcap = v.get_size();
241
242 else if ( v.is("protos") )
243 {
244 unsigned u = v.get_uint32();
245 if ( u & (PS_PROTO_ALL+1) )
246 u = PS_PROTO_ALL;
247 config->detect_scans = u;
248 }
249 else if ( v.is("scan_types") )
250 {
251 unsigned u = v.get_uint32();
252 if ( u & (PS_TYPE_ALL+1) )
253 u = PS_TYPE_ALL;
254 config->detect_scan_type = u;
255 }
256 else if ( v.is("alert_all") )
257 config->alert_all = v.get_bool();
258
259 else if ( v.is("include_midstream") )
260 config->include_midstream = v.get_bool();
261
262 else if ( v.is("watch_ip") )
263 {
264 IPSET*& ips = config->watch_ip;
265 ips = ipset_new();
266 if ( !ips || ipset_parse(ips, v.get_string()) )
267 return false;
268 }
269 else if ( v.is("ignore_scanners") )
270 {
271 IPSET*& ips = config->ignore_scanners;
272 ips = ipset_new();
273 if ( !ips || ipset_parse(ips, v.get_string()) )
274 return false;
275 }
276 else if ( v.is("ignore_scanned") )
277 {
278 IPSET*& ips = config->ignore_scanned;
279 ips = ipset_new();
280 if ( !ips || ipset_parse(ips, v.get_string()) )
281 return false;
282 }
283 else if ( v.is("scans") )
284 {
285 if ( auto p = get_alert_conf(fqn) )
286 p->connection_count = v.get_uint16();
287 else
288 return false;
289 }
290 else if ( v.is("rejects") )
291 {
292 if ( auto p = get_alert_conf(fqn) )
293 p->priority_count = v.get_uint16();
294 else
295 return false;
296 }
297 else if ( v.is("nets") )
298 {
299 if ( auto p = get_alert_conf(fqn) )
300 p->u_ip_count = v.get_uint16();
301 else
302 return false;
303 }
304 else if ( v.is("ports") )
305 {
306 if ( auto p = get_alert_conf(fqn) )
307 p->u_port_count = v.get_uint16();
308 else
309 return false;
310 }
311 else if ( v.is("tcp_window") )
312 config->tcp_window = v.get_uint32();
313
314 else if ( v.is("udp_window") )
315 config->udp_window = v.get_uint32();
316
317 else if ( v.is("ip_window") )
318 config->ip_window = v.get_uint32();
319
320 else if ( v.is("icmp_window") )
321 config->icmp_window = v.get_uint32();
322
323 return true;
324 }
325
end(const char * fqn,int,SnortConfig * sc)326 bool PortScanModule::end(const char* fqn, int, SnortConfig* sc)
327 {
328 if ( Snort::is_reloading() && strcmp(fqn, "port_scan") == 0 )
329 sc->register_reload_resource_tuner(new PortScanReloadTuner(config->memcap));
330 return true;
331 }
332
get_alert_conf(const char * fqn)333 PS_ALERT_CONF* PortScanModule::get_alert_conf(const char* fqn)
334 {
335 if ( !strncmp(fqn, "port_scan.tcp_ports", 19) )
336 return &config->tcp_ports;
337
338 else if ( !strncmp(fqn, "port_scan.tcp_decoy", 19) )
339 return &config->tcp_decoy;
340
341 else if ( !strncmp(fqn, "port_scan.tcp_sweep", 19) )
342 return &config->tcp_sweep;
343
344 else if ( !strncmp(fqn, "port_scan.tcp_dist", 18) )
345 return &config->tcp_dist;
346
347 else if ( !strncmp(fqn, "port_scan.udp_ports", 19) )
348 return &config->udp_ports;
349
350 else if ( !strncmp(fqn, "port_scan.udp_decoy", 19) )
351 return &config->udp_decoy;
352
353 else if ( !strncmp(fqn, "port_scan.udp_sweep", 19) )
354 return &config->udp_sweep;
355
356 else if ( !strncmp(fqn, "port_scan.udp_dist", 18) )
357 return &config->udp_dist;
358
359 else if ( !strncmp(fqn, "port_scan.ip_proto", 18) )
360 return &config->ip_proto;
361
362 else if ( !strncmp(fqn, "port_scan.ip_decoy", 18) )
363 return &config->ip_decoy;
364
365 else if ( !strncmp(fqn, "port_scan.ip_sweep", 18) )
366 return &config->ip_sweep;
367
368 else if ( !strncmp(fqn, "port_scan.ip_dist", 17) )
369 return &config->ip_dist;
370
371 else if ( !strncmp(fqn, "port_scan.icmp_sweep", 20) )
372 return &config->icmp_sweep;
373
374 return nullptr;
375 }
376
get_data()377 PortscanConfig* PortScanModule::get_data()
378 {
379 PortscanConfig* tmp = config;
380 config = nullptr;
381 return tmp;
382 }
383