]>
Commit | Line | Data |
---|---|---|
6c7aacce MK |
1 | /* Copyright (C) 2017 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 Mats Klepsland <mats.klepsland@gmail.com> | |
22 | * | |
7f102d95 | 23 | * Implements support for ja3.hash keyword. |
6c7aacce MK |
24 | */ |
25 | ||
26 | #include "suricata-common.h" | |
27 | #include "threads.h" | |
28 | #include "debug.h" | |
29 | #include "decode.h" | |
30 | #include "detect.h" | |
31 | ||
32 | #include "detect-parse.h" | |
33 | #include "detect-engine.h" | |
34 | #include "detect-engine-mpm.h" | |
35 | #include "detect-engine-prefilter.h" | |
36 | #include "detect-content.h" | |
37 | #include "detect-pcre.h" | |
38 | #include "detect-tls-ja3-hash.h" | |
39 | ||
40 | #include "flow.h" | |
41 | #include "flow-util.h" | |
42 | #include "flow-var.h" | |
43 | ||
44 | #include "conf.h" | |
45 | #include "conf-yaml-loader.h" | |
46 | ||
47 | #include "util-debug.h" | |
48 | #include "util-unittest.h" | |
49 | #include "util-spm.h" | |
50 | #include "util-print.h" | |
51 | #include "util-ja3.h" | |
52 | ||
53 | #include "stream-tcp.h" | |
54 | ||
55 | #include "app-layer.h" | |
56 | #include "app-layer-ssl.h" | |
57 | ||
58 | #include "util-unittest.h" | |
59 | #include "util-unittest-helper.h" | |
60 | ||
61 | static int DetectTlsJa3HashSetup(DetectEngineCtx *, Signature *, const char *); | |
74a7b7e3 | 62 | #ifdef UNITTESTS |
6c7aacce | 63 | static void DetectTlsJa3HashRegisterTests(void); |
74a7b7e3 | 64 | #endif |
6c7aacce MK |
65 | static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, |
66 | const DetectEngineTransforms *transforms, | |
1c04d7cd | 67 | Flow *f, const uint8_t flow_flags, |
6c7aacce | 68 | void *txv, const int list_id); |
f36d578e MK |
69 | static void DetectTlsJa3HashSetupCallback(const DetectEngineCtx *de_ctx, |
70 | Signature *s); | |
be4c6b85 | 71 | static bool DetectTlsJa3HashValidateCallback(const Signature *s, |
4c9d448f | 72 | const char **sigerror); |
6c7aacce MK |
73 | static int g_tls_ja3_hash_buffer_id = 0; |
74 | ||
75 | /** | |
76 | * \brief Registration function for keyword: ja3_hash | |
77 | */ | |
78 | void DetectTlsJa3HashRegister(void) | |
79 | { | |
7f102d95 JL |
80 | sigmatch_table[DETECT_AL_TLS_JA3_HASH].name = "ja3.hash"; |
81 | sigmatch_table[DETECT_AL_TLS_JA3_HASH].alias = "ja3_hash"; | |
6c7aacce | 82 | sigmatch_table[DETECT_AL_TLS_JA3_HASH].desc = "content modifier to match the JA3 hash buffer"; |
26bcc975 | 83 | sigmatch_table[DETECT_AL_TLS_JA3_HASH].url = "/rules/ja3-keywords.html#ja3-hash"; |
6c7aacce | 84 | sigmatch_table[DETECT_AL_TLS_JA3_HASH].Setup = DetectTlsJa3HashSetup; |
74a7b7e3 | 85 | #ifdef UNITTESTS |
6c7aacce | 86 | sigmatch_table[DETECT_AL_TLS_JA3_HASH].RegisterTests = DetectTlsJa3HashRegisterTests; |
74a7b7e3 | 87 | #endif |
6c7aacce | 88 | sigmatch_table[DETECT_AL_TLS_JA3_HASH].flags |= SIGMATCH_NOOPT; |
7f102d95 | 89 | sigmatch_table[DETECT_AL_TLS_JA3_HASH].flags |= SIGMATCH_INFO_STICKY_BUFFER; |
6c7aacce | 90 | |
7f102d95 | 91 | DetectAppLayerInspectEngineRegister2("ja3.hash", ALPROTO_TLS, SIG_FLAG_TOSERVER, 0, |
6c7aacce MK |
92 | DetectEngineInspectBufferGeneric, GetData); |
93 | ||
7f102d95 | 94 | DetectAppLayerMpmRegister2("ja3.hash", SIG_FLAG_TOSERVER, 2, |
6c7aacce MK |
95 | PrefilterGenericMpmRegister, GetData, ALPROTO_TLS, 0); |
96 | ||
7f102d95 | 97 | DetectBufferTypeSetDescriptionByName("ja3.hash", "TLS JA3 hash"); |
6c7aacce | 98 | |
7f102d95 | 99 | DetectBufferTypeRegisterSetupCallback("ja3.hash", |
f36d578e MK |
100 | DetectTlsJa3HashSetupCallback); |
101 | ||
7f102d95 | 102 | DetectBufferTypeRegisterValidateCallback("ja3.hash", |
4c9d448f MK |
103 | DetectTlsJa3HashValidateCallback); |
104 | ||
7f102d95 | 105 | g_tls_ja3_hash_buffer_id = DetectBufferTypeGetByName("ja3.hash"); |
6c7aacce MK |
106 | } |
107 | ||
108 | /** | |
7f102d95 | 109 | * \brief this function setup the ja3.hash modifier keyword used in the rule |
6c7aacce MK |
110 | * |
111 | * \param de_ctx Pointer to the Detection Engine Context | |
112 | * \param s Pointer to the Signature to which the current keyword belongs | |
113 | * \param str Should hold an empty string always | |
114 | * | |
0f7f35bd MK |
115 | * \retval 0 On success |
116 | * \retval -1 On failure | |
0771eb1e | 117 | * \retval -2 on failure that should be silent after the first |
6c7aacce MK |
118 | */ |
119 | static int DetectTlsJa3HashSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) | |
120 | { | |
0f7f35bd MK |
121 | if (DetectBufferSetActiveList(s, g_tls_ja3_hash_buffer_id) < 0) |
122 | return -1; | |
123 | ||
124 | if (DetectSignatureSetAppProto(s, ALPROTO_TLS) < 0) | |
125 | return -1; | |
6c7aacce | 126 | |
ca5226f0 VJ |
127 | /* try to enable JA3 */ |
128 | SSLEnableJA3(); | |
129 | ||
6c7aacce | 130 | /* Check if JA3 is disabled */ |
0771eb1e VJ |
131 | if (!RunmodeIsUnittests() && Ja3IsDisabled("rule")) { |
132 | if (!SigMatchSilentErrorEnabled(de_ctx, DETECT_AL_TLS_JA3_HASH)) { | |
133 | SCLogError(SC_WARN_JA3_DISABLED, "ja3 support is not enabled"); | |
134 | } | |
135 | return -2; | |
136 | } | |
6c7aacce MK |
137 | |
138 | return 0; | |
139 | } | |
140 | ||
141 | static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, | |
1c04d7cd MK |
142 | const DetectEngineTransforms *transforms, Flow *f, |
143 | const uint8_t flow_flags, void *txv, const int list_id) | |
6c7aacce | 144 | { |
0b3220a0 | 145 | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
6c7aacce | 146 | if (buffer->inspect == NULL) { |
008f08c1 | 147 | const SSLState *ssl_state = (SSLState *)f->alstate; |
6c7aacce | 148 | |
a4471987 | 149 | if (ssl_state->client_connp.ja3_hash == NULL) { |
6c7aacce MK |
150 | return NULL; |
151 | } | |
152 | ||
a4471987 MK |
153 | const uint32_t data_len = strlen(ssl_state->client_connp.ja3_hash); |
154 | const uint8_t *data = (uint8_t *)ssl_state->client_connp.ja3_hash; | |
6c7aacce | 155 | |
13cebb18 | 156 | InspectionBufferSetup(det_ctx, list_id, buffer, data, data_len); |
6c7aacce MK |
157 | InspectionBufferApplyTransforms(buffer, transforms); |
158 | } | |
159 | ||
160 | return buffer; | |
161 | } | |
162 | ||
be4c6b85 | 163 | static bool DetectTlsJa3HashValidateCallback(const Signature *s, |
4c9d448f MK |
164 | const char **sigerror) |
165 | { | |
166 | const SigMatch *sm = s->init_data->smlists[g_tls_ja3_hash_buffer_id]; | |
167 | for ( ; sm != NULL; sm = sm->next) | |
168 | { | |
169 | if (sm->type != DETECT_CONTENT) | |
170 | continue; | |
171 | ||
5b954212 MK |
172 | const DetectContentData *cd = (DetectContentData *)sm->ctx; |
173 | ||
174 | if (cd->flags & DETECT_CONTENT_NOCASE) { | |
7f102d95 | 175 | *sigerror = "ja3.hash should not be used together with " |
5b954212 MK |
176 | "nocase, since the rule is automatically " |
177 | "lowercased anyway which makes nocase redundant."; | |
178 | SCLogWarning(SC_WARN_POOR_RULE, "rule %u: %s", s->id, *sigerror); | |
179 | } | |
4c9d448f MK |
180 | |
181 | if (cd->content_len == 32) | |
1eeb9669 | 182 | return true; |
4c9d448f MK |
183 | |
184 | *sigerror = "Invalid length of the specified JA3 hash (should " | |
185 | "be 32 characters long). This rule will therefore " | |
186 | "never match."; | |
187 | SCLogWarning(SC_WARN_POOR_RULE, "rule %u: %s", s->id, *sigerror); | |
1eeb9669 | 188 | return false; |
4c9d448f MK |
189 | } |
190 | ||
1eeb9669 | 191 | return true; |
4c9d448f MK |
192 | } |
193 | ||
f36d578e MK |
194 | static void DetectTlsJa3HashSetupCallback(const DetectEngineCtx *de_ctx, |
195 | Signature *s) | |
196 | { | |
197 | SigMatch *sm = s->init_data->smlists[g_tls_ja3_hash_buffer_id]; | |
198 | for ( ; sm != NULL; sm = sm->next) | |
199 | { | |
200 | if (sm->type != DETECT_CONTENT) | |
201 | continue; | |
202 | ||
203 | DetectContentData *cd = (DetectContentData *)sm->ctx; | |
204 | ||
1eeb9669 | 205 | bool changed = false; |
f36d578e MK |
206 | uint32_t u; |
207 | for (u = 0; u < cd->content_len; u++) | |
208 | { | |
209 | if (isupper(cd->content[u])) { | |
210 | cd->content[u] = tolower(cd->content[u]); | |
1eeb9669 | 211 | changed = true; |
f36d578e MK |
212 | } |
213 | } | |
214 | ||
215 | /* recreate the context if changes were made */ | |
216 | if (changed) { | |
217 | SpmDestroyCtx(cd->spm_ctx); | |
218 | cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1, | |
219 | de_ctx->spm_global_thread_ctx); | |
220 | } | |
221 | } | |
222 | } | |
223 | ||
6c7aacce | 224 | #ifdef UNITTESTS |
74a7b7e3 MK |
225 | #include "tests/detect-tls-ja3-hash.c" |
226 | #endif |