Added support for reporting campaign emails as attachments

pull/1894/head
Glenn Wilkinson 2020-03-24 16:41:43 +00:00
parent b888c37346
commit 920f61d2ee
1 changed files with 56 additions and 19 deletions

View File

@ -14,12 +14,13 @@ import (
"time" "time"
log "github.com/gophish/gophish/logger" log "github.com/gophish/gophish/logger"
"github.com/jordan-wright/email"
"github.com/gophish/gophish/models" "github.com/gophish/gophish/models"
) )
// Pattern for GoPhish emails e.g ?rid=AbC123 // Pattern for GoPhish emails e.g ?rid=AbC123
var goPhishRegex = regexp.MustCompile("(\\?rid=[A-Za-z0-9]{7})") var goPhishRegex = regexp.MustCompile("(\\?rid=(3D)?([A-Za-z0-9]{7}))") // We include the optional quoted-printable 3D at the front, just in case decoding fails
// Monitor is a worker that monitors IMAP servers for reported campaign emails // Monitor is a worker that monitors IMAP servers for reported campaign emails
type Monitor struct { type Monitor struct {
@ -147,30 +148,34 @@ func checkForNewEmails(im models.IMAP) {
} }
} }
body := string(append(m.Email.Text, m.Email.HTML...)) // Not sure if we need to check the Text as well as the HTML. Perhaps sometimes Text only emails won't have an HTML component? rids, err := checkRIDs(m.Email) // Search email Text, HTML, and each attachment for rid parameters
rid := goPhishRegex.FindString(body) if err != nil {
log.Errorf("Error searching email for rids from user '%s': %s", m.Email.From, err.Error())
if rid != "" { } else {
rid = rid[5:] if len(rids) < 1 {
log.Infof("User '%s' reported email with rid %s", m.Email.From, rid) // In the future this should be an alert in Gophish
result, err := models.GetResult(rid) log.Infof("User '%s' reported email with subject '%s'. This is not a GoPhish campaign; you should investigate it.\n", m.Email.From, m.Email.Subject)
if err != nil { }
log.Error("Error reporting GoPhish email with rid ", rid, ": ", err.Error()) for rid := range rids {
reportingFailed = append(reportingFailed, m.SeqNum) log.Infof("User '%s' reported email with rid %s", m.Email.From, rid)
} else { result, err := models.GetResult(rid)
err = result.HandleEmailReport(models.EventDetails{})
if err != nil { if err != nil {
log.Error("Error updating GoPhish email with rid ", rid, ": ", err.Error()) log.Error("Error reporting GoPhish email with rid ", rid, ": ", err.Error())
reportingFailed = append(reportingFailed, m.SeqNum)
} else { } else {
if im.DeleteReportedCampaignEmail == true { err = result.HandleEmailReport(models.EventDetails{})
campaignEmails = append(campaignEmails, m.SeqNum) if err != nil {
log.Error("Error updating GoPhish email with rid ", rid, ": ", err.Error())
} else {
if im.DeleteReportedCampaignEmail == true {
campaignEmails = append(campaignEmails, m.SeqNum)
}
} }
} }
} }
} else {
// In the future this should be an alert in Gophish
log.Debugf("User '%s' reported email with subject '%s'. This is not a GoPhish campaign; you should investigate it.\n", m.Email.From, m.Email.Subject)
} }
// Check if any emails were unable to be reported, so we can mark them as unread // Check if any emails were unable to be reported, so we can mark them as unread
if len(reportingFailed) > 0 { if len(reportingFailed) > 0 {
log.Debugf("Marking %d emails as unread as failed to report\n", len(reportingFailed)) log.Debugf("Marking %d emails as unread as failed to report\n", len(reportingFailed))
@ -192,3 +197,35 @@ func checkForNewEmails(im models.IMAP) {
log.Debug("No new emails for ", im.Username) log.Debug("No new emails for ", im.Username)
} }
} }
// returns a slice of gophish rid paramters found in the email HTML, Text, and attachments
func checkRIDs(em *email.Email) (map[string]int, error) {
rids := make(map[string]int)
// Check Text and HTML
for _, r := range goPhishRegex.FindAllStringSubmatch(string(em.Text)+string(em.HTML), -1) {
newrid := r[len(r)-1]
if _, ok := rids[newrid]; ok {
rids[newrid]++
} else {
rids[newrid] = 1
}
}
//Next check each attachment
for _, a := range em.Attachments {
if a.Header.Get("Content-Type") == "message/rfc822" {
for _, r := range goPhishRegex.FindAllStringSubmatch(string(a.Content), -1) {
newrid := r[len(r)-1]
if _, ok := rids[newrid]; ok {
rids[newrid]++
} else {
rids[newrid] = 1
}
}
}
}
return rids, nil
}