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