Submit credentials to original origin after capturing them (#703)

This commit implements the ability to submit credentials upstream after they are submitted to Gophish.
703-submit-credentials
Claudio 2017-08-08 13:18:12 +12:00 committed by Jordan Wright
parent 75600f5812
commit ac7cb5e087
8 changed files with 131 additions and 24 deletions

View File

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

View File

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

View File

@ -299,8 +299,24 @@ func (s *ModelsSuite) TestPostPage(c *check.C) {
c.Assert(ok, check.Equals, true) c.Assert(ok, check.Equals, true)
c.Assert(u, check.Equals, "username") c.Assert(u, check.Equals, "username")
}) })
// Submit with SubmitToOriginal set to true
p.SubmitToOriginal = true
p.HTML = html
err = PutPage(&p)
c.Assert(err, check.Equals, nil)
d, err = goquery.NewDocumentFromReader(strings.NewReader(p.HTML))
forms = d.Find("form")
head := d.Find("head")
ok, _ := head.Html()
c.Assert(ok[31:62], check.Equals, "var __goSubmitToOriginal = true")
forms.Each(func(i int, f *goquery.Selection) {
// Check the action has been clearer
a, _ := f.Attr("action")
c.Assert(a, check.Equals, "example.com")
})
// Check what happens when we don't capture passwords // Check what happens when we don't capture passwords
p.CapturePasswords = false p.CapturePasswords = false
p.SubmitToOriginal = false
p.HTML = html p.HTML = html
p.RedirectURL = "" p.RedirectURL = ""
err = PutPage(&p) err = PutPage(&p)
@ -321,6 +337,9 @@ func (s *ModelsSuite) TestPostPage(c *check.C) {
c.Assert(ok, check.Equals, true) c.Assert(ok, check.Equals, true)
c.Assert(u, check.Equals, "username") c.Assert(u, check.Equals, "username")
}) })
head = d.Find("head")
ok, _ = head.Html()
c.Assert(ok[31:63], check.Equals, "var __goSubmitToOriginal = false")
// Finally, check when we don't capture credentials // Finally, check when we don't capture credentials
p.CaptureCredentials = false p.CaptureCredentials = false
p.HTML = html p.HTML = html

View File

@ -16,6 +16,7 @@ type Page struct {
HTML string `json:"html" gorm:"column:html"` HTML string `json:"html" gorm:"column:html"`
CaptureCredentials bool `json:"capture_credentials" gorm:"column:capture_credentials"` CaptureCredentials bool `json:"capture_credentials" gorm:"column:capture_credentials"`
CapturePasswords bool `json:"capture_passwords" gorm:"column:capture_passwords"` CapturePasswords bool `json:"capture_passwords" gorm:"column:capture_passwords"`
SubmitToOriginal bool `json:"submit_to_original" gorm:"column:submit_to_original"`
RedirectURL string `json:"redirect_url" gorm:"column:redirect_url"` RedirectURL string `json:"redirect_url" gorm:"column:redirect_url"`
ModifiedDate time.Time `json:"modified_date"` ModifiedDate time.Time `json:"modified_date"`
} }
@ -23,6 +24,36 @@ type Page struct {
// ErrPageNameNotSpecified is thrown if the name of the landing page is blank. // ErrPageNameNotSpecified is thrown if the name of the landing page is blank.
var ErrPageNameNotSpecified = errors.New("Page Name not specified") var ErrPageNameNotSpecified = errors.New("Page Name not specified")
var JsSubmitToOriginal = `<script type='text/javascript'>(function(){
if(typeof __goCaptureAndSubmitToOriginal !== 'function'){
window.__goCaptureAndSubmitToOriginal = function(){
if(!__goSubmitToOriginal) return;
var forms = jQuery('body').find('form');
jQuery.each(forms, function(i, f){
var form = jQuery(f);
form.submit(function(e){
e.preventDefault();
jQuery.post("", form.serialize(), function(done){
form.off('submit');
form.submit();
});
});
});
};
if(typeof jQuery === 'undefined'){
var script = document.createElement('script');
script.src = 'https://code.jquery.com/jquery-2.2.4.min.js';
script.type = 'text/javascript';
script.onload = function(){
__goCaptureAndSubmitToOriginal();
};
document.head.appendChild(script);
}else{
__goCaptureAndSubmitToOriginal();
}
}
})()</script>`
// parseHTML parses the page HTML on save to handle the // parseHTML parses the page HTML on save to handle the
// capturing (or lack thereof!) of credentials and passwords // capturing (or lack thereof!) of credentials and passwords
func (p *Page) parseHTML() error { func (p *Page) parseHTML() error {
@ -30,11 +61,27 @@ func (p *Page) parseHTML() error {
if err != nil { if err != nil {
return err return err
} }
head := d.Find("head")
if p.CaptureCredentials && p.CapturePasswords && p.SubmitToOriginal {
head.AppendHtml("<script type='text/javascript'>var __goSubmitToOriginal = true;</script>")
head.AppendHtml(JsSubmitToOriginal)
}
if !p.SubmitToOriginal {
head.AppendHtml("<script type='text/javascript'>var __goSubmitToOriginal = false;</script>")
}
forms := d.Find("form") forms := d.Find("form")
forms.Each(func(i int, f *goquery.Selection) { forms.Each(func(i int, f *goquery.Selection) {
// If we still want to submit to the original domain, do not override
if !p.SubmitToOriginal {
// We always want the submitted events to be // We always want the submitted events to be
// sent to our server // sent to our server
f.SetAttr("action", "") f.SetAttr("action", "")
}
if p.CaptureCredentials { if p.CaptureCredentials {
// If we don't want to capture passwords, // If we don't want to capture passwords,
// find all the password fields and remove the "name" attribute. // find all the password fields and remove the "name" attribute.

3
static/css/main.css vendored
View File

@ -512,6 +512,9 @@ td.details-control{
#capture_passwords { #capture_passwords {
display:none; display:none;
} }
#submit_to_original {
display:none;
}
#redirect_url { #redirect_url {
display:none; display:none;
} }

View File

@ -1 +1 @@
function save(e){var a={};a.name=$("#name").val(),editor=CKEDITOR.instances.html_editor,a.html=editor.getData(),a.capture_credentials=$("#capture_credentials_checkbox").prop("checked"),a.capture_passwords=$("#capture_passwords_checkbox").prop("checked"),a.redirect_url=$("#redirect_url_input").val(),e!=-1?(a.id=pages[e].id,api.pageId.put(a).success(function(e){successFlash("Page edited successfully!"),load(),dismiss()})):api.pages.post(a).success(function(e){successFlash("Page added successfully!"),load(),dismiss()}).error(function(e){modalError(e.responseJSON.message)})}function dismiss(){$("#modal\\.flashes").empty(),$("#name").val(""),$("#html_editor").val(""),$("#url").val(""),$("#redirect_url_input").val(""),$("#modal").find("input[type='checkbox']").prop("checked",!1),$("#capture_passwords").hide(),$("#redirect_url").hide(),$("#modal").modal("hide")}function deletePage(e){confirm("Delete "+pages[e].name+"?")&&api.pageId.delete(pages[e].id).success(function(e){successFlash(e.message),load()})}function importSite(){url=$("#url").val(),url?api.clone_site({url:url,include_resources:!1}).success(function(e){console.log($("#html_editor")),$("#html_editor").val(e.html),$("#importSiteModal").modal("hide")}).error(function(e){modalError(e.responseJSON.message)}):modalError("No URL Specified!")}function edit(e){$("#modalSubmit").unbind("click").click(function(){save(e)}),$("#html_editor").ckeditor();var a={};e!=-1&&(a=pages[e],$("#name").val(a.name),$("#html_editor").val(a.html),$("#capture_credentials_checkbox").prop("checked",a.capture_credentials),$("#capture_passwords_checkbox").prop("checked",a.capture_passwords),$("#redirect_url_input").val(a.redirect_url),a.capture_credentials&&($("#capture_passwords").show(),$("#redirect_url").show()))}function copy(e){$("#modalSubmit").unbind("click").click(function(){save(-1)}),$("#html_editor").ckeditor();var a=pages[e];$("#name").val("Copy of "+a.name),$("#html_editor").val(a.html)}function load(){$("#pagesTable").hide(),$("#emptyMessage").hide(),$("#loading").show(),api.pages.get().success(function(e){pages=e,$("#loading").hide(),pages.length>0?($("#pagesTable").show(),pagesTable=$("#pagesTable").DataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]}),pagesTable.clear(),$.each(pages,function(e,a){pagesTable.row.add([escapeHtml(a.name),moment(a.modified_date).format("MMMM Do YYYY, h:mm:ss a"),"<div class='pull-right'><span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Edit Page' onclick='edit("+e+")'> <i class='fa fa-pencil'></i> </button></span>\t\t <span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Page' onclick='copy("+e+")'> <i class='fa fa-copy'></i> </button></span> <button class='btn btn-danger' data-toggle='tooltip' data-placement='left' title='Delete Page' onclick='deletePage("+e+")'> <i class='fa fa-trash-o'></i> </button></div>"]).draw()}),$('[data-toggle="tooltip"]').tooltip()):$("#emptyMessage").show()}).error(function(){$("#loading").hide(),errorFlash("Error fetching pages")})}var pages=[];$(document).ready(function(){$(".modal").on("hidden.bs.modal",function(e){$(this).removeClass("fv-modal-stack"),$("body").data("fv_open_modals",$("body").data("fv_open_modals")-1)}),$(".modal").on("shown.bs.modal",function(e){"undefined"==typeof $("body").data("fv_open_modals")&&$("body").data("fv_open_modals",0),$(this).hasClass("fv-modal-stack")||($(this).addClass("fv-modal-stack"),$("body").data("fv_open_modals",$("body").data("fv_open_modals")+1),$(this).css("z-index",1040+10*$("body").data("fv_open_modals")),$(".modal-backdrop").not(".fv-modal-stack").css("z-index",1039+10*$("body").data("fv_open_modals")),$(".modal-backdrop").not("fv-modal-stack").addClass("fv-modal-stack"))}),$.fn.modal.Constructor.prototype.enforceFocus=function(){$(document).off("focusin.bs.modal").on("focusin.bs.modal",$.proxy(function(e){this.$element[0]===e.target||this.$element.has(e.target).length||$(e.target).closest(".cke_dialog, .cke").length||this.$element.trigger("focus")},this))},$("#modal").on("hidden.bs.modal",function(e){dismiss()}),$("#capture_credentials_checkbox").change(function(){$("#capture_passwords").toggle(),$("#redirect_url").toggle()}),load()}); function save(e){var a={};a.name=$("#name").val(),editor=CKEDITOR.instances.html_editor,a.html=editor.getData(),a.capture_credentials=$("#capture_credentials_checkbox").prop("checked"),a.capture_passwords=$("#capture_passwords_checkbox").prop("checked"),a.submit_to_original=$("#submit_to_original_checkbox").prop("checked"),a.redirect_url=$("#redirect_url_input").val(),-1!=e?(a.id=pages[e].id,api.pageId.put(a).success(function(e){successFlash("Page edited successfully!"),load(),dismiss()})):api.pages.post(a).success(function(e){successFlash("Page added successfully!"),load(),dismiss()}).error(function(e){modalError(e.responseJSON.message)})}function dismiss(){$("#modal\\.flashes").empty(),$("#name").val(""),$("#html_editor").val(""),$("#url").val(""),$("#redirect_url_input").val(""),$("#modal").find("input[type='checkbox']").prop("checked",!1),$("#capture_passwords").hide(),$("#redirect_url").hide(),$("#modal").modal("hide")}function deletePage(e){confirm("Delete "+pages[e].name+"?")&&api.pageId.delete(pages[e].id).success(function(e){successFlash(e.message),load()})}function importSite(){url=$("#url").val(),url?api.clone_site({url:url,include_resources:!1}).success(function(e){console.log($("#html_editor")),$("#html_editor").val(e.html),$("#importSiteModal").modal("hide")}).error(function(e){modalError(e.responseJSON.message)}):modalError("No URL Specified!")}function edit(e){$("#modalSubmit").unbind("click").click(function(){save(e)}),$("#html_editor").ckeditor();var a={};-1!=e&&(a=pages[e],$("#name").val(a.name),$("#html_editor").val(a.html),$("#capture_credentials_checkbox").prop("checked",a.capture_credentials),$("#capture_passwords_checkbox").prop("checked",a.capture_passwords),$("#submit_to_original_checkbox").prop("checked",a.submit_to_original),$("#redirect_url_input").val(a.redirect_url),a.capture_credentials&&($("#capture_passwords").show(),$("#redirect_url").show()))}function copy(e){$("#modalSubmit").unbind("click").click(function(){save(-1)}),$("#html_editor").ckeditor();var a=pages[e];$("#name").val("Copy of "+a.name),$("#html_editor").val(a.html)}function load(){$("#pagesTable").hide(),$("#emptyMessage").hide(),$("#loading").show(),api.pages.get().success(function(e){pages=e,$("#loading").hide(),pages.length>0?($("#pagesTable").show(),pagesTable=$("#pagesTable").DataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]}),pagesTable.clear(),$.each(pages,function(e,a){pagesTable.row.add([escapeHtml(a.name),moment(a.modified_date).format("MMMM Do YYYY, h:mm:ss a"),"<div class='pull-right'><span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Edit Page' onclick='edit("+e+")'> <i class='fa fa-pencil'></i> </button></span>\t\t <span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Page' onclick='copy("+e+")'> <i class='fa fa-copy'></i> </button></span> <button class='btn btn-danger' data-toggle='tooltip' data-placement='left' title='Delete Page' onclick='deletePage("+e+")'> <i class='fa fa-trash-o'></i> </button></div>"]).draw()}),$('[data-toggle="tooltip"]').tooltip()):$("#emptyMessage").show()}).error(function(){$("#loading").hide(),errorFlash("Error fetching pages")})}var pages=[];$(document).ready(function(){$(".modal").on("hidden.bs.modal",function(e){$(this).removeClass("fv-modal-stack"),$("body").data("fv_open_modals",$("body").data("fv_open_modals")-1)}),$(".modal").on("shown.bs.modal",function(e){void 0===$("body").data("fv_open_modals")&&$("body").data("fv_open_modals",0),$(this).hasClass("fv-modal-stack")||($(this).addClass("fv-modal-stack"),$("body").data("fv_open_modals",$("body").data("fv_open_modals")+1),$(this).css("z-index",1040+10*$("body").data("fv_open_modals")),$(".modal-backdrop").not(".fv-modal-stack").css("z-index",1039+10*$("body").data("fv_open_modals")),$(".modal-backdrop").not("fv-modal-stack").addClass("fv-modal-stack"))}),$.fn.modal.Constructor.prototype.enforceFocus=function(){$(document).off("focusin.bs.modal").on("focusin.bs.modal",$.proxy(function(e){this.$element[0]===e.target||this.$element.has(e.target).length||$(e.target).closest(".cke_dialog, .cke").length||this.$element.trigger("focus")},this))},$("#modal").on("hidden.bs.modal",function(e){dismiss()}),$("#capture_credentials_checkbox").change(function(){$("#capture_passwords").toggle(),$("#redirect_url").toggle(),$("#capture_passwords").is(":visible")||($("#submit_to_original").hide(),$("#submit_to_original_checkbox").prop("checked",!1))}),$("#capture_passwords_checkbox").change(function(){$("#capture_passwords_checkbox").is(":checked")?$("#submit_to_original").show():($("#submit_to_original").toggle(),$("#submit_to_original").is(":visible")||$("#submit_to_original_checkbox").prop("checked",!1))}),load()});

View File

@ -13,6 +13,7 @@ function save(idx) {
page.html = editor.getData() page.html = editor.getData()
page.capture_credentials = $("#capture_credentials_checkbox").prop("checked") page.capture_credentials = $("#capture_credentials_checkbox").prop("checked")
page.capture_passwords = $("#capture_passwords_checkbox").prop("checked") page.capture_passwords = $("#capture_passwords_checkbox").prop("checked")
page.submit_to_original = $("#submit_to_original_checkbox").prop("checked")
page.redirect_url = $("#redirect_url_input").val() page.redirect_url = $("#redirect_url_input").val()
if (idx != -1) { if (idx != -1) {
page.id = pages[idx].id page.id = pages[idx].id
@ -90,6 +91,7 @@ function edit(idx) {
$("#html_editor").val(page.html) $("#html_editor").val(page.html)
$("#capture_credentials_checkbox").prop("checked", page.capture_credentials) $("#capture_credentials_checkbox").prop("checked", page.capture_credentials)
$("#capture_passwords_checkbox").prop("checked", page.capture_passwords) $("#capture_passwords_checkbox").prop("checked", page.capture_passwords)
$("#submit_to_original_checkbox").prop("checked", page.submit_to_original)
$("#redirect_url_input").val(page.redirect_url) $("#redirect_url_input").val(page.redirect_url)
if (page.capture_credentials) { if (page.capture_credentials) {
$("#capture_passwords").show() $("#capture_passwords").show()
@ -199,6 +201,20 @@ $(document).ready(function() {
$("#capture_credentials_checkbox").change(function() { $("#capture_credentials_checkbox").change(function() {
$("#capture_passwords").toggle() $("#capture_passwords").toggle()
$("#redirect_url").toggle() $("#redirect_url").toggle()
}) if(!$("#capture_passwords").is(":visible")){
$("#submit_to_original").hide();
$("#submit_to_original_checkbox").prop("checked", false);
}
});
$("#capture_passwords_checkbox").change(function(){
if($("#capture_passwords_checkbox").is(":checked")){
$("#submit_to_original").show();
}else{
$("#submit_to_original").toggle()
if(!$("#submit_to_original").is(":visible")){
$("#submit_to_original_checkbox").prop("checked", false);
}
}
});
load() load()
}) })

View File

@ -87,7 +87,9 @@
</div> </div>
<div class="checkbox checkbox-primary"> <div class="checkbox checkbox-primary">
<input id="capture_credentials_checkbox" type="checkbox"> <input id="capture_credentials_checkbox" type="checkbox">
<label for="capture_credentials_checkbox">Capture Submitted Data <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="If the landing page contains a form, submitted input (except passwords!) will be captured."></i></label> <label for="capture_credentials_checkbox">Capture Submitted Data
<i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="If the landing page contains a form, submitted input (except passwords!) will be captured."></i>
</label>
</div> </div>
<div class="checkbox checkbox-primary" id="capture_passwords"> <div class="checkbox checkbox-primary" id="capture_passwords">
<input id="capture_passwords_checkbox" type="checkbox"> <input id="capture_passwords_checkbox" type="checkbox">
@ -96,6 +98,13 @@
<i class="fa fa-exclamation-circle"></i> <b>Warning:</b> Credentials are currently <b>not encrypted</b>. This means that captured passwords are stored in the database as cleartext. Be careful with this! <i class="fa fa-exclamation-circle"></i> <b>Warning:</b> Credentials are currently <b>not encrypted</b>. This means that captured passwords are stored in the database as cleartext. Be careful with this!
</div> </div>
</div> </div>
<div class="checkbox checkbox-primary" id="submit_to_original">
<input id="submit_to_original_checkbox" type="checkbox">
<label for="submit_to_original_checkbox">
Capture and Submit to the Original Origin.
<i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Credentials will be captured and the form submitted to the original origin. If there is no anti-CSRF protection in place, and the credentials are correct, users phished will get logged in. Note that the form 'action' needs to include the full URL, and the current form submission is not handled by existing JavaScript."></i>
</label>
</div>
<div id="redirect_url"> <div id="redirect_url">
<label class="control-label" for="redirect_url_input">Redirect to: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="This option lets you redirect the user to a page after credentials are submitted."></i></label> <label class="control-label" for="redirect_url_input">Redirect to: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="This option lets you redirect the user to a page after credentials are submitted."></i></label>
<div class="form-group"> <div class="form-group">