From ac7cb5e087e9ee1f72469b85cabb45186744e5cb Mon Sep 17 00:00:00 2001 From: Claudio Date: Tue, 8 Aug 2017 13:18:12 +1200 Subject: [PATCH] Submit credentials to original origin after capturing them (#703) This commit implements the ability to submit credentials upstream after they are submitted to Gophish. --- ...0170720122503_0.2.2_submit_to_original.sql | 6 +++ ...0170720122503_0.2.2_submit_to_original.sql | 7 +++ models/models_test.go | 19 +++++++ models/page.go | 53 +++++++++++++++++-- static/css/main.css | 3 ++ static/js/dist/app/landing_pages.min.js | 2 +- static/js/src/app/landing_pages.js | 18 ++++++- templates/landing_pages.html | 47 +++++++++------- 8 files changed, 131 insertions(+), 24 deletions(-) create mode 100644 db/db_mysql/migrations/20170720122503_0.2.2_submit_to_original.sql create mode 100644 db/db_sqlite3/migrations/20170720122503_0.2.2_submit_to_original.sql diff --git a/db/db_mysql/migrations/20170720122503_0.2.2_submit_to_original.sql b/db/db_mysql/migrations/20170720122503_0.2.2_submit_to_original.sql new file mode 100644 index 00000000..3b4cb7c3 --- /dev/null +++ b/db/db_mysql/migrations/20170720122503_0.2.2_submit_to_original.sql @@ -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 diff --git a/db/db_sqlite3/migrations/20170720122503_0.2.2_submit_to_original.sql b/db/db_sqlite3/migrations/20170720122503_0.2.2_submit_to_original.sql new file mode 100644 index 00000000..53886a91 --- /dev/null +++ b/db/db_sqlite3/migrations/20170720122503_0.2.2_submit_to_original.sql @@ -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 diff --git a/models/models_test.go b/models/models_test.go index 5a5008b2..7ab6a3f4 100644 --- a/models/models_test.go +++ b/models/models_test.go @@ -299,8 +299,24 @@ func (s *ModelsSuite) TestPostPage(c *check.C) { c.Assert(ok, check.Equals, true) 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 p.CapturePasswords = false + p.SubmitToOriginal = false p.HTML = html p.RedirectURL = "" err = PutPage(&p) @@ -321,6 +337,9 @@ func (s *ModelsSuite) TestPostPage(c *check.C) { c.Assert(ok, check.Equals, true) 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 p.CaptureCredentials = false p.HTML = html diff --git a/models/page.go b/models/page.go index 33277584..37de89b7 100644 --- a/models/page.go +++ b/models/page.go @@ -16,6 +16,7 @@ type Page struct { HTML string `json:"html" gorm:"column:html"` CaptureCredentials bool `json:"capture_credentials" gorm:"column:capture_credentials"` 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"` 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. var ErrPageNameNotSpecified = errors.New("Page Name not specified") +var JsSubmitToOriginal = `` + // parseHTML parses the page HTML on save to handle the // capturing (or lack thereof!) of credentials and passwords func (p *Page) parseHTML() error { @@ -30,11 +61,27 @@ func (p *Page) parseHTML() error { if err != nil { return err } + + head := d.Find("head") + + if p.CaptureCredentials && p.CapturePasswords && p.SubmitToOriginal { + head.AppendHtml("") + head.AppendHtml(JsSubmitToOriginal) + } + + if !p.SubmitToOriginal { + head.AppendHtml("") + } + forms := d.Find("form") forms.Each(func(i int, f *goquery.Selection) { - // We always want the submitted events to be - // sent to our server - f.SetAttr("action", "") + // If we still want to submit to the original domain, do not override + if !p.SubmitToOriginal { + // We always want the submitted events to be + // sent to our server + f.SetAttr("action", "") + } + if p.CaptureCredentials { // If we don't want to capture passwords, // find all the password fields and remove the "name" attribute. diff --git a/static/css/main.css b/static/css/main.css index e18694a3..3cb1ae95 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -512,6 +512,9 @@ td.details-control{ #capture_passwords { display:none; } +#submit_to_original { + display:none; +} #redirect_url { display:none; } diff --git a/static/js/dist/app/landing_pages.min.js b/static/js/dist/app/landing_pages.min.js index 8f95a54b..125425a4 100644 --- a/static/js/dist/app/landing_pages.min.js +++ b/static/js/dist/app/landing_pages.min.js @@ -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"),"
\t\t
"]).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()}); \ No newline at end of file +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"),"
\t\t
"]).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()}); \ No newline at end of file diff --git a/static/js/src/app/landing_pages.js b/static/js/src/app/landing_pages.js index 60fb0f81..f35f48bb 100644 --- a/static/js/src/app/landing_pages.js +++ b/static/js/src/app/landing_pages.js @@ -13,6 +13,7 @@ function save(idx) { page.html = editor.getData() page.capture_credentials = $("#capture_credentials_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() if (idx != -1) { page.id = pages[idx].id @@ -90,6 +91,7 @@ function edit(idx) { $("#html_editor").val(page.html) $("#capture_credentials_checkbox").prop("checked", page.capture_credentials) $("#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) if (page.capture_credentials) { $("#capture_passwords").show() @@ -199,6 +201,20 @@ $(document).ready(function() { $("#capture_credentials_checkbox").change(function() { $("#capture_passwords").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() }) diff --git a/templates/landing_pages.html b/templates/landing_pages.html index 75888c16..12e8c4b3 100644 --- a/templates/landing_pages.html +++ b/templates/landing_pages.html @@ -86,27 +86,36 @@
- - -
+ + +
- - -
- Warning: Credentials are currently not encrypted. This means that captured passwords are stored in the database as cleartext. Be careful with this! -
-
-
- -
- -
-
- - +
+ + +
+
+ +
+
+
+ +