Merge pull request #146 from gophish/77-fix-tls-issues

Adding support for self-signed certs. Fixes #77
pull/147/head
Jordan Wright 2016-02-12 21:34:53 -06:00
commit 0a7bfcf664
8 changed files with 77 additions and 30 deletions

View File

@ -0,0 +1,8 @@
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
ALTER TABLE smtp ADD COLUMN ignore_cert_errors BOOLEAN;
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back

View File

@ -4,12 +4,13 @@ import "errors"
// SMTP contains the attributes needed to handle the sending of campaign emails // SMTP contains the attributes needed to handle the sending of campaign emails
type SMTP struct { type SMTP struct {
SMTPId int64 `json:"-" gorm:"column:smtp_id; primary_key:yes"` SMTPId int64 `json:"-" gorm:"column:smtp_id; primary_key:yes"`
CampaignId int64 `json:"-" gorm:"column:campaign_id"` CampaignId int64 `json:"-" gorm:"column:campaign_id"`
Host string `json:"host"` Host string `json:"host"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
Password string `json:"password,omitempty" sql:"-"` Password string `json:"password,omitempty" sql:"-"`
FromAddress string `json:"from_address"` FromAddress string `json:"from_address"`
IgnoreCertErrors bool `json:"ignore_cert_errors"`
} }
// ErrFromAddressNotSpecified is thrown when there is no "From" address // ErrFromAddressNotSpecified is thrown when there is no "From" address

9
static/css/main.css vendored
View File

@ -374,6 +374,9 @@ table.dataTable thead .sorting_desc:after {
content: "\f0dd" !important; content: "\f0dd" !important;
opacity: .8 !important; opacity: .8 !important;
} }
td.details-control{
cursor:pointer;
}
.timeline{ .timeline{
text-align:left; text-align:left;
background-color:#ffffff; background-color:#ffffff;
@ -445,6 +448,10 @@ table.dataTable thead .sorting_desc:after {
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.timeline-event-table{ .timeline-event-results{
font-size:16px;
display:none; display:none;
} }
.tooltip-inner {
width:300px !important;
}

View File

@ -139,21 +139,28 @@ function renderTimeline(data) {
' <span class="timeline-date">' + moment(event.time).format('MMMM Do YYYY h:mm') + '</span>' ' <span class="timeline-date">' + moment(event.time).format('MMMM Do YYYY h:mm') + '</span>'
if (event.details) { if (event.details) {
results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>' results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>'
results += '<div class="timeline-event-table">'
results += ' <table class="table table-condensed table-bordered table-striped">'
results += ' <thead><tr><th>Parameter</th><th>Value(s)</tr></thead><tbody>'
details = JSON.parse(event.details) details = JSON.parse(event.details)
$.each(Object.keys(details.payload), function(i, param) { if (details.payload) {
if (param == "rid") { results += '<div class="timeline-event-results">'
return true; results += ' <table class="table table-condensed table-bordered table-striped">'
} results += ' <thead><tr><th>Parameter</th><th>Value(s)</tr></thead><tbody>'
results += ' <tr>' $.each(Object.keys(details.payload), function(i, param) {
results += ' <td>' + param + '</td>' if (param == "rid") {
results += ' <td>' + details.payload[param] + '</td>' return true;
results += ' </tr>' }
}) results += ' <tr>'
results += ' </tbody></table>' results += ' <td>' + param + '</td>'
results += '</div>' results += ' <td>' + details.payload[param] + '</td>'
results += ' </tr>'
})
results += ' </tbody></table>'
results += '</div>'
}
if (details.error) {
results += '<div class="timeline-event-results">'
results += '<span class="label label-default">Error</span> ' + details.error
results += '</div>'
}
} }
results += '</div></div>' results += '</div></div>'
} }
@ -174,15 +181,15 @@ $(document).ready(function() {
// Setup viewing the details of a result // Setup viewing the details of a result
$("#resultsTable").on("click", ".timeline-event-details", function() { $("#resultsTable").on("click", ".timeline-event-details", function() {
// Show the parameters // Show the parameters
payloadTable = $(this).parent().find(".timeline-event-table") payloadResults = $(this).parent().find(".timeline-event-results")
if (payloadTable.is(":visible")) { if (payloadResults.is(":visible")) {
$(this).find("i").removeClass("fa-caret-down") $(this).find("i").removeClass("fa-caret-down")
$(this).find("i").addClass("fa-caret-right") $(this).find("i").addClass("fa-caret-right")
payloadTable.hide() payloadResults.hide()
} else { } else {
$(this).find("i").removeClass("fa-caret-right") $(this).find("i").removeClass("fa-caret-right")
$(this).find("i").addClass("fa-caret-down") $(this).find("i").addClass("fa-caret-down")
payloadTable.show() payloadResults.show()
} }
}) })
// Setup our graphs // Setup our graphs

View File

@ -35,6 +35,7 @@ function launch() {
host: $("input[name=host]").val(), host: $("input[name=host]").val(),
username: $("input[name=username]").val(), username: $("input[name=username]").val(),
password: $("input[name=password]").val(), password: $("input[name=password]").val(),
ignore_cert_errors: $("#ignore_cert_errors").prop("checked")
}, },
groups: groups groups: groups
} }
@ -73,6 +74,7 @@ function sendTestEmail() {
host: $("input[name=host]").val(), host: $("input[name=host]").val(),
username: $("input[name=username]").val(), username: $("input[name=username]").val(),
password: $("input[name=password]").val(), password: $("input[name=password]").val(),
ignore_cert_errors: $("#ignore_cert_errors").prop("checked")
} }
} }
btnHtml = $("#sendTestModalSubmit").html() btnHtml = $("#sendTestModalSubmit").html()

View File

@ -96,7 +96,10 @@
<br /> <br />
<label class="control-label" for="smtp_server">Password:</label> <label class="control-label" for="smtp_server">Password:</label>
<input type="password" class="form-control" placeholder="Password" value="" name="password"> <input type="password" class="form-control" placeholder="Password" value="" name="password">
<br /> <div class="checkbox checkbox-primary">
<input id="ignore_cert_errors" type="checkbox" checked>
<label for="ignore_cert_errors">Ignore Certificate Errors <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Ignore common certificate errors such as self-signed certs (exposes you to MiTM attacks - use carefully!)"></i></label>
</div>
<button type="button" data-toggle="modal" data-target="#sendTestEmailModal" class="btn btn-primary"><i class="fa fa-envelope"></i> Send Test Email</button> <button type="button" data-toggle="modal" data-target="#sendTestEmailModal" class="btn btn-primary"><i class="fa fa-envelope"></i> Send Test Email</button>
</div> </div>
</div> </div>

View File

@ -8,8 +8,8 @@ import (
"net/http" "net/http"
"net/mail" "net/mail"
"github.com/jordan-wright/email"
"github.com/gophish/gophish/models" "github.com/gophish/gophish/models"
"github.com/jordan-wright/email"
) )
// ParseMail takes in an HTTP Request and returns an Email object // ParseMail takes in an HTTP Request and returns an Email object

View File

@ -2,6 +2,8 @@ package worker
import ( import (
"bytes" "bytes"
"crypto/tls"
"encoding/json"
"errors" "errors"
"log" "log"
"net/mail" "net/mail"
@ -52,6 +54,10 @@ func processCampaign(c *models.Campaign) {
if c.SMTP.Username != "" && c.SMTP.Password != "" { if c.SMTP.Username != "" && c.SMTP.Password != "" {
auth = smtp.PlainAuth("", c.SMTP.Username, c.SMTP.Password, strings.Split(c.SMTP.Host, ":")[0]) auth = smtp.PlainAuth("", c.SMTP.Username, c.SMTP.Password, strings.Split(c.SMTP.Host, ":")[0])
} }
tc := &tls.Config{
ServerName: c.SMTP.Host,
InsecureSkipVerify: c.SMTP.IgnoreCertErrors,
}
f, err := mail.ParseAddress(c.SMTP.FromAddress) f, err := mail.ParseAddress(c.SMTP.FromAddress)
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
@ -108,14 +114,23 @@ func processCampaign(c *models.Campaign) {
Logger.Println("Creating email using template") Logger.Println("Creating email using template")
e.To = []string{t.Email} e.To = []string{t.Email}
Logger.Printf("Sending Email to %s\n", t.Email) Logger.Printf("Sending Email to %s\n", t.Email)
err = e.Send(c.SMTP.Host, auth) err = e.SendWithTLS(c.SMTP.Host, auth, tc)
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
es := struct {
Error string `json:"error"`
}{
Error: err.Error(),
}
ej, err := json.Marshal(es)
if err != nil {
Logger.Println(err)
}
err = t.UpdateStatus(models.ERROR) err = t.UpdateStatus(models.ERROR)
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
} }
err = c.AddEvent(models.Event{Email: t.Email, Message: models.EVENT_SENDING_ERROR}) err = c.AddEvent(models.Event{Email: t.Email, Message: models.EVENT_SENDING_ERROR, Details: string(ej)})
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
} }
@ -145,6 +160,10 @@ func SendTestEmail(s *models.SendTestEmailRequest) error {
if s.SMTP.Username != "" && s.SMTP.Password != "" { if s.SMTP.Username != "" && s.SMTP.Password != "" {
auth = smtp.PlainAuth("", s.SMTP.Username, s.SMTP.Password, strings.Split(s.SMTP.Host, ":")[0]) auth = smtp.PlainAuth("", s.SMTP.Username, s.SMTP.Password, strings.Split(s.SMTP.Host, ":")[0])
} }
t := &tls.Config{
ServerName: s.SMTP.Host,
InsecureSkipVerify: s.SMTP.IgnoreCertErrors,
}
f, err := mail.ParseAddress(s.SMTP.FromAddress) f, err := mail.ParseAddress(s.SMTP.FromAddress)
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
@ -188,7 +207,7 @@ func SendTestEmail(s *models.SendTestEmailRequest) error {
e.Subject = string(subjBuff.Bytes()) e.Subject = string(subjBuff.Bytes())
e.To = []string{s.Email} e.To = []string{s.Email}
Logger.Printf("Sending Email to %s\n", s.Email) Logger.Printf("Sending Email to %s\n", s.Email)
err = e.Send(s.SMTP.Host, auth) err = e.SendWithTLS(s.SMTP.Host, auth, t)
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
// For now, let's split the error and return // For now, let's split the error and return