Skip to content

Commit

Permalink
Merge pull request #6 from domwhewell-sage/master
Browse files Browse the repository at this point in the history
Enhancement: Add ghostwriter profile
  • Loading branch information
t94j0 committed May 9, 2024
2 parents 453d784 + 568b83f commit 753bccf
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 0 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ email:
# Email host address
host_addr: smtp.gmail.com:587

# Ghostwriter Profile
ghostwriter:
# Ghostwriter graphql endpoint
graphql_endpoint: 'https://ghostwriter/v1/graphql'
# Ghostwriter API key
api_key: 'deadbeef'
# Oplog ID
oplog_id: 1
# (Optional) Disable email, username, and credentials from being sent to ghostwriter
disable_credentials: true
# Ignore SSL Self Signed error
ignore_self_signed_certificate: true

# You can also supply an email template for each notification
email_submitted_credentials_template: |
Someone submitted credentials!
Expand Down
15 changes: 15 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ Email Address - {{ .Email }}
IP Address - {{ .Address }}
User Agent - {{ .UserAgent }}`

var defaultgraphqlTemplate = `mutation InsertGophishLog ($oplog: bigint!, $sourceIp: String, $tool: String, $userContext: String, $description: String, $output: String, $comments: String, $extraFields: jsonb!) {
insert_oplogEntry(objects: {oplog: $oplog, sourceIp: $sourceIp, tool: $tool, userContext: $userContext, description: $description, comments: $comments, output: $output, extraFields: $extraFields}) {
returning {
id
}
}
}`

func init() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
Expand All @@ -48,6 +56,7 @@ func setDefaults() {
viper.SetDefault("email_send_click_template", defaultClickedTemplate)
viper.SetDefault("email_submitted_credentials_template", defaultSubmittedCredentailsTemplate)
viper.SetDefault("email_default_email_open_template", defaultEmailOpenedTemplate)
viper.SetDefault("graphql_default_query", defaultgraphqlTemplate)
viper.SetDefault("profiles", []string{"slack"})
}

Expand Down Expand Up @@ -87,6 +96,12 @@ func validateConfig() {
viper.GetString("email.recipient"))
continue
}
if profile == "ghostwriter" {
ghostwriterConfigs := []string{"ghostwriter.graphql_endpoint", "ghostwriter.api_key", "ghostwriter.oplog_id"}
checkKeysExist(ghostwriterConfigs...)
log.Infof("Using Ghostwriter sending profile. Will send messages to %s", viper.GetString("ghostwriter.graphql_endpoint"))
continue
}
log.Fatalf("Profile \"%s\" does not exist", profile)
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.12
require (
github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960
github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 // indirect
github.com/machinebox/graphql v0.2.2 // indirect
github.com/parnurzeal/gorequest v0.2.16 // indirect
github.com/sirupsen/logrus v1.8.1
github.com/spf13/viper v1.8.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo=
github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
Expand Down
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ func handler(w http.ResponseWriter, r *http.Request) {
return
}
}
if profile == "ghostwriter" {
if err := sender.SendGraphql(); err != nil {
log.Error(err)
return
}
}
}

w.WriteHeader(http.StatusNoContent)
Expand Down
39 changes: 39 additions & 0 deletions messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"html/template"
"net/url"
"strconv"
"strings"

"github.com/ashwanthkumar/slack-go-webhook"
Expand All @@ -21,6 +22,7 @@ var (
type Sender interface {
SendSlack() error
SendEmail() error
SendGraphql() error
}

func senderDispatch(status string, webhookResponse WebhookResponse, response []byte) (Sender, error) {
Expand Down Expand Up @@ -130,6 +132,21 @@ func (w SubmittedDetails) SendEmail() error {
return sendEmail("PhishBot - Credentials Submitted", body)
}

func (w SubmittedDetails) SendGraphql() error {
var output string
if !viper.GetBool("ghostwriter.disable_credentials") {
output = "\nUsername: " + w.Username + "\nPassword: " + w.Password
}
oplog_entry := ghostwriterOplogEntry{
SourceIp: w.Address,
UserContext: w.Email,
Description: "User ID: " + w.ID + "\nCampaign ID: " + strconv.FormatUint(uint64(w.CampaignID), 10),
Output: output,
Comments: SubmittedData,
}
return sendGraphql(oplog_entry)
}

type ClickDetails struct {
CampaignID uint
ID string
Expand Down Expand Up @@ -175,6 +192,17 @@ func (w ClickDetails) SendEmail() error {
return sendEmail("PhishBot - Email Clicked", body)
}

func (w ClickDetails) SendGraphql() error {
oplog_entry := ghostwriterOplogEntry{
SourceIp: w.Address,
UserContext: w.Email,
Description: "User ID: " + w.ID + "\nCampaign ID: " + strconv.FormatUint(uint64(w.CampaignID), 10),
Output: "UserAgent: " + w.UserAgent,
Comments: ClickedLink,
}
return sendGraphql(oplog_entry)
}

func getEmailBody(templateValue string, obj interface{}) (string, error) {
out := new(strings.Builder)
tpl, err := template.New("email").Parse(templateValue)
Expand Down Expand Up @@ -231,3 +259,14 @@ func (w OpenedDetails) SendEmail() error {
}
return sendEmail("PhishBot - Email Opened", body)
}

func (w OpenedDetails) SendGraphql() error {
oplog_entry := ghostwriterOplogEntry{
SourceIp: w.Address,
UserContext: w.Email,
Description: "User ID: " + w.ID + "\nCampaign ID: " + strconv.FormatUint(uint64(w.CampaignID), 10),
Output: "UserAgent: " + w.UserAgent,
Comments: EmailOpened,
}
return sendGraphql(oplog_entry)
}
59 changes: 59 additions & 0 deletions sending_helpers.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
package main

import (
"context"
"crypto/tls"
"fmt"
"net/http"
"net/smtp"

"github.com/ashwanthkumar/slack-go-webhook"
"github.com/machinebox/graphql"
"github.com/spf13/viper"
)

type ghostwriterOplogEntry struct {
Oplog int
StartDate string
EndDate string
SourceIp string
DestIp string
Tool string
UserContext string
Command string
Description string
Output string
Comments string
OperatorName string
EntryIdentifier string
ExtraFields string
}

func sendSlackAttachment(attachment slack.Attachment) error {
payload := slack.Payload{
Username: viper.GetString("slack.bot_username"),
Expand Down Expand Up @@ -36,3 +57,41 @@ func sendEmail(subject, body string) error {
}
return nil
}

func sendGraphql(data ghostwriterOplogEntry) error {
url := viper.GetString("ghostwriter.graphql_endpoint")
apiKey := viper.GetString("ghostwriter.api_key")
ignoreSelfSigned := viper.GetBool("ghostwriter.ignore_self_signed_certificate")
query := viper.GetString("graphql_default_query")
oplogId := viper.GetInt("ghostwriter.oplog_id")

var client *graphql.Client
if ignoreSelfSigned {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
httpClient := &http.Client{Transport: tr}
client = graphql.NewClient(url, graphql.WithHTTPClient(httpClient))
} else {
client = graphql.NewClient(url)
}

req := graphql.NewRequest(query)
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Var("oplog", oplogId)
req.Var("sourceIp", data.SourceIp)
req.Var("tool", "gophish")
req.Var("userContext", data.UserContext)
req.Var("description", data.Description)
req.Var("output", data.Output)
req.Var("comments", data.Comments)
req.Var("extraFields", "")

ctx := context.Background()
var respData map[string]interface{}
if err := client.Run(ctx, req, &respData); err != nil {
return err
}

return nil
}

0 comments on commit 753bccf

Please sign in to comment.