mirror of https://github.com/gophish/gophish
Ability to specify different Envelope-Sender and SMTP-From address (#986)
Adds the ability to specify an envelope sender in templates.986-custom-envelope-sender
parent
535fbf487b
commit
94794939f6
|
@ -0,0 +1,8 @@
|
|||
|
||||
-- +goose Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
ALTER TABLE templates ADD COLUMN envelope_sender varchar(255);
|
||||
|
||||
-- +goose Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
-- +goose Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
ALTER TABLE templates ADD COLUMN envelope_sender varchar(255);
|
||||
|
||||
-- +goose Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
||||
|
|
@ -51,6 +51,7 @@ type Mail interface {
|
|||
Success() error
|
||||
Generate(msg *gomail.Message) error
|
||||
GetDialer() (Dialer, error)
|
||||
GetSmtpFrom() (string, error)
|
||||
}
|
||||
|
||||
// Mailer is a global instance of the mailer that can
|
||||
|
@ -161,7 +162,13 @@ func sendMail(ctx context.Context, dialer Dialer, ms []Mail) {
|
|||
continue
|
||||
}
|
||||
|
||||
err = gomail.Send(sender, message)
|
||||
smtp_from, err := m.GetSmtpFrom()
|
||||
if err != nil {
|
||||
m.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = gomail.SendCustomFrom(sender, smtp_from, message)
|
||||
if err != nil {
|
||||
if te, ok := err.(*textproto.Error); ok {
|
||||
switch {
|
||||
|
|
|
@ -170,6 +170,10 @@ func (mm *mockMessage) Generate(message *gomail.Message) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (mm *mockMessage) GetSmtpFrom() (string, error) {
|
||||
return mm.from, nil
|
||||
}
|
||||
|
||||
func (mm *mockMessage) Success() error {
|
||||
mm.finished = true
|
||||
return nil
|
||||
|
|
|
@ -56,9 +56,14 @@ func (s *SendTestEmailRequest) Success() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *SendTestEmailRequest) GetSmtpFrom() (string, error) {
|
||||
return s.SMTP.FromAddress, nil
|
||||
}
|
||||
|
||||
// Generate fills in the details of a gomail.Message with the contents
|
||||
// from the SendTestEmailRequest.
|
||||
func (s *SendTestEmailRequest) Generate(msg *gomail.Message) error {
|
||||
// Naively use the SMTP-from as the Envelope-from for this test message
|
||||
f, err := mail.ParseAddress(s.SMTP.FromAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -94,6 +94,35 @@ func (s *ModelsSuite) TestEmailRequestGenerate(ch *check.C) {
|
|||
ch.Assert(string(got.HTML), check.Equals, string(expected.HTML))
|
||||
}
|
||||
|
||||
func (s *ModelsSuite) TestGetSmtpFrom(ch *check.C) {
|
||||
smtp := SMTP{
|
||||
FromAddress: "from@example.com",
|
||||
}
|
||||
template := Template{
|
||||
Name: "Test Template",
|
||||
Subject: "{{.FirstName}} - Subject",
|
||||
Text: "{{.Email}} - Text",
|
||||
HTML: "{{.Email}} - HTML",
|
||||
}
|
||||
target := Target{
|
||||
FirstName: "First",
|
||||
LastName: "Last",
|
||||
Email: "firstlast@example.com",
|
||||
}
|
||||
req := &SendTestEmailRequest{
|
||||
SMTP: smtp,
|
||||
Template: template,
|
||||
Target: target,
|
||||
}
|
||||
|
||||
msg := gomail.NewMessage()
|
||||
err = req.Generate(msg)
|
||||
smtp_from, err := req.GetSmtpFrom()
|
||||
|
||||
ch.Assert(err, check.Equals, nil)
|
||||
ch.Assert(smtp_from, check.Equals, "from@example.com")
|
||||
}
|
||||
|
||||
func (s *ModelsSuite) TestEmailRequestURLTemplating(ch *check.C) {
|
||||
smtp := SMTP{
|
||||
FromAddress: "from@example.com",
|
||||
|
@ -113,7 +142,7 @@ func (s *ModelsSuite) TestEmailRequestURLTemplating(ch *check.C) {
|
|||
SMTP: smtp,
|
||||
Template: template,
|
||||
Target: target,
|
||||
URL: "http://127.0.0.1/{{.Email}}",
|
||||
URL: "http://127.0.0.1/{{.Email}}",
|
||||
}
|
||||
|
||||
msg := gomail.NewMessage()
|
||||
|
|
|
@ -193,6 +193,16 @@ func buildTemplate(text string, data interface{}) (string, error) {
|
|||
return buff.String(), err
|
||||
}
|
||||
|
||||
func (m *MailLog) GetSmtpFrom() (string, error) {
|
||||
c, err := GetCampaign(m.CampaignId, m.UserId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
f, err := mail.ParseAddress(c.SMTP.FromAddress)
|
||||
return f.Address, err
|
||||
}
|
||||
|
||||
// Generate fills in the details of a gomail.Message instance with
|
||||
// the correct headers and body from the campaign and recipient listed in
|
||||
// the maillog. We accept the gomail.Message as an argument so that the caller
|
||||
|
@ -206,9 +216,13 @@ func (m *MailLog) Generate(msg *gomail.Message) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := mail.ParseAddress(c.SMTP.FromAddress)
|
||||
|
||||
f, err := mail.ParseAddress(c.Template.EnvelopeSender)
|
||||
if err != nil {
|
||||
return err
|
||||
f, err = mail.ParseAddress(c.SMTP.FromAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fn := f.Name
|
||||
if fn == "" {
|
||||
|
|
|
@ -188,6 +188,40 @@ func (s *ModelsSuite) TestGenerateMailLog(ch *check.C) {
|
|||
ch.Assert(m.Processing, check.Equals, false)
|
||||
}
|
||||
|
||||
func (s *ModelsSuite) TestMailLogGetSmtpFrom(ch *check.C) {
|
||||
template := Template{
|
||||
Name: "OverrideSmtpFrom",
|
||||
UserId: 1,
|
||||
Text: "dummytext",
|
||||
HTML: "Dummyhtml",
|
||||
Subject: "Dummysubject",
|
||||
EnvelopeSender: "spoofing@example.com",
|
||||
}
|
||||
ch.Assert(PostTemplate(&template), check.Equals, nil)
|
||||
campaign := s.createCampaignDependencies(ch)
|
||||
campaign.Template = template
|
||||
|
||||
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)
|
||||
|
||||
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.From, check.Equals, "spoofing@example.com")
|
||||
}
|
||||
|
||||
func (s *ModelsSuite) TestMailLogGenerate(ch *check.C) {
|
||||
campaign := s.createCampaign(ch)
|
||||
result := campaign.Results[0]
|
||||
|
@ -201,6 +235,7 @@ func (s *ModelsSuite) TestMailLogGenerate(ch *check.C) {
|
|||
ch.Assert(err, check.Equals, nil)
|
||||
|
||||
expected := &email.Email{
|
||||
From: "test@test.com", // Default smtp.FromAddress
|
||||
Subject: fmt.Sprintf("%s - Subject", result.RId),
|
||||
Text: []byte(fmt.Sprintf("%s - Text", result.RId)),
|
||||
HTML: []byte(fmt.Sprintf("%s - HTML", result.RId)),
|
||||
|
@ -212,6 +247,7 @@ func (s *ModelsSuite) TestMailLogGenerate(ch *check.C) {
|
|||
|
||||
got, err := email.NewEmailFromReader(msgBuff)
|
||||
ch.Assert(err, check.Equals, nil)
|
||||
ch.Assert(got.From, check.Equals, expected.From)
|
||||
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))
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"html/template"
|
||||
"net/mail"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
|
@ -11,14 +12,15 @@ import (
|
|||
|
||||
// Template models hold the attributes for an email template to be sent to targets
|
||||
type Template struct {
|
||||
Id int64 `json:"id" gorm:"column:id; primary_key:yes"`
|
||||
UserId int64 `json:"-" gorm:"column:user_id"`
|
||||
Name string `json:"name"`
|
||||
Subject string `json:"subject"`
|
||||
Text string `json:"text"`
|
||||
HTML string `json:"html" gorm:"column:html"`
|
||||
ModifiedDate time.Time `json:"modified_date"`
|
||||
Attachments []Attachment `json:"attachments"`
|
||||
Id int64 `json:"id" gorm:"column:id; primary_key:yes"`
|
||||
UserId int64 `json:"-" gorm:"column:user_id"`
|
||||
Name string `json:"name"`
|
||||
EnvelopeSender string `json:"envelope_sender"`
|
||||
Subject string `json:"subject"`
|
||||
Text string `json:"text"`
|
||||
HTML string `json:"html" gorm:"column:html"`
|
||||
ModifiedDate time.Time `json:"modified_date"`
|
||||
Attachments []Attachment `json:"attachments"`
|
||||
}
|
||||
|
||||
// ErrTemplateNameNotSpecified is thrown when a template name is not specified
|
||||
|
@ -34,6 +36,11 @@ func (t *Template) Validate() error {
|
|||
return ErrTemplateNameNotSpecified
|
||||
case t.Text == "" && t.HTML == "":
|
||||
return ErrTemplateMissingParameter
|
||||
case t.EnvelopeSender != "":
|
||||
_, err := mail.ParseAddress(t.EnvelopeSender)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var buff bytes.Buffer
|
||||
// Test that the variables used in the template
|
||||
|
|
|
@ -20,6 +20,7 @@ function save(idx) {
|
|||
}
|
||||
template.name = $("#name").val()
|
||||
template.subject = $("#subject").val()
|
||||
template.envelope_sender = $("#envelope-sender").val()
|
||||
template.html = CKEDITOR.instances["html_editor"].getData();
|
||||
// Fix the URL Scheme added by CKEditor (until we can remove it from the plugin)
|
||||
template.html = template.html.replace(/https?:\/\/{{\.URL}}/gi, "{{.URL}}")
|
||||
|
@ -152,6 +153,7 @@ function edit(idx) {
|
|||
template = templates[idx]
|
||||
$("#name").val(template.name)
|
||||
$("#subject").val(template.subject)
|
||||
$("#envelope-sender").val(template.envelope_sender)
|
||||
$("#html_editor").val(template.html)
|
||||
$("#text_editor").val(template.text)
|
||||
$.each(template.attachments, function (i, file) {
|
||||
|
@ -208,6 +210,7 @@ function copy(idx) {
|
|||
template = templates[idx]
|
||||
$("#name").val("Copy of " + template.name)
|
||||
$("#subject").val(template.subject)
|
||||
$("#envelope-sender").val(template.envelope_sender)
|
||||
$("#html_editor").val(template.html)
|
||||
$("#text_editor").val(template.text)
|
||||
$.each(template.attachments, function (i, file) {
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
<input type="text" class="form-control" placeholder="Profile name" id="name" autofocus/>
|
||||
<label class="control-label" for="interface_type">Interface Type:</label>
|
||||
<input type="text" class="form-control" value="SMTP" id="interface_type" disabled/>
|
||||
<label class="control-label" for="from">From:</label>
|
||||
<label class="control-label" for="from">SMTP From: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Set this to an email address from your sending domain to bypass SPF-checks. You can set the Envelope-sender in Template. The Envelope-sender is shown to the victim."></i></label>
|
||||
<input type="text" class="form-control" placeholder="First Last <test@example.com>" id="from" required/>
|
||||
<label class="control-label" for="host">Host:</label>
|
||||
<input type="text" class="form-control" placeholder="smtp.example.com:25" id="host" required/>
|
||||
|
|
|
@ -77,6 +77,10 @@
|
|||
<div class="form-group">
|
||||
<button class="btn btn-danger" data-toggle="modal" data-target="#importEmailModal"><i class="fa fa-envelope"></i> Import Email</button>
|
||||
</div>
|
||||
<label class="control-label" for="envelope-sender">Envelope Sender: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="This sender is shown to the user by most (all?) mailclients. Defaults to the SMTP-From as defined by Sender profile."></i></label>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" placeholder="John Doe <john.doe@victim.com>" id="envelope-sender" />
|
||||
</div>
|
||||
<label class="control-label" for="subject">Subject:</label>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" placeholder="Email Subject" id="subject" />
|
||||
|
|
Loading…
Reference in New Issue