suricata
detect-rpc.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2020 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
22 *
23 * Implements RPC keyword
24 */
25
26#include "suricata-common.h"
27#include "decode.h"
28
29#include "detect.h"
30#include "detect-rpc.h"
31#include "detect-parse.h"
32#include "detect-engine.h"
33#include "detect-engine-mpm.h"
36#include "detect-engine-build.h"
37
38#include "util-unittest.h"
40#include "util-debug.h"
41#include "util-byte.h"
42
43/**
44 * \brief Regex for parsing our rpc options
45 */
46#define PARSE_REGEX "^\\s*([0-9]{0,10})\\s*(?:,\\s*([0-9]{0,10}|[*])\\s*(?:,\\s*([0-9]{0,10}|[*]))?)?\\s*$"
47
48static DetectParseRegex parse_regex;
49
50static int DetectRpcMatch (DetectEngineThreadCtx *, Packet *,
51 const Signature *, const SigMatchCtx *);
52static int DetectRpcSetup (DetectEngineCtx *, Signature *, const char *);
53#ifdef UNITTESTS
54static void DetectRpcRegisterTests(void);
55#endif
56void DetectRpcFree(DetectEngineCtx *, void *);
57
58/**
59 * \brief Registration function for rpc keyword
60 */
62{
64 sigmatch_table[DETECT_RPC].desc = "match RPC procedure numbers and RPC version";
65 sigmatch_table[DETECT_RPC].url = "/rules/payload-keywords.html#rpc";
66 sigmatch_table[DETECT_RPC].Match = DetectRpcMatch;
67 sigmatch_table[DETECT_RPC].Setup = DetectRpcSetup;
69#ifdef UNITTESTS
70 sigmatch_table[DETECT_RPC].RegisterTests = DetectRpcRegisterTests;
71#endif
73}
74
75/*
76 * returns 0: no match
77 * 1: match
78 * -1: error
79 */
80
81/**
82 * \brief This function is used to match rpc request set on a packet with those passed via rpc
83 *
84 * \param t pointer to thread vars
85 * \param det_ctx pointer to the pattern matcher thread
86 * \param p pointer to the current packet
87 * \param m pointer to the sigmatch that we will cast into DetectRpcData
88 *
89 * \retval 0 no match
90 * \retval 1 match
91 */
92static int DetectRpcMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
93 const Signature *s, const SigMatchCtx *ctx)
94{
95 /* PrintRawDataFp(stdout, p->payload, p->payload_len); */
96 const DetectRpcData *rd = (const DetectRpcData *)ctx;
97 char *rpcmsg = (char *)p->payload;
98
99 if (PacketIsTCP(p)) {
100 /* if Rpc msg too small */
101 if (p->payload_len < 28) {
102 SCLogDebug("TCP packet to small for the rpc msg (%u)", p->payload_len);
103 return 0;
104 }
105 rpcmsg += 4;
106 } else if (PacketIsUDP(p)) {
107 /* if Rpc msg too small */
108 if (p->payload_len < 24) {
109 SCLogDebug("UDP packet to small for the rpc msg (%u)", p->payload_len);
110 return 0;
111 }
112 } else {
113 SCLogDebug("No valid proto for the rpc message");
114 return 0;
115 }
116
117 /* Point through the rpc msg structure. Use SCNtohl() to compare values */
118 RpcMsg *msg = (RpcMsg *)rpcmsg;
119
120 /* If its not a call, no match */
121 if (SCNtohl(msg->type) != 0) {
122 SCLogDebug("RPC message type is not a call");
123 return 0;
124 }
125
126 if (SCNtohl(msg->prog) != rd->program)
127 return 0;
128
129 if ((rd->flags & DETECT_RPC_CHECK_VERSION) && SCNtohl(msg->vers) != rd->program_version)
130 return 0;
131
132 if ((rd->flags & DETECT_RPC_CHECK_PROCEDURE) && SCNtohl(msg->proc) != rd->procedure)
133 return 0;
134
135 SCLogDebug("prog:%u pver:%u proc:%u matched", SCNtohl(msg->prog), SCNtohl(msg->vers), SCNtohl(msg->proc));
136 return 1;
137}
138
139/**
140 * \brief This function is used to parse rpc options passed via rpc keyword
141 *
142 * \param de_ctx Pointer to the detection engine context
143 * \param rpcstr Pointer to the user provided rpc options
144 *
145 * \retval rd pointer to DetectRpcData on success
146 * \retval NULL on failure
147 */
148static DetectRpcData *DetectRpcParse (DetectEngineCtx *de_ctx, const char *rpcstr)
149{
150 DetectRpcData *rd = NULL;
151 char *args[3] = {NULL,NULL,NULL};
152 int res = 0;
153 size_t pcre2_len;
154
155 pcre2_match_data *match = NULL;
156 int ret = DetectParsePcreExec(&parse_regex, &match, rpcstr, 0, 0);
157 if (ret < 1 || ret > 4) {
158 SCLogError("parse error, ret %" PRId32 ", string %s", ret, rpcstr);
159 goto error;
160 }
161
162 if (ret > 1) {
163 const char *str_ptr;
164 res = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
165 if (res < 0) {
166 SCLogError("pcre2_substring_get_bynumber failed");
167 goto error;
168 }
169 args[0] = (char *)str_ptr;
170
171 if (ret > 2) {
172 res = pcre2_substring_get_bynumber(match, 2, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
173 if (res < 0) {
174 SCLogError("pcre2_substring_get_bynumber failed");
175 goto error;
176 }
177 args[1] = (char *)str_ptr;
178 }
179 if (ret > 3) {
180 res = pcre2_substring_get_bynumber(match, 3, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
181 if (res < 0) {
182 SCLogError("pcre2_substring_get_bynumber failed");
183 goto error;
184 }
185 args[2] = (char *)str_ptr;
186 }
187 }
188
189 rd = SCMalloc(sizeof(DetectRpcData));
190 if (unlikely(rd == NULL))
191 goto error;
192 rd->flags = 0;
193 rd->program = 0;
194 rd->program_version = 0;
195 rd->procedure = 0;
196
197 int i;
198 for (i = 0; i < (ret - 1); i++) {
199 if (args[i]) {
200 switch (i) {
201 case 0:
202 if (StringParseUint32(&rd->program, 10, strlen(args[i]), args[i]) <= 0) {
203 SCLogError("Invalid size specified for the rpc program:\"%s\"", args[i]);
204 goto error;
205 }
207 break;
208 case 1:
209 if (args[i][0] != '*') {
210 if (StringParseUint32(&rd->program_version, 10, strlen(args[i]), args[i]) <= 0) {
212 "Invalid size specified for the rpc version:\"%s\"", args[i]);
213 goto error;
214 }
216 }
217 break;
218 case 2:
219 if (args[i][0] != '*') {
220 if (StringParseUint32(&rd->procedure, 10, strlen(args[i]), args[i]) <= 0) {
222 "Invalid size specified for the rpc procedure:\"%s\"", args[i]);
223 goto error;
224 }
226 }
227 break;
228 }
229 } else {
230 SCLogError("invalid rpc option %s", rpcstr);
231 goto error;
232 }
233 }
234 for (i = 0; i < (ret -1); i++){
235 if (args[i] != NULL)
236 pcre2_substring_free((PCRE2_UCHAR8 *)args[i]);
237 }
238 pcre2_match_data_free(match);
239 return rd;
240
241error:
242 if (match) {
243 pcre2_match_data_free(match);
244 }
245 for (i = 0; i < (ret -1) && i < 3; i++){
246 if (args[i] != NULL)
247 pcre2_substring_free((PCRE2_UCHAR8 *)args[i]);
248 }
249 if (rd != NULL)
251 return NULL;
252
253}
254
255/**
256 * \brief this function is used to add the parsed rpcdata into the current signature
257 *
258 * \param de_ctx pointer to the Detection Engine Context
259 * \param s pointer to the Current Signature
260 * \param m pointer to the Current SigMatch
261 * \param rpcstr pointer to the user provided rpc options
262 *
263 * \retval 0 on Success
264 * \retval -1 on Failure
265 */
266int DetectRpcSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rpcstr)
267{
268 DetectRpcData *rd = NULL;
269
270 rd = DetectRpcParse(de_ctx, rpcstr);
271 if (rd == NULL) goto error;
272
274 NULL) {
275 goto error;
276 }
278
279 return 0;
280
281error:
282 if (rd != NULL)
284 return -1;
285
286}
287
288/**
289 * \brief this function will free memory associated with DetectRpcData
290 *
291 * \param rd pointer to DetectRpcData
292 */
294{
295 SCEnter();
296
297 if (ptr == NULL) {
298 SCReturn;
299 }
300
301 DetectRpcData *rd = (DetectRpcData *)ptr;
302 SCFree(rd);
303
304 SCReturn;
305}
306
307#ifdef UNITTESTS
308#include "detect-engine-alert.h"
309/**
310 * \test DetectRpcTestParse01 is a test to make sure that we return "something"
311 * when given valid rpc opt
312 */
313static int DetectRpcTestParse01 (void)
314{
315 DetectRpcData *rd = DetectRpcParse(NULL, "123,444,555");
316 FAIL_IF_NULL(rd);
317
318 DetectRpcFree(NULL, rd);
319 PASS;
320}
321
322/**
323 * \test DetectRpcTestParse02 is a test for setting the established rpc opt
324 */
325static int DetectRpcTestParse02 (void)
326{
327 DetectRpcData *rd = NULL;
328 rd = DetectRpcParse(NULL, "111,222,333");
329 FAIL_IF_NULL(rd);
333 FAIL_IF_NOT(rd->program == 111);
334 FAIL_IF_NOT(rd->program_version == 222);
335 FAIL_IF_NOT(rd->procedure == 333);
336
337 DetectRpcFree(NULL, rd);
338
339 PASS;
340}
341
342/**
343 * \test DetectRpcTestParse03 is a test for checking the wildcards
344 * and not specified fields
345 */
346static int DetectRpcTestParse03 (void)
347{
348 DetectRpcData *rd = NULL;
349
350 rd = DetectRpcParse(NULL, "111,*,333");
351 FAIL_IF_NULL(rd);
352
356 FAIL_IF_NOT(rd->program == 111);
358 FAIL_IF_NOT(rd->procedure == 333);
359
360 DetectRpcFree(NULL, rd);
361
362 rd = DetectRpcParse(NULL, "111,222,*");
363 FAIL_IF_NULL(rd);
364
368 FAIL_IF_NOT(rd->program == 111);
369 FAIL_IF_NOT(rd->program_version == 222);
370 FAIL_IF_NOT(rd->procedure == 0);
371
372 DetectRpcFree(NULL, rd);
373
374 rd = DetectRpcParse(NULL, "111,*,*");
375 FAIL_IF_NULL(rd);
376
380 FAIL_IF_NOT(rd->program == 111);
382 FAIL_IF_NOT(rd->procedure == 0);
383
384 DetectRpcFree(NULL, rd);
385
386 rd = DetectRpcParse(NULL, "111,222");
387 FAIL_IF_NULL(rd);
388
392 FAIL_IF_NOT(rd->program == 111);
393 FAIL_IF_NOT(rd->program_version == 222);
394 FAIL_IF_NOT(rd->procedure == 0);
395
396 DetectRpcFree(NULL, rd);
397
398 rd = DetectRpcParse(NULL, "111");
399 FAIL_IF_NULL(rd);
400
404 FAIL_IF_NOT(rd->program == 111);
406 FAIL_IF_NOT(rd->procedure == 0);
407
408 DetectRpcFree(NULL, rd);
409 PASS;
410}
411
412/**
413 * \test DetectRpcTestParse04 is a test for check the discarding of empty options
414 */
415static int DetectRpcTestParse04 (void)
416{
417 DetectRpcData *rd = NULL;
418 rd = DetectRpcParse(NULL, "");
419
421 DetectRpcFree(NULL, rd);
422
423 PASS;
424}
425
426/**
427 * \test DetectRpcTestParse05 is a test for check invalid values
428 */
429static int DetectRpcTestParse05 (void)
430{
431 DetectRpcData *rd = NULL;
432 rd = DetectRpcParse(NULL, "111,aaa,*");
433
435 DetectRpcFree(NULL, rd);
436
437 PASS;
438}
439
440/**
441 * \test DetectRpcTestParse05 is a test to check the match function
442 */
443static int DetectRpcTestSig01(void)
444{
445 /* RPC Call */
446 uint8_t buf[] = {
447 /* XID */
448 0x64,0xb2,0xb3,0x75,
449 /* Message type: Call (0) */
450 0x00,0x00,0x00,0x00,
451 /* RPC Version (2) */
452 0x00,0x00,0x00,0x02,
453 /* Program portmap (100000) */
454 0x00,0x01,0x86,0xa0,
455 /* Program version (2) */
456 0x00,0x00,0x00,0x02,
457 /* Program procedure (3) = GETPORT */
458 0x00,0x00,0x00,0x03,
459 /* AUTH_NULL */
460 0x00,0x00,0x00,0x00,
461 /* Length 0 */
462 0x00,0x00,0x00,0x00,
463 /* VERIFIER NULL */
464 0x00,0x00,0x00,0x00,
465 /* Length 0 */
466 0x00,0x00,0x00,0x00,
467 /* Program portmap */
468 0x00,0x01,0x86,0xa2,
469 /* Version 2 */
470 0x00,0x00,0x00,0x02,
471 /* Proto UDP */
472 0x00,0x00,0x00,0x11,
473 /* Port 0 */
474 0x00,0x00,0x00,0x00 };
475 uint16_t buflen = sizeof(buf);
476 Packet *p = NULL;
477 Signature *s = NULL;
478 ThreadVars th_v;
479 DetectEngineThreadCtx *det_ctx;
480
481 memset(&th_v, 0, sizeof(th_v));
482
483 p = UTHBuildPacket(buf, buflen, IPPROTO_UDP);
484
487
489
491 "alert udp any any -> any any (msg:\"RPC Get Port Call\"; rpc:100000, 2, 3; sid:1;)");
492 FAIL_IF_NULL(s);
493
495 "alert udp any any -> any any (msg:\"RPC Get Port Call\"; rpc:100000, 2, *; sid:2;)");
496 FAIL_IF_NULL(s);
497
499 "alert udp any any -> any any (msg:\"RPC Get Port Call\"; rpc:100000, *, 3; sid:3;)");
500 FAIL_IF_NULL(s);
501
503 "alert udp any any -> any any (msg:\"RPC Get Port Call\"; rpc:100000, *, *; sid:4;)");
504 FAIL_IF_NULL(s);
505
506 s = DetectEngineAppendSig(de_ctx, "alert udp any any -> any any (msg:\"RPC Get XXX Call.. no "
507 "match\"; rpc:123456, *, 3; sid:5;)");
508 FAIL_IF_NULL(s);
509
511 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
512
513 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
514 FAIL_IF(PacketAlertCheck(p, 1) == 0);
515 FAIL_IF(PacketAlertCheck(p, 2) == 0);
516 FAIL_IF(PacketAlertCheck(p, 3) == 0);
517 FAIL_IF(PacketAlertCheck(p, 4) == 0);
518 FAIL_IF(PacketAlertCheck(p, 5) > 0);
519
520 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
522
523 UTHFreePackets(&p, 1);
524
525 PASS;
526}
527
528/**
529 * \brief this function registers unit tests for DetectRpc
530 */
531static void DetectRpcRegisterTests(void)
532{
533 UtRegisterTest("DetectRpcTestParse01", DetectRpcTestParse01);
534 UtRegisterTest("DetectRpcTestParse02", DetectRpcTestParse02);
535 UtRegisterTest("DetectRpcTestParse03", DetectRpcTestParse03);
536 UtRegisterTest("DetectRpcTestParse04", DetectRpcTestParse04);
537 UtRegisterTest("DetectRpcTestParse05", DetectRpcTestParse05);
538 UtRegisterTest("DetectRpcTestSig01", DetectRpcTestSig01);
539}
540#endif /* UNITTESTS */
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
DetectEngineCtx * DetectEngineCtxInit(void)
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
initialize thread specific detection engine context
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
int DetectParsePcreExec(DetectParseRegex *parse_regex, pcre2_match_data **match, const char *str, int start_offset, int options)
SigMatch * SCSigMatchAppendSMToList(DetectEngineCtx *de_ctx, Signature *s, uint16_t type, SigMatchCtx *ctx, const int list)
Append a SigMatch to the list type.
SigTableElmt * sigmatch_table
void DetectRpcFree(DetectEngineCtx *, void *)
this function will free memory associated with DetectRpcData
Definition detect-rpc.c:293
void DetectRpcRegister(void)
Registration function for rpc keyword.
Definition detect-rpc.c:61
#define PARSE_REGEX
Regex for parsing our rpc options.
Definition detect-rpc.c:46
#define DETECT_RPC_CHECK_PROGRAM
Definition detect-rpc.h:30
#define DETECT_RPC_CHECK_PROCEDURE
Definition detect-rpc.h:32
#define DETECT_RPC_CHECK_VERSION
Definition detect-rpc.h:31
void SigMatchSignatures(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition detect.c:2420
#define DE_QUIET
Definition detect.h:330
#define SIG_FLAG_REQUIRE_PACKET
Definition detect.h:254
@ DETECT_SM_LIST_MATCH
Definition detect.h:117
DetectEngineCtx * de_ctx
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
struct Thresholds ctx
main detection engine ctx
Definition detect.h:932
uint8_t flags
Definition detect.h:934
uint32_t procedure
Definition detect-rpc.h:47
uint32_t program_version
Definition detect-rpc.h:46
uint32_t program
Definition detect-rpc.h:45
uint8_t flags
Definition detect-rpc.h:48
uint8_t * payload
Definition decode.h:605
uint32_t prog
Definition detect-rpc.h:39
uint32_t proc
Definition detect-rpc.h:41
uint32_t type
Definition detect-rpc.h:37
uint32_t vers
Definition detect-rpc.h:40
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition detect.h:351
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
void(* Free)(DetectEngineCtx *, void *)
Definition detect.h:1446
const char * desc
Definition detect.h:1461
void(* RegisterTests)(void)
Definition detect.h:1448
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition detect.h:1421
const char * name
Definition detect.h:1459
Signature container.
Definition detect.h:668
uint32_t flags
Definition detect.h:669
Per thread variable structure.
Definition threadvars.h:58
#define SCNtohl(x)
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition util-byte.c:313
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCReturn
Definition util-debug.h:279
#define SCMalloc(sz)
Definition util-mem.h:47
#define SCFree(p)
Definition util-mem.h:61
#define unlikely(expr)
void UTHFreePackets(Packet **p, int numpkts)
UTHFreePackets: function to release the allocated data from UTHBuildPacket and the packet itself.
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.