PhishHandler now loads landing page content. Fixes #37

Now supports autocomplete for modal typeahead. Fixes #40
Users can now specify landing pages in campaigns. Fixes #39
Implemented "Email Opened" status. Fixes #38
pull/64/head
Jordan Wright 2015-10-22 22:29:10 -05:00
parent b574fb2741
commit 01c3da611b
5 changed files with 89 additions and 6 deletions

View File

@ -97,7 +97,11 @@ func PhishTracker(w http.ResponseWriter, r *http.Request) {
Logger.Println(err) Logger.Println(err)
} }
c.AddEvent(models.Event{Email: rs.Email, Message: models.EVENT_OPENED}) c.AddEvent(models.Event{Email: rs.Email, Message: models.EVENT_OPENED})
w.Write([]byte("It Works!")) err = rs.UpdateStatus(models.EVENT_OPENED)
if err != nil {
Logger.Println(err)
}
w.Write([]byte(""))
} }
// PhishHandler handles incoming client connections and registers the associated actions performed // PhishHandler handles incoming client connections and registers the associated actions performed
@ -119,8 +123,12 @@ func PhishHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
} }
p, err := models.GetPage(c.PageId, c.UserId)
if err != nil {
Logger.Println(err)
}
c.AddEvent(models.Event{Email: rs.Email, Message: models.EVENT_CLICKED}) c.AddEvent(models.Event{Email: rs.Email, Message: models.EVENT_CLICKED})
w.Write([]byte("It Works!")) w.Write([]byte(p.HTML))
} }
// Use allows us to stack middleware to process the request // Use allows us to stack middleware to process the request

View File

@ -20,12 +20,12 @@ type Campaign struct {
PageId int64 `json:"-"` PageId int64 `json:"-"`
Page Page `json:"page"` Page Page `json:"page"`
Status string `json:"status"` Status string `json:"status"`
EmailsSent string `json:"emails_sent"`
Results []Result `json:"results,omitempty"` Results []Result `json:"results,omitempty"`
Groups []Group `json:"groups,omitempty"` Groups []Group `json:"groups,omitempty"`
Events []Event `json:"timeline,omitemtpy"` Events []Event `json:"timeline,omitemtpy"`
SMTP SMTP `json:"smtp"` SMTP SMTP `json:"smtp"`
URL string `json:"url"` URL string `json:"url"`
Errors []string `json:"errors,omitempty"`
} }
// ErrCampaignNameNotSpecified indicates there was no template given by the user // ErrCampaignNameNotSpecified indicates there was no template given by the user
@ -37,12 +37,18 @@ var ErrGroupNotSpecified = errors.New("No groups specified")
// ErrTemplateNotSpecified indicates there was no template given by the user // ErrTemplateNotSpecified indicates there was no template given by the user
var ErrTemplateNotSpecified = errors.New("No email template specified") var ErrTemplateNotSpecified = errors.New("No email template specified")
// ErrPageNotSpecified indicates a landing page was not provided for the campaign
var ErrPageNotSpecified = errors.New("No landing page specified")
// ErrTemplateNotFound indicates the template specified does not exist in the database // ErrTemplateNotFound indicates the template specified does not exist in the database
var ErrTemplateNotFound = errors.New("Template not found") var ErrTemplateNotFound = errors.New("Template not found")
// ErrGroupnNotFound indicates a group specified by the user does not exist in the database // ErrGroupnNotFound indicates a group specified by the user does not exist in the database
var ErrGroupNotFound = errors.New("Group not found") var ErrGroupNotFound = errors.New("Group not found")
// ErrPageNotFound indicates a page specified by the user does not exist in the database
var ErrPageNotFound = errors.New("Page not found")
// Validate checks to make sure there are no invalid fields in a submitted campaign // Validate checks to make sure there are no invalid fields in a submitted campaign
func (c *Campaign) Validate() error { func (c *Campaign) Validate() error {
switch { switch {
@ -52,6 +58,8 @@ func (c *Campaign) Validate() error {
return ErrGroupNotSpecified return ErrGroupNotSpecified
case c.Template.Name == "": case c.Template.Name == "":
return ErrTemplateNotSpecified return ErrTemplateNotSpecified
case c.Page.Name == "":
return ErrPageNotSpecified
} }
return nil return nil
} }
@ -97,6 +105,10 @@ func GetCampaigns(uid int64) ([]Campaign, error) {
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
} }
err = db.Table("pages").Where("id=?", cs[i].PageId).Find(&cs[i].Page).Error
if err != nil {
Logger.Println(err)
}
} }
return cs, err return cs, err
} }
@ -117,6 +129,10 @@ func GetCampaign(id int64, uid int64) (Campaign, error) {
return c, err return c, err
} }
err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error
if err != nil {
return c, err
}
err = db.Table("pages").Where("id=?", c.PageId).Find(&c.Page).Error
return c, err return c, err
} }
@ -152,6 +168,17 @@ func PostCampaign(c *Campaign, uid int64) error {
} }
c.Template = t c.Template = t
c.TemplateId = t.Id c.TemplateId = t.Id
// Check to make sure the page exists
p, err := GetPageByName(c.Page.Name, uid)
if err == gorm.RecordNotFound {
Logger.Printf("Error - Page %s does not exist", p.Name)
return ErrPageNotFound
} else if err != nil {
Logger.Println(err)
return err
}
c.Page = p
c.PageId = p.Id
// Insert into the DB // Insert into the DB
err = db.Save(c).Error err = db.Save(c).Error
if err != nil { if err != nil {

View File

@ -21,6 +21,9 @@ function save(){
name: $("#template").val() name: $("#template").val()
}, },
url: $("#url").val(), url: $("#url").val(),
page: {
name: $("#page").val()
},
smtp: { smtp: {
from_address: $("input[name=from]").val(), from_address: $("input[name=from]").val(),
host: $("input[name=host]").val(), host: $("input[name=host]").val(),
@ -51,6 +54,7 @@ function edit(campaign){
// Clear the bloodhound instance // Clear the bloodhound instance
group_bh.clear(); group_bh.clear();
template_bh.clear(); template_bh.clear();
page_bh.clear();
if (campaign == "new") { if (campaign == "new") {
api.groups.get() api.groups.get()
.success(function(groups){ .success(function(groups){
@ -72,6 +76,16 @@ function edit(campaign){
template_bh.add(templates) template_bh.add(templates)
} }
}) })
api.pages.get()
.success(function(pages){
if (pages.length == 0){
modalError("No pages found!")
return false
}
else {
page_bh.add(pages)
}
})
} }
} }
@ -118,7 +132,6 @@ $(document).ready(function(){
}) })
// Create the group typeahead objects // Create the group typeahead objects
groupTable = $("#groupTable").DataTable() groupTable = $("#groupTable").DataTable()
suggestion_template = Hogan.compile('<div>{{name}}</div>')
group_bh = new Bloodhound({ group_bh = new Bloodhound({
datumTokenizer: function(g) { return Bloodhound.tokenizers.whitespace(g.name) }, datumTokenizer: function(g) { return Bloodhound.tokenizers.whitespace(g.name) },
queryTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace,
@ -140,6 +153,9 @@ $(document).ready(function(){
}) })
.bind('typeahead:select', function(ev, group){ .bind('typeahead:select', function(ev, group){
$("#groupSelect").typeahead('val', group.name) $("#groupSelect").typeahead('val', group.name)
})
.bind('typeahead:autocomplete', function(ev, group){
$("#groupSelect").typeahead('val', group.name)
}); });
// Create the template typeahead objects // Create the template typeahead objects
template_bh = new Bloodhound({ template_bh = new Bloodhound({
@ -164,4 +180,33 @@ $(document).ready(function(){
.bind('typeahead:select', function(ev, template){ .bind('typeahead:select', function(ev, template){
$("#template").typeahead('val', template.name) $("#template").typeahead('val', template.name)
}) })
.bind('typeahead:autocomplete', function(ev, template){
$("#template").typeahead('val', template.name)
});
// Create the landing page typeahead objects
page_bh = new Bloodhound({
datumTokenizer: function(p) { return Bloodhound.tokenizers.whitespace(p.name) },
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: []
})
page_bh.initialize()
$("#page.typeahead.form-control").typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: "pages",
source: page_bh,
templates: {
empty: function(data) {return '<div class="tt-suggestion">No pages matched that query</div>' },
suggestion: function(data){ return '<div>' + data.name + '</div>' }
}
})
.bind('typeahead:select', function(ev, page){
$("#page").typeahead('val', page.name)
})
.bind('typeahead:autocomplete', function(ev, page){
$("#page").typeahead('val', page.name)
});
}) })

View File

@ -68,9 +68,12 @@
<div class="form-group"> <div class="form-group">
<label for="name">Name:</label> <label for="name">Name:</label>
<input type="text" class="form-control" id="name" placeholder="Campaign name" autofocus> <input type="text" class="form-control" id="name" placeholder="Campaign name" autofocus>
<label class="control-label" for="template">Template:</label> <label class="control-label" for="template">Email Template:</label>
<input type="text" class="typeahead form-control" placeholder="Template Name" id="template"/> <input type="text" class="typeahead form-control" placeholder="Template Name" id="template"/>
<br> <br>
<label class="control-label" for="page">Landing Page:</label>
<input type="text" class="typeahead form-control" placeholder="Landing Page" id="page"/>
<br>
<label class="control-label" for="url">URL: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Location of gophish listener (must be reachable by targets!)"></i></label> <label class="control-label" for="url">URL: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Location of gophish listener (must be reachable by targets!)"></i></label>
<input type="text" class="form-control" placeholder="http://192.168.1.1" id="url"/> <input type="text" class="form-control" placeholder="http://192.168.1.1" id="url"/>
<br/> <br/>

View File

@ -110,7 +110,7 @@ func processCampaign(c *models.Campaign) {
err = e.Send(c.SMTP.Host, auth) err = e.Send(c.SMTP.Host, auth)
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
err = t.UpdateStatus("Error") err = t.UpdateStatus(models.ERROR)
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
} }