Added X-Mailer and X-Gophish-Contact headers (Ref: #1057)

pull/1148/merge
Jordan Wright 2018-06-18 21:37:59 -05:00
parent f09b448ec1
commit 9f334281ab
9 changed files with 94 additions and 55 deletions

View File

@ -40,6 +40,9 @@ var Conf Config
// Version contains the current gophish version // Version contains the current gophish version
var 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 // LoadConfig loads the configuration from the specified filepath
func LoadConfig(filepath string) { func LoadConfig(filepath string) {
// Get the config file // Get the config file

View File

@ -731,6 +731,7 @@ func API_Send_Test_Email(w http.ResponseWriter, r *http.Request) {
} }
s.SMTP = smtp s.SMTP = smtp
} }
s.FromAddress = s.SMTP.FromAddress
// Validate the given request // Validate the given request
if err = s.Validate(); err != nil { if err = s.Validate(); err != nil {

View File

@ -35,9 +35,6 @@ type TransparencyResponse struct {
// to return a transparency response. // to return a transparency response.
const TransparencySuffix = "+" 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. // CreatePhishingRouter creates the router that handles phishing connections.
func CreatePhishingRouter() http.Handler { func CreatePhishingRouter() http.Handler {
router := mux.NewRouter() router := mux.NewRouter()
@ -217,7 +214,7 @@ func RobotsHandler(w http.ResponseWriter, r *http.Request) {
func TransparencyHandler(w http.ResponseWriter, r *http.Request) { func TransparencyHandler(w http.ResponseWriter, r *http.Request) {
rs := ctx.Get(r, "result").(models.Result) rs := ctx.Get(r, "result").(models.Result)
tr := &TransparencyResponse{ tr := &TransparencyResponse{
Server: ServerName, Server: config.ServerName,
SendDate: rs.SendDate, SendDate: rs.SendDate,
ContactAddress: config.Conf.ContactAddress, ContactAddress: config.Conf.ContactAddress,
} }

View File

@ -29,6 +29,7 @@ func (s *ControllersSuite) getFirstEmailRequest() models.EmailRequest {
UserId: 1, UserId: 1,
BaseRecipient: campaign.Results[0].BaseRecipient, BaseRecipient: campaign.Results[0].BaseRecipient,
SMTP: campaign.SMTP, SMTP: campaign.SMTP,
FromAddress: campaign.SMTP.FromAddress,
} }
err := models.PostEmailRequest(&req) err := models.PostEmailRequest(&req)
s.Nil(err) s.Nil(err)
@ -94,7 +95,7 @@ func (s *ControllersSuite) transparencyRequest(r models.Result, rid, path string
s.Nil(err) s.Nil(err)
s.Equal(tr.ContactAddress, config.Conf.ContactAddress) s.Equal(tr.ContactAddress, config.Conf.ContactAddress)
s.Equal(tr.SendDate, r.SendDate) s.Equal(tr.SendDate, r.SendDate)
s.Equal(tr.Server, ServerName) s.Equal(tr.Server, config.ServerName)
} }
func (s *ControllersSuite) TestOpenedPhishingEmail() { func (s *ControllersSuite) TestOpenedPhishingEmail() {

View File

@ -152,14 +152,12 @@ func sendMail(ctx context.Context, dialer Dialer, ms []Mail) {
break break
} }
message.Reset() message.Reset()
err = m.Generate(message) err = m.Generate(message)
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
m.Error(err) m.Error(err)
continue continue
} }
err = gomail.Send(sender, message) err = gomail.Send(sender, message)
if err != nil { if err != nil {
if te, ok := err.(*textproto.Error); ok { if te, ok := err.(*textproto.Error); ok {

View File

@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/gophish/gomail" "github.com/gophish/gomail"
"github.com/gophish/gophish/config"
log "github.com/gophish/gophish/logger" log "github.com/gophish/gophish/logger"
"github.com/gophish/gophish/mailer" "github.com/gophish/gophish/mailer"
) )
@ -84,7 +85,6 @@ func PostEmailRequest(s *EmailRequest) error {
return err return err
} }
s.RId = fmt.Sprintf("%s%s", PreviewPrefix, rid) s.RId = fmt.Sprintf("%s%s", PreviewPrefix, rid)
s.FromAddress = s.SMTP.FromAddress
return db.Save(&s).Error return db.Save(&s).Error
} }
@ -120,6 +120,12 @@ func (s *EmailRequest) Generate(msg *gomail.Message) error {
} }
s.URL = url 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 // Parse the customHeader templates
for _, header := range s.SMTP.Headers { for _, header := range s.SMTP.Headers {
key, err := ExecuteTemplate(header.Key, ptx) key, err := ExecuteTemplate(header.Key, ptx)

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/gophish/gomail" "github.com/gophish/gomail"
"github.com/gophish/gophish/config"
"github.com/jordan-wright/email" "github.com/jordan-wright/email"
check "gopkg.in/check.v1" check "gopkg.in/check.v1"
) )
@ -75,6 +76,12 @@ func (s *ModelsSuite) TestEmailRequestGenerate(ch *check.C) {
FromAddress: smtp.FromAddress, 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() msg := gomail.NewMessage()
err = req.Generate(msg) err = req.Generate(msg)
ch.Assert(err, check.Equals, nil) 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(got.Subject, check.Equals, expected.Subject)
ch.Assert(string(got.Text), check.Equals, string(expected.Text)) ch.Assert(string(got.Text), check.Equals, string(expected.Text))
ch.Assert(string(got.HTML), check.Equals, string(expected.HTML)) 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) { func (s *ModelsSuite) TestEmailRequestURLTemplating(ch *check.C) {

View File

@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/gophish/gomail" "github.com/gophish/gomail"
"github.com/gophish/gophish/config"
log "github.com/gophish/gophish/logger" log "github.com/gophish/gophish/logger"
"github.com/gophish/gophish/mailer" "github.com/gophish/gophish/mailer"
) )
@ -157,6 +158,11 @@ func (m *MailLog) Generate(msg *gomail.Message) error {
return err 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 // Parse the customHeader templates
for _, header := range c.SMTP.Headers { for _, header := range c.SMTP.Headers {
key, err := ExecuteTemplate(header.Key, ptx) key, err := ExecuteTemplate(header.Key, ptx)

View File

@ -8,11 +8,33 @@ import (
"net/textproto" "net/textproto"
"time" "time"
"github.com/gophish/gophish/config"
"github.com/gophish/gomail" "github.com/gophish/gomail"
"github.com/jordan-wright/email" "github.com/jordan-wright/email"
"gopkg.in/check.v1" "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) { func (s *ModelsSuite) TestGetQueuedMailLogs(ch *check.C) {
campaign := s.createCampaign(ch) campaign := s.createCampaign(ch)
ms, err := GetQueuedMailLogs(campaign.LaunchDate) ms, err := GetQueuedMailLogs(campaign.LaunchDate)
@ -188,32 +210,56 @@ func (s *ModelsSuite) TestGenerateMailLog(ch *check.C) {
func (s *ModelsSuite) TestMailLogGenerate(ch *check.C) { func (s *ModelsSuite) TestMailLogGenerate(ch *check.C) {
campaign := s.createCampaign(ch) campaign := s.createCampaign(ch)
result := campaign.Results[0] 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{ expected := &email.Email{
Subject: fmt.Sprintf("%s - Subject", result.RId), Subject: fmt.Sprintf("%s - Subject", result.RId),
Text: []byte(fmt.Sprintf("%s - Text", result.RId)), Text: []byte(fmt.Sprintf("%s - Text", result.RId)),
HTML: []byte(fmt.Sprintf("%s - HTML", result.RId)), HTML: []byte(fmt.Sprintf("%s - HTML", result.RId)),
} }
got := s.emailFromFirstMailLog(campaign, ch)
msgBuff := &bytes.Buffer{}
_, err = msg.WriteTo(msgBuff)
ch.Assert(err, check.Equals, nil)
got, err := email.NewEmailFromReader(msgBuff)
ch.Assert(err, check.Equals, nil)
ch.Assert(got.Subject, check.Equals, expected.Subject) ch.Assert(got.Subject, check.Equals, expected.Subject)
ch.Assert(string(got.Text), check.Equals, string(expected.Text)) ch.Assert(string(got.Text), check.Equals, string(expected.Text))
ch.Assert(string(got.HTML), check.Equals, string(expected.HTML)) 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 <foo@example.com>",
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) { func (s *ModelsSuite) TestUnlockAllMailLogs(ch *check.C) {
campaign := s.createCampaign(ch) campaign := s.createCampaign(ch)
ms, err := GetMailLogsByCampaign(campaign.Id) ms, err := GetMailLogsByCampaign(campaign.Id)
@ -253,21 +299,7 @@ func (s *ModelsSuite) TestURLTemplateRendering(ch *check.C) {
result := campaign.Results[0] result := campaign.Results[0]
expectedURL := fmt.Sprintf("http://127.0.0.1/%s/?%s=%s", result.Email, RecipientParameter, result.RId) expectedURL := fmt.Sprintf("http://127.0.0.1/%s/?%s=%s", result.Email, RecipientParameter, result.RId)
m := &MailLog{} got := s.emailFromFirstMailLog(campaign, ch)
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)
ch.Assert(got.Subject, check.Equals, expectedURL) ch.Assert(got.Subject, check.Equals, expectedURL)
ch.Assert(string(got.Text), check.Equals, expectedURL) ch.Assert(string(got.Text), check.Equals, expectedURL)
ch.Assert(string(got.HTML), 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 campaign := s.createCampaignDependencies(ch, "") // specify empty subject
// Setup and "launch" our campaign // Setup and "launch" our campaign
ch.Assert(PostCampaign(&campaign, campaign.UserId), check.Equals, nil) ch.Assert(PostCampaign(&campaign, campaign.UserId), check.Equals, nil)
result := campaign.Results[0] 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{ expected := &email.Email{
Subject: "", Subject: "",
Text: []byte(fmt.Sprintf("%s - Text", result.RId)), Text: []byte(fmt.Sprintf("%s - Text", result.RId)),
HTML: []byte(fmt.Sprintf("%s - HTML", result.RId)), HTML: []byte(fmt.Sprintf("%s - HTML", result.RId)),
} }
got := s.emailFromFirstMailLog(campaign, ch)
msgBuff := &bytes.Buffer{}
_, err = msg.WriteTo(msgBuff)
ch.Assert(err, check.Equals, nil)
got, err := email.NewEmailFromReader(msgBuff)
ch.Assert(err, check.Equals, nil)
ch.Assert(got.Subject, check.Equals, expected.Subject) ch.Assert(got.Subject, check.Equals, expected.Subject)
} }