1

I am trying to build a SpamAssassin test that uses ClamAV's CLI tool, sigtool, to detect when an attached MS Office legacy file like an .xls or .doc that MAY have a macro, actually has an executable macro.

It is easy enough to call sigtool in perl and pass it a filename to be scanned like this

my $filename = "email_attach";
my $scan = `/usr/bin/sigtool --vba="$filename"`;
if ($scan =~ /autoopen/i ) {
  print "Scanning $file: INFECTED VBA\n";
}

However, as a SpamAssassin test I already have the email attachments in memory as variables passed to my test. So I do not want to take the time to write each attachment to disk and then tell sigtool to go read it.

I have researched the entire Ch 16 of Programming Perl and Perl Cookbook on Interprocess Communications and Process Management and Communication, and there is a TON of info there, but I did not see anything that addressed streaming your internal perl program data as input to an external application that is looking for a path/filename to be passed as a command line argument.

Thank you for any thoughts on how to accomplish this. Or if anyone knows of a simpler way to detect a VBA macro or executable in the MS Office legacy files, that would be fine as well.

Andy Dorman
  • 121
  • 2
  • If `sigtool` is written like a sane Unix utility, it supports `-` for stdin. If not, it might still support `/dev/stdin`. But if it really depends on the bloody thing being a file (e.g. being readable multiple times), you're out of luck. – reinierpost Jan 14 '16 at 20:40
  • You can call and pass data to sigtool using a pipe, but to get it to output the VBA macro info it wants that silly file name. We have gone to great lengths to keep our disk IO to an absolute minimum because in high volume spam filtering that turns out to be our limiting factor, but I may just have to bite the bullet and write the temp file. – Andy Dorman Jan 14 '16 at 21:10
  • You can try a memory-backed file system, e.g. `/dev/shm`. – reinierpost Jan 15 '16 at 10:08
  • And for output, `/dev/stdout` might work. – reinierpost Jan 15 '16 at 10:09

1 Answers1

3

sigtool requires a file name. There are ways of obtaining a file name for a pipe on some systems, so you could still use a pipe. However, if you do, you'd end up with two pipes (one from which the child reads, and one to which the child writes), and that's a mess to handle.

Normally, you'd have to use a non-blocking IO, asynchronous IO, a select loop or threads, but IPC::Run does us the favour of doing this for us!

Processes normally start with 3 file handles open: STDIN (fd 0), STDOUT (fd 1) and STDERR (fd 2). But the child can inherit any number of other handles too. The following creates a pipe tied to the child's fd 3, and tells the child to read from that pipe through a Linux-specific virtual file system.

use IPC::Run qw( run );
run [ '/usr/bin/sigtool', "--vba=/proc/self/fd/3" ], '3<', \$doc, '>', \my $scan;

Using a temporary file is quite simple, but it's put to shame by IPC::Run!

ikegami
  • 367,544
  • 15
  • 269
  • 518