Mail-SpamAssassin

 view release on metacpan or  search on metacpan

lib/Mail/SpamAssassin/Plugin/OLEVBMacro.pm  view on Meta::CPAN

# See the License for the specific language governing permissions and
# limitations under the License.
# </@LICENSE>

=head1 NAME

Mail::SpamAssassin::Plugin::OLEVBMacro - scan Office documents for evidence of OLE Macros or other exploits

=head1 SYNOPSIS

  loadplugin Mail::SpamAssassin::Plugin::OLEVBMacro

  ifplugin Mail::SpamAssassin::Plugin::OLEVBMacro
    body     OLEMACRO eval:check_olemacro()
    describe OLEMACRO Attachment has an Office Macro

    body     OLEOBJ eval:check_oleobject()
    describe OLEOBJ Attachment has an Ole Object

    body     OLERTF eval:check_olertfobject()
    describe OLERTF Attachment has an Ole Rtf Object

    body     OLEMACRO_MALICE eval:check_olemacro_malice()
    describe OLEMACRO_MALICE Potentially malicious Office Macro

    body     OLEMACRO_ENCRYPTED eval:check_olemacro_encrypted()
    describe OLEMACRO_ENCRYPTED Has an Office doc that is encrypted

    body     OLEMACRO_RENAME eval:check_olemacro_renamed()
    describe OLEMACRO_RENAME Has an Office doc that has been renamed

    body     OLEMACRO_ZIP_PW eval:check_olemacro_zip_password()
    describe OLEMACRO_ZIP_PW Has an Office doc that is password protected in a zip

    body     OLEMACRO_CSV eval:check_olemacro_csv()
    describe OLEMACRO_CSV Malicious csv file that tries to exec cmd.exe detected

    body     OLEMACRO_DOWNLOAD_EXE eval:check_olemacro_download_exe()
    describe OLEMACRO_DOWNLOAD_EXE Malicious code inside the Office doc that tries to download a .exe file detected

    body     OLEMACRO_URI_TARGET eval:check_olemacro_redirect_uri()
    describe OLEMACRO_URI_TARGET Uri inside an Office doc

    body     OLEMACRO_MHTML_TARGET eval:check_olemacro_mhtml_uri()
    describe OLEMACRO_MHTML_TARGET Exploitable mhtml uri inside an Office doc
  endif

=head1 DESCRIPTION

This plugin detects OLE Macros or other exploits inside Office documents
attached to emails.  It can detect documents inside zip files as well as
encrypted documents.

=head1 REQUIREMENT

This plugin requires Archive::Zip and IO::String perl modules.

=head1 USER PREFERENCES

The following options can be used in both site-wide (C<local.cf>) and
user-specific (C<user_prefs>) configuration files to customize how
the module handles attached documents

=cut

package Mail::SpamAssassin::Plugin::OLEVBMacro;
use strict;
use warnings;

use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Util qw(compile_regexp get_part_details);

use constant HAS_ARCHIVE_ZIP => eval { require Archive::Zip; };
use constant HAS_IO_STRING => eval { require IO::String; };

BEGIN
{
    eval{
      Archive::Zip->import(qw( :ERROR_CODES :CONSTANTS ))
    };
    eval{
      IO::String->import
    };
}

use re 'taint';

use vars qw(@ISA);
@ISA = qw(Mail::SpamAssassin::Plugin);

our $VERSION = '4.00';

# https://www.openoffice.org/sc/compdocfileformat.pdf
# http://blog.rootshell.be/2015/01/08/searching-for-microsoft-office-files-containing-macro/
my $marker1 = "\xd0\xcf\x11\xe0";
my $marker2 = "\x00\x41\x74\x74\x72\x69\x62\x75\x74\x00";
# Office 2003 embedded ole
my $marker2a = "\x01\x00\x4f\x00\x6c\x00\x65\x00\x31\x00\x30\x00\x4e\x00\x61\x00";
# embedded object in rtf files (https://www.biblioscape.com/rtf15_spec.htm)
my $marker3 = "\x5c\x6f\x62\x6a\x65\x6d\x62";
my $marker4 = "\x5c\x6f\x62\x6a\x64\x61\x74";
my $marker5 = "\x5c\x20\x6f\x62\x6a\x64\x61\x74";
# CVE-2025-21298
my $marker6 = '{\rtf1{\object\objhtml\objw1\objh1\objupdate\rsltpict{\*\objclass ';
# Excel .xlsx encrypted package, thanks to Dan Bagwell for the sample
my $encrypted_marker = "\x45\x00\x6e\x00\x63\x00\x72\x00\x79\x00\x70\x00\x74\x00\x65\x00\x64\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x61\x00\x67\x00\x65";
# Excel .xls marker present only on unencrypted files
my $workbook_marker = "\x57\x00\x6f\x00\x72\x00\x6b\x00\x62\x00\x6f\x00\x6f\x00\x6b\x00";
# .exe file downloaded from external website
my $exe_marker1 = "\x00(https?://[-a-z0-9+&@#/%?=~_|!:,.;]{5,1000}[-a-z0-9+&@#/%=~_|]{5,1000}\.(?:exe|cmd|bat))[\x06|\x00]";
my $exe_marker2 = "URLDownloadToFileA";

# CVE-2021-40444 marker
my $mhtml_marker1 = "^MHTML:&#x48;&#x54;&#x50;&#x3a;&#x5c;&#x5c;&#x31;&";
my $mhtml_marker2 = "^(?:mhtml:https?://|file:///)";

# this code burps an ugly message if it fails, but that's redirected elsewhere
# AZ_OK is a constant exported by Archive::Zip
my $az_ok;
eval '$az_ok = AZ_OK';



( run in 0.721 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )