mirror of https://github.com/gophish/gophish
Compare commits
4 Commits
573efee7ca
...
f7991f7bf1
Author | SHA1 | Date |
---|---|---|
Paul Werther | f7991f7bf1 | |
Jordan Wright | 9561846979 | |
Caetan | 908886f2cd | |
Paul Werther | a79ce72535 |
|
@ -1,5 +1,7 @@
|
|||
name: CI
|
||||
on: [push]
|
||||
on:
|
||||
- pull_request
|
||||
- push
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
@ -7,17 +9,17 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goVer: [1.16, 1.17, 1.18]
|
||||
goVer: [1.21, 1.22, 1.23]
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.goVer }}
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.goVer }}
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
|
@ -31,4 +33,4 @@ jobs:
|
|||
run: diff -u <(echo -n) <(gofmt -d .)
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
run: go test ./...
|
||||
|
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14
|
||||
go-version: 1.22
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-multilib
|
||||
- if: matrix.arch == '386'
|
||||
|
@ -47,7 +47,7 @@ jobs:
|
|||
run: echo "RELEASE=gophish-${{ github.event.release.tag_name }}-${{ matrix.releaseos }}-64bit" >> $GITHUB_ENV
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: echo "RELEASE=gophish-${{ github.event.release.tag_name }}-${{ matrix.releaseos }}-64bit" | Out-File -FilePath $env:GITHUB_ENV -Append # https://github.com/actions/runner/issues/1636
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build ${{ matrix.goos }}/${{ matrix.arch }}
|
||||
run: go build -o ${{ matrix.bin }}
|
||||
env:
|
||||
|
@ -55,7 +55,7 @@ jobs:
|
|||
GOARCH: ${{ matrix.arch }}
|
||||
CGO_ENABLED: 1
|
||||
- name: Upload to artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.RELEASE }}
|
||||
path: ${{ matrix.bin }}
|
||||
|
@ -65,8 +65,8 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: bin
|
||||
- name: Package Releases
|
||||
|
@ -96,7 +96,7 @@ jobs:
|
|||
done
|
||||
done
|
||||
- name: Upload to artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: releases
|
||||
path: releases/*.zip
|
||||
|
@ -106,7 +106,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
needs: package
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: releases
|
||||
path: releases/
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
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]+}/summary", as.CampaignSummary)
|
||||
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/summary", as.GroupsSummary)
|
||||
router.HandleFunc("/groups/{id:[0-9]+}", as.Group)
|
||||
|
|
|
@ -109,6 +109,7 @@ func (as *Server) Users(w http.ResponseWriter, r *http.Request) {
|
|||
Role: role,
|
||||
RoleID: role.ID,
|
||||
PasswordChangeRequired: ur.PasswordChangeRequired,
|
||||
AccountLocked: ur.AccountLocked,
|
||||
}
|
||||
err = models.PutUser(&user)
|
||||
if err != nil {
|
||||
|
|
|
@ -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
|
|
@ -115,8 +115,8 @@ func (im *Monitor) Shutdown() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// checkForNewEmails logs into an IMAP account and checks unread emails
|
||||
// for the rid campaign identifier.
|
||||
// checkForNewEmails logs into an IMAP account and checks unread emails for the
|
||||
// rid campaign identifier.
|
||||
func checkForNewEmails(im models.IMAP) {
|
||||
im.Host = im.Host + ":" + strconv.Itoa(int(im.Port)) // Append port
|
||||
mailServer := Mailbox{
|
||||
|
|
|
@ -74,12 +74,13 @@ type CampaignStats struct {
|
|||
// Event contains the fields for an event
|
||||
// that occurs during the campaign
|
||||
type Event struct {
|
||||
Id int64 `json:"-"`
|
||||
Id int64 `json:"id"`
|
||||
CampaignId int64 `json:"campaign_id"`
|
||||
Email string `json:"email"`
|
||||
Time time.Time `json:"time"`
|
||||
Message string `json:"message"`
|
||||
Details string `json:"details"`
|
||||
FalsePositive bool `json:"false_positive"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
func AddEvent(e *Event, campaignID int64) error {
|
||||
e.CampaignId = campaignID
|
||||
|
|
|
@ -742,3 +742,9 @@ table.dataTable {
|
|||
#password-strength-container {
|
||||
height: 40px;
|
||||
}
|
||||
.ms-1 {
|
||||
margin-left: 1em;
|
||||
}
|
||||
.mt-1 {
|
||||
margin-top: 1em;
|
||||
}
|
|
@ -42,6 +42,12 @@ var statuses = {
|
|||
icon: "fa-exclamation",
|
||||
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
|
||||
"Email Reported": {
|
||||
color: "#45d6ef",
|
||||
|
@ -236,6 +242,56 @@ function exportAsCSV(scope) {
|
|||
$("#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) {
|
||||
request = campaign.timeline[event_idx]
|
||||
details = JSON.parse(request.details)
|
||||
|
@ -400,7 +456,13 @@ function renderTimeline(data) {
|
|||
}
|
||||
if (event.message == "Submitted Data") {
|
||||
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>'
|
||||
}
|
||||
if (details.payload) {
|
||||
|
|
|
@ -255,6 +255,12 @@ var api = {
|
|||
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: {
|
||||
get: function() {
|
||||
return query("/webhooks/", "GET", {}, false)
|
||||
|
|
Loading…
Reference in New Issue