mirror of https://github.com/gophish/gophish
implement mark submitted data as false positive, reference #1915
parent
db63ee978d
commit
a79ce72535
|
@ -135,3 +135,18 @@ func (as *Server) CampaignComplete(w http.ResponseWriter, r *http.Request) {
|
||||||
JSONResponse(w, models.Response{Success: true, Message: "Campaign completed successfully!"}, http.StatusOK)
|
JSONResponse(w, models.Response{Success: true, Message: "Campaign completed successfully!"}, http.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (as *Server) FalsePositive(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
id, _ := strconv.ParseInt(vars["id"], 0, 64)
|
||||||
|
rid, _ := vars["rid"]
|
||||||
|
switch {
|
||||||
|
case r.Method == "GET":
|
||||||
|
err := models.MarkEvent(id, rid)
|
||||||
|
if err != nil {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "Error marking event as false positive"}, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
JSONResponse(w, models.Response{Success: true, Message: "Event marked as false positive!"}, http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ func (as *Server) registerRoutes() {
|
||||||
router.HandleFunc("/campaigns/{id:[0-9]+}/results", as.CampaignResults)
|
router.HandleFunc("/campaigns/{id:[0-9]+}/results", as.CampaignResults)
|
||||||
router.HandleFunc("/campaigns/{id:[0-9]+}/summary", as.CampaignSummary)
|
router.HandleFunc("/campaigns/{id:[0-9]+}/summary", as.CampaignSummary)
|
||||||
router.HandleFunc("/campaigns/{id:[0-9]+}/complete", as.CampaignComplete)
|
router.HandleFunc("/campaigns/{id:[0-9]+}/complete", as.CampaignComplete)
|
||||||
|
router.HandleFunc("/falsepositive/{id:[0-9]+}/rid/{rid:[a-zA-Z0-9]+}", as.FalsePositive)
|
||||||
router.HandleFunc("/groups/", as.Groups)
|
router.HandleFunc("/groups/", as.Groups)
|
||||||
router.HandleFunc("/groups/summary", as.GroupsSummary)
|
router.HandleFunc("/groups/summary", as.GroupsSummary)
|
||||||
router.HandleFunc("/groups/{id:[0-9]+}", as.Group)
|
router.HandleFunc("/groups/{id:[0-9]+}", as.Group)
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
-- +goose Up
|
||||||
|
-- SQL in section 'Up' is executed when this migration is applied
|
||||||
|
ALTER TABLE `events` ADD COLUMN false_positive BOOLEAN DEFAULT 0;
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- SQL section 'Down' is executed when this migration is rolled back
|
|
@ -0,0 +1,6 @@
|
||||||
|
-- +goose Up
|
||||||
|
-- SQL in section 'Up' is executed when this migration is applied
|
||||||
|
ALTER TABLE events ADD COLUMN false_positive BOOLEAN DEFAULT 0;
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- SQL section 'Down' is executed when this migration is rolled back
|
|
@ -74,12 +74,13 @@ type CampaignStats struct {
|
||||||
// Event contains the fields for an event
|
// Event contains the fields for an event
|
||||||
// that occurs during the campaign
|
// that occurs during the campaign
|
||||||
type Event struct {
|
type Event struct {
|
||||||
Id int64 `json:"-"`
|
Id int64 `json:"id"`
|
||||||
CampaignId int64 `json:"campaign_id"`
|
CampaignId int64 `json:"campaign_id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Details string `json:"details"`
|
Details string `json:"details"`
|
||||||
|
FalsePositive bool `json:"false_positive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventDetails is a struct that wraps common attributes we want to store
|
// EventDetails is a struct that wraps common attributes we want to store
|
||||||
|
@ -154,6 +155,33 @@ func (c *Campaign) UpdateStatus(s string) error {
|
||||||
return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error
|
return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the False Positive-field for the specified eventid to true.
|
||||||
|
// Checks if all Events with the specific email in that campaign where data has been submitted are set to false_positive "true" and if thats the case changes
|
||||||
|
// the status in the "results" table for that recipient back to "Clicked Link" hence it won´t be count in the Submitted statistic until new data is submitted
|
||||||
|
func MarkEvent(eveId int64, rid string) error {
|
||||||
|
var mailcount int64
|
||||||
|
var fpcount int64
|
||||||
|
s := Event{}
|
||||||
|
query := db.Table("events").Where("id=?", eveId).Update("false_positive", true).Error
|
||||||
|
if query != nil {
|
||||||
|
log.Errorf("Problem editing false_positive in database: Table \"events\" on id %d", eveId)
|
||||||
|
}
|
||||||
|
mailquery := db.Table("events").Where("id = ?", eveId)
|
||||||
|
mailquery.Select("id, campaign_id, email, time, message, details, false_positive")
|
||||||
|
err := mailquery.Scan(&s).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//Counter to check if all Data Submitted is flaged as false/positive
|
||||||
|
db.Table("events").Select("Count(*)").Where("email = ? AND message = ?", s.Email, "Submitted Data").Count(&mailcount)
|
||||||
|
db.Table("events").Select("Count(*)").Where("email = ? AND message = ? AND false_positive = ?", s.Email, "Submitted Data", true).Count(&fpcount)
|
||||||
|
if mailcount == fpcount {
|
||||||
|
return db.Table("results").Where("campaign_id = ? AND r_id = ?", s.CampaignId, rid).Update("status", "Clicked Link").Error
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
// AddEvent creates a new campaign event in the database
|
// AddEvent creates a new campaign event in the database
|
||||||
func AddEvent(e *Event, campaignID int64) error {
|
func AddEvent(e *Event, campaignID int64) error {
|
||||||
e.CampaignId = campaignID
|
e.CampaignId = campaignID
|
||||||
|
|
|
@ -742,3 +742,9 @@ table.dataTable {
|
||||||
#password-strength-container {
|
#password-strength-container {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
.ms-1 {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
.mt-1 {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
|
@ -42,6 +42,12 @@ var statuses = {
|
||||||
icon: "fa-exclamation",
|
icon: "fa-exclamation",
|
||||||
point: "ct-point-clicked"
|
point: "ct-point-clicked"
|
||||||
},
|
},
|
||||||
|
"True/False":{
|
||||||
|
color: "#6c7a89",
|
||||||
|
label: "label-info",
|
||||||
|
icon: "fa-eraser",
|
||||||
|
point: "ct-point-true_false"
|
||||||
|
},
|
||||||
//not a status, but is used for the campaign timeline and user timeline
|
//not a status, but is used for the campaign timeline and user timeline
|
||||||
"Email Reported": {
|
"Email Reported": {
|
||||||
color: "#45d6ef",
|
color: "#45d6ef",
|
||||||
|
@ -236,6 +242,56 @@ function exportAsCSV(scope) {
|
||||||
$("#exportButton").html(exportHTML)
|
$("#exportButton").html(exportHTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function false_positive(rid, eventid, cid, repo) {
|
||||||
|
if(repo === 'true'){
|
||||||
|
Swal.fire({
|
||||||
|
title: "Are you sure?",
|
||||||
|
text: "This will flag the submitted data as False Positive and substract it from the count. Please make sure that they are invalid!",
|
||||||
|
type: "question",
|
||||||
|
animation: false,
|
||||||
|
showCancelButton: true,
|
||||||
|
reverseButtons: true,
|
||||||
|
allowOutsideClick: false,
|
||||||
|
showLoaderOnConfirm: true
|
||||||
|
}).then(function (result) {
|
||||||
|
if (result.value){
|
||||||
|
api.eventId.falsepositive(eventid, rid).success((function() {
|
||||||
|
refresh();
|
||||||
|
} ));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else{ // will be called if Reported is not marked
|
||||||
|
Swal.fire({
|
||||||
|
title: "Are you sure?",
|
||||||
|
text: "This will flag the submitted data as False Positive and substract it from the count. Please make sure that they are invalid!",
|
||||||
|
type: "question",
|
||||||
|
animation: false,
|
||||||
|
showCancelButton: true,
|
||||||
|
input: 'checkbox',
|
||||||
|
inputValue: 1,
|
||||||
|
inputPlaceholder: 'Mark as reported too',
|
||||||
|
confirmButtonText: "Continue",
|
||||||
|
confirmButtonColor: "#428bca",
|
||||||
|
reverseButtons: true,
|
||||||
|
allowOutsideClick: false,
|
||||||
|
showLoaderOnConfirm: true
|
||||||
|
}).then(function (result) {
|
||||||
|
if (result.value && !result.reported){ // Reports the rid as Reported and the submitted data as false positive
|
||||||
|
report_mail(escapeHtml(rid), cid);
|
||||||
|
api.eventId.falsepositive(eventid, rid).success((function() {
|
||||||
|
refresh();
|
||||||
|
} ));
|
||||||
|
} else if (result.value === 0){ // Marks the Event with the submitted data as false positive
|
||||||
|
api.eventId.falsepositive(eventid, rid).success((function() {
|
||||||
|
refresh();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function replay(event_idx) {
|
function replay(event_idx) {
|
||||||
request = campaign.timeline[event_idx]
|
request = campaign.timeline[event_idx]
|
||||||
details = JSON.parse(request.details)
|
details = JSON.parse(request.details)
|
||||||
|
@ -400,7 +456,13 @@ function renderTimeline(data) {
|
||||||
}
|
}
|
||||||
if (event.message == "Submitted Data") {
|
if (event.message == "Submitted Data") {
|
||||||
results += '<div class="timeline-replay-button"><button onclick="replay(' + i + ')" class="btn btn-success">'
|
results += '<div class="timeline-replay-button"><button onclick="replay(' + i + ')" class="btn btn-success">'
|
||||||
results += '<i class="fa fa-refresh"></i> Replay Credentials</button></div>'
|
results += '<i class="fa fa-refresh"></i> Replay Credentials</button>'
|
||||||
|
if(campaign.timeline[i].false_positive === true){
|
||||||
|
results += '<div class="alert alert-warning mt-1" role="alert">Marked as False Positive!</div></div>'
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
results += '<button onclick="false_positive(\'' + record.id + '\',\'' + campaign.timeline[i].id + '\',\'' + campaign.id + '\',\'' + record.reported + '\')" id="false-positive-button" class="btn btn-warning ms-1"><i class="fa fa-ban"></i> False Positive</button></div>'
|
||||||
|
}
|
||||||
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>'
|
||||||
}
|
}
|
||||||
if (details.payload) {
|
if (details.payload) {
|
||||||
|
|
|
@ -255,6 +255,12 @@ var api = {
|
||||||
return query("/users/" + id, "DELETE", {}, true)
|
return query("/users/" + id, "DELETE", {}, true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
eventId:{
|
||||||
|
//marks an event as false positiv in the database
|
||||||
|
falsepositive: function (eventid, rid) {
|
||||||
|
return query("/falsepositive/" + eventid + "/rid/" + rid, "GET", {}, true)
|
||||||
|
}
|
||||||
|
},
|
||||||
webhooks: {
|
webhooks: {
|
||||||
get: function() {
|
get: function() {
|
||||||
return query("/webhooks/", "GET", {}, false)
|
return query("/webhooks/", "GET", {}, false)
|
||||||
|
|
Loading…
Reference in New Issue