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