Fixing SSRF by requiring an API key for all import endpoints. Fixes #1026

pull/986/head^2
Jordan Wright 2018-03-26 21:04:22 -05:00
parent 9ba3f04d1e
commit 2131c17c33
No known key found for this signature in database
GPG Key ID: 138D5AD2331B3C11
9 changed files with 134 additions and 114 deletions

View File

@ -102,6 +102,13 @@ func (s *ControllersSuite) SetupTest() {
c.UpdateStatus(models.CAMPAIGN_EMAILS_SENT)
}
func (s *ControllersSuite) TestRequireAPIKey() {
resp, err := http.Post(fmt.Sprintf("%s/api/import/site", as.URL), "application/json", nil)
s.Nil(err)
defer resp.Body.Close()
s.Equal(resp.StatusCode, http.StatusBadRequest)
}
func (s *ControllersSuite) TestSiteImportBaseHref() {
h := "<html><head></head><body><img src=\"/test.png\"/></body></html>"
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

View File

@ -59,9 +59,9 @@ func CreateAdminRouter() http.Handler {
api.HandleFunc("/smtp/", Use(API_SMTP, mid.RequireAPIKey))
api.HandleFunc("/smtp/{id:[0-9]+}", Use(API_SMTP_Id, mid.RequireAPIKey))
api.HandleFunc("/util/send_test_email", Use(API_Send_Test_Email, mid.RequireAPIKey))
api.HandleFunc("/import/group", API_Import_Group)
api.HandleFunc("/import/email", API_Import_Email)
api.HandleFunc("/import/site", API_Import_Site)
api.HandleFunc("/import/group", Use(API_Import_Group, mid.RequireAPIKey))
api.HandleFunc("/import/email", Use(API_Import_Email, mid.RequireAPIKey))
api.HandleFunc("/import/site", Use(API_Import_Site, mid.RequireAPIKey))
// Setup static file serving
router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))

View File

@ -1 +1 @@
function errorFlash(e){$("#flashes").empty(),$("#flashes").append('<div style="text-align:center" class="alert alert-danger"> <i class="fa fa-exclamation-circle"></i> '+e+"</div>")}function successFlash(e){$("#flashes").empty(),$("#flashes").append('<div style="text-align:center" class="alert alert-success"> <i class="fa fa-check-circle"></i> '+e+"</div>")}function modalError(e){$("#modal\\.flashes").empty().append('<div style="text-align:center" class="alert alert-danger"> <i class="fa fa-exclamation-circle"></i> '+e+"</div>")}function query(e,t,n,r){return $.ajax({url:"/api"+e+"?api_key="+user.api_key,async:r,method:t,data:JSON.stringify(n),dataType:"json",contentType:"application/json"})}function escapeHtml(e){return $("<div/>").text(e).html()}function unescapeHtml(e){return $("<div/>").html(e).text()}var capitalize=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},api={campaigns:{get:function(){return query("/campaigns/","GET",{},!1)},post:function(e){return query("/campaigns/","POST",e,!1)},summary:function(){return query("/campaigns/summary","GET",{},!1)}},campaignId:{get:function(e){return query("/campaigns/"+e,"GET",{},!0)},delete:function(e){return query("/campaigns/"+e,"DELETE",{},!1)},results:function(e){return query("/campaigns/"+e+"/results","GET",{},!0)},complete:function(e){return query("/campaigns/"+e+"/complete","GET",{},!0)},summary:function(e){return query("/campaigns/"+e+"/summary","GET",{},!0)}},groups:{get:function(){return query("/groups/","GET",{},!1)},post:function(e){return query("/groups/","POST",e,!1)},summary:function(){return query("/groups/summary","GET",{},!0)}},groupId:{get:function(e){return query("/groups/"+e,"GET",{},!1)},put:function(e){return query("/groups/"+e.id,"PUT",e,!1)},delete:function(e){return query("/groups/"+e,"DELETE",{},!1)}},templates:{get:function(){return query("/templates/","GET",{},!1)},post:function(e){return query("/templates/","POST",e,!1)}},templateId:{get:function(e){return query("/templates/"+e,"GET",{},!1)},put:function(e){return query("/templates/"+e.id,"PUT",e,!1)},delete:function(e){return query("/templates/"+e,"DELETE",{},!1)}},pages:{get:function(){return query("/pages/","GET",{},!1)},post:function(e){return query("/pages/","POST",e,!1)}},pageId:{get:function(e){return query("/pages/"+e,"GET",{},!1)},put:function(e){return query("/pages/"+e.id,"PUT",e,!1)},delete:function(e){return query("/pages/"+e,"DELETE",{},!1)}},SMTP:{get:function(){return query("/smtp/","GET",{},!1)},post:function(e){return query("/smtp/","POST",e,!1)}},SMTPId:{get:function(e){return query("/smtp/"+e,"GET",{},!1)},put:function(e){return query("/smtp/"+e.id,"PUT",e,!1)},delete:function(e){return query("/smtp/"+e,"DELETE",{},!1)}},import_email:function(e){return query("/import/email","POST",{},!1)},clone_site:function(e){return query("/import/site","POST",e,!1)},send_test_email:function(e){return query("/util/send_test_email","POST",e,!0)}};$(document).ready(function(){$.fn.dataTable.moment("MMMM Do YYYY, h:mm:ss a"),$('[data-toggle="tooltip"]').tooltip()});
function errorFlash(e){$("#flashes").empty(),$("#flashes").append('<div style="text-align:center" class="alert alert-danger"> <i class="fa fa-exclamation-circle"></i> '+e+"</div>")}function successFlash(e){$("#flashes").empty(),$("#flashes").append('<div style="text-align:center" class="alert alert-success"> <i class="fa fa-check-circle"></i> '+e+"</div>")}function modalError(e){$("#modal\\.flashes").empty().append('<div style="text-align:center" class="alert alert-danger"> <i class="fa fa-exclamation-circle"></i> '+e+"</div>")}function query(e,t,n,r){return $.ajax({url:"/api"+e+"?api_key="+user.api_key,async:r,method:t,data:JSON.stringify(n),dataType:"json",contentType:"application/json"})}function escapeHtml(e){return $("<div/>").text(e).html()}function unescapeHtml(e){return $("<div/>").html(e).text()}var capitalize=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},api={campaigns:{get:function(){return query("/campaigns/","GET",{},!1)},post:function(e){return query("/campaigns/","POST",e,!1)},summary:function(){return query("/campaigns/summary","GET",{},!1)}},campaignId:{get:function(e){return query("/campaigns/"+e,"GET",{},!0)},delete:function(e){return query("/campaigns/"+e,"DELETE",{},!1)},results:function(e){return query("/campaigns/"+e+"/results","GET",{},!0)},complete:function(e){return query("/campaigns/"+e+"/complete","GET",{},!0)},summary:function(e){return query("/campaigns/"+e+"/summary","GET",{},!0)}},groups:{get:function(){return query("/groups/","GET",{},!1)},post:function(e){return query("/groups/","POST",e,!1)},summary:function(){return query("/groups/summary","GET",{},!0)}},groupId:{get:function(e){return query("/groups/"+e,"GET",{},!1)},put:function(e){return query("/groups/"+e.id,"PUT",e,!1)},delete:function(e){return query("/groups/"+e,"DELETE",{},!1)}},templates:{get:function(){return query("/templates/","GET",{},!1)},post:function(e){return query("/templates/","POST",e,!1)}},templateId:{get:function(e){return query("/templates/"+e,"GET",{},!1)},put:function(e){return query("/templates/"+e.id,"PUT",e,!1)},delete:function(e){return query("/templates/"+e,"DELETE",{},!1)}},pages:{get:function(){return query("/pages/","GET",{},!1)},post:function(e){return query("/pages/","POST",e,!1)}},pageId:{get:function(e){return query("/pages/"+e,"GET",{},!1)},put:function(e){return query("/pages/"+e.id,"PUT",e,!1)},delete:function(e){return query("/pages/"+e,"DELETE",{},!1)}},SMTP:{get:function(){return query("/smtp/","GET",{},!1)},post:function(e){return query("/smtp/","POST",e,!1)}},SMTPId:{get:function(e){return query("/smtp/"+e,"GET",{},!1)},put:function(e){return query("/smtp/"+e.id,"PUT",e,!1)},delete:function(e){return query("/smtp/"+e,"DELETE",{},!1)}},import_email:function(e){return query("/import/email","POST",e,!1)},clone_site:function(e){return query("/import/site","POST",e,!1)},send_test_email:function(e){return query("/util/send_test_email","POST",e,!0)}};$(document).ready(function(){$.fn.dataTable.moment("MMMM Do YYYY, h:mm:ss a"),$('[data-toggle="tooltip"]').tooltip()});

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
function save(a){var e=[];$.each($("#targetsTable").DataTable().rows().data(),function(a,s){e.push({first_name:unescapeHtml(s[0]),last_name:unescapeHtml(s[1]),email:unescapeHtml(s[2]),position:unescapeHtml(s[3])})});var s={name:$("#name").val(),targets:e};a!=-1?(s.id=a,api.groupId.put(s).success(function(a){successFlash("Group updated successfully!"),load(),dismiss(),$("#modal").modal("hide")}).error(function(a){modalError(a.responseJSON.message)})):api.groups.post(s).success(function(a){successFlash("Group added successfully!"),load(),dismiss(),$("#modal").modal("hide")}).error(function(a){modalError(a.responseJSON.message)})}function dismiss(){$("#targetsTable").dataTable().DataTable().clear().draw(),$("#name").val(""),$("#modal\\.flashes").empty()}function edit(a){if(targets=$("#targetsTable").dataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]}),$("#modalSubmit").unbind("click").click(function(){save(a)}),a==-1);else api.groupId.get(a).success(function(a){$("#name").val(a.name),$.each(a.targets,function(a,e){targets.DataTable().row.add([escapeHtml(e.first_name),escapeHtml(e.last_name),escapeHtml(e.email),escapeHtml(e.position),'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>']).draw()})}).error(function(){errorFlash("Error fetching group")});$("#csvupload").fileupload({dataType:"json",add:function(a,e){$("#modal\\.flashes").empty();var s=/(csv|txt)$/i,t=e.originalFiles[0].name;return t&&!s.test(t.split(".").pop())?(modalError("Unsupported file extension (use .csv or .txt)"),!1):void e.submit()},done:function(a,e){$.each(e.result,function(a,e){addTarget(e.first_name,e.last_name,e.email,e.position)}),targets.DataTable().draw()}})}function deleteGroup(a){var e=groups.find(function(e){return e.id===a});return e?void(confirm("Delete "+e.name+"?")&&api.groupId.delete(a).success(function(a){successFlash(a.message),load()})):void console.log("wat")}function addTarget(a,e,s,t){var o=escapeHtml(s).toLowerCase(),r=[escapeHtml(a),escapeHtml(e),o,escapeHtml(t),'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>'],n=targets.DataTable(),i=n.column(2,{order:"index"}).data().indexOf(o);i>=0?n.row(i,{order:"index"}).data(r):n.row.add(r)}function load(){$("#groupTable").hide(),$("#emptyMessage").hide(),$("#loading").show(),api.groups.summary().success(function(a){if($("#loading").hide(),a.total>0){groups=a.groups,$("#emptyMessage").hide(),$("#groupTable").show();var e=$("#groupTable").DataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]});e.clear(),$.each(groups,function(a,s){e.row.add([escapeHtml(s.name),escapeHtml(s.num_targets),moment(s.modified_date).format("MMMM Do YYYY, h:mm:ss a"),"<div class='pull-right'><button class='btn btn-primary' data-toggle='modal' data-target='#modal' onclick='edit("+s.id+")'> <i class='fa fa-pencil'></i> </button> <button class='btn btn-danger' onclick='deleteGroup("+s.id+")'> <i class='fa fa-trash-o'></i> </button></div>"]).draw()})}else $("#emptyMessage").show()}).error(function(){errorFlash("Error fetching groups")})}var groups=[];$(document).ready(function(){load(),$("#targetForm").submit(function(){return addTarget($("#firstName").val(),$("#lastName").val(),$("#email").val(),$("#position").val()),targets.DataTable().draw(),$("#targetForm>div>input").val(""),$("#firstName").focus(),!1}),$("#targetsTable").on("click","span>i.fa-trash-o",function(){targets.DataTable().row($(this).parents("tr")).remove().draw()}),$("#modal").on("hide.bs.modal",function(){dismiss()})});
function save(a){var e=[];$.each($("#targetsTable").DataTable().rows().data(),function(a,s){e.push({first_name:unescapeHtml(s[0]),last_name:unescapeHtml(s[1]),email:unescapeHtml(s[2]),position:unescapeHtml(s[3])})});var s={name:$("#name").val(),targets:e};a!=-1?(s.id=a,api.groupId.put(s).success(function(a){successFlash("Group updated successfully!"),load(),dismiss(),$("#modal").modal("hide")}).error(function(a){modalError(a.responseJSON.message)})):api.groups.post(s).success(function(a){successFlash("Group added successfully!"),load(),dismiss(),$("#modal").modal("hide")}).error(function(a){modalError(a.responseJSON.message)})}function dismiss(){$("#targetsTable").dataTable().DataTable().clear().draw(),$("#name").val(""),$("#modal\\.flashes").empty()}function edit(a){if(targets=$("#targetsTable").dataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]}),$("#modalSubmit").unbind("click").click(function(){save(a)}),a==-1);else api.groupId.get(a).success(function(a){$("#name").val(a.name),$.each(a.targets,function(a,e){targets.DataTable().row.add([escapeHtml(e.first_name),escapeHtml(e.last_name),escapeHtml(e.email),escapeHtml(e.position),'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>']).draw()})}).error(function(){errorFlash("Error fetching group")});$("#csvupload").fileupload({url:"/api/import/group?api_key="+user.api_key,dataType:"json",add:function(a,e){$("#modal\\.flashes").empty();var s=/(csv|txt)$/i,t=e.originalFiles[0].name;return t&&!s.test(t.split(".").pop())?(modalError("Unsupported file extension (use .csv or .txt)"),!1):void e.submit()},done:function(a,e){$.each(e.result,function(a,e){addTarget(e.first_name,e.last_name,e.email,e.position)}),targets.DataTable().draw()}})}function deleteGroup(a){var e=groups.find(function(e){return e.id===a});return e?void(confirm("Delete "+e.name+"?")&&api.groupId.delete(a).success(function(a){successFlash(a.message),load()})):void console.log("wat")}function addTarget(a,e,s,t){var o=escapeHtml(s).toLowerCase(),r=[escapeHtml(a),escapeHtml(e),o,escapeHtml(t),'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>'],n=targets.DataTable(),i=n.column(2,{order:"index"}).data().indexOf(o);i>=0?n.row(i,{order:"index"}).data(r):n.row.add(r)}function load(){$("#groupTable").hide(),$("#emptyMessage").hide(),$("#loading").show(),api.groups.summary().success(function(a){if($("#loading").hide(),a.total>0){groups=a.groups,$("#emptyMessage").hide(),$("#groupTable").show();var e=$("#groupTable").DataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]});e.clear(),$.each(groups,function(a,s){e.row.add([escapeHtml(s.name),escapeHtml(s.num_targets),moment(s.modified_date).format("MMMM Do YYYY, h:mm:ss a"),"<div class='pull-right'><button class='btn btn-primary' data-toggle='modal' data-target='#modal' onclick='edit("+s.id+")'> <i class='fa fa-pencil'></i> </button> <button class='btn btn-danger' onclick='deleteGroup("+s.id+")'> <i class='fa fa-trash-o'></i> </button></div>"]).draw()})}else $("#emptyMessage").show()}).error(function(){errorFlash("Error fetching groups")})}var groups=[];$(document).ready(function(){load(),$("#targetForm").submit(function(){return addTarget($("#firstName").val(),$("#lastName").val(),$("#email").val(),$("#position").val()),targets.DataTable().draw(),$("#targetForm>div>input").val(""),$("#firstName").focus(),!1}),$("#targetsTable").on("click","span>i.fa-trash-o",function(){targets.DataTable().row($(this).parents("tr")).remove().draw()}),$("#modal").on("hide.bs.modal",function(){dismiss()})});

View File

@ -194,8 +194,8 @@ var api = {
}
},
// import handles all of the "import" functions in the api
import_email: function (raw) {
return query("/import/email", "POST", {}, false)
import_email: function (req) {
return query("/import/email", "POST", req, false)
},
// clone_site handles importing a site by url
clone_site: function (req) {

View File

@ -240,15 +240,9 @@ function importEmail() {
if (!raw) {
modalError("No Content Specified!")
} else {
$.ajax({
method: "POST",
url: "/api/import/email",
data: JSON.stringify({
api.import_email({
content: raw,
convert_links: convert_links
}),
dataType: "json",
contentType: "application/json"
})
.success(function (data) {
$("#text_editor").val(data.text)
@ -337,7 +331,8 @@ $(document).ready(function () {
if (
this.$element[0] !== e.target && !this.$element.has(e.target).length
// CKEditor compatibility fix start.
&& !$(e.target).closest('.cke_dialog, .cke').length
&&
!$(e.target).closest('.cke_dialog, .cke').length
// CKEditor compatibility fix end.
) {
this.$element.trigger('focus');

View File

@ -87,6 +87,7 @@ function edit(id) {
}
// Handle file uploads
$("#csvupload").fileupload({
url: "/api/import/group?api_key=" + user.api_key,
dataType: "json",
add: function (e, data) {
$("#modal\\.flashes").empty()
@ -112,7 +113,9 @@ function edit(id) {
}
function deleteGroup(id) {
var group = groups.find(function(x){return x.id === id})
var group = groups.find(function (x) {
return x.id === id
})
if (!group) {
console.log('wat');
return

View File

@ -3,24 +3,35 @@
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li><a href="/">Dashboard</a>
<li>
<a href="/">Dashboard</a>
</li>
<li><a href="/campaigns">Campaigns</a>
<li>
<a href="/campaigns">Campaigns</a>
</li>
<li class="active"><a href="/users">Users &amp; Groups</a>
<li class="active">
<a href="/users">Users &amp; Groups</a>
</li>
<li><a href="/templates">Email Templates</a>
<li>
<a href="/templates">Email Templates</a>
</li>
<li><a href="/landing_pages">Landing Pages</a>
<li>
<a href="/landing_pages">Landing Pages</a>
</li>
<li><a href="/sending_profiles">Sending Profiles</a>
<li>
<a href="/sending_profiles">Sending Profiles</a>
</li>
<li><a href="/settings">Settings</a>
<li>
<a href="/settings">Settings</a>
</li>
<li><hr></li>
<li><a href="https://gophish.gitbooks.io/user-guide/content/">User Guide</a>
<li>
<hr>
</li>
<li><a href="/api/">API Documentation</a>
<li>
<a href="https://gophish.gitbooks.io/user-guide/content/">User Guide</a>
</li>
<li>
<a href="/api/">API Documentation</a>
</li>
</ul>
</div>
@ -34,7 +45,8 @@
</div>
<div id="flashes" class="row"></div>
<div class="row">
<button type="button" class="btn btn-primary" onclick="edit(-1)" data-toggle="modal" data-target="#modal"><i class="fa fa-plus"></i> New Group</button>
<button type="button" class="btn btn-primary" onclick="edit(-1)" data-toggle="modal" data-target="#modal">
<i class="fa fa-plus"></i> New Group</button>
</div>
&nbsp;
<div id="loading">
@ -65,7 +77,9 @@
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title" id="groupModalLabel">New Group</h4>
</div>
<div class="modal-body">
@ -75,8 +89,9 @@
<input type="text" class="form-control" ng-model="group.name" placeholder="Group name" id="name" autofocus/>
</div>
<div class="form-group">
<span class="btn btn-danger btn-file" data-toggle="tooltip" data-placement="right" title="Supports CSV files" id="fileUpload"><i class="fa fa-plus"></i> Bulk Import Users
<input type="file" id="csvupload" data-url="/api/import/group" multiple>
<span class="btn btn-danger btn-file" data-toggle="tooltip" data-placement="right" title="Supports CSV files" id="fileUpload">
<i class="fa fa-plus"></i> Bulk Import Users
<input type="file" id="csvupload" multiple>
</span>
</div>
<div class="row">
@ -94,7 +109,8 @@
<input type="text" class="form-control" placeholder="Position" id="position">
</div>
<div class="col-sm-1">
<button type="submit" class="btn btn-danger btn-lg"><i class="fa fa-plus"></i> Add</button>
<button type="submit" class="btn btn-danger btn-lg">
<i class="fa fa-plus"></i> Add</button>
</div>
</form>
</div>
@ -118,7 +134,6 @@
</div>
</div>
</div>
{{end}}
{{define "scripts"}}
{{end}} {{define "scripts"}}
<script src="/js/dist/app/users.min.js"></script>
{{end}}