1 /*
2 * openldap buffer overflow dos attempt
3 *
4 * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
5 * Copyright (C) 2006-2013 Sourcefire, Inc. All Rights Reserved
6 *
7 * Writen by Patrick Mullen <pmullen@sourcefire.com>
8 *
9 * This file may contain proprietary rules that were created, tested and
10 * certified by Sourcefire, Inc. (the "VRT Certified Rules") as well as
11 * rules that were created by Sourcefire and other third parties and
12 * distributed under the GNU General Public License (the "GPL Rules"). The
13 * VRT Certified Rules contained in this file are the property of
14 * Sourcefire, Inc. Copyright 2005 Sourcefire, Inc. All Rights Reserved.
15 * The GPL Rules created by Sourcefire, Inc. are the property of
16 * Sourcefire, Inc. Copyright 2002-2005 Sourcefire, Inc. All Rights
17 * Reserved. All other GPL Rules are owned and copyrighted by their
18 * respective owners (please see www.snort.org/contributors for a list of
19 * owners and their respective copyrights). In order to determine what
20 * rules are VRT Certified Rules or GPL Rules, please refer to the VRT
21 * Certified Rules License Agreement.
22 */
23
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "sf_snort_plugin_api.h"
30 #include "sf_snort_packet.h"
31
32 /* declare detection functions */
33 static int ruleVD_OPENLDAPeval(void *p);
34
35 static RuleReference ruleVD_OPENLDAPref0 =
36 {
37 "bugtraq", /* type */
38 "20939" /* value */
39 };
40 static RuleReference ruleVD_OPENLDAPcve =
41 {
42 "cve", /* type */
43 "CVE-2006-5779" /* value */
44 };
45
46
47 static RuleReference *ruleVD_OPENLDAPrefs[] =
48 {
49 &ruleVD_OPENLDAPref0,
50 &ruleVD_OPENLDAPcve,
51 NULL
52 };
53
54 static FlowFlags ruleVD_OPENLDAPflow =
55 {
56 FLOW_ESTABLISHED|FLOW_TO_SERVER
57 };
58
59 static RuleOption ruleVD_OPENLDAPoption0 =
60 {
61 OPTION_TYPE_FLOWFLAGS,
62 {
63 &ruleVD_OPENLDAPflow
64 }
65 };
66
67 static ContentInfo ruleVD_OPENLDAPcontent =
68 {
69 (u_int8_t *)"|30|", /* pattern to search for */
70 1, /* depth */
71 0, /* offset */
72 0, /* flags */
73 NULL, /* holder for boyer/moore info */
74 NULL, /* holder for byte representation of "NetBus" */
75 0, /* holder for length of byte representation */
76 0, /* holder of increment length */
77 0, /* holder for fp offset */
78 0, /* holder for fp length */
79 0, /* holder for fp only */
80 NULL, // offset_refId
81 NULL, // depth_refId
82 NULL, // offset_location
83 NULL // depth_location
84 };
85
86 static RuleOption ruleVD_OPENLDAPoption1 =
87 {
88 OPTION_TYPE_CONTENT,
89 {
90 &ruleVD_OPENLDAPcontent
91 }
92 };
93
94
95 RuleOption *ruleVD_OPENLDAPoptions[] =
96 {
97 &ruleVD_OPENLDAPoption0,
98 &ruleVD_OPENLDAPoption1,
99 NULL
100 };
101
102 Rule ruleVD_OPENLDAP = {
103 /* rule header */
104 {
105 IPPROTO_TCP, /* proto */
106 "any", /* SRCIP */
107 "any", /* SRCPORT */
108 0, /* DIRECTION */
109 HOME_NET, /* DSTIP */
110 "389", /* DSTPORT */
111 },
112 /* metadata */
113 {
114 3, /* genid (HARDCODED!!!) */
115 32694, /* XXX sigid */
116 1, /* revision */
117
118 "attempted-admin", /* classification, generic */
119 0, /* hardcoded priority XXX NOT PROVIDED BY GRAMMAR YET! */
120 "openldap buffer overflow denial of service attempt", /* message */
121 ruleVD_OPENLDAPrefs, /* ptr to references */
122 NULL /* Meta data */
123 },
124 ruleVD_OPENLDAPoptions, /* ptr to rule options */
125 &ruleVD_OPENLDAPeval, /* ptr to rule detection function */
126 0, /* am I initialized yet? */
127 0, /* number of options */
128 0, /* don't alert */
129 NULL /* ptr to internal data... setup during rule registration */
130 };
131
132
133 /* detection functions */
134
135 /* process_val
136
137 Returns the u_int32_t value contained at the pointer after skipping
138 preceeding NULs. Returns an error if the data does not fit into
139 a u_int32_t.
140 */
process_val(const u_int8_t * data,u_int32_t data_len,u_int32_t * retvalue)141 static int process_val(const u_int8_t *data, u_int32_t data_len, u_int32_t *retvalue) {
142 u_int32_t actual_data_len, i;
143 *retvalue = 0;
144
145 /* Jump over NULLs */
146 i = 0;
147 while((i < data_len) && (data[i] == 0)) {
148 i++;
149 }
150 actual_data_len = data_len - i;
151 if(actual_data_len > 4) return(-1); /* Data doesn't fit into u_int32_t */
152
153 /* Now find the actual value */
154 for(;i<data_len;i++) {
155 *retvalue += data[i]<<(8*(data_len - i - 1));
156 }
157
158 return(0);
159 }
160
161
162 /* skip_over_data
163
164 Given an SFSnortPacket and a pointer to an index into the data,
165 this function will parse the size field at that index and move
166 the index to point after the size field and the data it describes.
167
168 Size fields are as described in BER encoding.
169 */
skip_over_data(SFSnortPacket * sp,u_int32_t * current_byte)170 static int skip_over_data(SFSnortPacket *sp, u_int32_t *current_byte) {
171 u_int32_t width = 0, value = 0;
172 int retval = 0;
173
174 if(sp->payload[*current_byte] & 0x80) {
175 width = sp->payload[*current_byte] & 0x0F;
176 (*current_byte)++;
177
178 if(*current_byte >= sp->payload_size - width)
179 return(-1);
180
181 retval = process_val(&(sp->payload[*current_byte]), width, &value);
182 if(retval < 0)
183 return(-1); /* width is > 4 */
184 *current_byte += width; /* width of data width specifier */
185 *current_byte += value; /* width of data itself */
186 } else {
187 *current_byte += sp->payload[*current_byte] + 1;
188 }
189
190 return(0);
191 }
192
193
194 /* Detection algorithm --
195 We're looking for more than 0x0101 (257) bytes of data in the authentication
196 mechanism data field. To do this, we minimally parse LDAP bind packets to
197 get to the data and ensure we encounter the correct data types along the way.
198
199 0x30 - Universal Sequence
200 [Message size] - no data type, just a size
201 [Message ID] - int data type (0x02)
202
203 0x60 - Bind request
204 [Bind Request size] - no data type, just a size
205 [LDAP version] - int data type (0x02)
206 [DN (username)] - string data type (0x04)
207
208 0xa3 - Extended Auth Type, SASL
209 [Auth Data Size] - no data type, just a size
210 [Mechanism Name] - string data type
211 [Mechanism Data] - string data type -- if > 0x0101 bytes long, RULE_MATCH
212
213 Note we don't actually care if the data is present in this
214 particular packet, which reduces evasion possibilities.
215 */
ruleVD_OPENLDAPeval(void * p)216 static int ruleVD_OPENLDAPeval(void *p) {
217 u_int32_t current_byte = 0;
218 u_int32_t width, value;
219 int retval;
220
221 const u_int8_t *cursor_normal;
222
223 SFSnortPacket *sp = (SFSnortPacket *) p;
224
225 if(sp == NULL)
226 return RULE_NOMATCH;
227
228 if(sp->payload == NULL)
229 return RULE_NOMATCH;
230
231 if(sp->payload_size <= 26) /* Minimum SASL bind request length (minus the auth data) */
232 return RULE_NOMATCH;
233
234 /* call flow match */
235 if (checkFlow(sp, ruleVD_OPENLDAPoptions[0]->option_u.flowFlags) <= 0 )
236 return RULE_NOMATCH;
237
238 /* call content match */
239 if (contentMatch(sp, ruleVD_OPENLDAPoptions[1]->option_u.content, &cursor_normal) <= 0) {
240 return RULE_NOMATCH;
241 }
242
243 /* our contentMatch already assures us the first byte is \x30, so just jump over it */
244 current_byte++;
245
246 /* Begin packet structure processing */
247 /* Packet length (only care about width of the specifier) */
248 if(sp->payload[current_byte] & 0x80) {
249 current_byte += sp->payload[current_byte] & 0x0F;
250 }
251 current_byte++;
252
253 /* Message number (only care about width of the specifier) */
254 if(current_byte >= (u_int32_t)(sp->payload_size - 22))
255 return RULE_NOMATCH;
256
257 if(sp->payload[current_byte] != 0x02) /* Int data type */
258 return RULE_NOMATCH;
259 current_byte++;
260
261 /* Skip over int width and the int value */
262 if(skip_over_data(sp, ¤t_byte) < 0)
263 return RULE_NOMATCH;
264
265 if(current_byte >= (u_int32_t)(sp->payload_size - 19))
266 return RULE_NOMATCH;
267
268 /* Bind Request */
269 if(sp->payload[current_byte] != 0x60)
270 return RULE_NOMATCH;
271
272 current_byte++;
273
274 /* Message length (only care about width of the specifier) */
275 if(sp->payload[current_byte] & 0x80) {
276 current_byte += sp->payload[current_byte] & 0x0F;
277 }
278 current_byte++;
279
280 /* ldap version */
281 if(current_byte >= (u_int32_t)(sp->payload_size - 15))
282 return RULE_NOMATCH;
283
284 /* ldap version */
285 if(sp->payload[current_byte] != 0x02) /* Int data type */
286 return RULE_NOMATCH;
287 current_byte++;
288
289 /* Skip over int width and the int value */
290 if(skip_over_data(sp, ¤t_byte) < 0)
291 return RULE_NOMATCH;
292
293 if(current_byte >= (u_int32_t)(sp->payload_size - 12))
294 return RULE_NOMATCH;
295
296 /* user name (DN) */
297 /* 0x04 - string data type */
298 if(sp->payload[current_byte] != 0x04) /* string data type */
299 return RULE_NOMATCH;
300 current_byte++;
301
302 /* Skip over string length specifier and the string */
303 if(skip_over_data(sp, ¤t_byte) < 0)
304 return RULE_NOMATCH;
305
306 if(current_byte >= (u_int32_t)(sp->payload_size - 10))
307 return RULE_NOMATCH;
308
309 /* 0xA3 - Auth type: SASL */
310 if(sp->payload[current_byte] != 0xA3)
311 return RULE_NOMATCH;
312 current_byte++;
313
314 /* Auth data length - only care about width of specifier */
315 if(sp->payload[current_byte] & 0x80) {
316 current_byte += sp->payload[current_byte] & 0x0F;
317 }
318 current_byte++;
319
320 if(current_byte >= (u_int32_t)(sp->payload_size - 6))
321 return RULE_NOMATCH;
322
323 /* Auth Mechanism */
324 /* 0x04 - string data type */
325 if(sp->payload[current_byte] != 0x04) /* string data type */
326 return RULE_NOMATCH;
327 current_byte++;
328
329 /* Skip over string length specifier and the string */
330 /* String value can be anything */
331 if(skip_over_data(sp, ¤t_byte) < 0)
332 return RULE_NOMATCH;
333
334 if(current_byte >= (u_int32_t)(sp->payload_size - 4))
335 return RULE_NOMATCH;
336
337 /* Auth data */
338 /* 0x04 - string data type */
339 if(sp->payload[current_byte] != 0x04) /* string data type */
340 return RULE_NOMATCH;
341 current_byte++;
342
343 /* Here we can't just jump over the value because it's what
344 we're looking for. */
345 /* length of string -- if 0x0400 (1024 dec) it's an exploit attempt */
346 if(sp->payload[current_byte] & 0x80) {
347 width = sp->payload[current_byte] & 0x0F;
348 current_byte++;
349
350 if(current_byte >= sp->payload_size - width)
351 return RULE_NOMATCH;
352
353 retval = process_val(&(sp->payload[current_byte]), width, &value);
354 if(retval < 0)
355 return RULE_NOMATCH; /* width is either 0 or > 4 */
356 current_byte += width; /* width of data width specifier */
357 /* value equals the length of the string */
358 } else {
359 value = sp->payload[current_byte]; /* length of the string */
360 current_byte++;
361 }
362
363 if(value > 0x0101) /* minimum length determined through testing */
364 return RULE_MATCH;
365
366 return RULE_NOMATCH;
367 }
368
369 /*
370 Rule *rules[] = {
371 &ruleVD_OPENLDAP,
372 NULL
373 };
374 */
375
376