Notably the commit adds a heuristic alert when VBA is extracted using
the new VBA extraction code and similarly adds "HasMacros":true to the
JSON scan properties.
In addition, a change was added to the cli_sanitize_filepath() function
so it converts posix pathseps to Windows pathseps on Windows and also
outputs a sanitized basename pointer (optional) which is used when
generating a temporary filename so that using a prefix with pathseps in
it won't cause file creation failures (observed with --leave-temps where
original filenames are incorporated into temporarily filenames).
Included soem error handling improvements for cli_vba_scandir() to
better track alert and macro detections.
Downgraded utf8 conversion error messages to debug messages because they
are too verbose in files with invalid filenames (observed in some
malware).
Changed the xlm macro and vba project temp filenames to include
"xlm_macros" and "vba_project" prefix, to make it easier to find them.
Relocated XLM and VBA temp files from the top-level tmp directory to the
current sub_tmpdir, so tempfiles for a given scan are more organized.
... | ... |
@@ -793,11 +793,16 @@ const char *cli_gettmpdir(void); |
793 | 793 |
/** |
794 | 794 |
* @brief Sanitize a relative path, so it cannot have a negative depth. |
795 | 795 |
* |
796 |
- * Caller is responsible for freeing the filename. |
|
797 |
- * |
|
798 |
- * @return char* filename or NULL. |
|
796 |
+ * Caller is responsible for freeing the sanitized filepath. |
|
797 |
+ * The optioal sanitized_filebase output param is a pointer into the filepath, |
|
798 |
+ * if set, and does not need to be freed. |
|
799 |
+ * |
|
800 |
+ * @param filepath The filepath to sanitize |
|
801 |
+ * @param filepath_len The length of the filepath |
|
802 |
+ * @param[out] sanitized_filebase Pointer to the basename portion of the sanitized filepath. (optional) |
|
803 |
+ * @return char* |
|
799 | 804 |
*/ |
800 |
-char *cli_sanitize_filepath(const char *filepath, size_t filepath_len); |
|
805 |
+char *cli_sanitize_filepath(const char *filepath, size_t filepath_len, char **sanitized_filebase); |
|
801 | 806 |
|
802 | 807 |
/** |
803 | 808 |
* @brief Generate tempfile filename (no path) with a random MD5 hash. |
... | ... |
@@ -55,6 +55,7 @@ |
55 | 55 |
|
56 | 56 |
#include "clamav.h" |
57 | 57 |
#include "others.h" |
58 |
+#include "str.h" |
|
58 | 59 |
#include "platform.h" |
59 | 60 |
#include "regex/regex.h" |
60 | 61 |
#include "ltdl.h" |
... | ... |
@@ -849,7 +850,7 @@ unsigned int cli_rndnum(unsigned int max) |
849 | 849 |
return 1 + (unsigned int)(max * (rand() / (1.0 + RAND_MAX))); |
850 | 850 |
} |
851 | 851 |
|
852 |
-char *cli_sanitize_filepath(const char *filepath, size_t filepath_len) |
|
852 |
+char *cli_sanitize_filepath(const char *filepath, size_t filepath_len, char **sanitized_filebase) |
|
853 | 853 |
{ |
854 | 854 |
uint32_t depth = 0; |
855 | 855 |
size_t index = 0; |
... | ... |
@@ -900,9 +901,9 @@ char *cli_sanitize_filepath(const char *filepath, size_t filepath_len) |
900 | 900 |
} |
901 | 901 |
#ifdef _WIN32 |
902 | 902 |
/* |
903 |
- * Windows' POSIX style API's accept both "/" and "\\" style path separators. |
|
904 |
- * The following checks using POSIX style path separators on Windows. |
|
905 |
- */ |
|
903 |
+ * Windows' POSIX style API's accept both "/" and "\\" style path separators. |
|
904 |
+ * The following checks using POSIX style path separators on Windows. |
|
905 |
+ */ |
|
906 | 906 |
} else if (0 == strncmp(filepath + index, "/", strlen("/"))) { |
907 | 907 |
/* |
908 | 908 |
* Is "/". |
... | ... |
@@ -931,17 +932,39 @@ char *cli_sanitize_filepath(const char *filepath, size_t filepath_len) |
931 | 931 |
sanitized_index += strlen("../"); |
932 | 932 |
index += strlen("../"); |
933 | 933 |
depth--; |
934 |
+ |
|
935 |
+ /* Convert path separator to Windows separator */ |
|
936 |
+ sanitized_filepath[sanitized_index - 1] = '\\'; |
|
934 | 937 |
} |
935 | 938 |
#endif |
936 | 939 |
} else { |
937 | 940 |
/* |
938 | 941 |
* Is not "/", "./", or "../". |
939 | 942 |
*/ |
943 |
+ |
|
940 | 944 |
/* Find the next path separator. */ |
941 |
- next_pathsep = CLI_STRNSTR(filepath + index, PATHSEP, filepath_len - index); |
|
945 |
+#ifdef _WIN32 |
|
946 |
+ char *next_windows_pathsep = NULL; |
|
947 |
+#endif |
|
948 |
+ next_pathsep = CLI_STRNSTR(filepath + index, "/", filepath_len - index); |
|
949 |
+ |
|
950 |
+#ifdef _WIN32 |
|
951 |
+ /* Check for both types of separators. */ |
|
952 |
+ next_windows_pathsep = CLI_STRNSTR(filepath + index, "\\", filepath_len - index); |
|
953 |
+ if (NULL != next_windows_pathsep) { |
|
954 |
+ if ((NULL == next_pathsep) || (next_windows_pathsep < next_pathsep)) { |
|
955 |
+ next_pathsep = next_windows_pathsep; |
|
956 |
+ } |
|
957 |
+ } |
|
958 |
+#endif |
|
942 | 959 |
if (NULL == next_pathsep) { |
943 | 960 |
/* No more path separators, copy the rest (filename) into the sanitized path */ |
944 | 961 |
strncpy(sanitized_filepath + sanitized_index, filepath + index, filepath_len - index); |
962 |
+ |
|
963 |
+ if (NULL != sanitized_filebase) { |
|
964 |
+ /* Set output variable to point to the file base name */ |
|
965 |
+ *sanitized_filebase = sanitized_filepath + sanitized_index; |
|
966 |
+ } |
|
945 | 967 |
break; |
946 | 968 |
} |
947 | 969 |
next_pathsep += strlen(PATHSEP); /* Include the path separator in the copy */ |
... | ... |
@@ -951,6 +974,11 @@ char *cli_sanitize_filepath(const char *filepath, size_t filepath_len) |
951 | 951 |
sanitized_index += next_pathsep - (filepath + index); |
952 | 952 |
index += next_pathsep - (filepath + index); |
953 | 953 |
depth++; |
954 |
+ |
|
955 |
+#ifdef _WIN32 |
|
956 |
+ /* Convert path separator to Windows separator */ |
|
957 |
+ sanitized_filepath[sanitized_index - 1] = '\\'; |
|
958 |
+#endif |
|
954 | 959 |
} |
955 | 960 |
} |
956 | 961 |
|
... | ... |
@@ -966,16 +994,19 @@ done: |
966 | 966 |
#define SHORT_HASH_LENGTH 10 |
967 | 967 |
char *cli_genfname(const char *prefix) |
968 | 968 |
{ |
969 |
- char *sanitized_prefix = NULL; |
|
970 |
- char *fname = NULL; |
|
969 |
+ char *sanitized_prefix = NULL; |
|
970 |
+ char *sanitized_prefix_base = NULL; |
|
971 |
+ char *fname = NULL; |
|
971 | 972 |
unsigned char salt[16 + 32]; |
972 | 973 |
char *tmp; |
973 | 974 |
int i; |
974 | 975 |
size_t len; |
975 | 976 |
|
976 | 977 |
if (prefix && (strlen(prefix) > 0)) { |
977 |
- sanitized_prefix = cli_sanitize_filepath(prefix, strlen(prefix)); |
|
978 |
- len = strlen(sanitized_prefix) + strlen(".") + SHORT_HASH_LENGTH + 1; /* {prefix}.{SHORT_HASH_LENGTH}\0 */ |
|
978 |
+ sanitized_prefix = cli_sanitize_filepath(prefix, strlen(prefix), &sanitized_prefix_base); |
|
979 |
+ } |
|
980 |
+ if (NULL != sanitized_prefix_base) { |
|
981 |
+ len = strlen(sanitized_prefix_base) + strlen(".") + SHORT_HASH_LENGTH + 1; /* {prefix}.{SHORT_HASH_LENGTH}\0 */ |
|
979 | 982 |
} else { |
980 | 983 |
len = strlen("clamav-") + 48 + strlen(".tmp") + 1; /* clamav-{48}.tmp\0 */ |
981 | 984 |
} |
... | ... |
@@ -1001,21 +1032,21 @@ char *cli_genfname(const char *prefix) |
1001 | 1001 |
pthread_mutex_unlock(&cli_gentemp_mutex); |
1002 | 1002 |
#endif |
1003 | 1003 |
|
1004 |
- if (!tmp) { |
|
1004 |
+ if (NULL == tmp) { |
|
1005 | 1005 |
free(fname); |
1006 | 1006 |
cli_dbgmsg("cli_genfname: out of memory\n"); |
1007 | 1007 |
return NULL; |
1008 | 1008 |
} |
1009 | 1009 |
|
1010 |
- if (sanitized_prefix) { |
|
1011 |
- if (strlen(sanitized_prefix) > 0) { |
|
1012 |
- snprintf(fname, len, "%s.%.*s", sanitized_prefix, SHORT_HASH_LENGTH, tmp); |
|
1013 |
- } |
|
1014 |
- free(sanitized_prefix); |
|
1010 |
+ if (NULL != sanitized_prefix_base) { |
|
1011 |
+ snprintf(fname, len, "%s.%.*s", sanitized_prefix_base, SHORT_HASH_LENGTH, tmp); |
|
1015 | 1012 |
} else { |
1016 | 1013 |
snprintf(fname, len, "clamav-%s.tmp", tmp); |
1017 | 1014 |
} |
1018 | 1015 |
|
1016 |
+ if (NULL != sanitized_prefix) { |
|
1017 |
+ free(sanitized_prefix); |
|
1018 |
+ } |
|
1019 | 1019 |
free(tmp); |
1020 | 1020 |
|
1021 | 1021 |
return (fname); |
... | ... |
@@ -1567,14 +1567,15 @@ cl_error_t find_file(const char *filename, const char *dir, char *result, size_t |
1567 | 1567 |
* Scan an OLE directory for a VBA project. |
1568 | 1568 |
* Contrary to cli_vba_scandir, this function uses the dir file to locate VBA modules. |
1569 | 1569 |
*/ |
1570 |
-static cl_error_t cli_vba_scandir_new(const char *dirname, cli_ctx *ctx, struct uniq *U) |
|
1570 |
+static cl_error_t cli_vba_scandir_new(const char *dirname, cli_ctx *ctx, struct uniq *U, int *has_macros) |
|
1571 | 1571 |
{ |
1572 | 1572 |
cl_error_t ret = CL_SUCCESS; |
1573 | 1573 |
uint32_t hashcnt = 0; |
1574 | 1574 |
char *hash = NULL; |
1575 | 1575 |
char path[PATH_MAX]; |
1576 | 1576 |
char filename[PATH_MAX]; |
1577 |
- int tempfd = -1; |
|
1577 |
+ int tempfd = -1; |
|
1578 |
+ int viruses_found = 0; |
|
1578 | 1579 |
|
1579 | 1580 |
if (CL_SUCCESS != (ret = uniq_get(U, "dir", 3, &hash, &hashcnt))) { |
1580 | 1581 |
cli_dbgmsg("cli_vba_scandir_new: uniq_get('dir') failed with ret code (%d)!\n", ret); |
... | ... |
@@ -1591,7 +1592,7 @@ static cl_error_t cli_vba_scandir_new(const char *dirname, cli_ctx *ctx, struct |
1591 | 1591 |
|
1592 | 1592 |
if (CL_SUCCESS == find_file(filename, dirname, path, sizeof(path))) { |
1593 | 1593 |
cli_dbgmsg("cli_vba_scandir_new: Found dir file: %s\n", path); |
1594 |
- if ((ret = cli_vba_readdir_new(ctx, path, U, hash, hashcnt, &tempfd)) != CL_SUCCESS) { |
|
1594 |
+ if ((ret = cli_vba_readdir_new(ctx, path, U, hash, hashcnt, &tempfd, has_macros)) != CL_SUCCESS) { |
|
1595 | 1595 |
//FIXME: Since we only know the stream name of the OLE2 stream, but not its path inside the |
1596 | 1596 |
// OLE2 archive, we don't know if we have the right file. The only thing we can do is |
1597 | 1597 |
// iterate all of them until one succeeds. |
... | ... |
@@ -1601,6 +1602,30 @@ static cl_error_t cli_vba_scandir_new(const char *dirname, cli_ctx *ctx, struct |
1601 | 1601 |
continue; |
1602 | 1602 |
} |
1603 | 1603 |
|
1604 |
+#if HAVE_JSON |
|
1605 |
+ if (*has_macros && SCAN_COLLECT_METADATA && (ctx->wrkproperty != NULL)) { |
|
1606 |
+ cli_jsonbool(ctx->wrkproperty, "HasMacros", 1); |
|
1607 |
+ json_object *macro_languages = cli_jsonarray(ctx->wrkproperty, "MacroLanguages"); |
|
1608 |
+ if (macro_languages) { |
|
1609 |
+ cli_jsonstr(macro_languages, NULL, "VBA"); |
|
1610 |
+ } else { |
|
1611 |
+ cli_dbgmsg("[cli_vba_scandir_new] Failed to add \"VBA\" entry to MacroLanguages JSON array\n"); |
|
1612 |
+ } |
|
1613 |
+ } |
|
1614 |
+#endif |
|
1615 |
+ if (SCAN_HEURISTIC_MACROS && *has_macros) { |
|
1616 |
+ ret = cli_append_virus(ctx, "Heuristics.OLE2.ContainsMacros"); |
|
1617 |
+ if (ret == CL_VIRUS) { |
|
1618 |
+ viruses_found++; |
|
1619 |
+ if (!SCAN_ALLMATCHES) { |
|
1620 |
+ goto done; |
|
1621 |
+ } |
|
1622 |
+ } |
|
1623 |
+ } |
|
1624 |
+ |
|
1625 |
+ /* |
|
1626 |
+ * Now rewind the extracted vba-project output FD and scan it! |
|
1627 |
+ */ |
|
1604 | 1628 |
if (lseek(tempfd, 0, SEEK_SET) != 0) { |
1605 | 1629 |
cli_dbgmsg("cli_vba_scandir_new: Failed to seek to beginning of temporary VBA project file\n"); |
1606 | 1630 |
ret = CL_ESEEK; |
... | ... |
@@ -1610,15 +1635,18 @@ static cl_error_t cli_vba_scandir_new(const char *dirname, cli_ctx *ctx, struct |
1610 | 1610 |
ctx->recursion += 1; |
1611 | 1611 |
cli_set_container(ctx, CL_TYPE_MSOLE2, 0); //TODO: set correct container size |
1612 | 1612 |
|
1613 |
- if (cli_scan_desc(tempfd, ctx, CL_TYPE_SCRIPT, 0, NULL, AC_SCAN_VIR, NULL, NULL) == CL_VIRUS) { |
|
1614 |
- ctx->recursion -= 1; |
|
1615 |
- ret = CL_VIRUS; |
|
1616 |
- goto done; |
|
1617 |
- } |
|
1613 |
+ ret = cli_scan_desc(tempfd, ctx, CL_TYPE_SCRIPT, 0, NULL, AC_SCAN_VIR, NULL, NULL); |
|
1618 | 1614 |
|
1619 | 1615 |
close(tempfd); |
1620 | 1616 |
tempfd = -1; |
1621 | 1617 |
ctx->recursion -= 1; |
1618 |
+ |
|
1619 |
+ if (CL_VIRUS == ret) { |
|
1620 |
+ viruses_found++; |
|
1621 |
+ if (!SCAN_ALLMATCHES) { |
|
1622 |
+ goto done; |
|
1623 |
+ } |
|
1624 |
+ } |
|
1622 | 1625 |
} |
1623 | 1626 |
|
1624 | 1627 |
hashcnt--; |
... | ... |
@@ -1629,16 +1657,20 @@ done: |
1629 | 1629 |
close(tempfd); |
1630 | 1630 |
tempfd = -1; |
1631 | 1631 |
} |
1632 |
+ |
|
1633 |
+ if (viruses_found > 0) |
|
1634 |
+ ret = CL_VIRUS; |
|
1632 | 1635 |
return ret; |
1633 | 1636 |
} |
1634 | 1637 |
|
1635 |
-static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U, int *hasmacros) |
|
1638 |
+static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U, int *has_macros) |
|
1636 | 1639 |
{ |
1637 |
- cl_error_t ret = CL_CLEAN; |
|
1640 |
+ cl_error_t status = CL_CLEAN; |
|
1641 |
+ cl_error_t ret; |
|
1638 | 1642 |
int i, j, fd; |
1639 | 1643 |
size_t data_len; |
1640 | 1644 |
vba_project_t *vba_project; |
1641 |
- DIR *dd; |
|
1645 |
+ DIR *dd = NULL; |
|
1642 | 1646 |
struct dirent *dent; |
1643 | 1647 |
STATBUF statbuf; |
1644 | 1648 |
char *fullname, vbaname[1024]; |
... | ... |
@@ -1650,7 +1682,8 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1650 | 1650 |
cli_dbgmsg("VBADir: %s\n", dirname); |
1651 | 1651 |
if (CL_SUCCESS != (ret = uniq_get(U, "_vba_project", 12, NULL, &hashcnt))) { |
1652 | 1652 |
cli_dbgmsg("VBADir: uniq_get('_vba_project') failed with ret code (%d)!\n", ret); |
1653 |
- return ret; |
|
1653 |
+ status = ret; |
|
1654 |
+ goto done; |
|
1654 | 1655 |
} |
1655 | 1656 |
while (hashcnt) { |
1656 | 1657 |
if (!(vba_project = (vba_project_t *)cli_vba_readdir(dirname, U, hashcnt))) { |
... | ... |
@@ -1669,7 +1702,7 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1669 | 1669 |
cli_dbgmsg("VBADir: Decompress VBA project '%s_%u'\n", vba_project->name[i], j); |
1670 | 1670 |
data = (unsigned char *)cli_vba_inflate(fd, vba_project->offset[i], &data_len); |
1671 | 1671 |
close(fd); |
1672 |
- *hasmacros = *hasmacros + 1; |
|
1672 |
+ *has_macros = *has_macros + 1; |
|
1673 | 1673 |
if (!data) { |
1674 | 1674 |
} else { |
1675 | 1675 |
/* cli_dbgmsg("Project content:\n%s", data); */ |
... | ... |
@@ -1681,13 +1714,15 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1681 | 1681 |
|
1682 | 1682 |
if ((ret = cli_gentempfd(ctx->sub_tmpdir, &tempfile, &of)) != CL_SUCCESS) { |
1683 | 1683 |
cli_warnmsg("VBADir: WARNING: VBA project '%s_%u' cannot be dumped to file\n", vba_project->name[i], j); |
1684 |
- return ret; |
|
1684 |
+ status = ret; |
|
1685 |
+ goto done; |
|
1685 | 1686 |
} |
1686 | 1687 |
if (cli_writen(of, data, data_len) != data_len) { |
1687 | 1688 |
cli_warnmsg("VBADir: WARNING: VBA project '%s_%u' failed to write to file\n", vba_project->name[i], j); |
1688 | 1689 |
close(of); |
1689 | 1690 |
free(tempfile); |
1690 |
- return CL_EWRITE; |
|
1691 |
+ status = CL_EWRITE; |
|
1692 |
+ goto done; |
|
1691 | 1693 |
} |
1692 | 1694 |
|
1693 | 1695 |
cli_dbgmsg("VBADir: VBA project '%s_%u' dumped to %s\n", vba_project->name[i], j, tempfile); |
... | ... |
@@ -1698,7 +1733,7 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1698 | 1698 |
viruses_found++; |
1699 | 1699 |
if (!SCAN_ALLMATCHES) { |
1700 | 1700 |
free(data); |
1701 |
- ret = CL_VIRUS; |
|
1701 |
+ status = CL_VIRUS; |
|
1702 | 1702 |
break; |
1703 | 1703 |
} |
1704 | 1704 |
} |
... | ... |
@@ -1706,23 +1741,24 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1706 | 1706 |
} |
1707 | 1707 |
} |
1708 | 1708 |
|
1709 |
- if (ret == CL_VIRUS && !SCAN_ALLMATCHES) |
|
1709 |
+ if (status == CL_VIRUS) |
|
1710 | 1710 |
break; |
1711 | 1711 |
} |
1712 | 1712 |
|
1713 | 1713 |
cli_free_vba_project(vba_project); |
1714 | 1714 |
vba_project = NULL; |
1715 | 1715 |
|
1716 |
- if (ret == CL_VIRUS && !SCAN_ALLMATCHES) |
|
1716 |
+ if (status == CL_VIRUS) |
|
1717 | 1717 |
break; |
1718 | 1718 |
|
1719 | 1719 |
hashcnt--; |
1720 | 1720 |
} |
1721 | 1721 |
|
1722 |
- if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALLMATCHES)) { |
|
1722 |
+ if (status == CL_CLEAN || (status == CL_VIRUS && SCAN_ALLMATCHES)) { |
|
1723 | 1723 |
if (CL_SUCCESS != (ret = uniq_get(U, "powerpoint document", 19, &hash, &hashcnt))) { |
1724 | 1724 |
cli_dbgmsg("VBADir: uniq_get('powerpoint document') failed with ret code (%d)!\n", ret); |
1725 |
- return ret; |
|
1725 |
+ status = ret; |
|
1726 |
+ goto done; |
|
1726 | 1727 |
} |
1727 | 1728 |
while (hashcnt) { |
1728 | 1729 |
snprintf(vbaname, 1024, "%s" PATHSEP "%s_%u", dirname, hash, hashcnt); |
... | ... |
@@ -1734,7 +1770,7 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1734 | 1734 |
} |
1735 | 1735 |
if ((fullname = cli_ppt_vba_read(fd, ctx))) { |
1736 | 1736 |
if (cli_magic_scan_dir(fullname, ctx) == CL_VIRUS) { |
1737 |
- ret = CL_VIRUS; |
|
1737 |
+ status = CL_VIRUS; |
|
1738 | 1738 |
viruses_found++; |
1739 | 1739 |
} |
1740 | 1740 |
if (!ctx->engine->keeptmp) |
... | ... |
@@ -1746,10 +1782,11 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1746 | 1746 |
} |
1747 | 1747 |
} |
1748 | 1748 |
|
1749 |
- if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALLMATCHES)) { |
|
1749 |
+ if (status == CL_CLEAN || (status == CL_VIRUS && SCAN_ALLMATCHES)) { |
|
1750 | 1750 |
if (CL_SUCCESS != (ret = uniq_get(U, "worddocument", 12, &hash, &hashcnt))) { |
1751 | 1751 |
cli_dbgmsg("VBADir: uniq_get('worddocument') failed with ret code (%d)!\n", ret); |
1752 |
- return ret; |
|
1752 |
+ status = ret; |
|
1753 |
+ goto done; |
|
1753 | 1754 |
} |
1754 | 1755 |
while (hashcnt) { |
1755 | 1756 |
snprintf(vbaname, sizeof(vbaname), "%s" PATHSEP "%s_%u", dirname, hash, hashcnt); |
... | ... |
@@ -1779,7 +1816,7 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1779 | 1779 |
viruses_found++; |
1780 | 1780 |
if (!SCAN_ALLMATCHES) { |
1781 | 1781 |
free(data); |
1782 |
- ret = CL_VIRUS; |
|
1782 |
+ status = CL_VIRUS; |
|
1783 | 1783 |
break; |
1784 | 1784 |
} |
1785 | 1785 |
} |
... | ... |
@@ -1791,24 +1828,20 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1791 | 1791 |
cli_free_vba_project(vba_project); |
1792 | 1792 |
vba_project = NULL; |
1793 | 1793 |
|
1794 |
- if (ret == CL_VIRUS) { |
|
1795 |
- viruses_found++; |
|
1796 |
- if (!SCAN_ALLMATCHES) |
|
1797 |
- break; |
|
1794 |
+ if (status == CL_VIRUS && !SCAN_ALLMATCHES) { |
|
1795 |
+ break; |
|
1798 | 1796 |
} |
1799 | 1797 |
hashcnt--; |
1800 | 1798 |
} |
1801 | 1799 |
} |
1802 | 1800 |
|
1803 |
- if (ret != CL_CLEAN && !(ret == CL_VIRUS && SCAN_ALLMATCHES)) |
|
1804 |
- return ret; |
|
1805 |
- |
|
1806 | 1801 |
#if HAVE_JSON |
1807 | 1802 |
/* JSON Output Summary Information */ |
1808 | 1803 |
if (SCAN_COLLECT_METADATA && (ctx->wrkproperty != NULL)) { |
1809 | 1804 |
if (CL_SUCCESS != (ret = uniq_get(U, "_5_summaryinformation", 21, &hash, &hashcnt))) { |
1810 | 1805 |
cli_dbgmsg("VBADir: uniq_get('_5_summaryinformation') failed with ret code (%d)!\n", ret); |
1811 |
- return ret; |
|
1806 |
+ status = ret; |
|
1807 |
+ goto done; |
|
1812 | 1808 |
} |
1813 | 1809 |
while (hashcnt) { |
1814 | 1810 |
snprintf(vbaname, sizeof(vbaname), "%s" PATHSEP "%s_%u", dirname, hash, hashcnt); |
... | ... |
@@ -1826,7 +1859,8 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1826 | 1826 |
|
1827 | 1827 |
if (CL_SUCCESS != (ret = uniq_get(U, "_5_documentsummaryinformation", 29, &hash, &hashcnt))) { |
1828 | 1828 |
cli_dbgmsg("VBADir: uniq_get('_5_documentsummaryinformation') failed with ret code (%d)!\n", ret); |
1829 |
- return ret; |
|
1829 |
+ status = ret; |
|
1830 |
+ goto done; |
|
1830 | 1831 |
} |
1831 | 1832 |
while (hashcnt) { |
1832 | 1833 |
snprintf(vbaname, sizeof(vbaname), "%s" PATHSEP "%s_%u", dirname, hash, hashcnt); |
... | ... |
@@ -1844,10 +1878,15 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1844 | 1844 |
} |
1845 | 1845 |
#endif |
1846 | 1846 |
|
1847 |
+ if (status != CL_CLEAN && !(status == CL_VIRUS && SCAN_ALLMATCHES)) { |
|
1848 |
+ goto done; |
|
1849 |
+ } |
|
1850 |
+ |
|
1847 | 1851 |
/* Check directory for embedded OLE objects */ |
1848 | 1852 |
if (CL_SUCCESS != (ret = uniq_get(U, "_1_ole10native", 14, &hash, &hashcnt))) { |
1849 | 1853 |
cli_dbgmsg("VBADir: uniq_get('_1_ole10native') failed with ret code (%d)!\n", ret); |
1850 |
- return ret; |
|
1854 |
+ status = ret; |
|
1855 |
+ goto done; |
|
1851 | 1856 |
} |
1852 | 1857 |
while (hashcnt) { |
1853 | 1858 |
snprintf(vbaname, sizeof(vbaname), "%s" PATHSEP "%s_%u", dirname, hash, hashcnt); |
... | ... |
@@ -1857,8 +1896,13 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1857 | 1857 |
if (fd >= 0) { |
1858 | 1858 |
ret = cli_scan_ole10(fd, ctx); |
1859 | 1859 |
close(fd); |
1860 |
- if (ret != CL_CLEAN && !(ret == CL_VIRUS && SCAN_ALLMATCHES)) |
|
1861 |
- return ret; |
|
1860 |
+ if (CL_VIRUS == ret) { |
|
1861 |
+ viruses_found++; |
|
1862 |
+ if (!SCAN_ALLMATCHES) { |
|
1863 |
+ status = ret; |
|
1864 |
+ goto done; |
|
1865 |
+ } |
|
1866 |
+ } |
|
1862 | 1867 |
} |
1863 | 1868 |
hashcnt--; |
1864 | 1869 |
} |
... | ... |
@@ -1883,7 +1927,7 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1883 | 1883 |
/* stat the file */ |
1884 | 1884 |
if (LSTAT(fullname, &statbuf) != -1) { |
1885 | 1885 |
if (S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) |
1886 |
- if (cli_vba_scandir(fullname, ctx, U, hasmacros) == CL_VIRUS) { |
|
1886 |
+ if (cli_vba_scandir(fullname, ctx, U, has_macros) == CL_VIRUS) { |
|
1887 | 1887 |
viruses_found++; |
1888 | 1888 |
if (!SCAN_ALLMATCHES) { |
1889 | 1889 |
ret = CL_VIRUS; |
... | ... |
@@ -1898,12 +1942,17 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1898 | 1898 |
} |
1899 | 1899 |
} else { |
1900 | 1900 |
cli_dbgmsg("VBADir: Can't open directory %s.\n", dirname); |
1901 |
- return CL_EOPEN; |
|
1901 |
+ status = CL_EOPEN; |
|
1902 |
+ goto done; |
|
1903 |
+ } |
|
1904 |
+ |
|
1905 |
+done: |
|
1906 |
+ if (NULL != dd) { |
|
1907 |
+ closedir(dd); |
|
1902 | 1908 |
} |
1903 | 1909 |
|
1904 |
- closedir(dd); |
|
1905 | 1910 |
#if HAVE_JSON |
1906 |
- if (*hasmacros && SCAN_COLLECT_METADATA && (ctx->wrkproperty != NULL)) { |
|
1911 |
+ if (*has_macros && SCAN_COLLECT_METADATA && (ctx->wrkproperty != NULL)) { |
|
1907 | 1912 |
cli_jsonbool(ctx->wrkproperty, "HasMacros", 1); |
1908 | 1913 |
json_object *macro_languages = cli_jsonarray(ctx->wrkproperty, "MacroLanguages"); |
1909 | 1914 |
if (macro_languages) { |
... | ... |
@@ -1913,14 +1962,16 @@ static cl_error_t cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq |
1913 | 1913 |
} |
1914 | 1914 |
} |
1915 | 1915 |
#endif |
1916 |
- if (SCAN_HEURISTIC_MACROS && *hasmacros) { |
|
1916 |
+ if (SCAN_HEURISTIC_MACROS && *has_macros) { |
|
1917 | 1917 |
ret = cli_append_virus(ctx, "Heuristics.OLE2.ContainsMacros"); |
1918 | 1918 |
if (ret == CL_VIRUS) |
1919 | 1919 |
viruses_found++; |
1920 | 1920 |
} |
1921 |
- if (SCAN_ALLMATCHES && viruses_found) |
|
1922 |
- return CL_VIRUS; |
|
1923 |
- return ret; |
|
1921 |
+ |
|
1922 |
+ if (SCAN_ALLMATCHES && viruses_found) { |
|
1923 |
+ status = CL_VIRUS; |
|
1924 |
+ } |
|
1925 |
+ return status; |
|
1924 | 1926 |
} |
1925 | 1927 |
|
1926 | 1928 |
static cl_error_t cli_xlm_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U) |
... | ... |
@@ -2341,7 +2392,6 @@ static cl_error_t cli_scanole2(cli_ctx *ctx) |
2341 | 2341 |
if (CL_VIRUS == ret) { |
2342 | 2342 |
viruses_found++; |
2343 | 2343 |
if (!SCAN_ALLMATCHES) { |
2344 |
- ctx->recursion--; |
|
2345 | 2344 |
goto done; |
2346 | 2345 |
} |
2347 | 2346 |
} |
... | ... |
@@ -2358,7 +2408,7 @@ static cl_error_t cli_scanole2(cli_ctx *ctx) |
2358 | 2358 |
} |
2359 | 2359 |
} |
2360 | 2360 |
|
2361 |
- ret = cli_vba_scandir_new(dir, ctx, files); |
|
2361 |
+ ret = cli_vba_scandir_new(dir, ctx, files, &has_macros); |
|
2362 | 2362 |
if (CL_VIRUS == ret) { |
2363 | 2363 |
viruses_found++; |
2364 | 2364 |
if (!SCAN_ALLMATCHES) { |
... | ... |
@@ -2367,9 +2417,6 @@ static cl_error_t cli_scanole2(cli_ctx *ctx) |
2367 | 2367 |
} |
2368 | 2368 |
} |
2369 | 2369 |
|
2370 |
- if (cli_magic_scan_dir(dir, ctx) == CL_VIRUS) |
|
2371 |
- ret = CL_VIRUS; |
|
2372 |
- |
|
2373 | 2370 |
ctx->recursion--; |
2374 | 2371 |
} |
2375 | 2372 |
|
... | ... |
@@ -2392,14 +2439,16 @@ static cl_error_t cli_scanole2(cli_ctx *ctx) |
2392 | 2392 |
} |
2393 | 2393 |
} |
2394 | 2394 |
|
2395 |
- if (cli_magic_scan_dir(dir, ctx) == CL_VIRUS) |
|
2396 |
- ret = CL_VIRUS; |
|
2397 |
- |
|
2398 | 2395 |
ctx->recursion--; |
2399 | 2396 |
} |
2400 | 2397 |
|
2401 |
- if (viruses_found > 0) { |
|
2402 |
- ret = CL_VIRUS; |
|
2398 |
+ if ((has_xlm || has_vba) && files) { |
|
2399 |
+ if (CL_VIRUS == cli_magic_scan_dir(dir, ctx)) { |
|
2400 |
+ viruses_found++; |
|
2401 |
+ if (!SCAN_ALLMATCHES) { |
|
2402 |
+ goto done; |
|
2403 |
+ } |
|
2404 |
+ } |
|
2403 | 2405 |
} |
2404 | 2406 |
|
2405 | 2407 |
done: |
... | ... |
@@ -2413,6 +2462,10 @@ done: |
2413 | 2413 |
free(dir); |
2414 | 2414 |
} |
2415 | 2415 |
|
2416 |
+ if (viruses_found > 0) { |
|
2417 |
+ ret = CL_VIRUS; |
|
2418 |
+ } |
|
2419 |
+ |
|
2416 | 2420 |
return ret; |
2417 | 2421 |
} |
2418 | 2422 |
|
... | ... |
@@ -357,8 +357,7 @@ static size_t vba_normalize(unsigned char *buffer, size_t size) |
357 | 357 |
* Read a VBA project in an OLE directory. |
358 | 358 |
* Contrary to cli_vba_readdir, this function uses the dir file to locate VBA modules. |
359 | 359 |
*/ |
360 |
-cl_error_t |
|
361 |
-cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *hash, uint32_t which, int *tempfd) |
|
360 |
+cl_error_t cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *hash, uint32_t which, int *tempfd, int *has_macros) |
|
362 | 361 |
{ |
363 | 362 |
cl_error_t ret = CL_SUCCESS; |
364 | 363 |
char fullname[1024]; |
... | ... |
@@ -375,7 +374,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
375 | 375 |
unsigned char *module_data = NULL, *module_data_utf8 = NULL; |
376 | 376 |
size_t module_data_size = 0, module_data_utf8_size = 0; |
377 | 377 |
|
378 |
- if (dir == NULL || hash == NULL || tempfd == NULL) { |
|
378 |
+ if (dir == NULL || hash == NULL || tempfd == NULL || has_macros == NULL) { |
|
379 | 379 |
return CL_EARG; |
380 | 380 |
} |
381 | 381 |
|
... | ... |
@@ -396,7 +395,9 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
396 | 396 |
goto done; |
397 | 397 |
} |
398 | 398 |
|
399 |
- if ((ret = cli_gentempfd(ctx->engine->tmpdir, &tempfile, tempfd)) != CL_SUCCESS) { |
|
399 |
+ *has_macros = *has_macros + 1; |
|
400 |
+ |
|
401 |
+ if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "vba_project", &tempfile, tempfd)) != CL_SUCCESS) { |
|
400 | 402 |
cli_warnmsg("vba_readdir_new: VBA project cannot be dumped to file\n"); |
401 | 403 |
goto done; |
402 | 404 |
} |
... | ... |
@@ -418,7 +419,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
418 | 418 |
for (i = 0; i < size; ++i) { \ |
419 | 419 |
char buf[4]; \ |
420 | 420 |
if (snprintf(buf, sizeof(buf), "%02x", (msg)[i]) != 2) { \ |
421 |
- cli_warnmsg("vba_readdir_new: Failed to write nex data to output file\n"); \ |
|
421 |
+ cli_warnmsg("vba_readdir_new: Failed to write hex data to output file\n"); \ |
|
422 | 422 |
ret = CL_EWRITE; \ |
423 | 423 |
goto done; \ |
424 | 424 |
} \ |
... | ... |
@@ -436,7 +437,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
436 | 436 |
free(utf8); \ |
437 | 437 |
utf8 = NULL; \ |
438 | 438 |
} else { \ |
439 |
- cli_errmsg("cli_vba_readdir_new: failed to convert codepage %" PRIu16 " to UTF-8\n", codepage); \ |
|
439 |
+ cli_dbgmsg("cli_vba_readdir_new: failed to convert codepage %" PRIu16 " to UTF-8\n", codepage); \ |
|
440 | 440 |
CLI_WRITEN("<error decoding string>", 23); \ |
441 | 441 |
} \ |
442 | 442 |
} \ |
... | ... |
@@ -452,7 +453,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
452 | 452 |
free(utf8); \ |
453 | 453 |
utf8 = NULL; \ |
454 | 454 |
} else { \ |
455 |
- cli_errmsg("cli_vba_readdir_new: failed to convert UTF16LE to UTF-8\n"); \ |
|
455 |
+ cli_dbgmsg("cli_vba_readdir_new: failed to convert UTF16LE to UTF-8\n"); \ |
|
456 | 456 |
CLI_WRITEN("<error decoding string>", 23); \ |
457 | 457 |
} \ |
458 | 458 |
} \ |
... | ... |
@@ -779,7 +780,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
779 | 779 |
if (CL_SUCCESS == cli_codepage_to_utf8((char *)&data[data_offset], size, codepage, &mbcs_name, &mbcs_name_size)) { |
780 | 780 |
CLI_WRITEN(mbcs_name, mbcs_name_size); |
781 | 781 |
} else { |
782 |
- cli_errmsg("cli_vba_readdir_new: failed to convert codepage %" PRIu16 " to UTF-8\n", codepage); |
|
782 |
+ cli_dbgmsg("cli_vba_readdir_new: failed to convert codepage %" PRIu16 " to UTF-8\n", codepage); |
|
783 | 783 |
CLI_WRITEN("<error decoding string>", 23); |
784 | 784 |
} |
785 | 785 |
} |
... | ... |
@@ -813,7 +814,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
813 | 813 |
if (CL_SUCCESS == cli_codepage_to_utf8((char *)&data[data_offset], size, CODEPAGE_UTF16_LE, &utf16_name, &utf16_name_size)) { |
814 | 814 |
CLI_WRITEN(utf16_name, utf16_name_size); |
815 | 815 |
} else { |
816 |
- cli_errmsg("cli_vba_readdir_new: failed to convert UTF16LE to UTF-8\n"); |
|
816 |
+ cli_dbgmsg("cli_vba_readdir_new: failed to convert UTF16LE to UTF-8\n"); |
|
817 | 817 |
CLI_WRITEN("<error decoding string>", 23); |
818 | 818 |
} |
819 | 819 |
} |
... | ... |
@@ -862,7 +863,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
862 | 862 |
if (CL_SUCCESS == cli_codepage_to_utf8((char *)&data[data_offset], size, codepage, &mbcs_name, &mbcs_name_size)) { |
863 | 863 |
CLI_WRITEN(mbcs_name, mbcs_name_size); |
864 | 864 |
} else { |
865 |
- cli_errmsg("cli_vba_readdir_new: failed to convert codepage %" PRIu16 " to UTF-8\n", codepage); |
|
865 |
+ cli_dbgmsg("cli_vba_readdir_new: failed to convert codepage %" PRIu16 " to UTF-8\n", codepage); |
|
866 | 866 |
CLI_WRITEN("<error decoding string>", 23); |
867 | 867 |
} |
868 | 868 |
} |
... | ... |
@@ -896,7 +897,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
896 | 896 |
if (CL_SUCCESS == cli_codepage_to_utf8((char *)&data[data_offset], module_stream_name_size, CODEPAGE_UTF16_LE, &utf16_name, &utf16_name_size)) { |
897 | 897 |
CLI_WRITEN(utf16_name, utf16_name_size); |
898 | 898 |
} else { |
899 |
- cli_errmsg("cli_vba_readdir_new: failed to convert UTF16LE to UTF-8\n"); |
|
899 |
+ cli_dbgmsg("cli_vba_readdir_new: failed to convert UTF16LE to UTF-8\n"); |
|
900 | 900 |
CLI_WRITEN("<error decoding string>", 23); |
901 | 901 |
} |
902 | 902 |
} |
... | ... |
@@ -945,7 +946,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
945 | 945 |
if (CL_SUCCESS == cli_codepage_to_utf8((char *)&data[data_offset], size, codepage, &mbcs_name, &mbcs_name_size)) { |
946 | 946 |
CLI_WRITEN(mbcs_name, mbcs_name_size); |
947 | 947 |
} else { |
948 |
- cli_errmsg("cli_vba_readdir_new: failed to convert codepage %" PRIu16 " to UTF-8\n", codepage); |
|
948 |
+ cli_dbgmsg("cli_vba_readdir_new: failed to convert codepage %" PRIu16 " to UTF-8\n", codepage); |
|
949 | 949 |
CLI_WRITEN("<error decoding string>", 23); |
950 | 950 |
} |
951 | 951 |
} |
... | ... |
@@ -978,7 +979,7 @@ cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *h |
978 | 978 |
if (CL_SUCCESS == cli_codepage_to_utf8((char *)&data[data_offset], size, CODEPAGE_UTF16_LE, &utf16_name, &utf16_name_size)) { |
979 | 979 |
CLI_WRITEN(utf16_name, utf16_name_size); |
980 | 980 |
} else { |
981 |
- cli_errmsg("cli_vba_readdir_new: failed to convert UTF16LE to UTF-8\n"); |
|
981 |
+ cli_dbgmsg("cli_vba_readdir_new: failed to convert UTF16LE to UTF-8\n"); |
|
982 | 982 |
CLI_WRITEN("<error decoding string>", 23); |
983 | 983 |
} |
984 | 984 |
} |
... | ... |
@@ -40,7 +40,7 @@ typedef struct vba_project_tag { |
40 | 40 |
} vba_project_t; |
41 | 41 |
|
42 | 42 |
vba_project_t *cli_vba_readdir(const char *dir, struct uniq *U, uint32_t which); |
43 |
-cl_error_t cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *hash, uint32_t which, int *tempfd); |
|
43 |
+cl_error_t cli_vba_readdir_new(cli_ctx *ctx, const char *dir, struct uniq *U, const char *hash, uint32_t which, int *tempfd, int *has_macros); |
|
44 | 44 |
vba_project_t *cli_wm_readdir(int fd); |
45 | 45 |
void cli_free_vba_project(vba_project_t *vba_project); |
46 | 46 |
|
... | ... |
@@ -4116,7 +4116,7 @@ cl_error_t cli_xlm_extract_macros(const char *dir, cli_ctx *ctx, struct uniq *U, |
4116 | 4116 |
goto done; |
4117 | 4117 |
} |
4118 | 4118 |
|
4119 |
- if ((ret = cli_gentempfd(dir, &tempfile, &out_fd)) != CL_SUCCESS) { |
|
4119 |
+ if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "xlm_macros", &tempfile, &out_fd)) != CL_SUCCESS) { |
|
4120 | 4120 |
cli_dbgmsg("[cli_xlm_extract_macros] Failed to open output file descriptor\n"); |
4121 | 4121 |
goto done; |
4122 | 4122 |
} |