1 //--------------------------------------------------------------------------
2 // Copyright (C) 2019-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // file_decomp_zip.cc author Brandon Stultz <brastult@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "file_decomp_zip.h"
25
26 #include "helpers/boyer_moore_search.h"
27 #include "utils/util.h"
28
29 using namespace snort;
30
31 // initialize zlib decompression
Inflate_Init(fd_session_t * SessionPtr)32 static fd_status_t Inflate_Init(fd_session_t* SessionPtr)
33 {
34 z_stream* z_s = &(SessionPtr->ZIP->Stream);
35
36 memset((char*)z_s, 0, sizeof(z_stream));
37
38 z_s->zalloc = (alloc_func)nullptr;
39 z_s->zfree = (free_func)nullptr;
40
41 SYNC_IN(z_s)
42
43 int z_ret = inflateInit2(z_s, -MAX_WBITS);
44
45 if ( z_ret != Z_OK )
46 return File_Decomp_Error;
47
48 return File_Decomp_OK;
49 }
50
51 // end zlib decompression
Inflate_End(fd_session_t * SessionPtr)52 static fd_status_t Inflate_End(fd_session_t* SessionPtr)
53 {
54 z_stream* z_s = &(SessionPtr->ZIP->Stream);
55
56 inflateEnd(z_s);
57
58 return File_Decomp_OK;
59 }
60
61 // perform zlib decompression
Inflate(fd_session_t * SessionPtr)62 static fd_status_t Inflate(fd_session_t* SessionPtr)
63 {
64 const uint8_t *zlib_start, *zlib_end;
65
66 z_stream* z_s = &(SessionPtr->ZIP->Stream);
67
68 zlib_start = SessionPtr->Next_In;
69
70 SYNC_IN(z_s)
71
72 int z_ret = inflate(z_s, Z_SYNC_FLUSH);
73
74 SYNC_OUT(z_s)
75
76 zlib_end = SessionPtr->Next_In;
77
78 // keep track of decompression progress
79 SessionPtr->ZIP->progress += zlib_end - zlib_start;
80
81 if ( z_ret == Z_STREAM_END )
82 return File_Decomp_Complete;
83
84 if ( z_ret != Z_OK )
85 return File_Decomp_Error;
86
87 if ( SessionPtr->Avail_Out == 0 and SessionPtr->Avail_In > 0)
88 return File_Decomp_BlockOut;
89
90 return File_Decomp_OK;
91 }
92
93 // allocate and set initial ZIP state
File_Decomp_Init_ZIP(fd_session_t * SessionPtr)94 fd_status_t File_Decomp_Init_ZIP(fd_session_t* SessionPtr)
95 {
96 if ( SessionPtr == nullptr )
97 return File_Decomp_Error;
98
99 SessionPtr->ZIP = (fd_ZIP_t*)snort_calloc(sizeof(fd_ZIP_t));
100
101 // file_decomp.cc already matched the local header
102 // skip the version (2 bytes)
103 SessionPtr->ZIP->State = ZIP_STATE_SKIP;
104 SessionPtr->ZIP->Length = 2;
105
106 // land on bitflag
107 SessionPtr->ZIP->Next = ZIP_STATE_BITFLAG;
108 SessionPtr->ZIP->Next_Length = 2;
109
110 return File_Decomp_OK;
111 }
112
113 // end ZIP processing
File_Decomp_End_ZIP(fd_session_t * SessionPtr)114 fd_status_t File_Decomp_End_ZIP(fd_session_t* SessionPtr)
115 {
116 if ( SessionPtr == nullptr )
117 return File_Decomp_Error;
118
119 // end zlib decompression if we are processing a stream
120 if ( SessionPtr->ZIP->State == ZIP_STATE_INFLATE )
121 Inflate_End(SessionPtr);
122
123 if (SessionPtr->ZIP->file_name)
124 {
125 delete[] SessionPtr->ZIP->file_name;
126 SessionPtr->ZIP->file_name = nullptr;
127 }
128
129 if ( SessionPtr->ZIP->header_searcher )
130 {
131 delete SessionPtr->ZIP->header_searcher;
132 SessionPtr->ZIP->header_searcher = nullptr;
133 }
134
135 // File_Decomp_Free() will free SessionPtr->ZIP
136 return File_Decomp_OK;
137 }
138
139 // run the ZIP state machine
File_Decomp_ZIP(fd_session_t * SessionPtr)140 fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
141 {
142 uint8_t byte;
143 bool output_blocked = false;
144
145 if ( SessionPtr == nullptr )
146 return File_Decomp_Error;
147
148 fd_ZIP_t* parser = SessionPtr->ZIP;
149
150 // while we have data to read in the stream
151 while ( SessionPtr->Avail_In > 0 )
152 {
153 if ( SessionPtr->Next_In == nullptr )
154 return File_Decomp_Error;
155
156 switch ( parser->State )
157 {
158 // local header
159 case ZIP_STATE_LH:
160 // check if we are done with the local_header
161 if ( parser->Index == parser->Length )
162 {
163 // check if we read a local_header
164 if ( parser->local_header != ZIP_LOCAL_HEADER )
165 return output_blocked ? File_Decomp_BlockOut : File_Decomp_Complete;
166
167 // read a local_header, reset the index
168 parser->Index = 0;
169
170 // reset ZIP fields
171 parser->local_header = 0;
172 parser->bitflag = 0;
173 parser->data_descriptor = false;
174 parser->method = 0;
175 parser->compressed_size = 0;
176 parser->filename_length = 0;
177 parser->extra_length = 0;
178
179 if (parser->file_name)
180 {
181 delete[] parser->file_name;
182 parser->file_name = nullptr;
183 }
184
185 // reset decompression progress
186 parser->progress = 0;
187
188 // skip the version (2 bytes)
189 parser->State = ZIP_STATE_SKIP;
190 parser->Length = 2;
191
192 // land on bitflag
193 parser->Next = ZIP_STATE_BITFLAG;
194 parser->Next_Length = 2;
195 continue;
196 }
197 // read the local header
198 byte = *SessionPtr->Next_In;
199 parser->local_header |= byte << (parser->Index * 8);
200 break;
201 // bitflag
202 case ZIP_STATE_BITFLAG:
203 // check if we are done with the bitflag
204 if ( parser->Index == parser->Length )
205 {
206 // read the bitflag, reset the index
207 parser->Index = 0;
208
209 // check the data descriptor bit
210 if ( parser->bitflag & DATA_DESC_BIT )
211 parser->data_descriptor = true;
212
213 // read the compression method next
214 parser->State = ZIP_STATE_METHOD;
215 parser->Length = 2;
216 continue;
217 }
218 // read the bitflag
219 byte = *SessionPtr->Next_In;
220 parser->bitflag |= byte << (parser->Index * 8);
221 break;
222 // compression method
223 case ZIP_STATE_METHOD:
224 // check if we are done with the method
225 if ( parser->Index == parser->Length )
226 {
227 // read the method, reset the index
228 parser->Index = 0;
229
230 // skip:
231 // modtime(2), moddate(2), crc(4) = 8 bytes
232 parser->State = ZIP_STATE_SKIP;
233 parser->Length = 8;
234
235 // land on compressed size
236 parser->Next = ZIP_STATE_COMPSIZE;
237 parser->Next_Length = 4;
238 continue;
239 }
240 // read the method
241 byte = *SessionPtr->Next_In;
242 parser->method |= byte << (parser->Index * 8);
243 break;
244 // compressed size
245 case ZIP_STATE_COMPSIZE:
246 // check if we are done with the compressed size
247 if ( parser->Index == parser->Length )
248 {
249 // read the compressed size, reset the index
250 parser->Index = 0;
251
252 // skip the uncompressed size (4 bytes)
253 parser->State = ZIP_STATE_SKIP;
254 parser->Length = 4;
255
256 // land on filename length
257 parser->Next = ZIP_STATE_FILENAMELEN;
258 parser->Next_Length = 2;
259 continue;
260 }
261 // read the compressed size
262 byte = *SessionPtr->Next_In;
263 parser->compressed_size |= byte << (parser->Index * 8);
264 break;
265 // filename length
266 case ZIP_STATE_FILENAMELEN:
267 // check if we are done with the filename length
268 if ( parser->Index == parser->Length )
269 {
270 // read the filename length, reset the index
271 parser->Index = 0;
272
273 // read the extra field length next
274 parser->State = ZIP_STATE_EXTRALEN;
275 parser->Length = 2;
276 continue;
277 }
278 // read the filename length
279 byte = *SessionPtr->Next_In;
280 parser->filename_length |= byte << (parser->Index * 8);
281 break;
282 //extra length
283 case ZIP_STATE_EXTRALEN:
284 // check if we are done with the extra length
285 if ( parser->Index == parser->Length )
286 {
287 // read the extra length, reset the index
288 parser->Index = 0;
289
290 // read the filename next
291 if (parser->file_name == nullptr and parser->filename_length > 0)
292 parser->file_name = new char[parser->filename_length + 1];
293
294 parser->State = ZIP_STATE_FILENAME;
295 parser->Length = parser->filename_length;
296 continue;
297 }
298 // read the extra length
299 byte = *SessionPtr->Next_In;
300 parser->extra_length |= byte << (parser->Index * 8);
301 break;
302
303 // filename
304 case ZIP_STATE_FILENAME:
305 // check if we are done with filename
306 if ( parser->Index == parser->Length )
307 {
308 // read the extra length, reset the index
309 parser->Index = 0;
310 if (parser->file_name)
311 {
312 parser->file_name[parser->filename_length] = '\0';
313 }
314
315 //skip the extra fields
316 parser->State = ZIP_STATE_SKIP;
317 parser->Length = parser->extra_length;
318
319 if ( (SessionPtr->Avail_Out > 0) && (parser->method == 8) )
320 {
321 // we have available output space and
322 // the compression type is deflate (8),
323 // land on the compressed stream, init zlib
324 //If the filename ends with vbaProject.bin, then
325 //the file has vbaMacros.
326
327 if (( parser->filename_length >= MACRO_BINNAME_LEN )and (parser->file_name)and (strcmp(
328 parser->file_name + parser->filename_length - MACRO_BINNAME_LEN,
329 macro_binname)==0))
330 {
331 parser->Next = ZIP_STATE_OLE_FILE;
332 parser->Next_Length = 0;
333 continue;
334 }
335 parser->Next = ZIP_STATE_INFLATE_INIT;
336 parser->Next_Length = 0;
337 continue;
338 }
339
340 // no output space or compression type isn't deflate
341 if ( parser->data_descriptor )
342 {
343 // search for the next file
344 parser->Next = ZIP_STATE_SEARCH;
345 parser->Next_Length = 0;
346 continue;
347 }
348
349 // skip the stream
350 parser->Length += parser->compressed_size;
351
352 // land on another local header
353 parser->Next = ZIP_STATE_LH;
354 parser->Next_Length = 4;
355 continue;
356 }
357
358 //read the filename
359 if (parser->file_name && parser->Index < parser->filename_length)
360 parser->file_name[parser->Index] = (char)*SessionPtr->Next_In;
361 break;
362
363 case ZIP_STATE_OLE_FILE:
364 if (SessionPtr->vba_analysis)
365 SessionPtr->ole_data_ptr = SessionPtr->Next_Out;
366 //fallthrough
367 // initialize zlib inflate
368 case ZIP_STATE_INFLATE_INIT:
369 parser->State = ZIP_STATE_INFLATE;
370
371 if ( Inflate_Init(SessionPtr) == File_Decomp_Error )
372 return File_Decomp_Error;
373
374 // fallthrough
375 // perform zlib inflate
376 case ZIP_STATE_INFLATE:
377 {
378 // run inflate
379 fd_status_t status = Inflate(SessionPtr);
380
381 if ( status == File_Decomp_Error )
382 {
383 // error inflating the stream
384 // File_Decomp_End_ZIP() will
385 // close the inflate stream
386 return File_Decomp_Error;
387 }
388
389 if (SessionPtr->ole_data_ptr)
390 SessionPtr->ole_data_len = SessionPtr->Next_Out - SessionPtr->ole_data_ptr;
391
392 if ( status == File_Decomp_BlockOut )
393 {
394 // ran out of output space
395 // Save this status because we need to return it to the inspector so it can alert
396 output_blocked = true;
397 if ( parser->data_descriptor )
398 {
399 // close the inflate stream
400 Inflate_End(SessionPtr);
401
402 // search for next file
403 parser->State = ZIP_STATE_SEARCH;
404 parser->Length = 0;
405 continue;
406 }
407
408 // progress should be < compressed_size
409 if ( parser->progress >= parser->compressed_size )
410 return File_Decomp_Error;
411
412 // close the inflate stream
413 Inflate_End(SessionPtr);
414
415 // skip the rest of the stream
416 parser->State = ZIP_STATE_SKIP;
417 parser->Length = parser->compressed_size - parser->progress;
418
419 // land on another local header
420 parser->Next = ZIP_STATE_LH;
421 parser->Next_Length = 4;
422 continue;
423 }
424
425 if ( status == File_Decomp_Complete )
426 {
427 // done decompressing the stream
428 // close the inflate stream
429 Inflate_End(SessionPtr);
430
431 if ( parser->data_descriptor )
432 {
433 // search for the next file
434 parser->State = ZIP_STATE_SEARCH;
435 parser->Length = 0;
436 continue;
437 }
438
439 // parse next local header
440 parser->State = ZIP_STATE_LH;
441 parser->Length = 4;
442 continue;
443 }
444
445 // keep the inflate stream open
446 // circle back for more input
447 return File_Decomp_OK;
448 }
449 // search state
450 case ZIP_STATE_SEARCH:
451 {
452 if ( parser->header_searcher == nullptr )
453 {
454 // initialize local file header searcher
455 parser->header_searcher = new BoyerMooreSearchCase(
456 header_pattern, sizeof(header_pattern));
457 }
458
459 // search for the next local file header
460 int pos = parser->header_searcher->search(
461 SessionPtr->Next_In, SessionPtr->Avail_In);
462
463 if ( pos < 0 )
464 {
465 // not found, skip the rest of this flush
466 parser->State = ZIP_STATE_SKIP;
467 parser->Length = SessionPtr->Avail_In;
468
469 // search for next file
470 parser->Next = ZIP_STATE_SEARCH;
471 parser->Next_Length = 0;
472 continue;
473 }
474
475 // found, skip the rest of this file
476 parser->State = ZIP_STATE_SKIP;
477 parser->Length = pos;
478
479 // land on local header
480 parser->Next = ZIP_STATE_LH;
481 parser->Next_Length = 4;
482 continue;
483 }
484 // skip state
485 case ZIP_STATE_SKIP:
486 // check if we need to skip
487 if ( parser->Index < parser->Length )
488 {
489 uint32_t skip = parser->Length - parser->Index;
490
491 // check if we can skip within this flush
492 if ( SessionPtr->Avail_In < skip )
493 {
494 // the available input is < skip
495 parser->Index += SessionPtr->Avail_In;
496
497 uint32_t min = SessionPtr->Avail_In < SessionPtr->Avail_Out ?
498 SessionPtr->Avail_In : SessionPtr->Avail_Out;
499
500 // copy what we can
501 Move_N(SessionPtr, min);
502
503 // get more input
504 return output_blocked ? File_Decomp_BlockOut : File_Decomp_BlockIn;
505 }
506
507 if ( SessionPtr->Avail_Out < skip )
508 {
509 // the available input is >= skip
510 // the available output is < skip <= available input
511 skip -= SessionPtr->Avail_Out;
512 // copy what we can
513 Move_N(SessionPtr, SessionPtr->Avail_Out);
514 // available output is now 0
515 // skip the rest
516 SessionPtr->Next_In += skip;
517 SessionPtr->Avail_In -= skip;
518 SessionPtr->Total_In += skip;
519 // done skipping, index should be 0
520 }
521 else
522 {
523 // the available input is >= skip
524 // the available output is >= skip
525 // copy skip bytes from input to output
526 Move_N(SessionPtr, skip);
527 // done skipping, index should be 0
528 }
529 }
530 // done skipping, reset the index
531 parser->Index = 0;
532
533 // switch to the next state
534 parser->State = parser->Next;
535 parser->Length = parser->Next_Length;
536 continue;
537 }
538
539 // make sure we can write a byte
540 if ( SessionPtr->Avail_Out > 0 )
541 {
542 // advance and copy the stream
543 Move_1(SessionPtr);
544 }
545 else
546 {
547 // advance the stream
548 SessionPtr->Next_In += 1;
549 SessionPtr->Avail_In -= 1;
550 SessionPtr->Total_In += 1;
551 }
552
553 parser->Index++;
554 }
555
556 return output_blocked ? File_Decomp_BlockOut : File_Decomp_BlockIn;
557 }
558
559