mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-08-21 09:57:15 +02:00
add option to add data to webhook events
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
@@ -979,11 +979,23 @@ func (s *Server) checkAndServePhishingPage(
|
||||
clientIP := vo.NewOptionalString64Must(utils.ExtractClientIP(c.Request))
|
||||
userAgent := vo.NewOptionalString255Must(utils.Substring(c.Request.UserAgent(), 0, MAX_USER_AGENT_SAVED))
|
||||
submittedData := vo.NewEmptyOptionalString1MB()
|
||||
|
||||
// prepare submitted data for webhook
|
||||
var webhookData map[string]interface{}
|
||||
if campaign.SaveSubmittedData.MustGet() {
|
||||
submittedData, err = vo.NewOptionalString1MB(c.Request.PostForm.Encode())
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("user submitted phishing data too large: %s", err)
|
||||
}
|
||||
// convert form data to map for webhook
|
||||
webhookData = make(map[string]interface{})
|
||||
for key, values := range c.Request.PostForm {
|
||||
if len(values) == 1 {
|
||||
webhookData[key] = values[0]
|
||||
} else {
|
||||
webhookData[key] = values
|
||||
}
|
||||
}
|
||||
}
|
||||
var event *model.CampaignEvent
|
||||
// only save data if red team flag is set
|
||||
@@ -1058,6 +1070,7 @@ func (s *Server) checkAndServePhishingPage(
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
data.EVENT_CAMPAIGN_RECIPIENT_SUBMITTED_DATA,
|
||||
webhookData,
|
||||
)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to handle webhook: %s", err)
|
||||
@@ -1317,6 +1330,7 @@ func (s *Server) checkAndServePhishingPage(
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
eventName,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Errorw("failed to handle webhook for Proxy page",
|
||||
@@ -1629,6 +1643,7 @@ func (s *Server) checkAndServePhishingPage(
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
eventName,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to handle webhook: %s", err)
|
||||
@@ -1836,6 +1851,7 @@ func (s *Server) renderDenyPage(
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
data.EVENT_CAMPAIGN_RECIPIENT_DENY_PAGE_VISITED,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Errorw("failed to handle webhook for deny page visit",
|
||||
|
||||
@@ -44,6 +44,7 @@ type Campaign struct {
|
||||
IsAnonymous bool `gorm:"not null;default:false"`
|
||||
IsTest bool `gorm:"not null;default:false"`
|
||||
Obfuscate bool `gorm:"not null;default:false"`
|
||||
WebhookIncludeData bool `gorm:"not null;default:false"`
|
||||
|
||||
// has one
|
||||
CampaignTemplateID *uuid.UUID `gorm:"index;type:uuid;"`
|
||||
|
||||
@@ -38,6 +38,7 @@ type Campaign struct {
|
||||
IsAnonymous nullable.Nullable[bool] `json:"isAnonymous"`
|
||||
IsTest nullable.Nullable[bool] `json:"isTest"`
|
||||
Obfuscate nullable.Nullable[bool] `json:"obfuscate"`
|
||||
WebhookIncludeData nullable.Nullable[bool] `json:"webhookIncludeData"`
|
||||
TemplateID nullable.Nullable[uuid.UUID] `json:"templateID"`
|
||||
Template *CampaignTemplate `json:"template"`
|
||||
CompanyID nullable.Nullable[uuid.UUID] `json:"companyID"`
|
||||
@@ -332,6 +333,12 @@ func (c *Campaign) ToDBMap() map[string]any {
|
||||
m["obfuscate"] = v
|
||||
}
|
||||
}
|
||||
if c.WebhookIncludeData.IsSpecified() {
|
||||
m["webhook_include_data"] = false
|
||||
if v, err := c.WebhookIncludeData.Get(); err == nil {
|
||||
m["webhook_include_data"] = v
|
||||
}
|
||||
}
|
||||
if c.TemplateID.IsSpecified() {
|
||||
m["campaign_template_id"] = nil
|
||||
if v, err := c.TemplateID.Get(); err == nil {
|
||||
|
||||
+15
-3
@@ -1584,7 +1584,11 @@ func (m *ProxyHandler) handlePathBasedCapture(capture service.ProxyServiceCaptur
|
||||
m.checkCaptureCompletion(session, capture.Name)
|
||||
|
||||
if session.CampaignRecipientID != nil && session.CampaignID != nil {
|
||||
m.createCampaignSubmitEvent(session, capturedData, resp.Request)
|
||||
// convert to map[string]interface{} for webhook
|
||||
webhookData := map[string]interface{}{
|
||||
capture.Name: capturedData,
|
||||
}
|
||||
m.createCampaignSubmitEvent(session, webhookData, resp.Request)
|
||||
}
|
||||
|
||||
// check if cookie bundle should be submitted now that this capture is complete
|
||||
@@ -1682,7 +1686,11 @@ func (m *ProxyHandler) captureFromText(text string, capture service.ProxyService
|
||||
|
||||
// submit non-cookie captures immediately
|
||||
if capture.From != "cookie" && session.CampaignRecipientID != nil && session.CampaignID != nil {
|
||||
m.createCampaignSubmitEvent(session, capturedData, req)
|
||||
// convert to map[string]interface{} for webhook
|
||||
webhookData := map[string]interface{}{
|
||||
capture.Name: capturedData,
|
||||
}
|
||||
m.createCampaignSubmitEvent(session, webhookData, req)
|
||||
}
|
||||
|
||||
// check if we should submit cookie bundle (only when all captures complete)
|
||||
@@ -2643,7 +2651,7 @@ func (m *ProxyHandler) buildCampaignFlowRedirectURL(session *service.ProxySessio
|
||||
return targetURL
|
||||
}
|
||||
|
||||
func (m *ProxyHandler) createCampaignSubmitEvent(session *service.ProxySession, capturedData interface{}, req *http.Request) {
|
||||
func (m *ProxyHandler) createCampaignSubmitEvent(session *service.ProxySession, capturedData map[string]interface{}, req *http.Request) {
|
||||
if session.CampaignID == nil || session.CampaignRecipientID == nil {
|
||||
return
|
||||
}
|
||||
@@ -2714,6 +2722,7 @@ func (m *ProxyHandler) createCampaignSubmitEvent(session *service.ProxySession,
|
||||
session.CampaignID,
|
||||
session.RecipientID,
|
||||
data.EVENT_CAMPAIGN_RECIPIENT_SUBMITTED_DATA,
|
||||
capturedData,
|
||||
)
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to handle webhook for MITM proxy submit",
|
||||
@@ -3442,6 +3451,7 @@ func (m *ProxyHandler) registerPageVisitEvent(req *http.Request, session *servic
|
||||
session.CampaignID,
|
||||
session.RecipientID,
|
||||
eventName,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to handle webhook for MITM page visit",
|
||||
@@ -3972,6 +3982,7 @@ func (m *ProxyHandler) registerDenyPageVisitEventDirect(req *http.Request, reqCt
|
||||
campaignID,
|
||||
recipientID,
|
||||
data.EVENT_CAMPAIGN_RECIPIENT_DENY_PAGE_VISITED,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to handle webhook for deny page visit",
|
||||
@@ -4044,6 +4055,7 @@ func (m *ProxyHandler) registerEvasionPageVisitEventDirect(req *http.Request, re
|
||||
campaignID,
|
||||
recipientID,
|
||||
data.EVENT_CAMPAIGN_RECIPIENT_EVASION_PAGE_VISITED,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to handle webhook for evasion page visit",
|
||||
|
||||
@@ -1487,6 +1487,7 @@ func ToCampaign(row *database.Campaign) (*model.Campaign, error) {
|
||||
isAnonymous := nullable.NewNullableWithValue(row.IsAnonymous)
|
||||
isTest := nullable.NewNullableWithValue(row.IsTest)
|
||||
obfuscate := nullable.NewNullableWithValue(row.Obfuscate)
|
||||
webhookIncludeData := nullable.NewNullableWithValue(row.WebhookIncludeData)
|
||||
var templateID nullable.Nullable[uuid.UUID]
|
||||
if row.CampaignTemplateID != nil {
|
||||
templateID = nullable.NewNullableWithValue(*row.CampaignTemplateID)
|
||||
@@ -1615,6 +1616,7 @@ func ToCampaign(row *database.Campaign) (*model.Campaign, error) {
|
||||
IsAnonymous: isAnonymous,
|
||||
IsTest: isTest,
|
||||
Obfuscate: obfuscate,
|
||||
WebhookIncludeData: webhookIncludeData,
|
||||
TemplateID: templateID,
|
||||
Template: template,
|
||||
RecipientGroups: recipientGroups,
|
||||
|
||||
@@ -1158,6 +1158,7 @@ func (c *Campaign) SaveTrackingPixelLoaded(
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
data.EVENT_CAMPAIGN_RECIPIENT_MESSAGE_READ,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
@@ -1274,6 +1275,9 @@ func (c *Campaign) UpdateByID(
|
||||
if v, err := incoming.Obfuscate.Get(); err == nil {
|
||||
current.Obfuscate.Set(v)
|
||||
}
|
||||
if v, err := incoming.WebhookIncludeData.Get(); err == nil {
|
||||
current.WebhookIncludeData.Set(v)
|
||||
}
|
||||
if v, err := incoming.SortField.Get(); err == nil {
|
||||
current.SortField.Set(v)
|
||||
}
|
||||
@@ -2272,6 +2276,7 @@ func (c *Campaign) saveSendingResult(
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
eventName,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
@@ -2320,6 +2325,7 @@ func (c *Campaign) saveEventCampaignClose(
|
||||
campaignID,
|
||||
nil,
|
||||
data.EVENT_CAMPAIGN_CLOSED,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
@@ -3023,6 +3029,7 @@ func (c *Campaign) SetSentAtByCampaignRecipientID(
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
data.EVENT_CAMPAIGN_RECIPIENT_MESSAGE_SENT,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
@@ -3038,6 +3045,7 @@ func (c *Campaign) HandleWebhook(
|
||||
campaignID *uuid.UUID,
|
||||
recipientID *uuid.UUID,
|
||||
eventName string,
|
||||
capturedData map[string]interface{},
|
||||
) error {
|
||||
campaignName, err := c.CampaignRepository.GetNameByID(ctx, campaignID)
|
||||
if err != nil {
|
||||
@@ -3051,11 +3059,25 @@ func (c *Campaign) HandleWebhook(
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
// check if campaign has webhookIncludeData enabled
|
||||
campaign, err := c.CampaignRepository.GetByID(ctx, campaignID, &repository.CampaignOption{})
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
// only include captured data if webhookIncludeData is enabled
|
||||
var dataToSend map[string]interface{}
|
||||
if campaign.WebhookIncludeData.MustGet() {
|
||||
dataToSend = capturedData
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
webhookReq := WebhookRequest{
|
||||
Time: &now,
|
||||
CampaignName: campaignName,
|
||||
Event: eventName,
|
||||
Data: dataToSend,
|
||||
}
|
||||
if email != nil {
|
||||
webhookReq.Email = email.String()
|
||||
|
||||
@@ -387,8 +387,9 @@ func (w *Webhook) Send(
|
||||
}
|
||||
|
||||
type WebhookRequest struct {
|
||||
Time *time.Time `json:"time"`
|
||||
CampaignName string `json:"campaignName"`
|
||||
Email string `json:"email"`
|
||||
Event string `json:"event"`
|
||||
Time *time.Time `json:"time"`
|
||||
CampaignName string `json:"campaignName"`
|
||||
Email string `json:"email"`
|
||||
Event string `json:"event"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user