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