From 9f334281ab7b768ab201a14040ffda658b34044b Mon Sep 17 00:00:00 2001 From: Jordan Wright Date: Mon, 18 Jun 2018 21:37:59 -0500 Subject: [PATCH] Added X-Mailer and X-Gophish-Contact headers (Ref: #1057) --- config/config.go | 3 + controllers/api.go | 1 + controllers/phish.go | 5 +- controllers/phish_test.go | 3 +- mailer/mailer.go | 2 - models/email_request.go | 8 ++- models/email_request_test.go | 10 ++++ models/maillog.go | 6 ++ models/maillog_test.go | 111 ++++++++++++++++++++--------------- 9 files changed, 94 insertions(+), 55 deletions(-) diff --git a/config/config.go b/config/config.go index 342fab5c..53165a64 100644 --- a/config/config.go +++ b/config/config.go @@ -40,6 +40,9 @@ var Conf Config // Version contains the current gophish version var Version = "" +// ServerName is the server type that is returned in the transparency response. +const ServerName = "gophish" + // LoadConfig loads the configuration from the specified filepath func LoadConfig(filepath string) { // Get the config file diff --git a/controllers/api.go b/controllers/api.go index 1bac5f12..75cc4072 100644 --- a/controllers/api.go +++ b/controllers/api.go @@ -731,6 +731,7 @@ func API_Send_Test_Email(w http.ResponseWriter, r *http.Request) { } s.SMTP = smtp } + s.FromAddress = s.SMTP.FromAddress // Validate the given request if err = s.Validate(); err != nil { diff --git a/controllers/phish.go b/controllers/phish.go index 5a3a934e..6715732c 100644 --- a/controllers/phish.go +++ b/controllers/phish.go @@ -35,9 +35,6 @@ type TransparencyResponse struct { // to return a transparency response. const TransparencySuffix = "+" -// ServerName is the server type that is returned in the transparency response. -const ServerName = "gophish" - // CreatePhishingRouter creates the router that handles phishing connections. func CreatePhishingRouter() http.Handler { router := mux.NewRouter() @@ -217,7 +214,7 @@ func RobotsHandler(w http.ResponseWriter, r *http.Request) { func TransparencyHandler(w http.ResponseWriter, r *http.Request) { rs := ctx.Get(r, "result").(models.Result) tr := &TransparencyResponse{ - Server: ServerName, + Server: config.ServerName, SendDate: rs.SendDate, ContactAddress: config.Conf.ContactAddress, } diff --git a/controllers/phish_test.go b/controllers/phish_test.go index 21855412..6676e4c8 100644 --- a/controllers/phish_test.go +++ b/controllers/phish_test.go @@ -29,6 +29,7 @@ func (s *ControllersSuite) getFirstEmailRequest() models.EmailRequest { UserId: 1, BaseRecipient: campaign.Results[0].BaseRecipient, SMTP: campaign.SMTP, + FromAddress: campaign.SMTP.FromAddress, } err := models.PostEmailRequest(&req) s.Nil(err) @@ -94,7 +95,7 @@ func (s *ControllersSuite) transparencyRequest(r models.Result, rid, path string s.Nil(err) s.Equal(tr.ContactAddress, config.Conf.ContactAddress) s.Equal(tr.SendDate, r.SendDate) - s.Equal(tr.Server, ServerName) + s.Equal(tr.Server, config.ServerName) } func (s *ControllersSuite) TestOpenedPhishingEmail() { diff --git a/mailer/mailer.go b/mailer/mailer.go index 22db4a6f..06a34125 100644 --- a/mailer/mailer.go +++ b/mailer/mailer.go @@ -152,14 +152,12 @@ func sendMail(ctx context.Context, dialer Dialer, ms []Mail) { break } message.Reset() - err = m.Generate(message) if err != nil { log.Warn(err) m.Error(err) continue } - err = gomail.Send(sender, message) if err != nil { if te, ok := err.(*textproto.Error); ok { diff --git a/models/email_request.go b/models/email_request.go index f4b1280f..7fb60fc7 100644 --- a/models/email_request.go +++ b/models/email_request.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/gophish/gomail" + "github.com/gophish/gophish/config" log "github.com/gophish/gophish/logger" "github.com/gophish/gophish/mailer" ) @@ -84,7 +85,6 @@ func PostEmailRequest(s *EmailRequest) error { return err } s.RId = fmt.Sprintf("%s%s", PreviewPrefix, rid) - s.FromAddress = s.SMTP.FromAddress return db.Save(&s).Error } @@ -120,6 +120,12 @@ func (s *EmailRequest) Generate(msg *gomail.Message) error { } s.URL = url + // Add the transparency headers + msg.SetHeader("X-Mailer", config.ServerName) + if config.Conf.ContactAddress != "" { + msg.SetHeader("X-Gophish-Contact", config.Conf.ContactAddress) + } + // Parse the customHeader templates for _, header := range s.SMTP.Headers { key, err := ExecuteTemplate(header.Key, ptx) diff --git a/models/email_request_test.go b/models/email_request_test.go index 58ae5e9d..ef3a3dae 100644 --- a/models/email_request_test.go +++ b/models/email_request_test.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/gophish/gomail" + "github.com/gophish/gophish/config" "github.com/jordan-wright/email" check "gopkg.in/check.v1" ) @@ -75,6 +76,12 @@ func (s *ModelsSuite) TestEmailRequestGenerate(ch *check.C) { FromAddress: smtp.FromAddress, } + config.Conf.ContactAddress = "test@test.com" + expectedHeaders := map[string]string{ + "X-Mailer": config.ServerName, + "X-Gophish-Contact": config.Conf.ContactAddress, + } + msg := gomail.NewMessage() err = req.Generate(msg) ch.Assert(err, check.Equals, nil) @@ -94,6 +101,9 @@ func (s *ModelsSuite) TestEmailRequestGenerate(ch *check.C) { ch.Assert(got.Subject, check.Equals, expected.Subject) ch.Assert(string(got.Text), check.Equals, string(expected.Text)) ch.Assert(string(got.HTML), check.Equals, string(expected.HTML)) + for k, v := range expectedHeaders { + ch.Assert(got.Headers.Get(k), check.Equals, v) + } } func (s *ModelsSuite) TestEmailRequestURLTemplating(ch *check.C) { diff --git a/models/maillog.go b/models/maillog.go index cd4a6fd4..80312902 100644 --- a/models/maillog.go +++ b/models/maillog.go @@ -11,6 +11,7 @@ import ( "time" "github.com/gophish/gomail" + "github.com/gophish/gophish/config" log "github.com/gophish/gophish/logger" "github.com/gophish/gophish/mailer" ) @@ -157,6 +158,11 @@ func (m *MailLog) Generate(msg *gomail.Message) error { return err } + // Add the transparency headers + msg.SetHeader("X-Mailer", config.ServerName) + if config.Conf.ContactAddress != "" { + msg.SetHeader("X-Gophish-Contact", config.Conf.ContactAddress) + } // Parse the customHeader templates for _, header := range c.SMTP.Headers { key, err := ExecuteTemplate(header.Key, ptx) diff --git a/models/maillog_test.go b/models/maillog_test.go index 6edba882..a866cd3a 100644 --- a/models/maillog_test.go +++ b/models/maillog_test.go @@ -8,11 +8,33 @@ import ( "net/textproto" "time" + "github.com/gophish/gophish/config" + "github.com/gophish/gomail" "github.com/jordan-wright/email" "gopkg.in/check.v1" ) +func (s *ModelsSuite) emailFromFirstMailLog(campaign Campaign, ch *check.C) *email.Email { + result := campaign.Results[0] + m := &MailLog{} + err := db.Where("r_id=? AND campaign_id=?", result.RId, campaign.Id). + Find(m).Error + ch.Assert(err, check.Equals, nil) + + msg := gomail.NewMessage() + err = m.Generate(msg) + ch.Assert(err, check.Equals, nil) + + msgBuff := &bytes.Buffer{} + _, err = msg.WriteTo(msgBuff) + ch.Assert(err, check.Equals, nil) + + got, err := email.NewEmailFromReader(msgBuff) + ch.Assert(err, check.Equals, nil) + return got +} + func (s *ModelsSuite) TestGetQueuedMailLogs(ch *check.C) { campaign := s.createCampaign(ch) ms, err := GetQueuedMailLogs(campaign.LaunchDate) @@ -188,32 +210,56 @@ func (s *ModelsSuite) TestGenerateMailLog(ch *check.C) { func (s *ModelsSuite) TestMailLogGenerate(ch *check.C) { campaign := s.createCampaign(ch) result := campaign.Results[0] - m := &MailLog{} - err := db.Where("r_id=? AND campaign_id=?", result.RId, campaign.Id). - Find(m).Error - ch.Assert(err, check.Equals, nil) - - msg := gomail.NewMessage() - err = m.Generate(msg) - ch.Assert(err, check.Equals, nil) - expected := &email.Email{ Subject: fmt.Sprintf("%s - Subject", result.RId), Text: []byte(fmt.Sprintf("%s - Text", result.RId)), HTML: []byte(fmt.Sprintf("%s - HTML", result.RId)), } - - msgBuff := &bytes.Buffer{} - _, err = msg.WriteTo(msgBuff) - ch.Assert(err, check.Equals, nil) - - got, err := email.NewEmailFromReader(msgBuff) - ch.Assert(err, check.Equals, nil) + got := s.emailFromFirstMailLog(campaign, ch) ch.Assert(got.Subject, check.Equals, expected.Subject) ch.Assert(string(got.Text), check.Equals, string(expected.Text)) ch.Assert(string(got.HTML), check.Equals, string(expected.HTML)) } +func (s *ModelsSuite) TestMailLogGenerateTransparencyHeaders(ch *check.C) { + config.Conf.ContactAddress = "test@test.com" + expectedHeaders := map[string]string{ + "X-Mailer": config.ServerName, + "X-Gophish-Contact": config.Conf.ContactAddress, + } + campaign := s.createCampaign(ch) + got := s.emailFromFirstMailLog(campaign, ch) + for k, v := range expectedHeaders { + ch.Assert(got.Headers.Get(k), check.Equals, v) + } +} + +func (s *ModelsSuite) TestMailLogGenerateOverrideTransparencyHeaders(ch *check.C) { + expectedHeaders := map[string]string{ + "X-Mailer": "", + "X-Gophish-Contact": "", + } + smtp := SMTP{ + Name: "Test SMTP", + Host: "1.1.1.1:25", + FromAddress: "Foo Bar ", + UserId: 1, + Headers: []Header{ + Header{Key: "X-Gophish-Contact", Value: ""}, + Header{Key: "X-Mailer", Value: ""}, + }, + } + ch.Assert(PostSMTP(&smtp), check.Equals, nil) + campaign := s.createCampaignDependencies(ch) + campaign.SMTP = smtp + + ch.Assert(PostCampaign(&campaign, campaign.UserId), check.Equals, nil) + got := s.emailFromFirstMailLog(campaign, ch) + for k, v := range expectedHeaders { + ch.Assert(got.Headers.Get(k), check.Equals, v) + } +} + func (s *ModelsSuite) TestUnlockAllMailLogs(ch *check.C) { campaign := s.createCampaign(ch) ms, err := GetMailLogsByCampaign(campaign.Id) @@ -253,21 +299,7 @@ func (s *ModelsSuite) TestURLTemplateRendering(ch *check.C) { result := campaign.Results[0] expectedURL := fmt.Sprintf("http://127.0.0.1/%s/?%s=%s", result.Email, RecipientParameter, result.RId) - m := &MailLog{} - err := db.Where("r_id=? AND campaign_id=?", result.RId, campaign.Id). - Find(m).Error - ch.Assert(err, check.Equals, nil) - - msg := gomail.NewMessage() - err = m.Generate(msg) - ch.Assert(err, check.Equals, nil) - - msgBuff := &bytes.Buffer{} - _, err = msg.WriteTo(msgBuff) - ch.Assert(err, check.Equals, nil) - - got, err := email.NewEmailFromReader(msgBuff) - ch.Assert(err, check.Equals, nil) + got := s.emailFromFirstMailLog(campaign, ch) ch.Assert(got.Subject, check.Equals, expectedURL) ch.Assert(string(got.Text), check.Equals, expectedURL) ch.Assert(string(got.HTML), check.Equals, expectedURL) @@ -281,28 +313,13 @@ func (s *ModelsSuite) TestMailLogGenerateEmptySubject(ch *check.C) { campaign := s.createCampaignDependencies(ch, "") // specify empty subject // Setup and "launch" our campaign ch.Assert(PostCampaign(&campaign, campaign.UserId), check.Equals, nil) - result := campaign.Results[0] - m := &MailLog{} - err := db.Where("r_id=? AND campaign_id=?", result.RId, campaign.Id). - Find(m).Error - ch.Assert(err, check.Equals, nil) - - msg := gomail.NewMessage() - err = m.Generate(msg) - ch.Assert(err, check.Equals, nil) expected := &email.Email{ Subject: "", Text: []byte(fmt.Sprintf("%s - Text", result.RId)), HTML: []byte(fmt.Sprintf("%s - HTML", result.RId)), } - - msgBuff := &bytes.Buffer{} - _, err = msg.WriteTo(msgBuff) - ch.Assert(err, check.Equals, nil) - - got, err := email.NewEmailFromReader(msgBuff) - ch.Assert(err, check.Equals, nil) + got := s.emailFromFirstMailLog(campaign, ch) ch.Assert(got.Subject, check.Equals, expected.Subject) }