A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.
1 ### Copyright (C) 1997-2004 John D. Hardin 2 ### This program is free software; you can redistribute it and/or modify 3 ### it under the terms of the GNU General Public License as published by 4 ### the Free Software Foundation; either version 2 of the License, or 5 ### (at your option) any later version. 6 ### 7 ### This program is distributed in the hope that it will be useful, 8 ### but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ### GNU General Public License for more details. 11 ### 12 ### Contact the copyright holder for commercial licensing terms 13 ### if you wish to incorporate this code or portions of it into 14 ### non-GPL software. 15 ### 16 # 17 # <jhardin@impsec.org> 18 # $Id: html-trap.procmail,v 1.151 2006-01-20 07:29:24-08 jhardin Exp jhardin $ 19 # 20 # Baroquely complex Procmail and Perl recipe to defang active-content HTML tags 21 # to protect those people foolish enough to read their mail from a web browser 22 # or HTML-enabled mail client. Also mangles the attachment name on executable 23 # attachments to prevent attacks, at the cost of not being able to run 24 # programs from within your mail client - which you shouldn't do anyway. 25 # Also protects against excessively long filenames in attachments, which 26 # can cause nasty things to happen in some clients, and excessively long 27 # MIME headers, which may crash or allow exploits of some clients. 28 # 29 # All of the configuration options discussed below must be set in the 30 # procmail script that calls this filter, before this filter is called. 31 # Please see http://www.impsec.org/email-tools/sanitizer-configuration.html 32 # for complete documentation of all configuration options. 33 # 34 # If you wish to override the default list of executable extensions, 35 # set MANGLE_EXTENSIONS to a regexp (see where it is defined below for 36 # the proper syntax). DO NOT start or end it with a vertical bar or have 37 # two adjacent vertical bars! 38 # 39 # If you wish to block specific executable or document filenames from 40 # attachments, define $POISONED_EXECUTABLES to point at a filename 41 # containing one filename per line, with no leading spaces. Trailing spaces 42 # and comments (beginning with "#") are permitted. 43 # Case is ignored. Wildcards are allowed, using a mishmash of filename 44 # globbing and regular expression syntax. 45 # 46 # Site policy for trapped messages can be specified within limited bounds. 47 # To redirect poisoned messages to a file or directory, set 48 # $SECURITY_QUARANTINE to the name of the file or directory. To notify 49 # administrator(s), set $SECURITY_NOTIFY and/or $SECURITY_NOTIFY_VERBOSE to a 50 # comma-delimited address list. The _VERBOSE recipients will get the whole 51 # message. 52 # 53 # If the trapped message cannot be saved in $SECURITY_QUARANTINE for any 54 # reason, the message will be bounced unless $SECURITY_QUARANTINE_OPTIONAL is 55 # set to any value. 56 # 57 # This performs limited scanning of attachments for M$ macros which contain 58 # possibly dangerous code (as opposed to specific strings from specific 59 # variants of specific exploits). To disable this scanning, set 60 # $DISABLE_MACRO_CHECK to anything. To adjust the score an attachment is 61 # considered "poisoned" at, set $POISONED_SCORE. The default is 25, which 62 # should be okay unless you see valid attachments with complex macros. Set 63 # $SCORE_HISTORY to a filename to track the scores on scanned documents. 64 # If you wish to do profiling before implementing poisoning, set 65 # $SCORE_ONLY to anything to scan and possibly record scores but not 66 # poison. This could also be accomplished by setting $POISONED_SCORE to a 67 # very high value (200?), which would trap attacks while still allowing 68 # profiling. 69 # 70 # This performs scanning of .ZIP archive attachments for filenames. .ZIP 71 # attachments will be handled by the POISON and STRIP lists, and 72 # $ZIPPED_EXECUTABLES lists filespecs that will cause the message to be 73 # quarantined if found in the .ZIP attachment 74 # 75 # If you wish to send a message to the author of a trapped message, set 76 # $SECURITY_NOTIFY_SENDER to any value. To replace the canned message that 77 # is sent to the author of a trapped message, set $SECURITY_NOTIFY_SENDER 78 # to point at a text file for the message body. No variable expansion is 79 # performed on the body of this message. If you want to notify the 80 # sender's postmaster as well, set $SECURITY_NOTIFY_SENDER_POSTMASTER 81 # to any value. If you are not running this on a mail relay, you can use 82 # $SECURITY_NOTIFY_RECIPIENT in the same manner as $SECURITY_NOTIFY_SENDER. 83 # 84 # This could also be extended fairly easily to allow virus-checking of 85 # attachments, assuming you have a virus-checker that will run under *nix. 86 # 87 # NOTES 88 # Requires perl. 89 # Attachment scanning requires the "mktemp" and "mimencode" programs OR 90 # the installation of the CPAN Perl modules MIME::Base64 and File::MkTemp. 91 # Set $USE_CPAN if you want to use CPAN modules. If you can't put those 92 # modules in their system directories (e.g. you're running this on your 93 # ISP's computer), put them somewhere accessible and set $PVT_CPAN to that 94 # directory. 95 # Scanning of ZIP attachments requires the "unzip" program. 96 # 97 # This is a non-delivering filter recipe unless $SECURITY_QUARANTINE is 98 # set. 99 # 100 # INVOCATION 101 # Insert 102 # CONFIG_VARIABLE=some_value 103 # {repeat as needed} 104 # INCLUDERC=html-trap.procmail 105 # into your .procmailrc at the beginning or end. 106 # 107 # For further details, particularly how to set up site-wide and mail hub 108 # or relay filtering, visit: 109 # http://www.impsec.org/email-tools/procmail-security.html 110 # 111 112 # possible bug workaround? 113 LINEBUF=8192 114 115 # Size LINEBUF dynamically to deal with excessively large headers 116 :0 H 117 * 32000^0 118 * 1^1 . 119 { 120 LINEBUF="$=" 121 } 122 123 # override csh and cousins 124 :0 125 * SHELL ?? csh$ 126 { 127 SHELL="/bin/sh" 128 } 129 130 # Make sure $LOGFILE exists so the shells don't barf 131 LOGFILE=${LOGFILE:-"/dev/null"} 132 133 #--------------------------------------------------------------------------- 134 # Grab some info for logging 135 # 136 NL=" 137 " 138 SUBJ="" 139 FROM="unknown" 140 FROMDOM="" 141 REPLY_SUPPRESSED="" 142 143 :0 144 * ^Subject[ ]*:[ ]+\/[^ ].+ 145 { 146 SUBJ=" in \"$MATCH\"" 147 } 148 149 OVERRIDEFORMAIL="Xx:" 150 151 # AOL mail servers look up who is dialled in and 152 # add that user's ID as the X-Apparently-From: header 153 # This is pretty spoof-proof 154 155 :0 156 * ^Received: from .*\.aol\.com .* by .*\.aol\.com 157 * ^X-Apparently-From:.*\/[^ ]+@aol\.com 158 { 159 FROM="$MATCH" 160 OVERRIDEFORMAIL="To: $FROM" 161 162 SUBJ="$SUBJ from $FROM" 163 FROMDOM="aol.com" 164 } 165 166 # otherwise, try Return-Path: (the envelope sender) 167 # which is still subject to spoofing but may be 168 # overlooked 169 170 :0 E 171 * ^Return-Path:.*\/<[^>]+@[^>]+> 172 { 173 FROM="$MATCH" 174 175 # Did a mailing list rewrite the Return-Path header? 176 :0 177 * -1^0 178 * 1^0 ^Precedence: (bulk|junk|list) 179 * 1^0 ^(List-Id|X-Mailing-List): 180 * 9876543210^0 FROM ?? \<owner- 181 * 9876543210^0 FROM ?? \<[^@ >]+-l-admin@ 182 { 183 :0 184 * ^From:.*\/<[^>]+> 185 { 186 FROM="$MATCH" 187 } 188 } 189 190 SUBJ="$SUBJ from $FROM" 191 :0 192 * ^Return-Path:.*<[^@]+@\/[^>]+ 193 { 194 FROMDOM="$MATCH" 195 } 196 } 197 198 # The following runs if Return-Path: header not found 199 # MAKE SURE your MTA puts in a Return-Path: header! 200 :0 E 201 * ^From:.*\/<[^>]+> 202 { 203 FROM="$MATCH" 204 SUBJ="$SUBJ from $FROM" 205 LOG=" WARN: No usable Return-Path: header found in message.${NL}" 206 207 :0 208 * SECURITY_NOTIFY_SENDER ?? [^ ] 209 * ! SECURITY_DISABLE_SMART_REPLY ?? [^ ] 210 { 211 REPLY_SUPPRESSED="NOTICE: No Return-Path: header. Suppressing sender notification.${NL}" 212 LOG=" $REPLY_SUPPRESSED" 213 SECURITY_NOTIFY_SENDER= 214 } 215 } 216 217 TO=<$LOGNAME> 218 219 :0 220 * LOGNAME ?? ^root$ 221 { 222 # If $LOGNAME is root, we're probably running as a gateway filter: 223 # get the "real" to name(s) out of the message headers. 224 :0 225 * ^To: +\/.* 226 { 227 TO="$MATCH" 228 } 229 } 230 231 # try to get the full recipient address from a Received: header 232 # this overrides LOGNAME and the To: header 233 :0 234 * ^Received: .*for \/<?[^> ]+@[^> ]+\.[^> ;]+>? 235 { 236 TO="$MATCH" 237 } 238 239 :0 240 * ^Received: .*for \/<[^> ]+@[^> ]+\.[a-z][a-z][a-z]?[a-z]?[a-z]?[a-z]?> 241 { 242 TO="$MATCH" 243 } 244 245 SUBJ="$SUBJ to $TO" 246 247 :0 248 * ^Message-ID:.*\/<[^>]+> 249 { 250 MSGID="$MATCH" 251 SUBJ="$SUBJ msgid=$MSGID" 252 } 253 254 SUBJ="$SUBJ 255 " 256 257 #--------------------------------------------------------------------------- 258 # Override these in /etc/procmailrc as needed 259 # 260 261 STRIPPED_WARNING=${STRIPPED_WARNING:-" 262 SECURITY NOTICE: 263 264 The mail system has removed a file attachment from this message. 265 The attachment has been discarded. 266 267 Please contact your system administrator for details. 268 269 "} 270 271 POISONED_WARNING=${POISONED_WARNING:-" 272 SECURITY WARNING! 273 274 The mail system has detected that the following 275 attachment may contain hazardous program code, is 276 a suspicious file type, or has a suspicious file name. 277 Do not trust it. Contact your system administrator immediately. 278 279 "} 280 281 MACRO_WARNING=${MACRO_WARNING:-" 282 SECURITY WARNING! 283 284 The mail delivery system has detected that the preceding 285 document attachment appears to contain hazardous macro code. 286 Macro Scanner score: "} 287 288 MACRO_DETAILS=${MACRO_DETAILS:-"Macro Scanner score details: 289 " 290 291 ZIP_MAGIC_WARNING=${ZIP_MAGIC_WARNING:-" 292 SECURITY WARNING! 293 294 The mail system has detected that the preceding attachment 295 claims to be a ZIP archive, but it does not have a valid ZIP 296 archive signature. 297 Do not trust it. Contact your system administrator immediately. 298 299 "} 300 301 ZIPPED_WARNING=${ZIPPED_WARNING:-" 302 SECURITY WARNING! 303 304 The mail system has detected that the preceding ZIP archive 305 attachment contains suspicious files. 306 Do not trust it. Contact your system administrator immediately. 307 308 The suspicious files in the archive are: 309 310 "} 311 312 :0 313 * ! DISABLE_RAR_SCAN ?? [^ ] 314 * ! ? type unrar >/dev/null 2>&1 315 { 316 # unrar not on $PATH 317 LOG=" unrar not found, disabling RAR scan - either install unrar or define DISABLE_RAR_SCAN${NL}" 318 DISABLE_RAR_SCAN=Y 319 } 320 321 322 RAR_MAGIC_WARNING=${RAR_MAGIC_WARNING:-" 323 SECURITY WARNING! 324 325 The mail system has detected that the preceding attachment 326 claims to be a RAR archive, but it does not have a valid RAR 327 archive signature. 328 Do not trust it. Contact your system administrator immediately. 329 330 "} 331 332 RARRED_WARNING=${RARRED_WARNING:-" 333 SECURITY WARNING! 334 335 The mail system has detected that the preceding RAR archive 336 attachment contains suspicious files. 337 Do not trust it. Contact your system administrator immediately. 338 339 The suspicious files in the archive are: 340 341 "} 342 343 BAD_BASE64_WARNING=${BAD_BASE64_WARNING:-" 344 SECURITY WARNING! 345 346 The mail system has detected that the preceding attachment 347 was encoded in a manner intended to bypass security filtering. 348 Do not trust it. Contact your system administrator immediately. 349 350 "} 351 352 BAD_JPEG_WARNING=${BAD_JPEG_WARNING:-" 353 SECURITY WARNING! 354 355 The mail system has detected that the preceding attachment 356 appears to be a JPEG image containing a Microsoft buffer overflow attack. 357 Do not trust it. Contact your system administrator immediately. 358 359 "} 360 361 WMF_WARNING=${WMF_WARNING:-" 362 SECURITY WARNING! 363 364 The mail system has detected that the preceding attachment 365 appears to be a WMF image. It has been blocked for security reasons. 366 Contact your system administrator immediately. 367 368 "} 369 370 TNEF_WARNING=${TNEF_WARNING:-" 371 SECURITY NOTICE: 372 373 The mail system has removed a Microsoft attachment for security reasons. 374 The sender should disable sending Rich Text format in Outlook and 375 disable sending TNEF to the Internet from their Microsoft Exchange gateway. 376 377 See http://support.microsoft.com/support/kb/articles/Q241/5/38.ASP 378 and http://www.microsoft.com/TechNet/exchange/2505ch10.asp 379 for more information. 380 381 "} 382 383 384 # FROM address for notifications 385 SECURITY_LOCAL_POSTMASTER=${SECURITY_LOCAL_POSTMASTER:-"postmaster"} 386 387 # MTA command line options when generating messages 388 # get recipient(s) from command line 389 MTA_FLAGS_CMDLN=${MTA_FLAGS_CMDLN:-"-U"} 390 # get recipient(s) from message headers 391 MTA_FLAGS_HDRS=${MTA_FLAGS_HDRS:-"-oi -t -f$SECURITY_LOCAL_POSTMASTER"} 392 393 # How paranoid to be about stuff embedded in documents 394 # default: very 395 SC_MBD=${SECURITY_OFFICE_EMBED_SCORE:-99} 396 397 398 #--------------------------------------------------------------------------- 399 # trap some excessively long RFC-822 headers 400 # 401 402 :0 403 * ^\/Subject: ..................................................................................................................................................................................................................................................* 404 { 405 LOG=" Trapped excessively long header$SUBJ" 406 STATUS="STATUS: Header truncated." 407 HDR="$MATCH" 408 409 # truncate the header 410 :0 fw h 411 * ^\/Subject: ................................................................................................................................................................................................................................................. 412 | formail -i "$MATCH" 413 414 SECURITY_NOTIFY=${SECURITY_NOTIFY:-"postmaster"} 415 416 :0 417 * ! SECURITY_NONOTIFY_LONGSUBJECT ?? [^ ] 418 * SECURITY_NOTIFY ?? [^ ] 419 * !$ ^X-Loop: EMAIL SECURITY WARNING $HOST $SECRET 420 { 421 LOG="${NL} NOTIFY $SECURITY_NOTIFY${NL}" 422 423 :0 h ci 424 | ( \ 425 echo "To: $SECURITY_NOTIFY";\ 426 echo 'From: "Procmail Security daemon"' "<${SECURITY_LOCAL_POSTMASTER}>";\ 427 echo 'Subject: SECURITY WARNING - possible email attack';\ 428 echo "X-Loop: EMAIL SECURITY WARNING $HOST $SECRET"; \ 429 echo ;\ 430 echo 'Trapped excessively long header:' ;\ 431 echo "$HDR";\ 432 echo ;\ 433 echo "$STATUS";\ 434 echo ;\ 435 echo 'Headers from message:';\ 436 echo ;\ 437 sed -e 's/^/> /' ;\ 438 ) | $SENDMAIL $MTA_FLAGS_CMDLN $SECURITY_NOTIFY 439 } 440 } 441 442 443 :0 444 * \/^((resent-)?(sender|from|(reply-)?to|cc|bcc)|(errors|disposition-notification|apparently)-to|Return-Path): .*<>.*<>.*<>.*<>.*<>.*<>.* 445 { 446 HDR=$MATCH 447 LOG=" Trapped multiple null addresses (possible sendmail attack)" 448 STATUS="STATUS: Header cleaned." 449 450 # truncate the header 451 :0 fw h 452 | sed -e 's/<>.*<>.*<>.*<>.*<>.*<>/<>/g' 453 454 SECURITY_NOTIFY=${SECURITY_NOTIFY:-"postmaster"} 455 456 :0 457 * SECURITY_NOTIFY ?? [^ ] 458 * !$ ^X-Loop: EMAIL SECURITY WARNING $HOST $SECRET 459 { 460 LOG="${NL} NOTIFY $SECURITY_NOTIFY${NL}" 461 462 :0 h ci 463 | ( \ 464 echo "To: $SECURITY_NOTIFY";\ 465 echo 'From: "Procmail Security daemon"' "<${SECURITY_LOCAL_POSTMASTER}>";\ 466 echo 'Subject: SECURITY WARNING - possible email attack';\ 467 echo "X-Loop: EMAIL SECURITY WARNING $HOST $SECRET"; \ 468 echo ;\ 469 echo 'Trapped multiple null addresses (possible sendmail exploit):' ;\ 470 echo "$HDR";\ 471 echo ;\ 472 echo "$STATUS";\ 473 echo ;\ 474 echo 'Headers from message:';\ 475 echo ;\ 476 sed -e 's/^/> /' ;\ 477 ) | $SENDMAIL $MTA_FLAGS_CMDLN $SECURITY_NOTIFY 478 } 479 } 480 481 482 :0 483 * ^\/(Mime-Version|(Resent-)?(Date|Sender|From|Reply-To)|(errors|disposition-notification|apparently)-to|Message-ID|Return-Path|Status|X-Status|X-Keywords|Content-(Class|Type|Disposition|Transfer-Encoding)): ......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................* 484 { 485 LOG=" Trapped excessively long header$SUBJ" 486 STATUS="STATUS: Message bounced." 487 HDR="$MATCH" 488 489 :0 490 * SECURITY_QUARANTINE ?? [^ ] 491 { 492 STATUS="STATUS: Message quarantined in $SECURITY_QUARANTINE, not delivered to recipient." 493 } 494 495 SECURITY_NOTIFY=${SECURITY_NOTIFY:-"postmaster"} 496 497 :0 498 * SECURITY_NOTIFY ?? [^ ] 499 * !$ ^X-Loop: EMAIL SECURITY WARNING $HOST $SECRET 500 { 501 LOG="${NL} NOTIFY $SECURITY_NOTIFY${NL}" 502 503 :0 h ci 504 | ( \ 505 echo "To: $SECURITY_NOTIFY";\ 506 echo 'From: "Procmail Security daemon"' "<${SECURITY_LOCAL_POSTMASTER}>";\ 507 echo 'Subject: SECURITY WARNING - possible email attack';\ 508 echo "X-Loop: EMAIL SECURITY WARNING $HOST $SECRET"; \ 509 echo ;\ 510 echo 'Trapped excessively long header:' ;\ 511 echo "$HDR";\ 512 echo ;\ 513 echo "$STATUS";\ 514 echo ;\ 515 echo 'Headers from message:';\ 516 echo ;\ 517 sed -e 's/^/> /' ;\ 518 ) | $SENDMAIL $MTA_FLAGS_CMDLN $SECURITY_NOTIFY 519 } 520 521 :0 522 * SECURITY_QUARANTINE ?? [^ ] 523 { 524 :0 :${SECURITY_QUARANTINE_LOCKFILE} 525 $SECURITY_QUARANTINE 526 527 :0 e 528 { 529 # Argh! Quarantine failed! 530 # notify administrator 531 LOG="${NL} ERR: QUARANTINE FAILED!${NL}" 532 533 # bounce it. 534 EXITCODE=65 535 536 :0 h 537 * SECURITY_NOTIFY ?? [^ ] 538 * !$ ^X-Loop: EMAIL SECURITY WARNING $HOST $SECRET 539 | ( \ 540 echo "To: $SECURITY_NOTIFY";\ 541 echo 'From: "Procmail Security daemon"' "<${SECURITY_LOCAL_POSTMASTER}>";\ 542 echo 'Subject: SECURITY WARNING - quarantine failed!';\ 543 echo "X-Loop: EMAIL SECURITY WARNING $HOST $SECRET"; \ 544 echo ;\ 545 echo 'Attempt to quarantine the following message in $SECURITY_QUARANTINE failed.';\ 546 echo 'Message has been bounced.';\ 547 echo 'Verify file access permissions:';\ 548 ls -l $SECURITY_QUARANTINE 2>&1 ;\ 549 echo ;\ 550 echo 'Headers from message:';\ 551 echo ;\ 552 sed -e 's/^/> /' ;\ 553 ) | $SENDMAIL $MTA_FLAGS_CMDLN $SECURITY_NOTIFY 554 555 } 556 } 557 558 # bounce it. 559 EXITCODE=65 560 561 # zap it. 562 :0 563 /dev/null 564 565 } 566 567 #--------------------------------------------------------------------------- 568 # Not all MIME can be sanitized... 569 # 570 :0 571 * ^Content-Type[ ]*:.*multipart/((signed)|(encrypted)); 572 * ! SECURITY_DEFANG_SIGNED ?? [^ ] 573 { 574 LOG=" WARN: Cannot sanitize message due to signing or encryption$SUBJ" 575 } 576 577 #--------------------------------------------------------------------------- 578 # Defang HTML active-content tags 579 # 580 # NB: In case you think the regexes should be /<[ ]*TAG/, I suggest 581 # you *try* such tags in your browser first... 582 # 583 # Unfortunately the "on*" (e.g. "onload=") syntax is such that we can't 584 # reliably look for /onload="/ - there may be whitespace around the =. 585 # This isn't intended to be a full HTML parser, so we'll err on the side of 586 # safety by defanging everything, even though it may be outside of an HTML 587 # context. 588 # 589 # This keeps getting uglier as more and more holes are discovered. 590 # 591 # This will be folded into a better sanitizer Real Soon Now... 592 # 593 594 :0 595 * 9876543210^1 ! ^Content-Type[ ]*:.*multipart/((signed)|(encrypted)); 596 * 9876543210^1 SECURITY_DEFANG_SIGNED ?? [^ ] 597 { 598 #----------- ALL OF THIS IS SKIPPED FOR SIGNED/ENCRYPTED MESSAGES 599 600 # "perl -e" has problems when run as root... 601 DROPPRIVS=YES 602 603 :0 604 * DEBUG_VERBOSE ?? [^ ] 605 { 606 VERBOSE=YES 607 } 608 609 :0 H 610 * [\015][^\012] 611 { 612 # this apparently happens a lot, so stop logging it 613 #LOG="Defanging bare CR in headers$SUBJ" 614 615 :0 fw h 616 | perl -p -e 's/\015/\012/g' 617 } 618 619 :0 B 620 * ! SECURITY_TRUST_HTML ?? [^ ] 621 * 9876543210^1 \<(html|title|body|meta|app|script|object|embed|i?frame|style|img|bgsound|i?layer|link|form|input|table|th|td|xml) 622 * 9876543210^1 =(3d)?[ ]*["'](&{|([a-z]+script|mocha):) 623 { 624 625 LOG="Defanging active HTML content$SUBJ" 626 627 HAVE_UUE= 628 629 :0 B 630 * ^begin[ ]+([0-9]+)?[ ]+[^ ]+ 631 { 632 HAVE_UUE=YES 633 LOG=" UUE content, HTML defang suppression enabled.$NL" 634 } 635 636 :0 fw b 637 | perl -p -e ' #\ 638 unless ($ENV{"HAVE_UUE"} && /^M.{60}$/ ) { #\ 639 if (/ / && /["\047][^"\047\s]*&#x?[1-9][0-9a-f]/i) { #\ 640 while (/["\047][^"\047\s]*&#((4[6-9]|5[0-8]|6[4-9]|[78][0-9]|9[07-9]|1[0-1][0-9]|12[0-2])(?![0-9]))/) { #\ 641 $char = chr($1); #\ 642 s/&#$1;?/$char/g; #\ 643 } #\ 644 while (/["\047][^"\047\s]*&#(x(2[ef]|3[0-9a]|4[0-9a-f]|5[0-9a]|6[1-9a-f]|7[0-9a]))/i) { #\ 645 $char = chr(hex("0$1")); #\ 646 s/&#$1;?/$char/gi; #\ 647 } #\ 648 } #\ 649 if (/ / && /["\047][^"\047\s]*%[2-7][0-9a-f]/i) { #\ 650 while (/["\047][^"\047\s]*%((2[ef]|3[0-9a]|4[0-9a-f]|5[0-9a]|6[1-9a-f]|7[0-9a]))/i) { #\ 651 $char = chr(hex("0x$1")); #\ 652 s/%$1/$char/gi; #\ 653 } #\ 654 } #\ 655 if (/<|%3c/) { #\ 656 s/(<|%3c)(META|APP|SCRIPT|OBJECT|EMBED|FRAME|IFRAME|LAYER|ILAYER|LINK|FORM|INPUT|XML)/$1DEFANGED_$2/gi; #\ 657 unless ($ENV{"SECURITY_TRUST_STYLE_TAGS"}) { #\ 658 s/<STYLE/ <!-- <DEFANGED_STYLE/gi; #\ 659 s/<\/STYLE/ --> <\/DEFANGED_STYLE/gi; #\ 660 s/\sSTYLE\s*=/ DEFANGED_STYLE=/gi; #\ 661 } #\ 662 if ($ENV{"DEFANG_WEBBUGS"}) { #\ 663 s/<IMG/<DEFANGED_IMG/gi; #\ 664 s/<BGSOUND/<DEFANGED_BGSOUND/gi; #\ 665 if (/<(BODY|TABLE|TH|TD)\s/i) { #\ 666 s/\sBACKGROUND\s*=\s*/ DEFANGED_BACKGROUND=/gi; #\ 667 } #\ 668 } #\ 669 s/\sOn(Abort|Blur|Change|Click|DblClick|DragDrop|Error|Focus|KeyDown|KeyPress|KeyUp|Load|MouseDown|MouseMove|MouseOut|MouseOver|MouseUp|Move|Reset|Resize|Select|Submit|Unload|ContextMenu|DragStart)/ DEFANGED_On$1/gi; #\ 670 } #\ 671 s/^\s*On(Abort|Blur|Change|Click|DblClick|DragDrop|Error|Focus|KeyDown|KeyPress|KeyUp|Load|MouseDown|MouseMove|MouseOut|MouseOver|MouseUp|Move|Reset|Resize|Select|Submit|Unload|ContextMenu|DragStart)/DEFANGED_On$1/gi; #\ 672 s/(["\047\075]|url\()([a-z]+script|mocha):/${1}DEFANGED_$2:/gi; #\ 673 s/(["\047\075])&{/${1}DEFANGED_&_{/g; #\ 674 } #\ 675 ' 676 } 677 678 #--------------------------------------------------------------------------- 679 # Mangle HTML and executable attachment filenames enough that they won't 680 # automatically execute, and limit the length of extremely long attachment 681 # filenames and MIME headers to prevent buffer overflows and client 682 # crashes (sigh). Adding ${$} to the mangling inserts a bit of randomness 683 # so that an active-HTML or BO exploit can't just look for an attachment 684 # named EXPLOIT.DEFANGED-EXE to get around the defanging. 685 # 686 # NOTE: the [ ] has a tab embedded in it - DO NOT remove it... 687 # 688 689 :0 690 * ! MANGLE_EXTENSIONS ?? [^ ] 691 { 692 MANGLE_EXTENSIONS='html?|exe|com|cmd|bat|pif|sc[rt]|lnk|dll|ocx|do[ct]|xl[swt]|p[po]t|rtf|vb[se]?|hta|p[lm]|sh[bs]|hlp|chm|eml|ws[cfh]|ad[ep]|jse?|md[abew]|ms[ip]|reg|as[dfx]|c[ip]l|pps|wm[avszdf]|vcf|nws|wsz|\{[-0-9a-f]+\}' 693 } 694 695 696 # UUE attachments 697 :0 B 698 * ^begin[ ]+([0-9]+)?[ ]+[^ ]+ 699 { 700 :0 B 701 * ^begin[ ]+([0-9]+)?[ ]+\/[^ ]....................................................................................................+$ 702 { 703 LOG="Truncating extremely long attachment filename $MATCH$SUBJ" 704 705 :0 fw b 706 | perl -p -e 'if (/^begin\s+[0-9]*\s/i) { #\ 707 ($mode, $filen) = /^begin\s+([0-9]*)\s+(.{64}).*$/i; #\ 708 $mode = "644" unless $mode; #\ 709 s/^.*$/begin $mode $filen.../ if $filen; #\ 710 }' 711 } 712 713 :0 B 714 * $ ^begin[ ]+([0-9]+)?[ ]+.+\.(${MANGLE_EXTENSIONS})[ ]*$ 715 { 716 LOG="Sanitizing executable UUE attachments$SUBJ" 717 718 :0 fw b 719 | perl -p -e ' #\ 720 if ($stripped) { #\ 721 chomp; #\ 722 if (/^end$/i || /^\s*$/) { #\ 723 $stripped = 0; #\ 724 } #\ 725 $_ = ""; next; #\ 726 } #\ 727 if (($junk,$filen) = /^begin\s+([0-9]+\s+)?((\\.|[^"])+\.($ENV{"MANGLE_EXTENSIONS"}|\{[-0-9a-f]+\}))[\.\s]*$/io) { #\ 728 if ($specf = $ENV{"STRIPPED_EXECUTABLES"}) { #\ 729 if (open(STRIPPED,$specf)) { #\ 730 warn " Checking UUE \"$filen\" for stripping.\n"; #\ 731 while (chomp($stp_spec = <STRIPPED>)) { #\ 732 $stp_spec =~ s/^\s+//g; #\ 733 $stp_spec =~ s/\s.*$//g; #\ 734 next unless $stp_spec; #\ 735 $stp_spec =~ s/([^\\])\./$1\\./g; #\ 736 $stp_spec =~ s/\*/.*/g; #\ 737 $stp_spec =~ s/\?\?/?./g; #\ 738 $stp_spec =~ s/([^\(]|^)\?/$1./g; #\ 739 $stp_spec .= "\$" unless $stp_spec =~ /\$/; #\ 740 warn " Checking against \"$stp_spec\"\n" if $ENV{"DEBUG"}; #\ 741 if ($filen =~ /^${stp_spec}/i) { #\ 742 warn " Stripped UUE attachment \"$filen\".\n"; #\ 743 $stripped = 1; #\ 744 print "\n"; #\ 745 print "X-Content-Security: [" . $ENV{"HOST"} . "] REPORT: UUE attachment \"$filen\" stripped\n"; #\ 746 print "\n"; #\ 747 print $ENV{"STRIPPED_WARNING"}; #\ 748 print "Filename: $filen\n\n"; #\ 749 last; #\ 750 } #\ 751 } #\ 752 close(STRIPPED); #\ 753 if ($stripped) { #\ 754 $_ = <>; #\ 755 $_ = ""; next; #\ 756 } #\ 757 } else { #\ 758 warn " ERR: Unable to open stripped-executables file \"$specf\".\n"; #\ 759 } #\ 760 } #\ 761 if ($specf = $ENV{"POISONED_EXECUTABLES"}) { #\ 762 if (open(POISONED,$specf)) { #\ 763 warn " Checking UUE \"$filen\" for poisoning.\n"; #\ 764 while (chomp($psn_spec = <POISONED>)) { #\ 765 $psn_spec =~ s/^\s+//g; #\ 766 $psn_spec =~ s/\s.*$//g; #\ 767 next unless $psn_spec; #\ 768 $psn_spec =~ s/([^\\])\./$1\\./g; #\ 769 $psn_spec =~ s/\*/.*/g; #\ 770 $psn_spec =~ s/\?\?/?./g; #\ 771 $psn_spec =~ s/([^\(]|^)\?/$1./g; #\ 772 $psn_spec .= "\$" unless $psn_spec =~ /\$/; #\ 773 warn " Checking against \"$psn_spec\"\n" if $ENV{"DEBUG"}; #\ 774 if ($filen =~ /^${psn_spec}/i) { #\ 775 warn " Trapped poisoned attachment \"$filen\".\n"; #\ 776 print "X-Content-Security: [", $ENV{"HOST"}, "] NOTIFY\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 777 print "X-Content-Security: [", $ENV{"HOST"}, "] REPORT: Trapped poisoned attachment \"$filen\"\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 778 print "X-Content-Security: [", $ENV{"HOST"}, "] QUARANTINE\n" if $ENV{"SECURITY_QUARANTINE"}; #\ 779 print "\n"; #\ 780 print $ENV{"POISONED_WARNING"}; #\ 781 print "SUSPICIOUS ATTACHMENT: "; #\ 782 last; #\ 783 } #\ 784 } #\ 785 close(POISONED); #\ 786 } else { #\ 787 warn " ERR: Unable to open poisoned-executables file \"$specf\".\n"; #\ 788 } #\ 789 } #\ 790 warn " Mangling executable UUE filename \"$filen\".\n"; #\ 791 $filen =~ s/\.([-a-z0-9{}]+)$/.${$}DEFANGED-$1/i; #\ 792 print "begin 666 $filen\n"; #\ 793 $_ = ""; #\ 794 } #\ 795 ' 2>> $LOGFILE 796 } 797 } 798 799 # MIME attachments and general header sanitizing 800 :0 801 * !$ ^X-Content-Security: \[${HOST}\] (QUARANTINE|DISCARD) 802 * 9876543210^0 ^Content-Type[ ]*:.*(application|multipart)/[^ ]*[ ]*; 803 * 9876543210^0 ^Content-Type[ ]*:.*message/rfc822 804 * 9876543210^0 ^Content-Disposition[ ]*:.*attachment 805 { 806 LOG="Sanitizing MIME & attachments$SUBJ" 807 808 # Due to procmail not unwrapping MIME attachment headers, 809 # (they're in the message body) this perl script has to run against 810 # *every* message with MIME attachments to ensure security. Sorry. 811 812 # If you get "Out of memory" errors in your procmail log, try changing to 813 # the following: 814 # :0 fw 815 # | ulimit -d 15000; perl -p -e ' #\ 816 817 POISONED_SCORE=${POISONED_SCORE:-25} 818 ZIPPED_EXECUTABLES=${ZIPPED_EXECUTABLES:-"$POISONED_EXECUTABLES"} 819 820 :0 fw 821 | perl -p -e ' #\ 822 $pastmsghdr = 1 if /^\s*$/; #\ 823 $XCS = "X-Content-Security: [" . $ENV{"HOST"} . "]" unless $XCS; #\ 824 if ($pastmsghdr) { #\ 825 if (!$mimebdry && $mimebdrs[0]) { #\ 826 warn " Found no MIME boundary in msg attachment.\n" if $ENV{"DEBUG"}; #\ 827 $mimebdry = pop @mimebdrs; #\ 828 $newbdry = pop @newbdrs; #\ 829 $rawbdry = pop @rawbdrs; #\ 830 $bdrytoolong = pop @bdrstoolong; #\ 831 $gotbdry = pop @gotbdrs; #\ 832 $nullbdry = pop @nullbdrs; #\ 833 } #\ 834 $_ = "" if $strip_att && !$gotbdry; #\ 835 } else { #\ 836 if (($type,$format,$junk) = /^Content-Type\s*:\s.*(application|multipart|message)\/(\S+)\s*(;.*)?$/i) { #\ 837 $wanthdr = 1; #\ 838 print "X-Security: message sanitized on ", $ENV{"HOST"}, "\n"; #\ 839 print "\tSee http://www.impsec.org/email-tools/sanitizer-intro.html\n"; #\ 840 print "\tfor details. \$Revision: 1.151 $x\$Date: 2006-01-20 07:29:24-08 $x\n"; #\ 841 print "X-Security: The postmaster has not enabled quarantine of poisoned messages.\n" unless $ENV{"SECURITY_QUARANTINE"}; #\ 842 if ($type =~ /application/i) { #\ 843 $inmimehdr = 1; #\ 844 } elsif ($type =~ /message/i && $format =~ /rfc822/i) { #\ 845 $rcrsmsg = $inmimehdr = 1; #\ 846 } #\ 847 } elsif (/^\S/) { #\ 848 $wanthdr = 0; #\ 849 } #\ 850 if ($wanthdr) { #\ 851 if (($mimebdry) = /boundary\s*=\s*(("")|("[^"]+")|([^"]\S+)|$)/i) { #\ 852 $mimebdry =~ s/(^"|"$)//g; #\ 853 $rawbdry = $mimebdry; #\ 854 $gotbdry = 1; #\ 855 $wanthdr = 0; #\ 856 $bdrytoolong = $nullbdry = 0; #\ 857 if ($bdrytoolong = (length($mimebdry) > 80)) { #\ 858 warn " Truncating long MIME boundary string.\n"; #\ 859 $newbdry = substr($mimebdry,0,64); #\ 860 $mimebdry = quotemeta($mimebdry); #\ 861 s/${mimebdry}/${newbdry}/; #\ 862 $rawbdry =~ s/${mimebdry}/${newbdry}/; #\ 863 } elsif ($nullbdry = (length($mimebdry) < 1)) { #\ 864 warn " Replacing null MIME boundary string.\n"; #\ 865 $newbdry = "==NULL_MIME_BOUNDARY_ATTACK_SANITIZED-${$}=="; #\ 866 s/boundary\s*=\s*(""|$)/boundary="${newbdry}"/i; #\ 867 } else { #\ 868 $newbdry = $mimebdry; #\ 869 $mimebdry = quotemeta($mimebdry); #\ 870 } #\ 871 } #\ 872 } #\ 873 } #\ 874 if ($mimebdry || ($gotbdry && $nullbdry) || $inmimehdr) { #\ 875 if (/^\s*$/) { #\ 876 $inmimehdr = 0; #\ 877 if ($rcrsmsg) { #\ 878 push @mimebdrs, $mimebdry; #\ 879 push @newbdrs, $newbdry; #\ 880 push @rawbdrs, $rawbdry; #\ 881 push @bdrstoolong, $bdrytoolong; #\ 882 push @gotbdrs, $gotbdry; #\ 883 push @nullbdrs, $nullbdry; #\ 884 $mimebdry = $newbdry = ""; #\ 885 $rcrsmsg = $pastmsghdr = $bdrytoolong = $gotbdry = 0; #\ 886 } #\ 887 } elsif (/^--${mimebdry}(--)?$/) { #\ 888 $mend = $1; #\ 889 s/${mimebdry}/${newbdry}/ if $bdrytoolong; #\ 890 s/^--/--${newbdry}${mend}/ if $nullbdry; #\ 891 if ($mend) { #\ 892 if ($mimebdrs[0]) { #\ 893 $mimebdry = pop @mimebdrs; #\ 894 $newbdry = pop @newbdrs; #\ 895 $rawbdry = pop @rawbdrs; #\ 896 $bdrytoolong = pop @bdrstoolong; #\ 897 $gotbdry = pop @gotbdrs; #\ 898 $nullbdry = pop @nullbdrs; #\ 899 } #\ 900 } else { #\ 901 $inmimehdr = 1; #\ 902 $rcrsmsg = $strip_att = $check_att = 0; #\ 903 } #\ 904 } elsif (!$inmimehdr && $strip_att) { #\ 905 $_ = ""; #\ 906 } elsif (!$inmimehdr && $check_att && !$base64) { #\ 907 warn " Not base64, not scanning\n"; #\ 908 $base64 = $check_att = 0; #\ 909 } elsif (!$inmimehdr && $check_att) { #\ 910 $ATTCH = $destf = $rarh = $ziph = ""; #\ 911 $SIG{PIPE} = 'IGNORE'; #\ 912 if ($ENV{"USE_CPAN"}) { #\ 913 push @INC, $ENV{"PVT_CPAN"} if $ENV{"PVT_CPAN"}; #\ 914 eval "use MIME::Base64; use File::MkTemp;"; die $@ if $@; #\ 915 if (($ATTCH,$destf) = mkstempt("mailchk.XXXXXX","/tmp")) { #\ 916 $destf = "/tmp/$destf"; #\ 917 } else { #\ 918 warn " ERR: mkstempt failed\n"; #\ 919 $ATTCH = $destf = ""; #\ 920 } #\ 921 } else { #\ 922 if (!chomp($destf = `mktemp /tmp/mailchk.XXXXXX`)) { #\ 923 warn " ERR: mktemp failed\n"; #\ 924 $ATTCH = $destf = ""; #\ 925 } else { #\ 926 if (open(ATTCH,"|mimencode -u -o $destf")) { #\ 927 $ATTCH = ATTCH; #\ 928 } else { #\ 929 warn " ERR: mimencode failed: $@\n"; #\ 930 unlink($destf); #\ 931 $ATTCH = $destf = ""; #\ 932 } #\ 933 } #\ 934 } #\ 935 if ($ATTCH && $destf) { #\ 936 warn " Decoding to \"$destf\"\n"; #\ 937 do { #\ 938 print $_; #\ 939 if (length($_) > 3) { #\ 940 if ($ENV{"USE_CPAN"}) { #\ 941 $de = decode_base64($_); #\ 942 print $ATTCH $de || die $@; #\ 943 $rarh = $ziph = $de unless $ziph; #\ 944 } else { #\ 945 print $ATTCH $_ || die $@; #\ 946 } #\ 947 } else { #\ 948 $rarh = $ziph = "XXX" if length($_) > 1; #\ 949 } #\ 950 $_ = <>; #\ 951 $lastline = $_; #\ 952 } until (/^--${mimebdry}(--)?$/ || ($mimebdrs[0] && /^--${mimebdrs[0]}(--)?$/) || !$_ ); #\ 953 if (close($ATTCH)) { #\ 954 # Run virus-checker against $destf here. #\ 955 } else { #\ 956 warn " ERR: decode failed! mimencode?\n"; #\ 957 $check_att = 0; #\ 958 } #\ 959 if ($check_att == 3) { #\ 960 warn " Scanning image\n"; #\ 961 $wmf = $jpegbo = 0; #\ 962 if ($ENV{"SECURITY_POISON_WMF"} && (`/usr/bin/od -N 4 -t x $destf` =~ /9ac6cdd7/i)) { #\ 963 $wmf = 1; #\ 964 warn " Poisoned WMF\n"; #\ 965 } elsif (open(JPEGI,"rdjpgcom $destf 2>&1 |")) { #\ 966 while (<JPEGI>) { #\ 967 warn "djpeg: $_" if $ENV{"DEBUG_VERBOSE"}; #\ 968 if (/Erroneous JPEG marker length/i) { #\ 969 $jpegbo = $_; last; #\ 970 } #\ 971 } #\ 972 close(JPEGI); #\ 973 } #\ 974 if ($jpegbo || $wmf) { #\ 975 print "\n\n--$newbdry\n"; #\ 976 print "Content-Type: TEXT/PLAIN;\n"; #\ 977 print "Content-Description: SECURITY WARNING\n"; #\ 978 print "$XCS NOTIFY\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 979 print "$XCS QUARANTINE\n" if $ENV{"SECURITY_QUARANTINE"}; #\ 980 } #\ 981 if ($jpegbo) { #\ 982 warn " JPEG BO?\n"; #\ 983 print "$XCS REPORT: Trapped possible JPEG attack: $jpegbo" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 984 print "\n"; #\ 985 print $ENV{"BAD_JPEG_WARNING"}; #\ 986 } #\ 987 if ($wmf) { #\ 988 print "$XCS REPORT: Trapped possible WMF attack." if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 989 print "\n"; #\ 990 print $ENV{"WMF_WARNING"}; #\ 991 } #\ 992 } elsif (($check_att == 2 || $check_att == 4) && ($specf = $ENV{"ZIPPED_EXECUTABLES"})) { #\ 993 if ($check_att == 2) { #\ 994 $rarh = "Rar"; #\ 995 warn " Scanning ZIP\n" if $ENV{"DEBUG"}; #\ 996 unless ($ENV{"USE_CPAN"} || $ziph) { #\ 997 if (open(ATTCH,"<$destf")) { #\ 998 $ziph = <ATTCH>; #\ 999 close(ATTCH); #\ 1000 } else { #\ 1001 warn " ERR: decode failed\n"; #\ 1002 $ziph = "PK\003\004"; #\ 1003 } #\ 1004 } #\ 1005 } elsif ($check_att == 4) { #\ 1006 $ziph = "PK\003\004"; #\ 1007 warn " Scanning RAR\n" if $ENV{"DEBUG"}; #\ 1008 unless ($rarh) { #\ 1009 if (open(ATTCH,"<$destf")) { #\ 1010 $rarh = <ATTCH>; #\ 1011 close(ATTCH); #\ 1012 } else { #\ 1013 warn " ERR: decode failed\n"; #\ 1014 $rarh = "Rar"; #\ 1015 } #\ 1016 } #\ 1017 } #\ 1018 if ($ziph !~ /^(?:PK00)?PK\003\004/ || $rarh !~ /^Rar/) { #\ 1019 if ($ziph eq "XXX") { #\ 1020 warn " Hostile BASE64\n"; #\ 1021 } else { #\ 1022 warn " ZIP/RAR magic not found\n"; #\ 1023 } #\ 1024 print "\n\n--$newbdry\n"; #\ 1025 print "Content-Type: TEXT/PLAIN;\n"; #\ 1026 print "$XCS NOTIFY\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1027 if ($ziph eq "XXX") { #\ 1028 print "$XCS REPORT: Trapped ZIP/RAR file \"$zfilen\" with hostile base64 encoding\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1029 } else { #\ 1030 print "$XCS REPORT: Trapped bogus ZIP/RAR file \"$zfilen\"\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1031 } #\ 1032 print "$XCS QUARANTINE\n" if $ENV{"SECURITY_QUARANTINE"}; #\ 1033 print "Content-Description: SECURITY WARNING\n\n"; #\ 1034 if ($ziph eq "XXX") { #\ 1035 print $ENV{"BAD_BASE64_WARNING"}; #\ 1036 } else { #\ 1037 if ($check_att == 2) { #\ 1038 print $ENV{"ZIP_MAGIC_WARNING"}; #\ 1039 } elsif ($check_att == 4) { #\ 1040 print $ENV{"RAR_MAGIC_WARNING"}; #\ 1041 } #\ 1042 } #\ 1043 } elsif (open(ZIPPOL,$specf)) { #\ 1044 %zipf = (); #\ 1045 if ($check_att == 2) { #\ 1046 if (open(ZIPL,"unzip -l $destf |")) { #\ 1047 while (<ZIPL>) { #\ 1048 if (($filen) = /^\s+\d+\s+\S+\s+\S+\s+(.+)$/) { #\ 1049 warn "$filen\n" if $ENV{"DEBUG"}; #\ 1050 $zipf{$filen} = 1; #\ 1051 } #\ 1052 } #\ 1053 warn " ERR: scan failed! unzip?\n" unless close(ZIPL); #\ 1054 if (open(ZIPL,"unzip -t -P _virus_$$ $destf 2>&1 |")) { #\ 1055 while (<ZIPL>) { #\ 1056 if (($junk,$filen) = /(skipp|test)ing:\s+(\S.+\.\w+)\W*\s+($|incorrect)/i) { #\ 1057 $zipf{$filen} = 1; #\ 1058 } #\ 1059 } #\ 1060 close(ZIPL); #\ 1061 } #\ 1062 } #\ 1063 } elsif ($check_att == 4) { #\ 1064 if (open(RARL,"unrar v $destf |")) { #\ 1065 while (<RARL>) { #\ 1066 if (($filen) = /^[\s*](?:[^\/]+\/)*(\S.*\..+)$/) { #\ 1067 warn "$filen\n" if $ENV{"DEBUG"}; #\ 1068 $zipf{$filen} = 1; #\ 1069 } #\ 1070 } #\ 1071 warn " ERR: scan failed! unrar?\n" unless close(RARL); #\ 1072 } #\ 1073 } #\ 1074 $filel = ""; #\ 1075 foreach $filen (keys %zipf) { #\ 1076 $fn = $filen; $fn =~ s/\s{10,}/ (many spaces) /; #\ 1077 warn " Checking archived \"$fn\"\n"; #\ 1078 seek(ZIPPOL,0,0); #\ 1079 while (chomp($pol_spec = <ZIPPOL>)) { #\ 1080 $pol_spec =~ s/^\s+//g; #\ 1081 $pol_spec =~ s/\s.*$//g; #\ 1082 next unless $pol_spec; #\ 1083 $pol_spec =~ s/([^\\])\./$1\\./g; #\ 1084 $pol_spec =~ s/\*/.*/g; #\ 1085 $pol_spec =~ s/\?\?/?./g; #\ 1086 $pol_spec =~ s/([^\(]|^)\?/$1./g; #\ 1087 warn " Checking against \"$pol_spec\"\n" if $ENV{"DEBUG"}; #\ 1088 if ($filen =~ /^${pol_spec}$/i) { #\ 1089 warn " Trapped archived \"$fn\".\n"; #\ 1090 if (!$poisoned) { #\ 1091 print "\n\n--$newbdry\n"; #\ 1092 print "Content-Type: TEXT/PLAIN;\n"; #\ 1093 print "$XCS NOTIFY\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1094 print "$XCS REPORT: Scanned attachment \"$zfilen\"\n$XCS REPORT: Trapped suspicious archived files:\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1095 $poisoned = 1; #\ 1096 } #\ 1097 print "$XCS REPORT: $fn\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1098 $filel .= " $fn\n"; #\ 1099 last; #\ 1100 } #\ 1101 } #\ 1102 } #\ 1103 if ($poisoned) { #\ 1104 print "$XCS QUARANTINE\n" if $ENV{"SECURITY_QUARANTINE"}; #\ 1105 print "Content-Description: SECURITY WARNING\n\n"; #\ 1106 if ($check_att == 2) { #\ 1107 print $ENV{"ZIPPED_WARNING"}; #\ 1108 } elsif ($check_att == 4) { #\ 1109 print $ENV{"RARRED_WARNING"}; #\ 1110 } #\ 1111 print "$filel\n"; #\ 1112 } #\ 1113 } else { #\ 1114 warn " ERR: Unable to open ZIP/RAR list \"$specf\".\n"; #\ 1115 } #\ 1116 } elsif ($check_att == 1) { # CUT \ 1117 warn " Scanning MSOffice file\n"; #\ 1118 $msapp = $score = 0; #\ 1119 @scores = (); #\ 1120 $why = ""; #\ 1121 if (open(ATTCH,"<$destf")) { #\ 1122 while (<ATTCH>) { #\ 1123 if (/\023 (INCLUDE(PICTURE|TEXT)[^\000-\037]+)/i) { #\ 1124 $why .= " " . $ENV{"SC_MBD"} . " for $1\n"; #\ 1125 $score+= $ENV{"SC_MBD"}; #\ 1126 } #\ 1127 if (/(\000|\001|\004)(VirusProtection)/i) { #\ 1128 $why .= " 99 for $&\n"; #\ 1129 $score+= 99; #\ 1130 } #\ 1131 if (/\000(select\s[^\000]*shell\s*\(\s*["\047])/i) { #\ 1132 $why .= " 99 for $1\n"; #\ 1133 $score+= 99; #\ 1134 } #\ 1135 if (/\000(ID="{.{36}[^}][^\000\012\015]{5,})/i) { #\ 1136 $why .= " 99 for $1\n"; #\ 1137 $score+= 99; #\ 1138 } #\ 1139 if (/\000(regedit)/i) { #\ 1140 $why .= " 9 for $1\n"; #\ 1141 $score+= 9; #\ 1142 } #\ 1143 if (/\000(Shell\s*\()/i) { #\ 1144 $why .= " 9 for $1\n"; #\ 1145 $score+= 9; #\ 1146 } #\ 1147 if (/\000(Save(Normal|Properties)Prompt)/i) { #\ 1148 $why .= " 9 for $1\n"; #\ 1149 $score+= 9; #\ 1150 } #\ 1151 if (/\000(Outlook\.Application)\000/i) { #\ 1152 $why .= " 9 for $1\n"; #\ 1153 $score+= 9; #\ 1154 } #\ 1155 if (/\000(CountOfLines)/i) { #\ 1156 $why .= " 9 for $1\n"; #\ 1157 $score+= 9; #\ 1158 } #\ 1159 if (/\000(AddFromString)/i) { #\ 1160 $why .= " 9 for $1\n"; #\ 1161 $score+= 9; #\ 1162 } #\ 1163 if (/\000(StartupPath)/i) { #\ 1164 $why .= " 9 for $1\n"; #\ 1165 $score+= 9; #\ 1166 } #\ 1167 if (/\000(CreateObject)/i) { #\ 1168 $why .= " 4 for $1\n"; #\ 1169 $score+= 4; #\ 1170 } #\ 1171 if (/(\000|\004)([a-z0-9_]\.)*(Autoexec|Workbook_(Open|BeforeClose|Window(De)?activate)|Document_(Open|New|Close))/i) { #\ 1172 $why .= " 4 for $&\n"; #\ 1173 $score+= 4; #\ 1174 } #\ 1175 if (/(\000|\004)(Logon|AddressLists|AddressEntries|Recipients|Attachments|Logoff)/i) { #\ 1176 $why .= " 4 for $&\n"; #\ 1177 $score+= 4; #\ 1178 } #\ 1179 if (/(\000|\004)(Subject|Body)/i) { #\ 1180 $why .= " 4 for $&\n" unless $scores[0]; #\ 1181 $scores[0] = 4; #\ 1182 } #\ 1183 if (/\000(Options[^\w\s])/i) { #\ 1184 $why .= " 2 for $1\n"; #\ 1185 $score+= 2; #\ 1186 } #\ 1187 if (/\000(CodeModule)/i) { #\ 1188 $why .= " 2 for $1\n"; #\ 1189 $score+= 2; #\ 1190 } #\ 1191 if (/\000(([a-z]+\.)?Application)\000/i) { #\ 1192 $why .= " 2 for $1\n"; #\ 1193 $score+= 2; #\ 1194 } #\ 1195 if (/(\000|\004)stdole/i) { #\ 1196 $why .= " 2 for $&\n"; #\ 1197 $score+= 2; #\ 1198 } #\ 1199 if (/(\000|\004)NormalTemplate/i) { #\ 1200 $why .= " 2 for $&\n"; #\ 1201 $score+= 2; #\ 1202 } #\ 1203 if (/\000(ID="{[-0-9A-F]{36}}")/i) { #\ 1204 $why .= " 4 for $1\n"; #\ 1205 $score+= 4; #\ 1206 } #\ 1207 if (/\000(ThisWorkbook)\000/i) { #\ 1208 $why .= " 1 for $1\n"; #\ 1209 $score+= 1; #\ 1210 } #\ 1211 if (/\000(PrivateProfileString)/i) { #\ 1212 $why .= " 1 for $1\n"; #\ 1213 $score+= 1; #\ 1214 } #\ 1215 if (/(\000|\004)(ActiveDocument|ThisDocument|ThisWorkbook)/i) { #\ 1216 $why .= " 1 for $&\n"; #\ 1217 $score+= 1; #\ 1218 } #\ 1219 if (/\000(\[?HKEY_(CLASSES_ROOT|CURRENT_USER|LOCAL_MACHINE))/) { #\ 1220 $why .= " 1 for $1\n"; #\ 1221 $score+= 1; #\ 1222 } #\ 1223 $msapp++ if /\000(Microsoft (Word Document|Excel Worksheet|Excel|PowerPoint)|MSWordDoc|Word\.Document\.[0-9]+|Excel\.Sheet\.[0-9]+)\000/; #\ 1224 } #\ 1225 close(ATTCH); #\ 1226 } else { #\ 1227 warn " ERR: Scan failed\n"; #\ 1228 } #\ 1229 if ($msapp) { #\ 1230 for (@scores) { #\ 1231 $score += $_; #\ 1232 } #\ 1233 if ($histfile = $ENV{"SCORE_HISTORY"}) { #\ 1234 if (open(HIST,">>$histfile")) { #\ 1235 print HIST "score=$score to=".$ENV{"TO"}." from=".$ENV{"FROM"}."\n"; #\ 1236 close HIST; #\ 1237 } #\ 1238 } #\ 1239 $poison_score = $ENV{"POISONED_SCORE"}; #\ 1240 $poison_score = 5 if $poison_score < 5; #\ 1241 if ($score > $poison_score && !$ENV{"SCORE_ONLY"}) { #\ 1242 $why =~ s/[\000-\011\013-\037]//g; #\ 1243 warn " POSSIBLE MACRO EXPLOIT: Score=$score\n"; #\ 1244 print "\n\n--$newbdry\n"; #\ 1245 print "Content-Type: TEXT/PLAIN;\n"; #\ 1246 print "$XCS NOTIFY\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1247 print "$XCS REPORT: Trapped poisoned Microsoft Office attachment\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1248 if ($ENV{"SCORE_DETAILS"}) { #\ 1249 $why2 = $why; #\ 1250 $why2 =~ s/^/$XCS REPORT:/gm; #\ 1251 print $why2; #\ 1252 } #\ 1253 print "$XCS QUARANTINE\n" if $ENV{"SECURITY_QUARANTINE"}; #\ 1254 print "Content-Description: SECURITY WARNING\n\n"; #\ 1255 print $ENV{"MACRO_WARNING"}, "$score\n"; #\ 1256 if ($ENV{"SCORE_DETAILS"}) { #\ 1257 print $ENV{"MACRO_DETAILS"}; #\ 1258 print $why; #\ 1259 } #\ 1260 } #\ 1261 } # CUT \ 1262 } #\ 1263 unlink($destf); #\ 1264 if ($lastline =~ /^--${mimebdry}(--)?$/) { #\ 1265 $mend = $1; #\ 1266 $lastline =~ s/${mimebdry}/${newbdry}/ if $bdrytoolong; #\ 1267 s/^--/--${newbdry}${mend}/ if $nullbdry; #\ 1268 if ($mend) { #\ 1269 if ($mimebdrs[0]) { #\ 1270 $mimebdry = pop @mimebdrs; #\ 1271 $newbdry = pop @newbdrs; #\ 1272 $rawbdry = pop @rawbdrs; #\ 1273 $bdrytoolong = pop @bdrstoolong; #\ 1274 $gotbdry = pop @gotbdrs; #\ 1275 $nullbdry = pop @nullbdrs; #\ 1276 } #\ 1277 } else { #\ 1278 $inmimehdr = 1; #\ 1279 } #\ 1280 } #\ 1281 print $lastline; $_ = ""; #\ 1282 } #\ 1283 $check_att = 0; #\ 1284 } #\ 1285 if ($inmimehdr || $hdrcnt) { #\ 1286 if (/^(\s+\S|(file)?name)/) { #\ 1287 s/^\s*/ /; #\ 1288 s/^\s*// if $hdrtxt =~ /"[^"]*[^;]$/; #\ 1289 s/\s*\n$//; #\ 1290 $hdrtxt .= $_; #\ 1291 $_ = ""; #\ 1292 } else { #\ 1293 if ($hdrtxt) { #\ 1294 $hdrtxt =~ s/([^\\])\\"/\1\\ÿ/g; #\ 1295 if ($hdrtxt =~ /`\s*`/) { #\ 1296 warn " Fixing double backquotes.\n"; #\ 1297 $hdrtxt =~ s/`\s*`/\\"/g; #\ 1298 } #\ 1299 if ($hdrtxt =~ /^[-\w]+\s*:.*name\s*=\s*"[^"]+$/i) { #\ 1300 warn " Fixing missing close quote on filename.\n"; #\ 1301 $hdrtxt .= "\""; #\ 1302 } #\ 1303 while (($hdr, $val) = $hdrtxt =~ /^([-\w]+)\s*:.*\s(\S+)\s*=\s*""/i) { #\ 1304 warn " Null $val in $hdr header.\n"; #\ 1305 $sval = quotemeta($val); #\ 1306 $hdrtxt =~ s/\s$sval\s*=\s*""/ X-$val="{null value sanitized}"/; #\ 1307 } #\ 1308 unless ($ENV{"SECURITY_DISABLE_OUTLOOK_HACKS"}) { #\ 1309 while (($hdr,$filen) = $hdrtxt =~ /^(Content-Description)\s*:\s*text\s+from\s+file\s+\047([^\047]+)\047/i) { #\ 1310 warn " Fixing file name \"$filen\" in ${hdr}:\n"; #\ 1311 $newfilen = $filen; $filen = quotemeta($filen); #\ 1312 $hdrtxt =~ s/\s+\047${filen}\047/, filename="${newfilen}"/ig; #\ 1313 } #\ 1314 } #\ 1315 while (($junk,$filen,$junk) = $hdrtxt =~ /^Content-[-\w]+\s*:[^"]*("[^"]*"[^"]+)*name\s*=\s*([^"\s]([^;]|;(?!\s))+)/i) { #\ 1316 warn " Fixing unquoted filename \"$filen\".\n"; #\ 1317 $newfilen = $filen; $filen = quotemeta($filen); #\ 1318 if ($newfilen =~ /\.[a-z0-9]+"[a-z0-9"]+[\.\s]*$/i) { #\ 1319 warn " Defanging quotes-in-extension attack.\n"; #\ 1320 while ($newfilen =~ /\.[a-z0-9]+"[a-z0-9"]+[\.\s]*$/i) { #\ 1321 $newfilen =~ s/\.([a-z0-9]+)"([a-z0-9"]+)[\.\s]*$/.$1$2/i; #\ 1322 } #\ 1323 } #\ 1324 $newfilen =~ s/\"/\\"/g; #\ 1325 if ($newfilen =~ /\([^)]*\)/) { #\ 1326 warn " Removing embedded RFC822 comments.\n"; #\ 1327 $newfilen =~ s/\([^)]*\)//g; #\ 1328 } #\ 1329 $hdrtxt =~ s/name\s*=\s*${filen}/name="$newfilen"/ig; #\ 1330 } #\ 1331 while (($filen) = $hdrtxt =~ /^Content-[-\w]+\s*:.*name\s*=\s*"(=\?[^"]+\?Q\?[^"]+=(2e|3[0-9]|[46][1-9a-f]|[57][0-9a])[^"]+\?=)"/i) { #\ 1332 warn " Fixing encoded plain characters in \"$filen\".\n"; #\ 1333 $newfilen = $filen; $filen = quotemeta($filen); #\ 1334 while ($newfilen =~ /=(2e|3[0-9]|[46][1-9a-f]|[57][0-9a])/i) { #\ 1335 $char = chr(hex("0x$1")); #\ 1336 $newfilen =~ s/=$1/$char/gi; #\ 1337 } #\ 1338 $hdrtxt =~ s/name\s*=\s*"${filen}"/name="$newfilen"/ig; #\ 1339 } #\ 1340 while (($filen) = $hdrtxt =~ /^Content-[-\w]+\s*:.*name\s*=\s*"([^"]+)[\.\s]+"/i) { #\ 1341 warn " Fixing trailing spaces/periods in filename.\n"; #\ 1342 $newfilen = $filen; $filen = quotemeta($filen); #\ 1343 $hdrtxt =~ s/name\s*=\s*"${filen}[\.\s]+"/name="$newfilen"/ig; #\ 1344 } #\ 1345 while (($filen) = $hdrtxt =~ /^Content-[-\w]+\s*:.*name\s*=\s*"([^"]{128,})"/i) { #\ 1346 warn " Shortening long filename \"$filen\".\n"; #\ 1347 $filen =~ s/\s+/ /g; #\ 1348 substr ($filen,64,32) = "..." while (length($filen) > 120); #\ 1349 $hdrtxt =~ s/name\s*=\s*"[^"]{120,}"/name="$filen"/i; #\ 1350 $mangle_mime_type = 1; #\ 1351 warn " Filename now \"$filen\".\n"; #\ 1352 } #\ 1353 if (($mtype) = $hdrtxt =~ /^Content-Type:\s+([a-z0-9-_]+\/[a-z0-9-_]+)/i) { #\ 1354 warn " MIME body part type \"$mtype\".\n" if $ENV{"DEBUG"}; #\ 1355 unless ($mtype =~ /^(multipart|text|message)\//i) { #\ 1356 unless ($hdrtxt =~ /name\s*=\s*"/i) { #\ 1357 $dfrhdr .= "$hdrtxt\n"; $hdrtxt = ""; #\ 1358 } #\ 1359 } #\ 1360 if ($mtype =~ /^application\/x-ms-?download/i && ! ($ENV{"SECURITY_TRUST_MS_DOWNLOAD"} || $poisoned)) { #\ 1361 warn " Trapped poisoned $mtype\n"; #\ 1362 $poisoned = 1; $check_att = 0; #\ 1363 print "Content-Type: TEXT/PLAIN;\n"; #\ 1364 print "$XCS NOTIFY\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1365 print "$XCS REPORT: Trapped poisoned $mtype attachment\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1366 print "$XCS QUARANTINE\n" if $ENV{"SECURITY_QUARANTINE"}; #\ 1367 print "Content-Description: SECURITY WARNING\n\n"; #\ 1368 print $ENV{"POISONED_WARNING"}; #\ 1369 print "Scanner score: 0 (poisoned by MIME type, scan skipped)\n\n"; #\ 1370 } #\ 1371 if ($mtype =~ /^application\/x(-zip)?-compress(ed)?/i) { #\ 1372 $check_att = 2 unless $ENV{"DISABLE_ZIP_SCAN"} || $poisoned; #\ 1373 } #\ 1374 if ($mtype =~ /^image\//i) { #\ 1375 $check_att = 3 unless $ENV{"DISABLE_JPEG_SCAN"} || $poisoned; #\ 1376 } #\ 1377 } #\ 1378 if ($hdrtxt =~ /^Content-Transfer-Encoding\s*:/i) { #\ 1379 $base64 = ($hdrtxt =~ /:\s+base64/i); #\ 1380 $dfrhdr .= "$hdrtxt\n"; $hdrtxt = ""; #\ 1381 } #\ 1382 if ($hdrtxt =~ /^Content-[-\w]+\s*:.*name\s*=\s*"([^"]*\.(p?jp[eg]+|bmp|gif|png|wmf)(\?=)?)"/i) { #\ 1383 $check_att = 3 unless $ENV{"DISABLE_JPEG_SCAN"} || $poisoned; #\ 1384 } #\ 1385 if (($filen) = $hdrtxt =~ /^Content-[-\w]+\s*:.*name\s*=\s*"([^"]*\.(do[ct]|xl[swt]|p[po]t|rtf|pps|zip|rar)(\?=)?)"/i) { #\ 1386 $fn = $filen; $fn =~ s/\s{10,}/ (many spaces) /; #\ 1387 $stripped = 0; #\ 1388 if ($filen =~ /\.rar(\?=)?$/i) { #\ 1389 $typ = "RAR archive"; #\ 1390 $check_att = 4 unless $ENV{"DISABLE_RAR_SCAN"} || $poisoned; #\ 1391 $zfilen = $filen; #\ 1392 } elsif ($filen =~ /\.zip(\?=)?$/i) { #\ 1393 $typ = "ZIP archive"; #\ 1394 $check_att = 2 unless $ENV{"DISABLE_ZIP_SCAN"} || $poisoned; #\ 1395 $zfilen = $filen; #\ 1396 } else { #\ 1397 $typ = "Office document"; #\ 1398 $check_att = 1 unless $ENV{"DISABLE_MACRO_CHECK"} || $poisoned; #\ 1399 } #\ 1400 if (!$poisoned && ($specf = $ENV{"STRIPPED_EXECUTABLES"})) { #\ 1401 if (open(STRIPPED,$specf)) { #\ 1402 warn " Checking $typ \"$fn\" for stripping.\n"; #\ 1403 while (chomp($stp_spec = <STRIPPED>)) { #\ 1404 $stp_spec =~ s/^\s+//g; #\ 1405 $stp_spec =~ s/\s.*$//g; #\ 1406 next unless $stp_spec; #\ 1407 $stp_spec =~ s/([^\\])\./$1\\./g; #\ 1408 $stp_spec =~ s/\*/.*/g; #\ 1409 $stp_spec =~ s/\?\?/?./g; #\ 1410 $stp_spec =~ s/([^\(]|^)\?/$1./g; #\ 1411 $stp_spec .= "(\\?=)?\$" unless $stp_spec =~ /\$/; #\ 1412 warn " Checking against \"$stp_spec\"\n" if $ENV{"DEBUG"}; #\ 1413 if ($filen =~ /^${stp_spec}/i) { #\ 1414 warn " Stripped $typ \"$fn\".\n"; #\ 1415 print "Content-Type: TEXT/PLAIN;\n"; #\ 1416 print "$XCS REPORT: $typ attachment \"$fn\" stripped\n"; #\ 1417 print "Content-Description: SECURITY NOTICE\n\n"; #\ 1418 print $ENV{"STRIPPED_WARNING"}; #\ 1419 print "Filename: $fn\n\n"; #\ 1420 print "More headers follow:\n\n" unless $pastmsghdr; #\ 1421 $_ = $dfrhdr = $hdrtxt = ""; #\ 1422 $stripped = $strip_att = 1; #\ 1423 $check_att = $inmimehdr = 0; #\ 1424 last; #\ 1425 } #\ 1426 } #\ 1427 close(STRIPPED); #\ 1428 } else { #\ 1429 warn " ERR: Unable to open strip list \"$specf\".\n"; #\ 1430 } #\ 1431 } #\ 1432 if (!$poisoned && !$stripped && ($specf = $ENV{"POISONED_EXECUTABLES"})) { #\ 1433 if (open(POISONED,$specf)) { #\ 1434 warn " Checking $typ \"$fn\" for poisoning.\n"; #\ 1435 while (chomp($psn_spec = <POISONED>)) { #\ 1436 $psn_spec =~ s/^\s+//g; #\ 1437 $psn_spec =~ s/\s.*$//g; #\ 1438 next unless $psn_spec; #\ 1439 $psn_spec =~ s/([^\\])\./$1\\./g; #\ 1440 $psn_spec =~ s/\*/.*/g; #\ 1441 $psn_spec =~ s/\?\?/?./g; #\ 1442 $psn_spec =~ s/([^\(]|^)\?/$1./g; #\ 1443 $psn_spec .= "(\\?=)?\$" unless $psn_spec =~ /\$/; #\ 1444 warn " Checking against \"$psn_spec\"\n" if $ENV{"DEBUG"}; #\ 1445 if ($filen =~ /^${psn_spec}/i) { #\ 1446 warn " Trapped poisoned $typ \"$fn\".\n"; #\ 1447 $poisoned = 1; $check_att = 0; #\ 1448 print "Content-Type: TEXT/PLAIN;\n"; #\ 1449 print "$XCS NOTIFY\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1450 print "$XCS REPORT: Trapped poisoned $typ attachment \"$fn\"\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1451 print "$XCS QUARANTINE\n" if $ENV{"SECURITY_QUARANTINE"}; #\ 1452 print "Content-Description: SECURITY WARNING\n\n"; #\ 1453 print $ENV{"POISONED_WARNING"}; #\ 1454 print "Scanner score: 0 (poisoned by name, scan skipped)\n\n"; #\ 1455 last; #\ 1456 } #\ 1457 } #\ 1458 close(POISONED); #\ 1459 } else { #\ 1460 warn " ERR: Unable to open poison list \"$specf\".\n"; #\ 1461 } #\ 1462 } #\ 1463 } #\ 1464 if (($bndry) = $hdrtxt =~ /^Content-Type:\s+multipart\/.*\s+boundary\s*=\s*"?([^"]+)"?/i) { #\ 1465 push @mimebdrs, $mimebdry; #\ 1466 push @newbdrs, $newbdry; #\ 1467 push @rawbdrs, $rawbdry; #\ 1468 push @bdrstoolong, $bdrytoolong; #\ 1469 push @gotbdrs, $gotbdry; #\ 1470 push @nullbdrs, $nullbdry; #\ 1471 $mimebdry = $newbdry = $bndry; #\ 1472 $mimebdry = quotemeta($mimebdry); #\ 1473 $rcrsmsg = $bdrytoolong = $gotbdry = 0; #\ 1474 } #\ 1475 if ($hdrtxt =~ /^Content-Type:\s+message\/rfc822/i) { #\ 1476 if (!$inmimehdr) { #\ 1477 push @mimebdrs, $mimebdry; #\ 1478 push @newbdrs, $newbdry; #\ 1479 push @rawbdrs, $rawbdry; #\ 1480 push @bdrstoolong, $bdrytoolong; #\ 1481 push @gotbdrs, $gotbdry; #\ 1482 push @nullbdrs, $nullbdry; #\ 1483 $mimebdry = $newbdry = ""; #\ 1484 $rcrsmsg = $pastmsghdr = $bdrytoolong = $gotbdry = 0; #\ 1485 } else { #\ 1486 $rcrsmsg = 1; #\ 1487 } #\ 1488 } #\ 1489 if ($ENV{"SECURITY_STRIP_MSTNEF"} && $hdrtxt =~ /^Content-Type:\s+application\/MS-TNEF/i) { #\ 1490 print "Content-Type: TEXT/PLAIN;\n"; #\ 1491 print "$XCS REPORT: Stripped MS-TNEF attachment\n"; #\ 1492 print "Content-Description: SECURITY NOTICE\n\n"; #\ 1493 print $ENV{"TNEF_WARNING"}; #\ 1494 $_ = $dfrhdr = $hdrtxt = ""; #\ 1495 $strip_att = 1; #\ 1496 $inmimehdr = 0; #\ 1497 } #\ 1498 while (($filen) = $hdrtxt =~ /^Content-[-\w]+\s*:.*name\s*=\s*"([^"]*\.($ENV{"MANGLE_EXTENSIONS"})(\?=)?)"/io) { #\ 1499 $fn = $filen; $fn =~ s/\s{10,}/ (many spaces) /; #\ 1500 $stripped = 0; #\ 1501 if (!$poisoned && ($specf = $ENV{"STRIPPED_EXECUTABLES"})) { #\ 1502 if (open(STRIPPED,$specf)) { #\ 1503 warn " Checking \"$fn\" for stripping.\n"; #\ 1504 while (chomp($stp_spec = <STRIPPED>)) { #\ 1505 $stp_spec =~ s/^\s+//g; #\ 1506 $stp_spec =~ s/\s.*$//g; #\ 1507 next unless $stp_spec; #\ 1508 $stp_spec =~ s/([^\\])\./$1\\./g; #\ 1509 $stp_spec =~ s/\*/.*/g; #\ 1510 $stp_spec =~ s/\?\?/?./g; #\ 1511 $stp_spec =~ s/([^\(]|^)\?/$1./g; #\ 1512 $stp_spec .= "(\\?=)?\$" unless $stp_spec =~ /\$/; #\ 1513 warn " Checking against \"$stp_spec\"\n" if $ENV{"DEBUG"}; #\ 1514 if ($filen =~ /^${stp_spec}/i) { #\ 1515 warn " Stripped executable \"$fn\".\n"; #\ 1516 print "Content-Type: TEXT/PLAIN;\n"; #\ 1517 print "$XCS REPORT: Attachment \"$fn\" stripped\n"; #\ 1518 print "Content-Description: SECURITY NOTICE\n\n"; #\ 1519 print $ENV{"STRIPPED_WARNING"}; #\ 1520 print "Filename: $fn\n\n"; #\ 1521 print "More headers follow:\n\n" unless $pastmsghdr; #\ 1522 $_ = $dfrhdr = $hdrtxt = ""; #\ 1523 $strip_att = $stripped = 1; #\ 1524 $inmimehdr = $check_att = 0; #\ 1525 last; #\ 1526 } #\ 1527 } #\ 1528 close(STRIPPED); #\ 1529 } else { #\ 1530 warn " ERR: Unable to open stripped-executables file \"$specf\".\n"; #\ 1531 } #\ 1532 } #\ 1533 if (!$poisoned && !$stripped && ($specf = $ENV{"POISONED_EXECUTABLES"})) { #\ 1534 if (open(POISONED,$specf)) { #\ 1535 warn " Checking \"$fn\" for poisoning.\n"; #\ 1536 while (chomp($psn_spec = <POISONED>)) { #\ 1537 $psn_spec =~ s/^\s+//g; #\ 1538 $psn_spec =~ s/\s.*$//g; #\ 1539 next unless $psn_spec; #\ 1540 $psn_spec =~ s/([^\\])\./$1\\./g; #\ 1541 $psn_spec =~ s/\*/.*/g; #\ 1542 $psn_spec =~ s/\?\?/?./g; #\ 1543 $psn_spec =~ s/([^\(]|^)\?/$1./g; #\ 1544 $psn_spec .= "(\\?=)?\$" unless $psn_spec =~ /\$/; #\ 1545 warn " Checking against \"$psn_spec\"\n" if $ENV{"DEBUG"}; #\ 1546 if ($filen =~ /^${psn_spec}/i) { #\ 1547 warn " Trapped poisoned executable \"$fn\".\n"; #\ 1548 $poisoned = 1; $check_att = 0; #\ 1549 print "Content-Type: TEXT/PLAIN;\n"; #\ 1550 print "$XCS NOTIFY\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1551 print "$XCS REPORT: Trapped poisoned executable \"$fn\"\n" if $ENV{"SECURITY_NOTIFY"} || $ENV{"SECURITY_NOTIFY_VERBOSE"}; #\ 1552 print "$XCS QUARANTINE\n" if $ENV{"SECURITY_QUARANTINE"}; #\ 1553 print "Content-Description: SECURITY WARNING\n\n"; #\ 1554 print $ENV{"POISONED_WARNING"}; #\ 1555 last; #\ 1556 } #\ 1557 } #\ 1558 close(POISONED); #\ 1559 } else { #\ 1560 warn " ERR: Unable to open poisoned-executables file \"$specf\".\n"; #\ 1561 } #\ 1562 } #\ 1563 unless ($stripped) { #\ 1564 warn " Mangling executable filename \"$fn\".\n"; #\ 1565 $newfilen = $filen; $filen = quotemeta($filen); #\ 1566 $newfilen =~ s/\.([-a-z0-9{}]+(\?=)?)$/.${$}DEFANGED-$1/i; #\ 1567 $hdrtxt =~ s/name\s*=\s*"?${filen}"?/name="$newfilen"/ig; #\ 1568 $mangle_mime_type = 1; #\ 1569 } #\ 1570 } #\ 1571 if ($mangle_mime_type && $hdrtxt =~ /^Content-Type:\s/i) { #\ 1572 ($oct) = $hdrtxt =~ /^Content-Type:.*\s(\S+\/\S+;?)/i; #\ 1573 warn " Mangling MIME type \"$oct\".\n"; #\ 1574 unless ($oct =~ /text\/plain;/i) { #\ 1575 print "$XCS original Content-Type was $oct\n"; #\ 1576 $oct = quotemeta($oct); #\ 1577 $hdrtxt =~ s/${oct}/APPLICATION\/DEFANGED;/i; #\ 1578 } #\ 1579 } #\ 1580 if ($mangle_mime_type && $hdrtxt =~ /\sx-mac-\S+/i) { #\ 1581 $eudora = ""; #\ 1582 while (($eh) = $hdrtxt =~ /(\sx-mac-\S+\s*=\s*\S+;?)/i) { #\ 1583 $eudora .= $eh; #\ 1584 $eh = quotemeta($eh); #\ 1585 $hdrtxt =~ s/${eh}//i; #\ 1586 } #\ 1587 print "$XCS removed$eudora\n"; #\ 1588 } #\ 1589 if (($junk) = $hdrtxt =~ /^Content-Type\s*:\s+(.{128}).{100,}$/i) { #\ 1590 warn " Truncating long Content-Type header.\n"; #\ 1591 $junk =~ s/"/\\"/g; #\ 1592 $hdrtxt = "Content-Type: X-BOGUS\/X-BOGUS; originally=\"$junk...\""; #\ 1593 } elsif (($junk) = $hdrtxt =~ /^Content-Description\s*:\s+(.{128}).{100,}$/i) { #\ 1594 warn " Truncating long Content-Description header.\n"; #\ 1595 $hdrtxt = "Content-Description: $junk..."; #\ 1596 } elsif (($junk) = $hdrtxt =~ /^Content-[-\w]+\s*:\s+(.{128}).{100,}$/i) { #\ 1597 warn " Truncating long MIME header.\n"; #\ 1598 $junk =~ s/"/\\"/g; #\ 1599 $hdrtxt =~ s/^Content-([-\w]+)\s*:.*$/X-Overflow: Content-$1; originally="$junk..."/i; #\ 1600 } #\ 1601 $hdrtxt =~ s/\\ÿ/\\"/g; #\ 1602 print "$hdrtxt\n" if $hdrtxt; #\ 1603 $hdrtxt = ""; #\ 1604 if (!$inmimehdr) { #\ 1605 if ($dfrhdr) { #\ 1606 if ($mangle_mime_type && $dfrhdr =~ /^Content-Type:\s/i) { #\ 1607 ($oct) = $dfrhdr =~ /^Content-Type:[^\n]*\s(\S+\/\S+;?)/i; #\ 1608 warn " Mangling MIME type \"$oct\".\n"; #\ 1609 unless ($oct =~ /text\/plain;/i) { #\ 1610 print "$XCS original Content-Type was $oct\n"; #\ 1611 $oct = quotemeta($oct); #\ 1612 $dfrhdr =~ s/${oct}/APPLICATION\/DEFANGED;/i; #\ 1613 } #\ 1614 } #\ 1615 print $dfrhdr; $dfrhdr = ""; #\ 1616 } #\ 1617 $poisoned = $mangle_mime_type = 0; #\ 1618 } #\ 1619 } #\ 1620 if (/^\S/) { #\ 1621 s/\s*\n$//; #\ 1622 $hdrtxt = $_; #\ 1623 $_ = ""; #\ 1624 $hdrcnt++; #\ 1625 } else { #\ 1626 $hdrcnt = 0; #\ 1627 $hdrtxt = ""; #\ 1628 } #\ 1629 } #\ 1630 } #\ 1631 } #\ 1632 ' 2>> $LOGFILE 1633 } 1634 1635 } # ---- END OF SIGNED/ENCRYPTED SKIP 1636 1637 :0 HB 1638 * SECURITY_POISON_WINEXE ?? [^ ] 1639 * !$ ^X-Content-Security: \[${HOST}\] (QUARANTINE|DISCARD) 1640 * ^Content-Transfer-Encoding[ ]*:.*base64 1641 * 9876543210^0 ^Content-Type[ ]*:.*(application|multipart)/[^ ]*[ ]*; 1642 * 9876543210^0 ^Content-Disposition[ ]*:.*attachment 1643 { 1644 # not already quarantined 1645 # check for Windows executable attachments that were not blocked by name 1646 # rules for postfix from <hobbit@avian.org>, adapted to sanitizer 1647 # http://archives.neohapsis.com/archives/postfix/2002-04/1841.html 1648 # NOT 100% reliable, but will catch the simple case of using 1649 # a benign filename and a bogus MIME type, and letting Windows figure out 1650 # to execute the attachment directly (vs. opening in a viewer) by its magic 1651 1652 :0 B D hf 1653 * 9876543210^0 ^TV[nopqr]....[AB]..A.A....*AAAA...*AAAA 1654 * 9876543210^0 ^[^ ]*LnJkYXRhAA 1655 * 9876543210^0 ^[^ ]*cmRhdGEAA 1656 * 9876543210^0 ^[^ ]*5yZGF0YQAA 1657 * 9876543210^0 ^[^ ]*LnJlbG9JAA 1658 * 9876543210^0 ^[^ ]*cmVsb2MAA 1659 * 9876543210^0 ^[^ ]*5yZWxvYwAA 1660 | formail -A "X-Content-Security: [$HOST] NOTIFY" \ 1661 -A "X-Content-Security: [$HOST] QUARANTINE" \ 1662 -A "X-Content-Security: [$HOST] REPORT: Trapped Windows executable attachment" 1663 } 1664 1665 :0 HB 1666 * $ ^X-Content-Security: \[${HOST}\] (NOTIFY|QUARANTINE|DISCARD) 1667 { 1668 :0 1669 * SECURITY_MSGID_LOG ?? [^ ] 1670 { JUNK=`echo "$MSGID" >> $SECURITY_MSGID_LOG` } 1671 1672 :0 HB 1673 * $ ^X-Content-Security: \[${HOST}\] DISCARD 1674 { 1675 SECURITY_QUARANTINE=/dev/null 1676 } 1677 1678 :0 HB 1679 * $ ^X-Content-Security: \[${HOST}\] SPOOFED_SENDER 1680 { 1681 # don't bother the sender, it's probably forged 1682 SECURITY_NOTIFY_SENDER= 1683 } 1684 1685 :0 1686 * 9876543210^0 SECURITY_NOTIFY ?? [^ ] 1687 * 9876543210^0 SECURITY_NOTIFY_VERBOSE ?? [^ ] 1688 { 1689 # Notify administration and sender of the attack 1690 1691 STATUS="STATUS: Message delivered to $TO" 1692 STATUS_PUBLIC="STATUS: Message delivered." 1693 REPORT="REPORT: No details available." 1694 SCORE="REPORT: Not a document, or already poisoned by filename. Not scanned for macros." 1695 1696 :0 1697 * SECURITY_QUARANTINE ?? [^ ] 1698 { 1699 STATUS="STATUS: Message quarantined in $SECURITY_QUARANTINE, not delivered to recipient." 1700 STATUS_PUBLIC="STATUS: Message quarantined, not delivered to recipient." 1701 } 1702 1703 :0 HB 1704 * $ ^X-Content-Security: \[${HOST}\] DISCARD 1705 { 1706 STATUS="STATUS: Message discarded, not delivered to recipient." 1707 STATUS_PUBLIC="$STATUS" 1708 } 1709 1710 :0 HB 1711 * ^\/Macro Scanner score: [1-9][0-9]+ 1712 { 1713 SCORE="REPORT: $MATCH" 1714 } 1715 1716 :0 HB 1717 * $ ^X-Content-Security: \[${HOST}\] REPORT: 1718 { REPORT=`grep "^X-Content-Security: \[${HOST}\] REPORT: " | sed -e 's/^.* REPORT:/REPORT:/g'` } 1719 1720 1721 #--------------------------------------------------------------------------- 1722 # Smart Sender Notify Suppression 1723 # If the Return-Path: domain is not supported by any of the Received: domains 1724 # then the envelope sender address is probably forged. Don't waste time notifying. 1725 # 1726 1727 :0 1728 * SECURITY_NOTIFY_SENDER ?? [^ ] 1729 * FROMDOM ?? [^ ] 1730 * ! SECURITY_DISABLE_SMART_REPLY ?? [^ ] 1731 { 1732 FROMDOM2="_" 1733 FROMDOM3="_" 1734 1735 :0 1736 * FROMDOM ?? ^.+\.\/[^.]+\.[^.]+$ 1737 { 1738 FROMDOM2="$MATCH" 1739 # ignore some domains (e.g. ac.uk, com.ar, etc.) 1740 :0 1741 * FROMDOM2 ?? ^(co|ac|com|net|org)\.[a-z][a-z]$ 1742 { 1743 FROMDOM2="_" 1744 } 1745 } 1746 1747 :0 1748 * FROMDOM ?? ^.+\.\/[^.]+\.[^.]+\.[^.]+$ 1749 { 1750 FROMDOM3="$MATCH" 1751 } 1752 1753 # Look for Received: headers that support the sender's claimed domain 1754 # Line 1: sendmail, postfix 1755 # Line 2: qmail 1756 :0 H 1757 * $! ^Received: from [^ ]+ \(([^ .]+\.)*($FROMDOM|$FROMDOM3|$FROMDOM2) [^)]+\)+[ ]+by (${SECURITY_TRUSTED_MTAS:-${HOST}}) 1758 * $! ^Received: from ([^ .]+\.)*($FROMDOM|$FROMDOM3|$FROMDOM2)[ ]+\(HELO .*[ ]+by (${SECURITY_TRUSTED_MTAS:-${HOST}}) 1759 { 1760 REPLY_SUPPRESSED="NOTICE: Envelope sender domain $FROMDOM not supported by trusted Received: path. Suppressing sender notification.${NL}" 1761 LOG=" $REPLY_SUPPRESSED" 1762 SECURITY_NOTIFY_SENDER= 1763 } 1764 1765 # Did a mailing list rewrite the Return-Path header? 1766 :0E 1767 * -1^0 1768 * 1^0 ^Precedence: (bulk|junk|list) 1769 * 1^0 ^(List-Id|X-Mailing-List): 1770 * 9876543210^0 FROM ?? \<owner- 1771 * 9876543210^0 FROM ?? \<[^@ >]+-l-admin@ 1772 { 1773 REPLY_SUPPRESSED="NOTICE: Message from mailing list. Suppressing sender notification.${NL}" 1774 LOG=" $REPLY_SUPPRESSED" 1775 SECURITY_NOTIFY_SENDER= 1776 } 1777 } 1778 #--------------------------------------------------------------------------- 1779 1780 :0 HB 1781 * !$ ^X-Content-Security: \[${HOST}\] NONOTIFY 1782 { 1783 :0 1784 * SECURITY_NOTIFY ?? [^ ] 1785 * !$ ^X-Loop: EMAIL SECURITY WARNING $HOST $SECRET 1786 { 1787 LOG="${NL} NOTIFY ADMIN ($SECURITY_NOTIFY)${NL}" 1788 1789 :0 h ci 1790 | ( \ 1791 echo "To: $SECURITY_NOTIFY";\ 1792 echo 'From: "Procmail Security daemon"' "<${SECURITY_LOCAL_POSTMASTER}>";\ 1793 echo 'Subject: SECURITY WARNING - possible email attack';\ 1794 echo "X-Loop: EMAIL SECURITY WARNING $HOST $SECRET"; \ 1795 echo ;\ 1796 echo "\$Revision: 1.151 $";\ 1797 echo ;\ 1798 echo "$REPORT";\ 1799 echo "$SCORE";\ 1800 echo "$STATUS";\ 1801 echo "$REPLY_SUPPRESSED";\ 1802 echo ;\ 1803 echo 'Headers from message:';\ 1804 echo ;\ 1805 sed -e 's/^/> /' ;\ 1806 ) | $SENDMAIL $MTA_FLAGS_CMDLN $SECURITY_NOTIFY 1807 } 1808 1809 :0 1810 * SECURITY_NOTIFY_VERBOSE ?? [^ ] 1811 * !$ ^X-Loop: EMAIL SECURITY WARNING $HOST $SECRET 1812 { 1813 LOG="${NL} NOTIFY ADMIN VERBOSE ($SECURITY_NOTIFY_VERBOSE)${NL}" 1814 1815 :0 hb ci 1816 | ( \ 1817 echo "To: $SECURITY_NOTIFY_VERBOSE";\ 1818 echo 'From: "Procmail Security daemon"' "<${SECURITY_LOCAL_POSTMASTER}>";\ 1819 echo 'Subject: SECURITY WARNING - possible email attack';\ 1820 echo "X-Loop: EMAIL SECURITY WARNING $HOST $SECRET"; \ 1821 echo ;\ 1822 echo "$REPORT";\ 1823 echo "$SCORE";\ 1824 echo "$STATUS";\ 1825 echo "$REPLY_SUPPRESSED";\ 1826 echo ;\ 1827 echo 'Message:';\ 1828 echo ;\ 1829 sed -e 's/^/> /' ;\ 1830 ) | $SENDMAIL $MTA_FLAGS_CMDLN $SECURITY_NOTIFY_VERBOSE 1831 } 1832 } 1833 1834 :0 H 1835 * SECURITY_NOTIFY_SENDER ?? [^ ] 1836 * ! ^FROM_DAEMON 1837 * !$ ^X-Loop: EMAIL SECURITY WARNING $HOST $SECRET 1838 { 1839 LOG="${NL} NOTIFY SENDER${NL}" 1840 PM_BCC="X-Placeholder:" 1841 PM_CC="X-Placeholder:" 1842 1843 :0 HB 1844 * !$ ^X-Content-Security: \[${HOST}\] NONOTIFY 1845 { 1846 PM_BCC="Bcc: $SECURITY_NOTIFY" 1847 } 1848 1849 :0 1850 * SECURITY_NOTIFY_SENDER_POSTMASTER ?? [^ ] 1851 * FROMDOM ?? [^ ] 1852 { 1853 PM_CC="postmaster@$FROMDOM" 1854 1855 :0 1856 * SECURITY_NOTIFY_SENDER_ABUSE ?? [^ ] 1857 { 1858 PM_CC="$PM_CC>, <abuse@$FROMDOM" 1859 } 1860 1861 PM_CC="Cc: <$PM_CC>" 1862 } 1863 1864 MSG_HEADERS=`sed -e '/^$/q; s/^/> /'` 1865 1866 :0 h ci 1867 | ( \ 1868 formail -r \ 1869 -I "From: \"Procmail Security daemon\" <${SECURITY_LOCAL_POSTMASTER}>"\ 1870 -I "$PM_BCC" -I "$PM_CC" -I "References: $MSGID" \ 1871 -I "Sender: <${SECURITY_LOCAL_POSTMASTER}>" \ 1872 -I "Errors-To: <${SECURITY_LOCAL_POSTMASTER}>" \ 1873 -I "X-Loop: EMAIL SECURITY WARNING $HOST $SECRET" \ 1874 -I "$OVERRIDEFORMAIL" \ 1875 ;\ 1876 echo ;\ 1877 if [ -f "$SECURITY_NOTIFY_SENDER" -a -s "$SECURITY_NOTIFY_SENDER" -a -r "$SECURITY_NOTIFY_SENDER" ] ;\ 1878 then \ 1879 echo 'Regarding your message to';\ 1880 echo "$TO";\ 1881 echo ;\ 1882 cat $SECURITY_NOTIFY_SENDER; \ 1883 else \ 1884 echo '*** SECURITY WARNING ***';\ 1885 echo 'Our email gateway has detected that your message to';\ 1886 echo "$TO";\ 1887 echo 'MAY contain hazardous embedded scripting or attachments,';\ 1888 echo 'or has been rejected by our site security policy for some other reason.';\ 1889 echo 'If you have a question, please reply to this notification message.';\ 1890 echo ;\ 1891 echo 'It is POSSIBLE that your message was infected by a virus.';\ 1892 echo 'You should make sure your virus signature file';\ 1893 echo 'is up-to-date and then rescan your computer,';\ 1894 echo 'especially if you do not remember sending this message.';\ 1895 echo ;\ 1896 echo 'If the macro scanner score is large yet your virus scanner reports';\ 1897 echo 'that the document is not infected, try saving it using a different';\ 1898 echo 'format (such as Rich Text - "RTF") that will remove all macros.';\ 1899 fi ;\ 1900 echo ;\ 1901 echo "$REPORT";\ 1902 echo "$SCORE";\ 1903 echo "$STATUS_PUBLIC";\ 1904 echo ;\ 1905 echo 'Headers from message:';\ 1906 echo ;\ 1907 echo "$MSG_HEADERS";\ 1908 echo ;\ 1909 echo ;\ 1910 echo '--';\ 1911 echo 'Message sanitized on' $HOST;\ 1912 echo 'See http://www.impsec.org/email-tools/sanitizer-intro.html for details.';\ 1913 echo ;\ 1914 ) | $SENDMAIL $MTA_FLAGS_HDRS 1915 1916 } 1917 } 1918 1919 :0 1920 * SECURITY_QUARANTINE ?? [^ ] 1921 { 1922 :0 1923 * SECURITY_NOTIFY_RECIPIENT ?? [^ ] 1924 { 1925 LOG="${NL} NOTIFY RECIPIENT${NL}" 1926 1927 # We could stuff this directly into $DEFAULT but then 1928 # we'd have to worry about generating Message-ID and Date headers 1929 # and it wouldn't work on a relay... 1930 :0 h ci 1931 | ( \ 1932 echo "To: $TO";\ 1933 echo 'From: "Procmail Security daemon"' "<${SECURITY_LOCAL_POSTMASTER}>";\ 1934 if [ "$SECURITY_QUARANTINE" = "/dev/null" ] ;\ 1935 then \ 1936 echo 'Subject: SECURITY WARNING - email discarded';\ 1937 else \ 1938 echo 'Subject: SECURITY WARNING - email quarantined';\ 1939 fi ;\ 1940 echo ;\ 1941 if [ -f "$SECURITY_NOTIFY_RECIPIENT" -a -s "$SECURITY_NOTIFY_RECIPIENT" -a -r "$SECURITY_NOTIFY_RECIPIENT" ] ;\ 1942 then \ 1943 cat $SECURITY_NOTIFY_RECIPIENT; \ 1944 else \ 1945 echo '*** SECURITY WARNING ***';\ 1946 echo 'Our email gateway has detected that a message sent to you';\ 1947 echo 'MAY contain hazardous embedded scripting or attachments.';\ 1948 echo 'The message has been quarantined or discarded per our site security policy.';\ 1949 echo 'Please contact your system administrator for further details.';\ 1950 echo ;\ 1951 fi ;\ 1952 echo ;\ 1953 echo "$REPORT";\ 1954 echo "$SCORE";\ 1955 echo "$STATUS_PUBLIC";\ 1956 echo ;\ 1957 echo 'Headers from message:';\ 1958 echo ;\ 1959 sed -e 's/^/> /' ;\ 1960 echo ;\ 1961 echo '--';\ 1962 echo 'Message sanitized on' $HOST;\ 1963 echo 'See http://www.impsec.org/email-tools/sanitizer-intro.html for details.';\ 1964 echo ;\ 1965 ) | $SENDMAIL $MTA_FLAGS_HDRS 1966 1967 } 1968 1969 :0 :${SECURITY_QUARANTINE_LOCKFILE} 1970 $SECURITY_QUARANTINE 1971 1972 :0 e 1973 * ! SECURITY_QUARANTINE_OPTIONAL ?? [^ ] 1974 { 1975 # Argh! Quarantine failed, and not explicitly marked as optional! 1976 # Bounce message, and notify administrator 1977 LOG="${NL} ERR: QUARANTINE FAILED!${NL}" 1978 EXITCODE=65 1979 1980 :0 h 1981 * SECURITY_NOTIFY ?? [^ ] 1982 * !$ ^X-Loop: EMAIL SECURITY WARNING $HOST $SECRET 1983 | ( \ 1984 echo "To: $SECURITY_NOTIFY";\ 1985 echo 'From: "Procmail Security daemon"' "<${SECURITY_LOCAL_POSTMASTER}>";\ 1986 echo 'Subject: SECURITY WARNING - quarantine failed!';\ 1987 echo "X-Loop: EMAIL SECURITY WARNING $HOST $SECRET"; \ 1988 echo ;\ 1989 echo 'Attempt to quarantine the following message in $SECURITY_QUARANTINE failed.';\ 1990 echo 'Message has been bounced.';\ 1991 echo 'Verify file access permissions (file must be writable):';\ 1992 ls -l $SECURITY_QUARANTINE ;\ 1993 echo ;\ 1994 echo "$REPORT";\ 1995 echo "$SCORE";\ 1996 echo ;\ 1997 echo 'Headers from message:';\ 1998 echo ;\ 1999 sed -e 's/^/> /' ;\ 2000 ) | $SENDMAIL $MTA_FLAGS_CMDLN $SECURITY_NOTIFY 2001 2002 # zap it, just in case 2003 :0 2004 /dev/null 2005 } 2006 } 2007 } 2008 2009 #eof