diff --git a/eicar.go b/eicar.go new file mode 100644 index 0000000..414b38c --- /dev/null +++ b/eicar.go @@ -0,0 +1,70 @@ +package main + +import ( + "fmt" + "net/smtp" +) + +type eicarResult struct { + sent bool + result string +} + +// eicarString assembles the EICAR test string at runtime to avoid static AV detection. +func eicarString() string { + p1 := `X5O!P%@AP[4\PZX54(P^)7CC)7}` + p2 := `$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*` + return p1 + p2 +} + +// sendEICAR sends the EICAR test file as an email attachment to the target mail server. +// A virus filter that detects it will reject the message; sent=true means it was accepted. +func sendEICAR(mailFrom string, mailTo string, targetHost string, targetPort string) (eicarResult, error) { + var er eicarResult + + c, err := smtp.Dial(targetHost + ":" + targetPort) + if err != nil { + return er, err + } + defer c.Quit() //nolint:errcheck + + if err = c.Mail(mailFrom); err != nil { + er.result = err.Error() + return er, nil + } + + if err = c.Rcpt(mailTo); err != nil { + er.result = err.Error() + return er, nil + } + + wc, err := c.Data() + if err != nil { + return er, err + } + + body := "MIME-Version: 1.0\r\n" + + "Content-Type: multipart/mixed; boundary=\"eicartest\"\r\n" + + "Subject: EICAR AV Test\r\n" + + "From: " + mailFrom + "\r\n" + + "To: " + mailTo + "\r\n\r\n" + + "--eicartest\r\n" + + "Content-Type: application/octet-stream\r\n" + + "Content-Disposition: attachment; filename=\"eicar.com\"\r\n\r\n" + + eicarString() + "\r\n" + + "--eicartest--\r\n" + + if _, err = fmt.Fprint(wc, body); err != nil { + er.result = err.Error() + wc.Close() //nolint:errcheck + return er, nil + } + + if err = wc.Close(); err != nil { + er.result = err.Error() + return er, nil + } + + er.sent = true + return er, nil +} diff --git a/main.go b/main.go index ac0c973..816b91d 100644 --- a/main.go +++ b/main.go @@ -58,6 +58,7 @@ type mxresult struct { smtps bool openrelay bool vrfysupport bool + eicardelivered bool smugglevuln bool smuggleresp string smuggleerror string @@ -65,6 +66,7 @@ type mxresult struct { func main() { blacklist := flag.BoolP("blacklist", "b", false, "Check if the service is on blacklists") + eicar := flag.BoolP("eicar", "e", false, "Send EICAR test file to check AV filtering (use only on servers you are authorized to test)") dkimSelector := flag.StringP("dkim-selector", "S", "", "The DKIM selector. If set a DKIM check is performed on the provided service domain") dnsServer := flag.StringP("dnsserver", "d", "8.8.8.8", "The dns server to be requested") @@ -396,6 +398,23 @@ func main() { printOK("Server is not an open relay") } + // EICAR test + if *eicar { + printSection("EICAR AV Test") + er, err := sendEICAR(*mailFrom, *mailTo, targetHost, port) + if err != nil { + printError(err.Error()) + } else if er.sent { + singlemx.eicardelivered = true + printInfo("EICAR test file", "accepted by server") + } else { + printInfo("EICAR test file", "rejected by server") + if len(er.result) > 0 { + printInfo("Response", er.result) + } + } + } + // STARTTLS test printSection("STARTTLS") singlemx.starttls = orresult.starttlsbool