1 //--------------------------------------------------------------------------
2 // Copyright (C) 2016-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 // ips_dce_iface.cc author Maya Dagon <mdagon@cisco.com>
20 // based on work by Todd Wease
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <cerrno>
27
28 #include "detection/pattern_match_data.h"
29 #include "framework/module.h"
30 #include "framework/ips_option.h"
31 #include "framework/range.h"
32 #include "hash/hash_key_operations.h"
33 #include "profiler/profiler.h"
34 #include "target_based/snort_protocols.h"
35 #include "utils/util.h"
36
37 #include "dce_common.h"
38
39 using namespace snort;
40
41 //-------------------------------------------------------------------------
42 // dcerpc2 interface rule options
43 //-------------------------------------------------------------------------
44
45 #define DCE2_RTOKEN__ARG_SEP " \t" /* Rule option argument separator */
46 #define DCE2_RTOKEN__IFACE_SEP "-" /* Rule option interface separator */
47
48 #define DCE2_IFACE__LEN 36 /* counting the dashes */
49 #define DCE2_IFACE__TIME_LOW_LEN 8
50 #define DCE2_IFACE__TIME_MID_LEN 4
51 #define DCE2_IFACE__TIME_HIGH_LEN 4
52 #define DCE2_IFACE__CLOCK_SEQ_LEN 4
53 #define DCE2_IFACE__NODE_LEN 12
54
55 #define s_name "dce_iface"
56 #define s_help \
57 "detection option to check dcerpc interface"
58
59 static THREAD_LOCAL ProfileStats dce2_iface_perf_stats;
60
DCE2_ParseIface(char * token,Uuid * uuid)61 static bool DCE2_ParseIface(char* token, Uuid* uuid)
62 {
63 char* iface, * ifaceptr = nullptr;
64 char* if_hex, * if_hexptr = nullptr;
65 int num_pieces = 0;
66
67 /* Has to be a uuid in string format, e.g 4b324fc8-1670-01d3-1278-5a47bf6ee188
68 * Check the length */
69 if (strlen(token) != DCE2_IFACE__LEN)
70 return false;
71
72 /* Detach token */
73 iface = strtok_r(token, DCE2_RTOKEN__ARG_SEP, &ifaceptr);
74 if (iface == nullptr)
75 return false;
76
77 /* Cut into pieces separated by '-' */
78 if_hex = strtok_r(iface, DCE2_RTOKEN__IFACE_SEP, &if_hexptr);
79 if (if_hex == nullptr)
80 return false;
81
82 do
83 {
84 char* endptr;
85
86 switch (num_pieces)
87 {
88 case 0:
89 {
90 unsigned long int time_low;
91
92 if (strlen(if_hex) != DCE2_IFACE__TIME_LOW_LEN)
93 return false;
94
95 time_low = strtoul(if_hex, &endptr, 16);
96 if ((errno == ERANGE) || (*endptr != '\0'))
97 return false;
98
99 uuid->time_low = (uint32_t)time_low;
100 }
101
102 break;
103
104 case 1:
105 {
106 unsigned long int time_mid;
107
108 if (strlen(if_hex) != DCE2_IFACE__TIME_MID_LEN)
109 return false;
110
111 time_mid = strtoul(if_hex, &endptr, 16);
112 if ((errno == ERANGE) || (*endptr != '\0'))
113 return false;
114
115 /* Length check ensures 16 bit value */
116 uuid->time_mid = (uint16_t)time_mid;
117 }
118
119 break;
120
121 case 2:
122 {
123 unsigned long int time_high;
124
125 if (strlen(if_hex) != DCE2_IFACE__TIME_HIGH_LEN)
126 return false;
127
128 time_high = strtoul(if_hex, &endptr, 16);
129 if ((errno == ERANGE) || (*endptr != '\0'))
130 return false;
131
132 /* Length check ensures 16 bit value */
133 uuid->time_high_and_version = (uint16_t)time_high;
134 }
135
136 break;
137
138 case 3:
139 {
140 unsigned long int clock_seq_and_reserved, clock_seq_low;
141
142 if (strlen(if_hex) != DCE2_IFACE__CLOCK_SEQ_LEN)
143 return false;
144
145 /* Work backwards */
146 clock_seq_low = strtoul(&if_hex[2], &endptr, 16);
147 if ((errno == ERANGE) || (*endptr != '\0'))
148 return false;
149
150 uuid->clock_seq_low = (uint8_t)clock_seq_low;
151
152 /* Set third byte to null so we can _dpd.SnortStrtoul the first part */
153 if_hex[2] = '\x00';
154
155 clock_seq_and_reserved = strtoul(if_hex, &endptr, 16);
156 if ((errno == ERANGE) || (*endptr != '\0'))
157 return false;
158
159 uuid->clock_seq_and_reserved = (uint8_t)clock_seq_and_reserved;
160 }
161
162 break;
163
164 case 4:
165 {
166 int i, j;
167
168 if (strlen(if_hex) != DCE2_IFACE__NODE_LEN)
169 return false;
170
171 /* Walk back a byte at a time - 2 hex digits */
172 for (i = DCE2_IFACE__NODE_LEN - 2, j = sizeof(uuid->node) - 1;
173 (i >= 0) && (j >= 0);
174 i -= 2, j--)
175 {
176 /* Only giving strtoul 1 byte */
177 uuid->node[j] = (uint8_t)strtoul(&if_hex[i], &endptr, 16);
178 if ((errno == ERANGE) || (*endptr != '\0'))
179 return false;
180 if_hex[i] = '\0';
181 }
182 }
183 break;
184
185 default:
186 break;
187 }
188
189 num_pieces++;
190 }
191 while ((if_hex = strtok_r(nullptr, DCE2_RTOKEN__IFACE_SEP, &if_hexptr)) != nullptr);
192
193 if (num_pieces != 5)
194 return false;
195
196 /* Check for more arguments */
197 iface = strtok_r(nullptr, DCE2_RTOKEN__ARG_SEP, &ifaceptr);
198 if (iface != nullptr)
199 return false;
200
201 return true;
202 }
203
204 class Dce2IfaceOption : public IpsOption
205 {
206 public:
Dce2IfaceOption(const RangeCheck & iface_version,bool iface_any_frag,const Uuid & iface_uuid)207 Dce2IfaceOption(const RangeCheck& iface_version, bool iface_any_frag, const Uuid& iface_uuid) :
208 IpsOption(s_name), version(iface_version), any_frag(iface_any_frag), uuid(iface_uuid),
209 pmd(), alt_pmd()
210 {
211 pmd.set_literal();
212 alt_pmd.set_literal();
213 }
214
215 uint32_t hash() const override;
216 bool operator==(const IpsOption&) const override;
217 EvalStatus eval(Cursor&, Packet*) override;
218 PatternMatchData* get_pattern(SnortProtocolId snort_protocol_id, RuleDirection direction) override;
219 PatternMatchData* get_alternate_pattern() override;
220 ~Dce2IfaceOption() override;
221
222 private:
223 const RangeCheck version;
224 const bool any_frag;
225 const Uuid uuid;
226 PatternMatchData pmd;
227 PatternMatchData alt_pmd;
228 };
229
~Dce2IfaceOption()230 Dce2IfaceOption::~Dce2IfaceOption()
231 {
232 if ( pmd.pattern_buf)
233 {
234 snort_free(const_cast<char*>(pmd.pattern_buf));
235 }
236 if ( alt_pmd.pattern_buf)
237 {
238 snort_free(const_cast<char*>(alt_pmd.pattern_buf));
239 }
240 }
241
make_pattern_buffer(const Uuid & uuid,DceRpcBoFlag type)242 static char* make_pattern_buffer( const Uuid &uuid, DceRpcBoFlag type )
243 {
244 int index = 0;
245 char* pattern_buf = (char*)snort_alloc(sizeof(Uuid));
246
247 uint32_t time32 = DceRpcNtohl(&uuid.time_low, type);
248 memcpy(&pattern_buf[index], &time32, sizeof(uint32_t));
249 index += sizeof(uint32_t);
250
251 uint16_t time16 = DceRpcNtohs(&uuid.time_mid, type);
252 memcpy(&pattern_buf[index], &time16, sizeof(uint16_t));
253 index += sizeof(uint16_t);
254
255 time16 = DceRpcNtohs(&uuid.time_high_and_version, type);
256 memcpy(&pattern_buf[index], &time16, sizeof(uint16_t));
257 index += sizeof(uint16_t);
258
259 pattern_buf[index] = uuid.clock_seq_and_reserved;
260 index += sizeof(uint8_t);
261
262 pattern_buf[index] = uuid.clock_seq_low;
263 index += sizeof(uint8_t);
264
265 memcpy(&pattern_buf[index], uuid.node, 6);
266
267 return pattern_buf;
268 }
269
get_pattern(SnortProtocolId snort_protocol_id,RuleDirection direction)270 PatternMatchData* Dce2IfaceOption::get_pattern(SnortProtocolId snort_protocol_id, RuleDirection direction)
271 {
272 if (pmd.pattern_buf)
273 {
274 return &pmd;
275 }
276
277 if (snort_protocol_id == SNORT_PROTO_TCP)
278 {
279 const char client_fp[] = "\x05\x00\x00";
280 const char server_fp[] = "\x05\x00\x02";
281 const char no_dir_fp[] = "\x05\x00";
282
283 switch (direction)
284 {
285 case RULE_FROM_CLIENT:
286 pmd.pattern_size = 3;
287 pmd.pattern_buf = (char*)snort_alloc(pmd.pattern_size);
288 memcpy((void*)pmd.pattern_buf, client_fp, pmd.pattern_size);
289 break;
290
291 case RULE_FROM_SERVER:
292 pmd.pattern_size = 3;
293 pmd.pattern_buf = (char*)snort_alloc(pmd.pattern_size);
294 memcpy((void*)pmd.pattern_buf, server_fp, pmd.pattern_size);
295 break;
296
297 default:
298 pmd.pattern_size = 2;
299 pmd.pattern_buf = (char*)snort_alloc(pmd.pattern_size);
300 memcpy((void*)pmd.pattern_buf, no_dir_fp, pmd.pattern_size);
301 break;
302 }
303 return &pmd;
304 }
305 else if (snort_protocol_id == SNORT_PROTO_UDP)
306 {
307 pmd.pattern_buf = make_pattern_buffer( uuid, DCERPC_BO_FLAG__LITTLE_ENDIAN );
308 pmd.pattern_size = sizeof(Uuid);
309 alt_pmd.pattern_buf = make_pattern_buffer( uuid, DCERPC_BO_FLAG__BIG_ENDIAN );
310 alt_pmd.pattern_size = sizeof(Uuid);
311
312 return &pmd;
313 }
314
315 return nullptr;
316 }
317
get_alternate_pattern()318 PatternMatchData* Dce2IfaceOption::get_alternate_pattern()
319 {
320 if (alt_pmd.pattern_buf)
321 {
322 return &alt_pmd;
323 }
324
325 return nullptr;
326 }
327
hash() const328 uint32_t Dce2IfaceOption::hash() const
329 {
330 uint32_t a, b, c;
331
332 a = uuid.time_low;
333 b = (uuid.time_mid << 16) | (uuid.time_high_and_version);
334 c = (uuid.clock_seq_and_reserved << 24) |
335 (uuid.clock_seq_low << 16) |
336 (uuid.node[0] << 8) |
337 (uuid.node[1]);
338
339 mix(a, b, c);
340
341 a += (uuid.node[2] << 24) |
342 (uuid.node[3] << 16) |
343 (uuid.node[4] << 8) |
344 (uuid.node[5]);
345 b += version.hash();
346 c += any_frag;
347
348 mix(a, b, c);
349
350 c += IpsOption::hash();
351
352 finalize(a, b, c);
353
354 return c;
355 }
356
operator ==(const IpsOption & ips) const357 bool Dce2IfaceOption::operator==(const IpsOption& ips) const
358 {
359 // FIXIT-L
360 // Fast pattern is calculated only after the entire rule is parsed.
361 // The rule option can be mistaken as a duplicate because we don't take the fast pattern into
362 // account. Instead of comparing values, make sure it is the same object.
363 return this == &ips;
364 }
365
eval(Cursor &,Packet * p)366 IpsOption::EvalStatus Dce2IfaceOption::eval(Cursor&, Packet* p)
367 {
368 RuleProfile profile(dce2_iface_perf_stats);
369
370 if (p->dsize == 0)
371 {
372 return NO_MATCH;
373 }
374
375 if (DceContextData::is_noinspect(p))
376 {
377 return NO_MATCH;
378 }
379
380 DCE2_Roptions* ropts = DceContextData::get_current_ropts(p);
381
382 if ( !ropts )
383 return NO_MATCH;
384
385 if (ropts->first_frag == DCE2_SENTINEL)
386 {
387 return NO_MATCH;
388 }
389
390 if (!any_frag && !ropts->first_frag)
391 {
392 return NO_MATCH;
393 }
394
395 if (DCE2_UuidCompare((void*)&ropts->iface, &uuid) != 0)
396 {
397 return NO_MATCH;
398 }
399
400 if (version.is_set())
401 {
402 if (p->has_tcp_data())
403 {
404 if (!version.eval(ropts->iface_vers_maj))
405 {
406 return NO_MATCH;
407 }
408 }
409 else
410 {
411 if (!version.eval(ropts->iface_vers))
412 {
413 return NO_MATCH;
414 }
415 }
416 }
417
418 return MATCH;
419 }
420
421 //-------------------------------------------------------------------------
422 // dce2_iface module
423 //-------------------------------------------------------------------------
424
425 #define RANGE "0:"
426
427 static const Parameter s_params[] =
428 {
429 { "uuid", Parameter::PT_STRING, nullptr, nullptr,
430 "match given dcerpc uuid" },
431 { "version",Parameter::PT_INTERVAL, RANGE, nullptr,
432 "interface version" },
433 { "any_frag", Parameter::PT_IMPLIED, nullptr, nullptr,
434 "match on any fragment" },
435 { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
436 };
437
438 class Dce2IfaceModule : public Module
439 {
440 public:
Dce2IfaceModule()441 Dce2IfaceModule() : Module(s_name, s_help, s_params) { }
442
443 bool begin(const char*, int, SnortConfig*) override;
444 bool set(const char*, Value&, SnortConfig*) override;
445 ProfileStats* get_profile() const override;
446
get_usage() const447 Usage get_usage() const override
448 { return DETECT; }
449
450 public:
451 RangeCheck version;
452 bool any_frag;
453 Uuid uuid;
454 };
455
begin(const char *,int,SnortConfig *)456 bool Dce2IfaceModule::begin(const char*, int, SnortConfig*)
457 {
458 version.init();
459 any_frag = false;
460 return true;
461 }
462
set(const char *,Value & v,SnortConfig *)463 bool Dce2IfaceModule::set(const char*, Value& v, SnortConfig*)
464 {
465 if ( v.is("version") )
466 return version.validate(v.get_string(), RANGE);
467 else if ( v.is("any_frag") )
468 any_frag = true;
469 else if ( v.is("uuid") )
470 {
471 char* token = const_cast<char*>(v.get_string());
472 token = DCE2_PruneWhiteSpace(token);
473 return DCE2_ParseIface(token, &uuid);
474 }
475 return true;
476 }
477
get_profile() const478 ProfileStats* Dce2IfaceModule::get_profile() const
479 {
480 return &dce2_iface_perf_stats;
481 }
482
483 //-------------------------------------------------------------------------
484 // dce2_iface api
485 //-------------------------------------------------------------------------
486
dce2_iface_mod_ctor()487 static Module* dce2_iface_mod_ctor()
488 {
489 return new Dce2IfaceModule;
490 }
491
dce2_iface_mod_dtor(Module * m)492 static void dce2_iface_mod_dtor(Module* m)
493 {
494 delete m;
495 }
496
dce2_iface_ctor(Module * p,OptTreeNode *)497 static IpsOption* dce2_iface_ctor(Module* p, OptTreeNode*)
498 {
499 Dce2IfaceModule* m = (Dce2IfaceModule*)p;
500 return new Dce2IfaceOption(m->version, m->any_frag, m->uuid);
501 }
502
dce2_iface_dtor(IpsOption * p)503 static void dce2_iface_dtor(IpsOption* p)
504 {
505 delete p;
506 }
507
508 static const IpsApi dce2_iface_api =
509 {
510 {
511 PT_IPS_OPTION,
512 sizeof(IpsApi),
513 IPSAPI_VERSION,
514 0,
515 API_RESERVED,
516 API_OPTIONS,
517 s_name,
518 s_help,
519 dce2_iface_mod_ctor,
520 dce2_iface_mod_dtor
521 },
522 OPT_TYPE_DETECTION,
523 0, PROTO_BIT__TCP | PROTO_BIT__UDP,
524 nullptr,
525 nullptr,
526 nullptr,
527 nullptr,
528 dce2_iface_ctor,
529 dce2_iface_dtor,
530 nullptr
531 };
532
533 //-------------------------------------------------------------------------
534 // plugin
535 //-------------------------------------------------------------------------
536
537 // added to snort_plugins in dce2.cc
538 const BaseApi* ips_dce_iface = &dce2_iface_api.base;
539
540