mirror of https://github.com/gophish/gophish
adding extended_templates feature
parent
07b46d226a
commit
7debe78745
|
@ -265,11 +265,13 @@ func (ps *PhishingServer) PhishHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// connection. This usually involves writing out the page HTML or redirecting
|
// connection. This usually involves writing out the page HTML or redirecting
|
||||||
// the user to the correct URL.
|
// the user to the correct URL.
|
||||||
func renderPhishResponse(w http.ResponseWriter, r *http.Request, ptx models.PhishingTemplateContext, p models.Page) {
|
func renderPhishResponse(w http.ResponseWriter, r *http.Request, ptx models.PhishingTemplateContext, p models.Page) {
|
||||||
|
// Getting all template params.. current + extended.
|
||||||
|
allTemplateParams := models.GetAllTemplateParams(ptx)
|
||||||
// If the request was a form submit and a redirect URL was specified, we
|
// If the request was a form submit and a redirect URL was specified, we
|
||||||
// should send the user to that URL
|
// should send the user to that URL
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
if p.RedirectURL != "" {
|
if p.RedirectURL != "" {
|
||||||
redirectURL, err := models.ExecuteTemplate(p.RedirectURL, ptx)
|
redirectURL, err := models.ExecuteTemplate(p.RedirectURL, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
|
@ -280,7 +282,7 @@ func renderPhishResponse(w http.ResponseWriter, r *http.Request, ptx models.Phis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, we just need to write out the templated HTML
|
// Otherwise, we just need to write out the templated HTML
|
||||||
html, err := models.ExecuteTemplate(p.HTML, ptx)
|
html, err := models.ExecuteTemplate(p.HTML, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- +goose Up
|
||||||
|
-- SQL in this section is executed when the migration is applied.
|
||||||
|
ALTER TABLE `targets` ADD COLUMN extended_template BLOB;
|
||||||
|
ALTER TABLE `email_requests` ADD COLUMN extended_template BLOB;
|
||||||
|
ALTER TABLE `results` ADD COLUMN extended_template BLOB;
|
||||||
|
-- +goose Down
|
||||||
|
-- SQL in this section is executed when the migration is rolled back.
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- +goose Up
|
||||||
|
-- SQL in this section is executed when the migration is applied.
|
||||||
|
ALTER TABLE targets ADD COLUMN extended_template BLOB;
|
||||||
|
ALTER TABLE email_requests ADD COLUMN extended_template BLOB;
|
||||||
|
ALTER TABLE results ADD COLUMN extended_template BLOB;
|
||||||
|
-- +goose Down
|
||||||
|
-- SQL in this section is executed when the migration is rolled back.
|
|
@ -552,10 +552,11 @@ func PostCampaign(c *Campaign, uid int64) error {
|
||||||
sendDate := c.generateSendDate(recipientIndex, totalRecipients)
|
sendDate := c.generateSendDate(recipientIndex, totalRecipients)
|
||||||
r := &Result{
|
r := &Result{
|
||||||
BaseRecipient: BaseRecipient{
|
BaseRecipient: BaseRecipient{
|
||||||
Email: t.Email,
|
Email: t.Email,
|
||||||
Position: t.Position,
|
Position: t.Position,
|
||||||
FirstName: t.FirstName,
|
FirstName: t.FirstName,
|
||||||
LastName: t.LastName,
|
LastName: t.LastName,
|
||||||
|
ExtendedTemplate: t.ExtendedTemplate,
|
||||||
},
|
},
|
||||||
Status: StatusScheduled,
|
Status: StatusScheduled,
|
||||||
CampaignId: c.Id,
|
CampaignId: c.Id,
|
||||||
|
|
|
@ -113,8 +113,10 @@ func (s *EmailRequest) Generate(msg *gomail.Message) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Getting all template params.. current + extended.
|
||||||
|
allTemplateParams := GetAllTemplateParams(ptx)
|
||||||
|
|
||||||
url, err := ExecuteTemplate(s.URL, ptx)
|
url, err := ExecuteTemplate(s.URL, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -128,12 +130,12 @@ func (s *EmailRequest) Generate(msg *gomail.Message) error {
|
||||||
|
|
||||||
// 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, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := ExecuteTemplate(header.Value, ptx)
|
value, err := ExecuteTemplate(header.Value, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -143,7 +145,7 @@ func (s *EmailRequest) Generate(msg *gomail.Message) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse remaining templates
|
// Parse remaining templates
|
||||||
subject, err := ExecuteTemplate(s.Template.Subject, ptx)
|
subject, err := ExecuteTemplate(s.Template.Subject, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -154,14 +156,14 @@ func (s *EmailRequest) Generate(msg *gomail.Message) error {
|
||||||
|
|
||||||
msg.SetHeader("To", s.FormatAddress())
|
msg.SetHeader("To", s.FormatAddress())
|
||||||
if s.Template.Text != "" {
|
if s.Template.Text != "" {
|
||||||
text, err := ExecuteTemplate(s.Template.Text, ptx)
|
text, err := ExecuteTemplate(s.Template.Text, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
msg.SetBody("text/plain", text)
|
msg.SetBody("text/plain", text)
|
||||||
}
|
}
|
||||||
if s.Template.HTML != "" {
|
if s.Template.HTML != "" {
|
||||||
html, err := ExecuteTemplate(s.Template.HTML, ptx)
|
html, err := ExecuteTemplate(s.Template.HTML, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,10 +53,11 @@ type Target struct {
|
||||||
// BaseRecipient contains the fields for a single recipient. This is the base
|
// BaseRecipient contains the fields for a single recipient. This is the base
|
||||||
// struct used in members of groups and campaign results.
|
// struct used in members of groups and campaign results.
|
||||||
type BaseRecipient struct {
|
type BaseRecipient struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
Position string `json:"position"`
|
Position string `json:"position"`
|
||||||
|
ExtendedTemplate string `json:"extended_template,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatAddress returns the email address to use in the "To" header of the email
|
// FormatAddress returns the email address to use in the "To" header of the email
|
||||||
|
@ -348,6 +349,10 @@ func UpdateTarget(tx *gorm.DB, target Target) error {
|
||||||
"last_name": target.LastName,
|
"last_name": target.LastName,
|
||||||
"position": target.Position,
|
"position": target.Position,
|
||||||
}
|
}
|
||||||
|
// handling front end overrides..extended_template is not sent from front end.
|
||||||
|
if target.ExtendedTemplate != "" {
|
||||||
|
targetInfo["extended_template"] = target.ExtendedTemplate
|
||||||
|
}
|
||||||
err := tx.Model(&target).Where("id = ?", target.Id).Updates(targetInfo).Error
|
err := tx.Model(&target).Where("id = ?", target.Id).Updates(targetInfo).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(logrus.Fields{
|
log.WithFields(logrus.Fields{
|
||||||
|
@ -360,6 +365,6 @@ func UpdateTarget(tx *gorm.DB, target Target) error {
|
||||||
// GetTargets performs a many-to-many select to get all the Targets for a Group
|
// GetTargets performs a many-to-many select to get all the Targets for a Group
|
||||||
func GetTargets(gid int64) ([]Target, error) {
|
func GetTargets(gid int64) ([]Target, error) {
|
||||||
ts := []Target{}
|
ts := []Target{}
|
||||||
err := db.Table("targets").Select("targets.id, targets.email, targets.first_name, targets.last_name, targets.position").Joins("left join group_targets gt ON targets.id = gt.target_id").Where("gt.group_id=?", gid).Scan(&ts).Error
|
err := db.Table("targets").Select("targets.id, targets.email, targets.first_name, targets.last_name, targets.position,targets.extended_template").Joins("left join group_targets gt ON targets.id = gt.target_id").Where("gt.group_id=?", gid).Scan(&ts).Error
|
||||||
return ts, err
|
return ts, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,6 +179,8 @@ func (m *MailLog) Generate(msg *gomail.Message) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Getting all template params.. current + extended.
|
||||||
|
allTemplateParams := GetAllTemplateParams(ptx)
|
||||||
|
|
||||||
// Add the transparency headers
|
// Add the transparency headers
|
||||||
msg.SetHeader("X-Mailer", config.ServerName)
|
msg.SetHeader("X-Mailer", config.ServerName)
|
||||||
|
@ -195,12 +197,12 @@ func (m *MailLog) Generate(msg *gomail.Message) error {
|
||||||
|
|
||||||
// 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, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Warn(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := ExecuteTemplate(header.Value, ptx)
|
value, err := ExecuteTemplate(header.Value, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Warn(err)
|
||||||
}
|
}
|
||||||
|
@ -210,7 +212,7 @@ func (m *MailLog) Generate(msg *gomail.Message) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse remaining templates
|
// Parse remaining templates
|
||||||
subject, err := ExecuteTemplate(c.Template.Subject, ptx)
|
subject, err := ExecuteTemplate(c.Template.Subject, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Warn(err)
|
||||||
}
|
}
|
||||||
|
@ -221,14 +223,14 @@ func (m *MailLog) Generate(msg *gomail.Message) error {
|
||||||
|
|
||||||
msg.SetHeader("To", r.FormatAddress())
|
msg.SetHeader("To", r.FormatAddress())
|
||||||
if c.Template.Text != "" {
|
if c.Template.Text != "" {
|
||||||
text, err := ExecuteTemplate(c.Template.Text, ptx)
|
text, err := ExecuteTemplate(c.Template.Text, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Warn(err)
|
||||||
}
|
}
|
||||||
msg.SetBody("text/plain", text)
|
msg.SetBody("text/plain", text)
|
||||||
}
|
}
|
||||||
if c.Template.HTML != "" {
|
if c.Template.HTML != "" {
|
||||||
html, err := ExecuteTemplate(c.Template.HTML, ptx)
|
html, err := ExecuteTemplate(c.Template.HTML, allTemplateParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Warn(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,12 +79,13 @@ func (p *Page) Validate() error {
|
||||||
if p.CapturePasswords && !p.CaptureCredentials {
|
if p.CapturePasswords && !p.CaptureCredentials {
|
||||||
p.CaptureCredentials = true
|
p.CaptureCredentials = true
|
||||||
}
|
}
|
||||||
if err := ValidateTemplate(p.HTML); err != nil {
|
// bypass this.. since we can't predict with all extended params in advance.
|
||||||
|
/*if err := ValidateTemplate(p.HTML); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ValidateTemplate(p.RedirectURL); err != nil {
|
if err := ValidateTemplate(p.RedirectURL); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}*/
|
||||||
return p.parseHTML()
|
return p.parseHTML()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,20 +123,21 @@ func (s *ModelsSuite) TestPageValidation(c *check.C) {
|
||||||
err = p.Validate()
|
err = p.Validate()
|
||||||
c.Assert(err, check.Equals, nil)
|
c.Assert(err, check.Equals, nil)
|
||||||
c.Assert(p.CaptureCredentials, check.Equals, true)
|
c.Assert(p.CaptureCredentials, check.Equals, true)
|
||||||
|
// bypassing..no longer needed
|
||||||
|
/*
|
||||||
|
// Validate that if the HTML contains an invalid template tag, that we
|
||||||
|
// catch it
|
||||||
|
p.HTML = `<html>
|
||||||
|
<head></head>
|
||||||
|
<body>{{.INVALIDTAG}}</body>
|
||||||
|
</html>`
|
||||||
|
err = p.Validate()
|
||||||
|
c.Assert(err, check.NotNil)
|
||||||
|
|
||||||
// Validate that if the HTML contains an invalid template tag, that we
|
// Validate that if the RedirectURL contains an invalid template tag, that
|
||||||
// catch it
|
// we catch it
|
||||||
p.HTML = `<html>
|
p.HTML = "valid data"
|
||||||
<head></head>
|
p.RedirectURL = "http://example.com/{{.INVALIDTAG}}"
|
||||||
<body>{{.INVALIDTAG}}</body>
|
err = p.Validate()
|
||||||
</html>`
|
c.Assert(err, check.NotNil)*/
|
||||||
err = p.Validate()
|
|
||||||
c.Assert(err, check.NotNil)
|
|
||||||
|
|
||||||
// Validate that if the RedirectURL contains an invalid template tag, that
|
|
||||||
// we catch it
|
|
||||||
p.HTML = "valid data"
|
|
||||||
p.RedirectURL = "http://example.com/{{.INVALIDTAG}}"
|
|
||||||
err = p.Validate()
|
|
||||||
c.Assert(err, check.NotNil)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,14 @@ func (t *Template) Validate() error {
|
||||||
case t.Text == "" && t.HTML == "":
|
case t.Text == "" && t.HTML == "":
|
||||||
return ErrTemplateMissingParameter
|
return ErrTemplateMissingParameter
|
||||||
}
|
}
|
||||||
if err := ValidateTemplate(t.HTML); err != nil {
|
|
||||||
|
// bypass this.. since we can't predict all extended params in advance.
|
||||||
|
/*if err := ValidateTemplate(t.HTML); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ValidateTemplate(t.Text); err != nil {
|
if err := ValidateTemplate(t.Text); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}*/
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +111,6 @@ func GetTemplateByName(n string, uid int64) (Template, error) {
|
||||||
|
|
||||||
// PostTemplate creates a new template in the database.
|
// PostTemplate creates a new template in the database.
|
||||||
func PostTemplate(t *Template) error {
|
func PostTemplate(t *Template) error {
|
||||||
// Insert into the DB
|
|
||||||
if err := t.Validate(); err != nil {
|
if err := t.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
@ -76,7 +77,8 @@ func NewPhishingTemplateContext(ctx TemplateContext, r BaseRecipient, rid string
|
||||||
// template body and data.
|
// template body and data.
|
||||||
func ExecuteTemplate(text string, data interface{}) (string, error) {
|
func ExecuteTemplate(text string, data interface{}) (string, error) {
|
||||||
buff := bytes.Buffer{}
|
buff := bytes.Buffer{}
|
||||||
tmpl, err := template.New("template").Parse(text)
|
// replacing template params with no corresponding map keys with empty string.
|
||||||
|
tmpl, err := template.New("template").Option("missingkey=zero").Parse(text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return buff.String(), err
|
return buff.String(), err
|
||||||
}
|
}
|
||||||
|
@ -124,3 +126,31 @@ func ValidateTemplate(text string) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllTemplateParams merges the PhishingTemplateContext with the extended_parameters
|
||||||
|
// if extended_parameters are valid JSON
|
||||||
|
func GetAllTemplateParams(ptx PhishingTemplateContext) map[string]string {
|
||||||
|
currentTemplateParams := map[string]string{
|
||||||
|
"From": ptx.From,
|
||||||
|
"URL": ptx.URL,
|
||||||
|
"Tracker": ptx.Tracker,
|
||||||
|
"TrackingURL": ptx.TrackingURL,
|
||||||
|
"RId": ptx.RId,
|
||||||
|
"BaseURL": ptx.BaseURL,
|
||||||
|
"Email": ptx.BaseRecipient.Email,
|
||||||
|
"FirstName": ptx.BaseRecipient.FirstName,
|
||||||
|
"LastName": ptx.BaseRecipient.LastName,
|
||||||
|
"Position": ptx.BaseRecipient.Position,
|
||||||
|
}
|
||||||
|
extendedTemplateParams := map[string]string{}
|
||||||
|
err := json.Unmarshal([]byte(ptx.BaseRecipient.ExtendedTemplate), &extendedTemplateParams)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
// only merge if we have a valid JSON in extended_parameters
|
||||||
|
for k, v := range extendedTemplateParams {
|
||||||
|
currentTemplateParams[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentTemplateParams
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue