Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect multiprotocol keywords 7304 v3 #12310

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 68 additions & 2 deletions src/detect-ja4-hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ int Ja4IsDisabled(const char *type);
static InspectionBuffer *Ja4DetectGetHash(DetectEngineThreadCtx *det_ctx,
const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
const int list_id);
#ifdef UNITTESTS
static void DetectJa4RegisterTests(void);
#endif

static int g_ja4_hash_buffer_id = 0;
#endif
Expand All @@ -70,6 +73,9 @@ void DetectJa4HashRegister(void)
sigmatch_table[DETECT_AL_JA4_HASH].url = "/rules/ja4-keywords.html#ja4-hash";
#ifdef HAVE_JA4
sigmatch_table[DETECT_AL_JA4_HASH].Setup = DetectJa4HashSetup;
#ifdef UNITTESTS
sigmatch_table[DETECT_AL_JA4_HASH].RegisterTests = DetectJa4RegisterTests;
#endif
#else /* HAVE_JA4 */
sigmatch_table[DETECT_AL_JA4_HASH].Setup = DetectJA4SetupNoSupport;
#endif /* HAVE_JA4 */
Expand Down Expand Up @@ -111,7 +117,8 @@ static int DetectJa4HashSetup(DetectEngineCtx *de_ctx, Signature *s, const char
if (DetectBufferSetActiveList(de_ctx, s, g_ja4_hash_buffer_id) < 0)
return -1;

if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS && s->alproto != ALPROTO_QUIC) {
AppProto alprotos[] = { ALPROTO_TLS, ALPROTO_QUIC, ALPROTO_UNKNOWN };
if (DetectSignatureSetMultiAppProto(s, alprotos) < 0) {
SCLogError("rule contains conflicting protocols.");
return -1;
}
Expand All @@ -126,7 +133,6 @@ static int DetectJa4HashSetup(DetectEngineCtx *de_ctx, Signature *s, const char
}
return -2;
}
s->init_data->init_flags |= SIG_FLAG_INIT_JA;

return 0;
}
Expand Down Expand Up @@ -174,4 +180,64 @@ static InspectionBuffer *Ja4DetectGetHash(DetectEngineThreadCtx *det_ctx,
}
return buffer;
}

#ifdef UNITTESTS
static int DetectJa4TestParse01(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();

// invalid tests
Signature *s =
SigInit(de_ctx, "alert ip any any -> any any (sid: 1; file.data; content: \"toto\"; "
"ja4.hash; content: \"q13d0310h3_55b375c5d22e_cd85d2d88918\";)");
// cannot have file.data with ja4.hash (quic or tls)
FAIL_IF_NOT_NULL(s);
s = SigInit(de_ctx, "alert ip any any -> any any (sid: 1; "
"ja4.hash; content: \"q13d0310h3_55b375c5d22e_cd85d2d88918\"; file.data; "
"content: \"toto\";)");
// cannot have file.data with ja4.hash (quic or tls)
FAIL_IF_NOT_NULL(s);
s = SigInit(de_ctx, "alert smb any any -> any any (sid: 1; "
"ja4.hash; content: \"q13d0310h3_55b375c5d22e_cd85d2d88918\";)");
// cannot have alproto=smb with ja4.hash (quic or tls)
FAIL_IF_NOT_NULL(s);
s = SigInit(de_ctx, "alert ip any any -> any any (sid: 1; "
"ja4.hash; content: \"q13d0310h3_55b375c5d22e_cd85d2d88918\"; smb.share; "
"content:\"toto\";)");
// cannot have a smb keyword with ja4.hash (quic or tls)
FAIL_IF_NOT_NULL(s);
s = SigInit(de_ctx, "alert ip any any -> any any (sid: 1; "
"smb.share; content:\"toto\"; ja4.hash; content: "
"\"q13d0310h3_55b375c5d22e_cd85d2d88918\";)");
// cannot have a smb keyword with ja4.hash (quic or tls)
FAIL_IF_NOT_NULL(s);

// valid tests
s = SigInit(de_ctx, "alert ip any any -> any any (sid: 1; "
"ja4.hash; content: \"q13d0310h3_55b375c5d22e_cd85d2d88918\";)");
// just ja4.hash any proto
FAIL_IF_NULL(s);
s = SigInit(de_ctx, "alert quic any any -> any any (sid: 1; "
"ja4.hash; content: \"q13d0310h3_55b375c5d22e_cd85d2d88918\";)");
// just ja4.hash only quic
FAIL_IF_NULL(s);
s = SigInit(de_ctx, "alert tls any any -> any any (sid: 1; "
"ja4.hash; content: \"q13d0310h3_55b375c5d22e_cd85d2d88918\";)");
// just ja4.hash only tls
FAIL_IF_NULL(s);
s = SigInit(de_ctx, "alert ip any any -> any any (sid: 1; "
"ja4.hash; content: \"q13d0310h3_55b375c5d22e_cd85d2d88918\"; "
"quic.version; content:\"|00|\";)");
// ja4.hash and a quic keyword
FAIL_IF_NULL(s);
DetectEngineCtxFree(de_ctx);
PASS;
}

static void DetectJa4RegisterTests(void)
{
UtRegisterTest("DetectJa4TestParse01", DetectJa4TestParse01);
}
#endif

#endif // HAVE_JA4
192 changes: 187 additions & 5 deletions src/detect-parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,85 @@ int DetectSignatureAddTransform(Signature *s, int transform, void *options)
SCReturnInt(0);
}

// alprotos parameter is expected as an array terminated by ALPROTO_UNKNOWN
int DetectSignatureSetMultiAppProto(Signature *s, const AppProto *alprotos)
{
if (s->alproto != ALPROTO_UNKNOWN) {
// One alproto was set, check if it matches the new ones proposed
while (*alprotos != ALPROTO_UNKNOWN) {
if (s->alproto == *alprotos) {
// alproto already set to only one
return 0;
}
alprotos++;
}
// alproto already set and not matching the new set of alprotos
return -1;
}
if (s->init_data->alprotos[0] != ALPROTO_UNKNOWN) {
// check intersection of already used alprotos and new ones
for (size_t i = 0; i < SIG_ALPROTO_MAX; i++) {
if (s->init_data->alprotos[i] == ALPROTO_UNKNOWN) {
break;
}
// first disable the ones that do not match
bool found = false;
const AppProto *args = alprotos;
while (*args != ALPROTO_UNKNOWN) {
if (s->init_data->alprotos[i] == *args) {
found = true;
break;
}
args++;
}
if (!found) {
s->init_data->alprotos[i] = ALPROTO_UNKNOWN;
}
}
// Then put at the beginning every defined protocol
for (size_t i = 0; i < SIG_ALPROTO_MAX; i++) {
if (s->init_data->alprotos[i] == ALPROTO_UNKNOWN) {
for (size_t j = SIG_ALPROTO_MAX - 1; j > i; j--) {
if (s->init_data->alprotos[j] != ALPROTO_UNKNOWN) {
s->init_data->alprotos[i] = s->init_data->alprotos[j];
s->init_data->alprotos[j] = ALPROTO_UNKNOWN;
break;
}
}
if (s->init_data->alprotos[i] == ALPROTO_UNKNOWN) {
if (i == 0) {
// there was no intersection
return -1;
} else if (i == 1) {
// intersection is singleton, set it as usual
AppProto alproto = s->init_data->alprotos[0];
s->init_data->alprotos[0] = ALPROTO_UNKNOWN;
return DetectSignatureSetAppProto(s, alproto);
}
break;
}
}
}
} else {
if (alprotos[0] == ALPROTO_UNKNOWN) {
// do not allow empty set
return -1;
}
if (alprotos[1] == ALPROTO_UNKNOWN) {
// allow singleton, but call traditional setter
return DetectSignatureSetAppProto(s, alprotos[0]);
}
// first time we enforce alprotos
for (size_t i = 0; i < SIG_ALPROTO_MAX; i++) {
if (alprotos[i] == ALPROTO_UNKNOWN) {
break;
}
s->init_data->alprotos[i] = alprotos[i];
}
}
return 0;
}

int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
{
if (alproto == ALPROTO_UNKNOWN ||
Expand All @@ -1743,6 +1822,21 @@ int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
return -1;
}

if (s->init_data->alprotos[0] != ALPROTO_UNKNOWN) {
// Multiple protocols were set, check if we restrict to one
bool found = false;
for (size_t i = 0; i < SIG_ALPROTO_MAX; i++) {
if (s->init_data->alprotos[i] == alproto) {
found = true;
break;
}
}
if (!found) {
return -1;
}
s->init_data->alprotos[0] = ALPROTO_UNKNOWN;
}

if (s->alproto != ALPROTO_UNKNOWN) {
alproto = AppProtoCommon(s->alproto, alproto);
if (alproto == ALPROTO_FAILED) {
Expand Down Expand Up @@ -2066,11 +2160,6 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
}
DetectLuaPostSetup(s);

if ((s->init_data->init_flags & SIG_FLAG_INIT_JA) && s->alproto != ALPROTO_UNKNOWN &&
s->alproto != ALPROTO_TLS && s->alproto != ALPROTO_QUIC) {
SCLogError("Cannot have ja3/ja4 with protocol %s.", AppProtoToString(s->alproto));
SCReturnInt(0);
}
if ((s->flags & SIG_FLAG_FILESTORE) || s->file_flags != 0 ||
(s->init_data->init_flags & SIG_FLAG_INIT_FILEDATA)) {
if (s->alproto != ALPROTO_UNKNOWN &&
Expand All @@ -2080,6 +2169,21 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
"support file matching",
AppProtoToString(s->alproto));
SCReturnInt(0);
} else if (s->init_data->alprotos[0] != ALPROTO_UNKNOWN) {
bool found = false;
for (size_t i = 0; i < SIG_ALPROTO_MAX; i++) {
if (s->init_data->alprotos[i] == ALPROTO_UNKNOWN) {
break;
}
if (AppLayerParserSupportsFiles(IPPROTO_TCP, s->init_data->alprotos[i])) {
found = true;
break;
}
}
if (!found) {
SCLogError("No protocol support file matching");
SCReturnInt(0);
}
}
if (s->alproto == ALPROTO_HTTP2 && (s->file_flags & FILE_SIG_NEED_FILENAME)) {
SCLogError("protocol HTTP2 doesn't support file name matching");
Expand Down Expand Up @@ -4439,6 +4543,81 @@ static int SigParseTestActionDrop(void)
PASS;
}

static int SigSetMultiAppProto(void)
{
Signature *s = SigAlloc();
FAIL_IF_NULL(s);

AppProto alprotos[] = { 1, 2, 3, ALPROTO_UNKNOWN };
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) < 0);

// check intersection gives multiple entries
alprotos[0] = 3;
alprotos[1] = 2;
alprotos[2] = ALPROTO_UNKNOWN;
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) < 0);
FAIL_IF(s->init_data->alprotos[0] != 3);
FAIL_IF(s->init_data->alprotos[1] != 2);
FAIL_IF(s->init_data->alprotos[2] != ALPROTO_UNKNOWN);

// check single after multiple
FAIL_IF(DetectSignatureSetAppProto(s, 3) < 0);
FAIL_IF(s->init_data->alprotos[0] != ALPROTO_UNKNOWN);
FAIL_IF(s->alproto != 3);
alprotos[0] = 4;
alprotos[1] = 3;
alprotos[2] = ALPROTO_UNKNOWN;
// check multiple containing singleton
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) < 0);
FAIL_IF(s->alproto != 3);

// reset
s->alproto = ALPROTO_UNKNOWN;
alprotos[0] = 1;
alprotos[1] = 2;
alprotos[2] = 3;
alprotos[3] = ALPROTO_UNKNOWN;
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) < 0);
// fail if set single not in multiple
FAIL_IF(DetectSignatureSetAppProto(s, 4) >= 0);

s->init_data->alprotos[0] = ALPROTO_UNKNOWN;
s->alproto = ALPROTO_UNKNOWN;
alprotos[0] = 1;
alprotos[1] = 2;
alprotos[2] = 3;
alprotos[3] = ALPROTO_UNKNOWN;
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) < 0);
alprotos[0] = 4;
alprotos[1] = 5;
alprotos[2] = ALPROTO_UNKNOWN;
// fail if multiple do not have intersection
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) >= 0);

s->init_data->alprotos[0] = ALPROTO_UNKNOWN;
s->alproto = ALPROTO_UNKNOWN;
alprotos[0] = 1;
alprotos[1] = 2;
alprotos[2] = 3;
alprotos[3] = ALPROTO_UNKNOWN;
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) < 0);
alprotos[0] = 3;
alprotos[1] = 4;
alprotos[2] = 5;
alprotos[3] = ALPROTO_UNKNOWN;
// check multiple intersect to singleton
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) < 0);
FAIL_IF(s->alproto != 3);
alprotos[0] = 5;
alprotos[1] = 4;
alprotos[2] = ALPROTO_UNKNOWN;
// fail if multiple do not belong to singleton
FAIL_IF(DetectSignatureSetMultiAppProto(s, alprotos) >= 0);

SigFree(NULL, s);
PASS;
}

#endif /* UNITTESTS */

#ifdef UNITTESTS
Expand Down Expand Up @@ -4514,5 +4693,8 @@ void SigParseRegisterTests(void)
SigParseBidirWithSameSrcAndDest02);
UtRegisterTest("SigParseTestActionReject", SigParseTestActionReject);
UtRegisterTest("SigParseTestActionDrop", SigParseTestActionDrop);

UtRegisterTest("SigSetMultiAppProto", SigSetMultiAppProto);

#endif /* UNITTESTS */
}
1 change: 1 addition & 0 deletions src/detect-parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ SigMatch *DetectGetLastSMByListId(const Signature *s, int list_id, ...);

int DetectSignatureAddTransform(Signature *s, int transform, void *options);
int WARN_UNUSED DetectSignatureSetAppProto(Signature *s, AppProto alproto);
int WARN_UNUSED DetectSignatureSetMultiAppProto(Signature *s, const AppProto *alprotos);

/* parse regex setup and free util funcs */

Expand Down
4 changes: 2 additions & 2 deletions src/detect-tls-ja3-hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ static int DetectTlsJa3HashSetup(DetectEngineCtx *de_ctx, Signature *s, const ch
if (DetectBufferSetActiveList(de_ctx, s, g_tls_ja3_hash_buffer_id) < 0)
return -1;

if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS && s->alproto != ALPROTO_QUIC) {
AppProto alprotos[] = { ALPROTO_TLS, ALPROTO_QUIC, ALPROTO_UNKNOWN };
if (DetectSignatureSetMultiAppProto(s, alprotos) < 0) {
SCLogError("rule contains conflicting protocols.");
return -1;
}
Expand All @@ -151,7 +152,6 @@ static int DetectTlsJa3HashSetup(DetectEngineCtx *de_ctx, Signature *s, const ch
}
return -2;
}
s->init_data->init_flags |= SIG_FLAG_INIT_JA;

return 0;
}
Expand Down
4 changes: 2 additions & 2 deletions src/detect-tls-ja3-string.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ static int DetectTlsJa3StringSetup(DetectEngineCtx *de_ctx, Signature *s, const
if (DetectBufferSetActiveList(de_ctx, s, g_tls_ja3_str_buffer_id) < 0)
return -1;

if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS && s->alproto != ALPROTO_QUIC) {
AppProto alprotos[] = { ALPROTO_TLS, ALPROTO_QUIC, ALPROTO_UNKNOWN };
if (DetectSignatureSetMultiAppProto(s, alprotos) < 0) {
SCLogError("rule contains conflicting protocols.");
return -1;
}
Expand All @@ -140,7 +141,6 @@ static int DetectTlsJa3StringSetup(DetectEngineCtx *de_ctx, Signature *s, const
}
return -2;
}
s->init_data->init_flags |= SIG_FLAG_INIT_JA;

return 0;
}
Expand Down
4 changes: 2 additions & 2 deletions src/detect-tls-ja3s-hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ static int DetectTlsJa3SHashSetup(DetectEngineCtx *de_ctx, Signature *s, const c
if (DetectBufferSetActiveList(de_ctx, s, g_tls_ja3s_hash_buffer_id) < 0)
return -1;

if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS && s->alproto != ALPROTO_QUIC) {
AppProto alprotos[] = { ALPROTO_TLS, ALPROTO_QUIC, ALPROTO_UNKNOWN };
if (DetectSignatureSetMultiAppProto(s, alprotos) < 0) {
SCLogError("rule contains conflicting protocols.");
return -1;
}
Expand All @@ -149,7 +150,6 @@ static int DetectTlsJa3SHashSetup(DetectEngineCtx *de_ctx, Signature *s, const c
}
return -2;
}
s->init_data->init_flags |= SIG_FLAG_INIT_JA;

return 0;
}
Expand Down
Loading
Loading