1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2002-2013 Sourcefire, Inc.
4 // Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
5 //
6 // This program is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU General Public License Version 2 as published
8 // by the Free Software Foundation. You may not use, modify or distribute
9 // this program under any other version of the GNU General Public License.
10 //
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 //--------------------------------------------------------------------------
20
21
22 // Telnet sessions can contain telnet negotiation strings that can disrupt
23 // pattern matching. This plugin detects negotiation strings in stream and
24 // normalizes them much like the http_inspect normalizes encoded URLs.
25 //
26 // official registry of options
27 // http://www.iana.org/assignments/telnet-options
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "pp_telnet.h"
34
35 #include "detection/detection_engine.h"
36 #include "detection/detection_util.h"
37 #include "protocols/packet.h"
38 #include "stream/stream.h"
39
40 #include "ftpp_return_codes.h"
41 #include "telnet_module.h"
42
43 using namespace snort;
44
45 #define NUL 0x00
46 #define CR 0x0d
47 #define LF 0x0a
48
49 /* This is the allowable number of 8 bit characters,
50 * ie, non-ASCII, before we declare this packet/stream
51 * as encrypted.
52 */
53 #define CONSECUTIVE_8BIT_THRESHOLD 3
54
reset_telnet_buffer(Packet * p)55 void reset_telnet_buffer(Packet* p)
56 {
57 DetectionEngine::get_alt_buffer(p).len = 0;
58 }
59
get_telnet_buffer(Packet * p,unsigned & len)60 const uint8_t* get_telnet_buffer(Packet* p, unsigned& len)
61 {
62 const DataBuffer& buf = DetectionEngine::get_alt_buffer(p);
63 len = buf.len;
64 return len ? buf.data : nullptr;
65 }
66
normalize_telnet(TELNET_SESSION * tnssn,Packet * p,DataBuffer & buf,int iMode,char ignoreEraseCmds,bool on_ftp_channel)67 int normalize_telnet(
68 TELNET_SESSION* tnssn, Packet* p, DataBuffer& buf,
69 int iMode, char ignoreEraseCmds, bool on_ftp_channel)
70 {
71 int ret = FTPP_NORMALIZED;
72 const unsigned char* read_ptr, * sb_start = nullptr;
73 unsigned char* write_ptr;
74 const unsigned char* end;
75 int normalization_required = 0;
76 int consec_8bit_chars = 0;
77
78 const unsigned char* start = buf.data;
79 buf.len = 0;
80
81 /* Telnet commands are handled in here.
82 * They can be 2 bytes long -- ie, IAC NOP, IAC AYT, etc.
83 * Sub-negotiation strings are at least 4 bytes, IAC SB x IAC SE */
84 if (p->dsize < 2)
85 {
86 if (tnssn && iMode == FTPP_SI_CLIENT_MODE)
87 tnssn->consec_ayt = 0;
88 return FTPP_SUCCESS;
89 }
90
91 /* setup the pointers */
92 read_ptr = p->data;
93 end = p->data + p->dsize;
94
95 /* look to see if we have any telnet negotiation codes in the data */
96 while (!normalization_required && (read_ptr < end))
97 {
98 /* look for the start of a negotiation string */
99 if (*read_ptr == (unsigned char)TNC_IAC)
100 {
101 /* set a flag for stage 2 normalization */
102 normalization_required = 1;
103 }
104 else
105 {
106 if ( on_ftp_channel )
107 {
108 return FTPP_SUCCESS;
109 }
110 /* Okay, it wasn't an IAC also its a midstream pickup */
111 if (*read_ptr > 0x7F && Stream::is_midstream(p->flow))
112 {
113 consec_8bit_chars++;
114 if (consec_8bit_chars > CONSECUTIVE_8BIT_THRESHOLD)
115 {
116 /* This data stream had a series of 8 bit characters.
117 * It is very likely encrypted. This handles the case
118 * where we either missed the option negotiation, or
119 * lost state of an already encrypted telnet session.
120 */
121 if (tnssn)
122 {
123 tnssn->encr_state = 1;
124 DetectionEngine::queue_event(GID_TELNET, TELNET_ENCRYPTED);
125
126 if (!tnssn->telnet_conf->check_encrypted_data)
127 {
128 /* Mark this session & packet as one to ignore */
129 Stream::stop_inspection(p->flow, p, SSN_DIR_BOTH, -1, 0);
130 /* No point to do further normalization */
131 return FTPP_ALERT;
132 }
133 }
134 break;
135 }
136 }
137 else
138 {
139 consec_8bit_chars = 0;
140 }
141 }
142
143 read_ptr++;
144 }
145
146 if (!normalization_required)
147 {
148 if (tnssn && iMode == FTPP_SI_CLIENT_MODE)
149 tnssn->consec_ayt = 0;
150 return FTPP_SUCCESS;
151 }
152
153 /*
154 * if we found telnet negotiation strings OR backspace characters,
155 * we're going to have to normalize the data
156 *
157 * Note that this is always ( now: 2002-08-12 ) done to a
158 * alternative data buffer.
159 */
160 /* rewind the data stream to p->data */
161 read_ptr = p->data;
162
163 /* setup for overwriting the negotiation strings with
164 * the follow-on data
165 */
166 write_ptr = (unsigned char*)buf.data;
167
168 /* walk thru the remainder of the packet */
169 while ((read_ptr < end) &&
170 (write_ptr < ((unsigned char*)buf.data) + sizeof(buf.data)))
171 {
172 /* if the following byte isn't a subnegotiation initialization */
173 if (((read_ptr + 1) < end) &&
174 (*read_ptr == (unsigned char)TNC_IAC) &&
175 (*(read_ptr + 1) != (unsigned char)TNC_SB))
176 {
177 int saw_ayt = 0;
178
179 /* NOPs are two bytes long */
180 switch (*((const unsigned char*)(read_ptr + 1)))
181 {
182 case TNC_NOP:
183 read_ptr += 2;
184 break;
185 case TNC_EAC:
186 read_ptr += 2;
187 /* wind it back a character? */
188 if (ignoreEraseCmds == FTPP_APPLY_TNC_ERASE_CMDS)
189 {
190 if (write_ptr > start)
191 {
192 write_ptr--;
193 buf.len--;
194 }
195 }
196 break;
197 case TNC_EAL:
198 read_ptr += 2;
199 /* wind it back a line? */
200 if (ignoreEraseCmds == FTPP_APPLY_TNC_ERASE_CMDS)
201 {
202 /* Go back to previous CR null or CR LF? */
203 while (write_ptr > start)
204 {
205 /* Go to previous char */
206 write_ptr--;
207 buf.len--;
208
209 if ((*write_ptr == CR) &&
210 ((*(write_ptr+1) == NUL) || (*(write_ptr+1) == LF)) )
211 {
212 /* Okay, found the CR NUL or CR LF, move it
213 * forward past those two -- that is the
214 * beginning of this line
215 */
216 write_ptr+=2;
217 buf.len+=2;
218 break;
219 }
220 }
221 }
222 break;
223 /* These are two bytes long */
224 case TNC_AYT:
225 saw_ayt = 1;
226 if (tnssn)
227 {
228 tnssn->consec_ayt++;
229 if ((tnssn->telnet_conf->ayt_threshold > 0) &&
230 (tnssn->consec_ayt >
231 tnssn->telnet_conf->ayt_threshold))
232 {
233 /* Alert on consecutive AYT commands */
234 DetectionEngine::queue_event(GID_TELNET, TELNET_AYT_OVERFLOW);
235 tnssn->consec_ayt = 0;
236 return FTPP_ALERT;
237 }
238 }
239 /* Fall through */
240 case TNC_BRK:
241 case TNC_DM:
242 case TNC_IP:
243 case TNC_AO:
244 case TNC_GA:
245 #ifdef RFC1184
246 case TNC_EOF:
247 case TNC_SUSP:
248 case TNC_ABOR:
249 #endif
250 #ifdef RFC885
251 case TNC_EOR:
252 #endif
253 read_ptr += 2;
254 break;
255 case TNC_SE:
256 /* Uh, what the heck is a Subnegotiation-end
257 * doing here without SB?. could generate an alert.
258 * Will just normalize it out since we may have
259 * processed the SB in a previous packet.
260 */
261 read_ptr += 2;
262 break;
263 case TNC_IAC:
264 /* IAC IAC -- means the IAC character (0xff) should be
265 * in the data stream since it was escaped */
266 read_ptr++; /* skip past the first IAC */
267 *write_ptr++ = *read_ptr++;
268 buf.len++;
269 break;
270 case TNC_WILL:
271 case TNC_WONT:
272 case TNC_DO:
273 case TNC_DONT:
274 read_ptr += 3;
275 break;
276 default:
277 /* move the read ptr up 2 bytes */
278 read_ptr += 2;
279 }
280 /* If not an AYT, reset it */
281 if (!saw_ayt)
282 {
283 if (tnssn && iMode == FTPP_SI_CLIENT_MODE)
284 tnssn->consec_ayt = 0;
285 }
286 }
287 /* check for subnegotiation */
288 else if (((read_ptr + 1) < end) &&
289 (*read_ptr == (unsigned char)TNC_IAC) &&
290 (*(read_ptr+1) == (unsigned char)TNC_SB))
291 {
292 sb_start = read_ptr;
293
294 switch (*(read_ptr+2))
295 {
296 case 0x26: /* Encryption -- RFC 2946 */
297 /* printf("Telnet: Saw SB for Encryption\n"); */
298 read_ptr += 3;
299 switch (*read_ptr)
300 {
301 #ifdef TRACK_ENCRYPTION_NEGOTIATION
302 case 0x00:
303 /* Client sending the Encryption IS marker
304 * followed by address. */
305 {
306 read_ptr++;
307 if (*read_ptr != 0x00)
308 /* Encryption type is not null */
309 {
310 /* printf("Encryption being negotiated by
311 * telnet client\n"); */
312 }
313 }
314 break;
315 #endif
316 case 0x03:
317 /* Client sending the Encryption START marker
318 * followed by address. */
319 {
320 read_ptr++;
321 /* printf("Encryption started by telnet client\n"); */
322 if (tnssn)
323 {
324 tnssn->encr_state = 1;
325 DetectionEngine::queue_event(GID_TELNET, TELNET_ENCRYPTED);
326
327 if (!tnssn->telnet_conf->check_encrypted_data)
328 {
329 /* Mark this session & packet as one to ignore */
330 Stream::stop_inspection(p->flow, p, SSN_DIR_BOTH, -1, 0);
331 /* No point to do further normalization */
332 return FTPP_ALERT;
333 }
334 }
335 }
336 break;
337 }
338 break;
339 }
340
341 /* find the end of the subneg -- this handles when there are
342 * embedded IAC IACs within a sub negotiation. Just looking
343 * for the TNC_SE could cause problems. Similarly, just looking
344 * for the TNC_IAC could end it too early. */
345 while (read_ptr < end)
346 {
347 if ((*read_ptr == (unsigned char)TNC_IAC) &&
348 (*(read_ptr+1) == (unsigned char)TNC_SE))
349 {
350 sb_start = nullptr;
351 break;
352 }
353 read_ptr++;
354 }
355
356 if (sb_start)
357 {
358 /* Didn't find the IAC SE. Normalize out the IAC SB
359 * and restart from there. Presumption is this is
360 * just someone trying to fool us, since we usually
361 * see the entire IAC SB ... IAC SE in one packet. */
362 read_ptr = sb_start+2;
363 if (!tnssn)
364 {
365 /* Its an FTP session */
366 ret = FTPP_ALERT;
367 }
368 else
369 {
370 /* Alert on SB without SE */
371 DetectionEngine::queue_event(GID_TELNET, TELNET_SB_NO_SE);
372 ret = FTPP_ALERT;
373 }
374
375 continue;
376 }
377
378 /* Okay, found the IAC SE -- move past it */
379 if (read_ptr < end)
380 {
381 read_ptr += 2;
382 }
383
384 if (tnssn && iMode == FTPP_SI_CLIENT_MODE)
385 tnssn->consec_ayt = 0;
386 }
387 else
388 {
389 /* overwrite the negotiation bytes with the follow-on bytes */
390 switch (*((const unsigned char*)(read_ptr)))
391 {
392 case 0x7F: /* Delete */
393 case 0x08: /* Backspace/Ctrl-H */
394 /* wind it back a character */
395 if (write_ptr > start)
396 {
397 write_ptr--;
398 buf.len--;
399 }
400 read_ptr++;
401 break;
402 default:
403 *write_ptr++ = *read_ptr++;
404 buf.len++;
405 break;
406 }
407
408 if (tnssn && iMode == FTPP_SI_CLIENT_MODE)
409 tnssn->consec_ayt = 0;
410 }
411 }
412
413 return ret;
414 }
415
416