1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2011-2013 Sourcefire, Inc.
4  *
5  *  The code is based on Flasm, command line assembler & disassembler of Flash
6  *  ActionScript bytecode Copyright (c) 2001 Opaque Industries, (c) 2002-2007
7  *  Igor Kogan, (c) 2005 Wang Zhen. All rights reserved.
8  *
9  *  Redistribution and use in source and binary forms, with or without modification,
10  *  are permitted provided that the following conditions are met:
11  *
12  *  - Redistributions of source code must retain the above copyright notice, this list
13  *  of conditions and the following disclaimer.
14  *  - Redistributions in binary form must reproduce the above copyright notice, this
15  *  list of conditions and the following disclaimer in the documentation and/or other
16  *  materials provided with the distribution.
17  *  - Neither the name of the Opaque Industries nor the names of its contributors may
18  *  be used to endorse or promote products derived from this software without specific
19  *  prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
22  *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24  *  SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26  *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29  *  WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #if HAVE_CONFIG_H
33 #include "clamav-config.h"
34 #endif
35 
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <sys/stat.h>
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 #include <time.h>
46 #include <zlib.h>
47 
48 #include "swf.h"
49 #include "clamav.h"
50 #include "scanners.h"
51 #include "lzma_iface.h"
52 
53 #define EC16(v) le16_to_host(v)
54 #define EC32(v) le32_to_host(v)
55 
56 #define INITBITS                                                                       \
57     {                                                                                  \
58         if (fmap_readn(map, &get_c, offset, sizeof(get_c)) == sizeof(get_c)) {         \
59             bitpos = 8;                                                                \
60             bitbuf = (unsigned int)get_c;                                              \
61             offset += sizeof(get_c);                                                   \
62         } else {                                                                       \
63             cli_warnmsg("cli_scanswf: INITBITS: Can't read file or file truncated\n"); \
64             return CL_EFORMAT;                                                         \
65         }                                                                              \
66     }
67 
68 #define GETBITS(v, n)                                                                     \
69     {                                                                                     \
70         getbits_n = n;                                                                    \
71         bits      = 0;                                                                    \
72         while (getbits_n > bitpos) {                                                      \
73             getbits_n -= bitpos;                                                          \
74             bits |= bitbuf << getbits_n;                                                  \
75             if (fmap_readn(map, &get_c, offset, sizeof(get_c)) == sizeof(get_c)) {        \
76                 bitbuf = (unsigned int)get_c;                                             \
77                 bitpos = 8;                                                               \
78                 offset += sizeof(get_c);                                                  \
79             } else {                                                                      \
80                 cli_warnmsg("cli_scanswf: GETBITS: Can't read file or file truncated\n"); \
81                 return CL_EFORMAT;                                                        \
82             }                                                                             \
83         }                                                                                 \
84         bitpos -= getbits_n;                                                              \
85         bits |= bitbuf >> bitpos;                                                         \
86         bitbuf &= 0xff >> (8 - bitpos);                                                   \
87         v = bits & 0xffff;                                                                \
88     }
89 
90 #define GETWORD(v)                                                                    \
91     {                                                                                 \
92         if (fmap_readn(map, &get_c, offset, sizeof(get_c)) == sizeof(get_c)) {        \
93             getword_1 = (unsigned int)get_c;                                          \
94             offset += sizeof(get_c);                                                  \
95         } else {                                                                      \
96             cli_warnmsg("cli_scanswf: GETWORD: Can't read file or file truncated\n"); \
97             return CL_EFORMAT;                                                        \
98         }                                                                             \
99         if (fmap_readn(map, &get_c, offset, sizeof(get_c)) == sizeof(get_c)) {        \
100             getword_2 = (unsigned int)get_c;                                          \
101             offset += sizeof(get_c);                                                  \
102         } else {                                                                      \
103             cli_warnmsg("cli_scanswf: GETWORD: Can't read file or file truncated\n"); \
104             return CL_EFORMAT;                                                        \
105         }                                                                             \
106         v = (uint16_t)(getword_1 & 0xff) | ((getword_2 & 0xff) << 8);                 \
107     }
108 
109 #define GETDWORD(v)                                      \
110     {                                                    \
111         GETWORD(getdword_1);                             \
112         GETWORD(getdword_2);                             \
113         v = (uint32_t)(getdword_1 | (getdword_2 << 16)); \
114     }
115 
116 struct swf_file_hdr {
117     char signature[3];
118     uint8_t version;
119     uint32_t filesize;
120 };
121 
scanzws(cli_ctx * ctx,struct swf_file_hdr * hdr)122 static int scanzws(cli_ctx *ctx, struct swf_file_hdr *hdr)
123 {
124     struct CLI_LZMA lz;
125     unsigned char inbuff[FILEBUFF], outbuff[FILEBUFF];
126     fmap_t *map = ctx->fmap;
127     /* strip off header */
128     off_t offset = 8;
129     uint32_t d_insize;
130     size_t outsize = 8;
131     int ret, lret;
132     size_t count;
133     char *tmpname;
134     int fd;
135 
136     if ((ret = cli_gentempfd(ctx->sub_tmpdir, &tmpname, &fd)) != CL_SUCCESS) {
137         cli_errmsg("scanzws: Can't generate temporary file\n");
138         return ret;
139     }
140 
141     hdr->signature[0] = 'F';
142     if (cli_writen(fd, hdr, sizeof(struct swf_file_hdr)) != sizeof(struct swf_file_hdr)) {
143         cli_errmsg("scanzws: Can't write to file %s\n", tmpname);
144         close(fd);
145         if (cli_unlink(tmpname)) {
146             free(tmpname);
147             return CL_EUNLINK;
148         }
149         free(tmpname);
150         return CL_EWRITE;
151     }
152 
153     /* read 4 bytes (for compressed 32-bit filesize) [not used for LZMA] */
154     if (fmap_readn(map, &d_insize, offset, sizeof(d_insize)) != sizeof(d_insize)) {
155         cli_errmsg("scanzws: Error reading SWF file\n");
156         close(fd);
157         if (cli_unlink(tmpname)) {
158             free(tmpname);
159             return CL_EUNLINK;
160         }
161         free(tmpname);
162         return CL_EREAD;
163     }
164     offset += sizeof(d_insize);
165 
166     /* check if declared input size matches actual output size */
167     /* map->len = header (8 bytes) + d_insize (4 bytes) + flags (5 bytes) + compressed stream */
168     if (d_insize != (map->len - 17)) {
169         cli_warnmsg("SWF: declared input length != compressed stream size, %u != %llu\n",
170                     d_insize, (long long unsigned)(map->len - 17));
171     } else {
172         cli_dbgmsg("SWF: declared input length == compressed stream size, %u == %llu\n",
173                    d_insize, (long long unsigned)(map->len - 17));
174     }
175 
176     /* first buffer required for initializing LZMA */
177     ret = fmap_readn(map, inbuff, offset, FILEBUFF);
178     if (ret < 0) {
179         cli_errmsg("scanzws: Error reading SWF file\n");
180         close(fd);
181         if (cli_unlink(tmpname)) {
182             free(tmpname);
183             return CL_EUNLINK;
184         }
185         free(tmpname);
186         return CL_EUNPACK;
187     }
188     /* nothing written, likely truncated */
189     if (!ret) {
190         cli_errmsg("scanzws: possibly truncated file\n");
191         close(fd);
192         if (cli_unlink(tmpname)) {
193             free(tmpname);
194             return CL_EUNLINK;
195         }
196         free(tmpname);
197         return CL_EFORMAT;
198     }
199     offset += ret;
200 
201     memset(&lz, 0, sizeof(lz));
202     lz.next_in   = inbuff;
203     lz.next_out  = outbuff;
204     lz.avail_in  = ret;
205     lz.avail_out = FILEBUFF;
206 
207     lret = cli_LzmaInit(&lz, hdr->filesize);
208     if (lret != LZMA_RESULT_OK) {
209         cli_errmsg("scanzws: LzmaInit() failed\n");
210         close(fd);
211         if (cli_unlink(tmpname)) {
212             free(tmpname);
213             return CL_EUNLINK;
214         }
215         free(tmpname);
216         return CL_EUNPACK;
217     }
218 
219     while (lret == LZMA_RESULT_OK) {
220         if (lz.avail_in == 0) {
221             lz.next_in = inbuff;
222 
223             ret = fmap_readn(map, inbuff, offset, FILEBUFF);
224             if (ret < 0) {
225                 cli_errmsg("scanzws: Error reading SWF file\n");
226                 cli_LzmaShutdown(&lz);
227                 close(fd);
228                 if (cli_unlink(tmpname)) {
229                     free(tmpname);
230                     return CL_EUNLINK;
231                 }
232                 free(tmpname);
233                 return CL_EUNPACK;
234             }
235             if (!ret)
236                 break;
237             lz.avail_in = ret;
238             offset += ret;
239         }
240         lret  = cli_LzmaDecode(&lz);
241         count = FILEBUFF - lz.avail_out;
242         if (count) {
243             if (cli_checklimits("SWF", ctx, outsize + count, 0, 0) != CL_SUCCESS)
244                 break;
245             if (cli_writen(fd, outbuff, count) != count) {
246                 cli_errmsg("scanzws: Can't write to file %s\n", tmpname);
247                 cli_LzmaShutdown(&lz);
248                 close(fd);
249                 if (cli_unlink(tmpname)) {
250                     free(tmpname);
251                     return CL_EUNLINK;
252                 }
253                 free(tmpname);
254                 return CL_EWRITE;
255             }
256             outsize += count;
257         }
258         lz.next_out  = outbuff;
259         lz.avail_out = FILEBUFF;
260     }
261 
262     cli_LzmaShutdown(&lz);
263 
264     if (lret != LZMA_STREAM_END && lret != LZMA_RESULT_OK) {
265         /* outsize starts at 8, therefore, if its still 8, nothing was decompressed */
266         if (outsize == 8) {
267             cli_infomsg(ctx, "scanzws: Error decompressing SWF file. No data decompressed.\n");
268             close(fd);
269             if (cli_unlink(tmpname)) {
270                 free(tmpname);
271                 return CL_EUNLINK;
272             }
273             free(tmpname);
274             return CL_EUNPACK;
275         }
276         cli_infomsg(ctx, "scanzws: Error decompressing SWF file. Scanning what was decompressed.\n");
277     }
278     cli_dbgmsg("SWF: Decompressed[LZMA] to %s, size %llu\n", tmpname, (long long unsigned)outsize);
279 
280     /* check if declared output size matches actual output size */
281     if (hdr->filesize != outsize) {
282         cli_warnmsg("SWF: declared output length != inflated stream size, %u != %llu\n",
283                     hdr->filesize, (long long unsigned)outsize);
284     } else {
285         cli_dbgmsg("SWF: declared output length == inflated stream size, %u == %llu\n",
286                    hdr->filesize, (long long unsigned)outsize);
287     }
288 
289     ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL);
290 
291     close(fd);
292     if (!(ctx->engine->keeptmp)) {
293         if (cli_unlink(tmpname)) {
294             free(tmpname);
295             return CL_EUNLINK;
296         }
297     }
298     free(tmpname);
299     return ret;
300 }
301 
scancws(cli_ctx * ctx,struct swf_file_hdr * hdr)302 static int scancws(cli_ctx *ctx, struct swf_file_hdr *hdr)
303 {
304     z_stream stream;
305     char inbuff[FILEBUFF], outbuff[FILEBUFF];
306     fmap_t *map    = ctx->fmap;
307     int offset     = 8, ret, zret, zend;
308     size_t outsize = 8;
309     size_t count;
310     char *tmpname;
311     int fd;
312 
313     if ((ret = cli_gentempfd(ctx->sub_tmpdir, &tmpname, &fd)) != CL_SUCCESS) {
314         cli_errmsg("scancws: Can't generate temporary file\n");
315         return ret;
316     }
317 
318     hdr->signature[0] = 'F';
319     if (cli_writen(fd, hdr, sizeof(struct swf_file_hdr)) != sizeof(struct swf_file_hdr)) {
320         cli_errmsg("scancws: Can't write to file %s\n", tmpname);
321         close(fd);
322         if (cli_unlink(tmpname)) {
323             free(tmpname);
324             return CL_EUNLINK;
325         }
326         free(tmpname);
327         return CL_EWRITE;
328     }
329 
330     stream.avail_in  = 0;
331     stream.next_in   = (Bytef *)inbuff;
332     stream.next_out  = (Bytef *)outbuff;
333     stream.zalloc    = (alloc_func)NULL;
334     stream.zfree     = (free_func)NULL;
335     stream.opaque    = (voidpf)0;
336     stream.avail_out = FILEBUFF;
337 
338     zret = inflateInit(&stream);
339     if (zret != Z_OK) {
340         cli_errmsg("scancws: inflateInit() failed\n");
341         close(fd);
342         if (cli_unlink(tmpname)) {
343             free(tmpname);
344             return CL_EUNLINK;
345         }
346         free(tmpname);
347         return CL_EUNPACK;
348     }
349 
350     do {
351         if (stream.avail_in == 0) {
352             stream.next_in = (Bytef *)inbuff;
353             ret            = fmap_readn(map, inbuff, offset, FILEBUFF);
354             if (ret < 0) {
355                 cli_errmsg("scancws: Error reading SWF file\n");
356                 close(fd);
357                 inflateEnd(&stream);
358                 if (cli_unlink(tmpname)) {
359                     free(tmpname);
360                     return CL_EUNLINK;
361                 }
362                 free(tmpname);
363                 return CL_EUNPACK;
364             }
365             if (!ret)
366                 break;
367             stream.avail_in = ret;
368             offset += ret;
369         }
370         zret  = inflate(&stream, Z_SYNC_FLUSH);
371         count = FILEBUFF - stream.avail_out;
372         if (count) {
373             if (cli_checklimits("SWF", ctx, outsize + count, 0, 0) != CL_SUCCESS)
374                 break;
375             if (cli_writen(fd, outbuff, count) != count) {
376                 cli_errmsg("scancws: Can't write to file %s\n", tmpname);
377                 inflateEnd(&stream);
378                 close(fd);
379                 if (cli_unlink(tmpname)) {
380                     free(tmpname);
381                     return CL_EUNLINK;
382                 }
383                 free(tmpname);
384                 return CL_EWRITE;
385             }
386             outsize += count;
387         }
388         stream.next_out  = (Bytef *)outbuff;
389         stream.avail_out = FILEBUFF;
390     } while (zret == Z_OK);
391 
392     zend = inflateEnd(&stream);
393 
394     if ((zret != Z_STREAM_END && zret != Z_OK) || zend != Z_OK) {
395         /*
396          * outsize is initialized to 8, it being 8 here means that we couldn't even read a single byte.
397          * If outsize > 8, then we have data. Let's scan what we have.
398          */
399         if (outsize == 8) {
400             cli_infomsg(ctx, "scancws: Error decompressing SWF file. No data decompressed.\n");
401             close(fd);
402             if (cli_unlink(tmpname)) {
403                 free(tmpname);
404                 return CL_EUNLINK;
405             }
406             free(tmpname);
407             return CL_EUNPACK;
408         }
409         cli_infomsg(ctx, "scancws: Error decompressing SWF file. Scanning what was decompressed.\n");
410     }
411     cli_dbgmsg("SWF: Decompressed[zlib] to %s, size %zu\n", tmpname, outsize);
412 
413     /* check if declared output size matches actual output size */
414     if (hdr->filesize != outsize) {
415         cli_warnmsg("SWF: declared output length != inflated stream size, %u != %zu\n",
416                     hdr->filesize, outsize);
417     } else {
418         cli_dbgmsg("SWF: declared output length == inflated stream size, %u == %zu\n",
419                    hdr->filesize, outsize);
420     }
421 
422     ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL);
423 
424     close(fd);
425     if (!ctx->engine->keeptmp) {
426         if (cli_unlink(tmpname)) {
427             free(tmpname);
428             return CL_EUNLINK;
429         }
430     }
431     free(tmpname);
432     return ret;
433 }
434 
tagname(tag_id id)435 static const char *tagname(tag_id id)
436 {
437     unsigned int i;
438 
439     for (i = 0; tag_names[i].name; i++)
440         if (tag_names[i].id == id)
441             return tag_names[i].name;
442     return NULL;
443 }
444 
cli_scanswf(cli_ctx * ctx)445 int cli_scanswf(cli_ctx *ctx)
446 {
447     struct swf_file_hdr file_hdr;
448     fmap_t *map = ctx->fmap;
449     unsigned int bitpos, bitbuf, getbits_n, nbits, getword_1, getword_2, getdword_1, getdword_2;
450     const char *pt;
451     unsigned char get_c;
452     size_t offset = 0;
453     unsigned int val, foo, tag_hdr, tag_type, tag_len;
454     unsigned long int bits;
455 
456     cli_dbgmsg("in cli_scanswf()\n");
457 
458     if (fmap_readn(map, &file_hdr, offset, sizeof(file_hdr)) != sizeof(file_hdr)) {
459         cli_dbgmsg("SWF: Can't read file header\n");
460         return CL_CLEAN;
461     }
462     offset += sizeof(file_hdr);
463     /*
464     **  SWF stores the integer bytes with the least significate byte first
465     */
466 
467     file_hdr.filesize = le32_to_host(file_hdr.filesize);
468 
469     cli_dbgmsg("SWF: Version: %u\n", file_hdr.version);
470     cli_dbgmsg("SWF: File size: %u\n", file_hdr.filesize);
471 
472     if (!strncmp(file_hdr.signature, "CWS", 3)) {
473         cli_dbgmsg("SWF: zlib compressed file\n");
474         return scancws(ctx, &file_hdr);
475     } else if (!strncmp(file_hdr.signature, "ZWS", 3)) {
476         cli_dbgmsg("SWF: LZMA compressed file\n");
477         return scanzws(ctx, &file_hdr);
478     } else if (!strncmp(file_hdr.signature, "FWS", 3)) {
479         cli_dbgmsg("SWF: Uncompressed file\n");
480     } else {
481         cli_dbgmsg("SWF: Not a SWF file\n");
482         return CL_CLEAN;
483     }
484 
485     INITBITS;
486 
487     GETBITS(nbits, 5);
488     cli_dbgmsg("SWF: FrameSize RECT size bits: %u\n", nbits);
489     {
490         uint32_t xMin = 0, xMax = 0, yMin = 0, yMax = 0;
491         GETBITS(xMin, nbits); /* Should be zero */
492         GETBITS(xMax, nbits);
493         GETBITS(yMin, nbits); /* Should be zero */
494         GETBITS(yMax, nbits);
495         cli_dbgmsg("SWF: FrameSize xMin %u xMax %u yMin %u yMax %u\n", xMin, xMax, yMin, yMax);
496     }
497 
498     GETWORD(foo);
499     GETWORD(val);
500     cli_dbgmsg("SWF: Frames total: %d\n", val);
501 
502     /* Skip Flash tag walk unless debug mode */
503     if (!cli_debug_flag) {
504         return CL_CLEAN;
505     }
506 
507     while (offset < map->len) {
508         GETWORD(tag_hdr);
509         tag_type = tag_hdr >> 6;
510         if (tag_type == 0)
511             break;
512         tag_len = tag_hdr & 0x3f;
513         if (tag_len == 0x3f)
514             GETDWORD(tag_len);
515 
516         pt = tagname(tag_type);
517         cli_dbgmsg("SWF: %s\n", pt ? pt : "UNKNOWN TAG");
518         cli_dbgmsg("SWF: Tag length: %u\n", tag_len);
519         if (tag_len > map->len) {
520             cli_dbgmsg("SWF: Invalid tag length.\n");
521             return CL_EFORMAT;
522         }
523         if ((offset + tag_len) < offset) {
524             cli_warnmsg("SWF: Tag length too large.\n");
525             break;
526         }
527         if (!pt) {
528             offset += tag_len;
529             continue;
530         }
531 
532         switch (tag_type) {
533             case TAG_SCRIPTLIMITS: {
534                 unsigned int recursion, timeout;
535                 GETWORD(recursion);
536                 GETWORD(timeout);
537                 cli_dbgmsg("SWF: scriptLimits recursion %u timeout %u\n", recursion, timeout);
538                 break;
539             }
540 
541             case TAG_FILEATTRIBUTES:
542                 GETDWORD(val);
543                 cli_dbgmsg("SWF: File attributes:\n");
544                 if (val & SWF_ATTR_USENETWORK)
545                     cli_dbgmsg("    * Use network\n");
546                 if (val & SWF_ATTR_RELATIVEURLS)
547                     cli_dbgmsg("    * Relative URLs\n");
548                 if (val & SWF_ATTR_SUPPRESSCROSSDOMAINCACHE)
549                     cli_dbgmsg("    * Suppress cross domain cache\n");
550                 if (val & SWF_ATTR_ACTIONSCRIPT3)
551                     cli_dbgmsg("    * ActionScript 3.0\n");
552                 if (val & SWF_ATTR_HASMETADATA)
553                     cli_dbgmsg("    * Has metadata\n");
554                 if (val & SWF_ATTR_USEDIRECTBLIT)
555                     cli_dbgmsg("    * Use hardware acceleration\n");
556                 if (val & SWF_ATTR_USEGPU)
557                     cli_dbgmsg("    * Use GPU\n");
558                 break;
559 
560             default:
561                 offset += tag_len;
562                 continue;
563         }
564     }
565 
566     return CL_CLEAN;
567 }
568