├── power-automate
├── images
│ ├── diagram.png
│ ├── flow-overview.png
│ ├── reccuring-flow.png
│ ├── reminder-flow.png
│ ├── sharepoint-list.png
│ ├── you-got-phished.gif
│ ├── how-many-reminders.png
│ ├── training-completed.png
│ └── you-found-the-phish.gif
└── Office365-Power-Automate.md
├── getusers.sh
├── gophish-parser
├── src
│ ├── go.mod
│ ├── main.go
│ └── go.sum
└── gophish-parser.yaml
├── gophish.ini
├── getusergroups.ps1
├── GoPhishValidate.swagger.json
├── README.md
├── gophishautomationstop.py
└── gophishautomationstart.py
/power-automate/images/diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/diagram.png
--------------------------------------------------------------------------------
/power-automate/images/flow-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/flow-overview.png
--------------------------------------------------------------------------------
/power-automate/images/reccuring-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/reccuring-flow.png
--------------------------------------------------------------------------------
/power-automate/images/reminder-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/reminder-flow.png
--------------------------------------------------------------------------------
/power-automate/images/sharepoint-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/sharepoint-list.png
--------------------------------------------------------------------------------
/power-automate/images/you-got-phished.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/you-got-phished.gif
--------------------------------------------------------------------------------
/power-automate/images/how-many-reminders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/how-many-reminders.png
--------------------------------------------------------------------------------
/power-automate/images/training-completed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/training-completed.png
--------------------------------------------------------------------------------
/power-automate/images/you-found-the-phish.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwtechnologies/dw-gophish-automation/HEAD/power-automate/images/you-found-the-phish.gif
--------------------------------------------------------------------------------
/getusers.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | userpath=$(awk -v TARGET=localsys -F ' *= *' '{ if ($0 ~ /^\[.*\]$/) { gsub(/^\[|\]$/, "", $0); SECTION=$0 } else if (($2 != "") && (SECTION==TARGET)) { print $2 "" }}' gophish.ini)
4 |
5 | aws s3 cp --recursive s3://bucketname/csvfile $userpath
6 |
7 |
--------------------------------------------------------------------------------
/gophish-parser/src/go.mod:
--------------------------------------------------------------------------------
1 | module rel-gophish-parser
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/aws/aws-lambda-go v1.24.0 // indirect
7 | github.com/aws/aws-sdk-go-v2 v1.7.0 // indirect
8 | github.com/aws/aws-sdk-go-v2/config v1.4.1 // indirect
9 | github.com/aws/aws-sdk-go-v2/service/ssm v1.7.0 // indirect
10 | github.com/go-sql-driver/mysql v1.5.0 // indirect
11 | github.com/russross/blackfriday/v2 v2.1.0 // indirect
12 | github.com/urfave/cli/v2 v2.3.0 // indirect
13 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
14 | )
15 |
--------------------------------------------------------------------------------
/gophish.ini:
--------------------------------------------------------------------------------
1 | [gophish]
2 | #Gophish URL https://gophishserver.com:3333
3 | gophishurl =
4 | #Gophish API key
5 | gophishapikey =
6 | #Set number of days to send out emails for the gophish campaigns
7 | #Recommenden value minimum 21 days to avoid URLs being recognized by google safe browsing.
8 | campaigndays =
9 | #Tag added to ELBs for removal date.
10 | #Recommended value 7 to give users a chance to click emails sent out-
11 | elb_remove_date_tag =
12 | #request headers
13 | headers = {'Authorization': 'Bearer '+ gophishapikey,}
14 | #Gophish usergroup prefix "allusers"
15 | groupprefix =
16 | #How many days to wait after last email is sent before ending campagin, Recommended is 7.
17 | delaydays =
18 |
19 |
20 |
21 | [aws]
22 | #AZ for ELBs
23 | availabilityzone =
24 | #SG for Elbs
25 | securitygroup =
26 | #Instance ID for Elbs
27 | #Use line below as variable to get instance ID if script is run on gophish server
28 | #os.system('curl -s http://169.254.169.254/latest/meta-data/instance-id')
29 | instanceid =
30 | #ELB prefix "gophish
31 | elbprefix =
32 |
33 | [localsys]
34 | #Path to usergroups "/home/user/user/tmp"
35 | userpath =
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/getusergroups.ps1:
--------------------------------------------------------------------------------
1 | Start-Transcript -Path "C:\Users\user\Documents\transcript.txt"
2 |
3 | Get-ADUser -SearchBase "OU=AADSync,OU=Users,OU=DW,DC=ad,DC=domain,DC=com" -Filter {(enabled -eq $true) -and (EmailAddress -like "*@emaildomain.com")} -Properties * | Select SamAccountName, EmailAddress, department, stronghold | Sort Name | Select @{Name="First Name";Expression={$_.SamAccountName}},@{Name="Email";Expression={$_.EmailAddress}},@{Name="Last Name";Expression={$_.department}},@{Name="Position";Expression={$_.stronghold}} | Export-CSV -NoTypeInformation -Path "C:\Users\user\Documents\users.csv"
4 |
5 | $sourceCSV = "C:\Users\user\Documents\users.csv" ;
6 |
7 | $startrow = 0 ;
8 |
9 | $counter = 1 ;
10 |
11 | $header = Get-Content -Path C:\Users\user\Documents\users.csv -TotalCount 1
12 |
13 | $rows = (Import-Csv C:\Users\user\Documents\users.csv).count
14 |
15 | Remove-Item C:\Users\user\Documents\tmp\*.*
16 |
17 | while ($startrow -lt $rows)
18 | {
19 |
20 | Import-CSV C:\Users\user\Documents\users.csv | select-object -skip $startrow -first 280 | Export-CSV "C:\Users\user\Documents\tmp\$($counter).csv" -Force -NoTypeInformation;
21 |
22 | $startrow += 280 ;
23 |
24 | $counter++ ;
25 |
26 | }
27 |
28 | aws s3 rm s3://s3-bucket-name/adusers/ --recursive
29 |
30 | aws s3 cp --recursive C:\Users\user\Documents\tmp\ s3://s3-bucket-name/adusers/
31 |
32 |
--------------------------------------------------------------------------------
/GoPhishValidate.swagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": "2.0",
3 | "info": {
4 | "title": "Default title",
5 | "description": "",
6 | "version": "1.0"
7 | },
8 | "host": "",
9 | "basePath": "/",
10 | "schemes": [
11 | "https"
12 | ],
13 | "consumes": [],
14 | "produces": [],
15 | "paths": {
16 | "/": {
17 | "post": {
18 | "responses": {
19 | "default": {
20 | "description": "default",
21 | "schema": {
22 | "type": "object",
23 | "properties": {
24 | "status": {
25 | "type": "string",
26 | "description": "status"
27 | }
28 | }
29 | }
30 | }
31 | },
32 | "summary": "ValidateGophishSig",
33 | "description": "Validates the Gophish hash",
34 | "operationId": "ValidateGophishSig",
35 | "parameters": [
36 | {
37 | "name": "X-Gophish-Signature",
38 | "in": "header",
39 | "required": false,
40 | "type": "string"
41 | },
42 | {
43 | "name": "body",
44 | "in": "body",
45 | "required": false,
46 | "schema": {
47 | "type": "object",
48 | "properties": {
49 | "email": {
50 | "type": "string",
51 | "description": "email"
52 | },
53 | "time": {
54 | "type": "string",
55 | "description": "time"
56 | },
57 | "message": {
58 | "type": "string",
59 | "description": "message"
60 | },
61 | "details": {
62 | "type": "string",
63 | "description": "details"
64 | }
65 | }
66 | }
67 | }
68 | ]
69 | }
70 | }
71 | },
72 | "definitions": {},
73 | "parameters": {},
74 | "responses": {},
75 | "securityDefinitions": {},
76 | "security": [],
77 | "tags": []
78 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dw-gophish-automation
2 |
3 |
4 | ## Table of contents
5 | * [General info](#general-info)
6 | * [Technologies](#technologies)
7 | * [Setup](#setup)
8 |
9 | ## General info
10 | This project is intended to automate the procedure internal phishing campaigns via Gophish as well as avoiding the GoPhish URL being flagged by google safebrowsing. The idea is to run it completely by itself by scheduling the scripts to run in the prefered manner. This means all GoPhish campagin settings such as templates or landing pages should be considered in production since the script will choose from them randomly. We have found the sweetspot to avoid google safebrowsing is to have 250-300 users per campaign sent out over 21 days. This has been run and tested using crontab to run start script, stop script, and getuser.sh and using task scheduler in windows to run getusergroups.ps1 to keep your userlist updated.
11 |
12 |
13 |
14 | **getusergroups.ps1**
15 | - Powershell script to get enabled users from active directory, split them randomly to 5 different CSV files in the gophish format and upload them to an s3 bucket. Schedule to run weekly.
16 |
17 | **getusers.sh**
18 | - Shell script to download userslist CSV's to Gophish instance. Schedule to run daily.
19 |
20 | **gophish.ini**
21 | - Config file
22 |
23 | **gophishautomationstart.py**
24 | - Start file. Schedule to run on prefered intervall or run manually.
25 |
26 | **gophishautomationstop.py**
27 | - Stop file. Schedule to run daily.
28 |
29 | ### A few things to remember
30 |
31 |
32 | 1. Userlist - You need to supply CSVs in the acceptable GoPhish format (https://docs.getgophish.com/user-guide/building-your-first-campaign/importing-groups). A powershell script is included to export userlists and upload them to an S3 bucket.
33 | 2. AWS Permissions - We use several AWS services such as S3, VPC, and IAM. You will need....
34 | - S3: GetBucket, PutObject, ListBucket, DeleteObject
35 | - elasticloadbalancing: DescribeLoadBalancers, DeleteLoadBalancer, CreateLoadBalancer, AddTags, SetSecurityGroups, RegisterTargets, ModifyListener
36 | 3. Config
37 | - getusers.sh: specify s3 bucket path.
38 | - getusergroups.ps1 : specify OU, paths, S3 bucket path.
39 | - gophish.ini : specify everything.
40 |
41 | 
42 |
43 | ## Technologies
44 | Project is created with:
45 | * Python 3.9.5
46 | * Powershell 7.0
47 | * Boto3
48 |
49 | ## Setup
50 | To run this project, clone repo, install dependencies, change config, run:
51 |
52 | ```
53 | $ git clone https://github.com/dwtechnologies/gophishautomation.git
54 | $ install depenedencies
55 | $ change config (gophish.ini,getusers.sh,getusers.sh)
56 | ```
57 |
58 |
--------------------------------------------------------------------------------
/gophish-parser/gophish-parser.yaml:
--------------------------------------------------------------------------------
1 | Description: Reliability Lambda Gophish parser / validator
2 | Outputs:
3 | LambdaArn:
4 | Description: Lambda ARN
5 | Value: !GetAtt LambdaFunction.Arn
6 | TGArn:
7 | Description: Target Group ARN
8 | Value: !Ref LambdaTargetGroup
9 | Parameters:
10 | FunctionName:
11 | Default: gophish-parser
12 | Description: Project name
13 | Type: String
14 | Resources:
15 | LambdaIAMRole:
16 | Type: AWS::IAM::Role
17 | Properties:
18 | RoleName: !Sub ${FunctionName}-lambda-role
19 | AssumeRolePolicyDocument:
20 | Version: "2012-10-17"
21 | Statement:
22 | -
23 | Effect: "Allow"
24 | Principal:
25 | Service:
26 | - "lambda.amazonaws.com"
27 | Action:
28 | - "sts:AssumeRole"
29 | Path: "/"
30 | Policies:
31 | - PolicyName: !Sub ${FunctionName}-lambda-policy
32 | PolicyDocument:
33 | Version: "2012-10-17"
34 | Statement:
35 | - Effect: "Allow"
36 | Action:
37 | # Allow modify / creation of Logstreams/groups
38 | - "logs:DescribeLogGroups"
39 | - "logs:DescribeLogStreams"
40 | - "logs:CreateLogStream"
41 | - "logs:CreateLogGroup"
42 | - "logs:PutLogEvents"
43 | Resource: "*"
44 | - Effect: "Allow"
45 | Action:
46 | # Allow decrypt of gophish secret
47 | - "ssm:GetParameter"
48 | Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/"
49 | # If you want to use KMS for paramater encryption
50 | - Effect: "Allow"
51 | Action:
52 | # Allow decrypt of gophish secret
53 | - "kms:Decrypt"
54 | Resource: "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/"
55 | Condition:
56 | StringEquals:
57 | kms:EncryptionContext:PARAMETER_ARN: "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/"
58 | LambdaLogGroup:
59 | Properties:
60 | LogGroupName: !Sub /aws/lambda/${LambdaFunction}
61 | RetentionInDays: 90
62 | Type: AWS::Logs::LogGroup
63 | LambdaFunction:
64 | Properties:
65 | CodeUri: ./src/main.zip
66 | Description: "Gophish validator"
67 | FunctionName: !Sub ${FunctionName}
68 | Handler: main
69 | MemorySize: 128
70 | Policies:
71 | - AWSLambdaBasicExecutionRole
72 | Role: !GetAtt LambdaIAMRole.Arn
73 | Runtime: go1.x
74 | Timeout: 20
75 | Type: AWS::Serverless::Function
76 | ## If you want to attach this to a loadbalancer here is an example of how to create such a target group and Permission
77 | LambdaPermission:
78 | Type: AWS::Lambda::Permission
79 | Properties:
80 | FunctionName: !GetAtt LambdaFunction.Arn
81 | Action: 'lambda:InvokeFunction'
82 | Principal: elasticloadbalancing.amazonaws.com
83 | LambdaTargetGroup:
84 | Type: AWS::ElasticLoadBalancingV2::TargetGroup
85 | Properties:
86 | HealthCheckEnabled: false
87 | Name: !Sub ${LambdaFunction}-tg
88 | TargetType: lambda
89 | Targets:
90 | - Id: !GetAtt LambdaFunction.Arn
91 | Transform: AWS::Serverless-2016-10-31
--------------------------------------------------------------------------------
/gophish-parser/src/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "crypto/hmac"
6 | "crypto/sha256"
7 | "encoding/hex"
8 | "encoding/json"
9 | "errors"
10 | "strings"
11 | "fmt"
12 | "github.com/aws/aws-lambda-go/events"
13 | "github.com/aws/aws-lambda-go/lambda"
14 | "github.com/aws/aws-sdk-go-v2/config"
15 | "github.com/aws/aws-sdk-go-v2/aws"
16 | "github.com/aws/aws-sdk-go-v2/service/ssm"
17 | )
18 |
19 | var (
20 | errNoSignature = errors.New("No X-Gophish-Signature header provided")
21 | errInvalidSignature = errors.New("Invalid signature provided")
22 | )
23 |
24 | type SSMGetParameterAPI interface {
25 | GetParameter(ctx context.Context,
26 | params *ssm.GetParameterInput,
27 | optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)
28 | }
29 |
30 | type eventBody struct {
31 | Email string `json:"email"`
32 | Time string `json:"time"`
33 | Message string `json:"message"`
34 | Details json.RawMessage `json:"details"`
35 | }
36 |
37 | func FindParameter(c context.Context, api SSMGetParameterAPI, input *ssm.GetParameterInput) (*ssm.GetParameterOutput, error) {
38 | return api.GetParameter(c, input)
39 |
40 |
41 | }
42 |
43 | func handleRequest(ctx context.Context, request events.ALBTargetGroupRequest) (events.ALBTargetGroupResponse, error) {
44 | fmt.Printf("Processing request data for traceId %s.\n", request.Headers["x-amzn-trace-id"])
45 | // Get the provided signature
46 | signatureHeader := request.Headers["x-gophish-signature"]
47 | if signatureHeader == "" {
48 | fmt.Printf(errNoSignature.Error())
49 | out := `{ "status": "` + errNoSignature.Error() + `" }`
50 | return events.ALBTargetGroupResponse{Body: out, StatusCode: 200, StatusDescription: "200 OK", IsBase64Encoded: false, Headers: map[string]string{"content-type": "application/json"}}, nil
51 | }
52 |
53 | signatureParts := strings.SplitN(signatureHeader, "=", 2)
54 | if len(signatureParts) != 2 {
55 | fmt.Printf(errInvalidSignature.Error())
56 | out := `{ "status": "` + errInvalidSignature.Error() + `" }`
57 | return events.ALBTargetGroupResponse{Body: out, StatusCode: 200, StatusDescription: "200 OK", IsBase64Encoded: false, Headers: map[string]string{"content-type": "application/json"}}, nil
58 | }
59 |
60 | signature := signatureParts[1]
61 |
62 | gotHash, err := hex.DecodeString(signature)
63 | if err != nil {
64 | fmt.Printf("Failed to Decode string")
65 | out := `{ "status": "Failed to Decode string" }`
66 | return events.ALBTargetGroupResponse{Body: out, StatusCode: 200, StatusDescription: "200 OK", IsBase64Encoded: false, Headers: map[string]string{"content-type": "application/json"}}, nil
67 | }
68 |
69 | // Copy out the request body so we can validate the signature
70 | fmt.Printf("Processing body \n %s.\n", request.Body)
71 | eb := &eventBody{}
72 | json.Unmarshal([]byte(request.Body), eb)
73 | body, err := json.Marshal(eb)
74 | fmt.Printf("Marshalled \n %s.\n", body)
75 | if err != nil {
76 | fmt.Printf("Failed to read Body")
77 | out := `{ "status": "Failed to read Body" }`
78 | return events.ALBTargetGroupResponse{Body: out, StatusCode: 200, StatusDescription: "200 OK", IsBase64Encoded: false, Headers: map[string]string{"content-type": "application/json"}}, nil
79 | }
80 |
81 | // Validate the signature
82 | cfg, err := config.LoadDefaultConfig(context.TODO())
83 | if err != nil {
84 | fmt.Printf("configuration error, " + err.Error())
85 | out := `{ "status": "configuration error ` + err.Error() + `" }`
86 | return events.ALBTargetGroupResponse{Body: out, StatusCode: 200, StatusDescription: "200 OK", IsBase64Encoded: false, Headers: map[string]string{"content-type": "application/json"}}, nil
87 | }
88 | client := ssm.NewFromConfig(cfg)
89 |
90 | input := &ssm.GetParameterInput{
91 | // Add your secret, SSM Parameter, with or without decryption
92 | Name: aws.String(""),
93 | WithDecryption: true,
94 | }
95 |
96 | results, err := FindParameter(context.TODO(), client, input)
97 | if err != nil {
98 | fmt.Printf("configuration error, " + err.Error())
99 | out := `{ "status": "Failed to read SSM Parameter ` + err.Error() + `" }`
100 | return events.ALBTargetGroupResponse{Body: out, StatusCode: 200, StatusDescription: "200 OK", IsBase64Encoded: false, Headers: map[string]string{"content-type": "application/json"}}, nil
101 | }
102 |
103 | expectedMAC := hmac.New(sha256.New, []byte(*results.Parameter.Value))
104 | expectedMAC.Write(body)
105 | expectedHash := expectedMAC.Sum(nil)
106 |
107 | if !hmac.Equal(gotHash, expectedHash) {
108 | fmt.Printf("invalid signature provided. expected %s got %s", hex.EncodeToString(expectedHash), signature)
109 | out := `{ "status": "invalid signature provided" }`
110 | return events.ALBTargetGroupResponse{Body: out, StatusCode: 200, StatusDescription: "200 OK", IsBase64Encoded: false, Headers: map[string]string{"content-type": "application/json"}}, nil
111 | } else {
112 | fmt.Printf("Matches")
113 | out := `{ "status": "Matches" }`
114 | return events.ALBTargetGroupResponse{Body: out, StatusCode: 200, StatusDescription: "200 OK", IsBase64Encoded: false, Headers: map[string]string{"content-type": "application/json"}}, nil
115 | }
116 | }
117 |
118 | func main() {
119 | lambda.Start(handleRequest)
120 | }
--------------------------------------------------------------------------------
/gophish-parser/src/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2 | github.com/aws/aws-lambda-go v1.24.0 h1:bOMerM175hLqHLdF1Nonfv1NA20nTIatuC0HK8eMoYg=
3 | github.com/aws/aws-lambda-go v1.24.0/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XOkbv4w9SGLU=
4 | github.com/aws/aws-sdk-go-v2 v1.7.0 h1:UYGnoIPIzed+ycmgw8Snb/0HK+KlMD+SndLTneG8ncE=
5 | github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4=
6 | github.com/aws/aws-sdk-go-v2/config v1.4.1 h1:PcGp9Kf+1dHJmP3EIDZJmAmWfGABFTU0obuvYQNzWH8=
7 | github.com/aws/aws-sdk-go-v2/config v1.4.1/go.mod h1:HCDWZ/oeY59TPtXslxlbkCqLQBsVu6b09kiG43tdP+I=
8 | github.com/aws/aws-sdk-go-v2/credentials v1.3.0 h1:vXxTINCsHn6LKhR043jwSLd6CsL7KOEU7b1woMr1K1A=
9 | github.com/aws/aws-sdk-go-v2/credentials v1.3.0/go.mod h1:tOcv+qDZ0O+6Jk2beMl5JnZX6N0H7O8fw9UsD3bP7GI=
10 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.2.0 h1:ucExzYCoAiL9GpKOsKkQLsa43wTT23tcdP4cDTSbZqY=
11 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.2.0/go.mod h1:XvzoGzuS0kKPzCQtJCC22Xh/mMgVAzfGo/0V+mk/Cu0=
12 | github.com/aws/aws-sdk-go-v2/internal/ini v1.1.0 h1:DJq/vXXF+LAFaa/kQX9C6arlf4xX4uaaqGWIyAKOCpM=
13 | github.com/aws/aws-sdk-go-v2/internal/ini v1.1.0/go.mod h1:qGQ/9IfkZonRNSNLE99/yBJ7EPA/h8jlWEqtJCcaj+Q=
14 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.0 h1:g2npzssI/6XsoQaPYCxliMFeC5iNKKvO0aC+/wWOE0A=
15 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.0/go.mod h1:a7XLWNKuVgOxjssEF019IiHPv35k8KHBaWv/wJAfi2A=
16 | github.com/aws/aws-sdk-go-v2/service/ssm v1.7.0 h1:Ds8h/ClZRkL4CNJZAh/CFcq3Nay8YpgWRKYs9AbAlgQ=
17 | github.com/aws/aws-sdk-go-v2/service/ssm v1.7.0/go.mod h1:qjyWCAIVHuJLNoKLhVvVskck15QGVszv4+V+gIHDLbY=
18 | github.com/aws/aws-sdk-go-v2/service/sso v1.3.0 h1:DMi9w+TpUam7eJ8ksL7svfzpqpqem2MkDAJKW8+I2/k=
19 | github.com/aws/aws-sdk-go-v2/service/sso v1.3.0/go.mod h1:qWR+TUuvfji9udM79e4CPe87C5+SjMEb2TFXkZaI0Vc=
20 | github.com/aws/aws-sdk-go-v2/service/sts v1.5.0 h1:Y1K9dHE2CYOWOvaJSIITq4mJfLX43iziThTvqs5FqOg=
21 | github.com/aws/aws-sdk-go-v2/service/sts v1.5.0/go.mod h1:HjDKUmissf6Mlut+WzG2r35r6LeTKmLEDJ6p9NryzLg=
22 | github.com/aws/smithy-go v1.5.0 h1:2grDq7LxZlo8BZUDeqRfQnQWLZpInmh2TLPPkJku3YM=
23 | github.com/aws/smithy-go v1.5.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
24 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
25 | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
26 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
27 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
28 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
29 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
30 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
31 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
32 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
33 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
34 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
35 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
36 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
37 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
38 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
39 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
40 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
41 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
42 | github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
43 | github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
44 | github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
45 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
46 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
47 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
48 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
49 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
50 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
51 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
52 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
53 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
54 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
55 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
56 |
--------------------------------------------------------------------------------
/power-automate/Office365-Power-Automate.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | We needed a way to catch the users that got phished and also an automated way to send reminders to these users.
4 | We have previously built some good concepts around Power Platform and using Sharepoint and Power Automate to handle policy reminders.
5 | Do not expect a complete how-to here. This is how we have done it and what you can use.
6 |
7 | ## Table of contents
8 | * [Architectual Overview](#architectual-overview)
9 | * [Description of steps when user gets phished](#description-of-steps-when-user-gets-phished)
10 | * [Description of steps when user identifies a phishing attempt](#description-of-steps-when-user-identifies-a-phishing-attempt)
11 |
12 |
13 | # Architectual Overview
14 |
15 |
16 | # The Sharepoint Lists
17 | To keep track of active users there are two lists involved
18 |
19 | The list keeps track on
20 | Active users
21 | How many reminders the user have got so far
22 | If they are on hold
23 | If they have an active support ticket
24 | ## Users archive
25 | The list keeps track of two things
26 | If a user has been phished historically
27 | How many times that user has been phished
28 |
29 | # The Flows
30 | ## Part one, the gophish webhook
31 | ### Flow overview
32 |
33 |
34 |
35 | ### Description
36 |
37 | So a few things. We wanted to catch the phished users but also send kudos when users successfully identified a phishing email. For this purpose we decided to use the [Gophish webhook}(https://docs.getgophish.com/user-guide/documentation/webhooks).
38 |
39 | As for validating the webhook we use a Custom Connector in Power Automate to send a request to an Lambda in AWS to verify the request (X-Gophish-Signature) using go. We could do this on Azure but since a lot of our infrastructure in general is in AWS this was the simplest solution for us. On our github you can find the Gophish lambda, aswell as a swagger template for the Custom Connector.
40 |
41 | If the signature is validated then we check that the user is not already in the active period (sometimes gophish send multiple requests for the same submitted data). If not we add the users to the Phishing Users list in Sharepoint and also update the Archive list depending on how many times they have been phished before.
42 |
43 | As a final step of this flow we trigger a child flow to send the first reminder to the user
44 |
45 | For reference here is the Kudos card!
46 |
47 |
48 | ## Part two, send phishing reminder notifications
49 |
50 | ### Flow overview
51 |
52 |
53 |
54 | ### Description
55 | So this flow might seem a bit intimidating at first glance so let us break this down into three parts. Validation of the user, Reminders and last but not least the Samanage Integration
56 |
57 | Validation of User
58 |
59 | When we say validation of user. Since this involves disabling of accounts we do have a validator for some users should be noted. It also verifies if manager value can be retrieved (the highest one on the food chain have no manager after all).
60 | Users are also allowed to be put on hold (parental leave or similiar leave of abscence) which cancels the flow
61 | As a last part of this we also validate how many times the user have been phished, this is because we send two different types of quizzes depending on that number.
62 |
63 | Reminders
64 |
65 | We have a switch depending on how many reminders a user has got so far
66 |
67 | So this sends a teams notification using adaptive cards and also an email to the user.
68 | Editors Note: Please note that send email will always trigger due to the fact that a user can block the bot conversation and will result in errors
69 |
70 | Here is a card example of the first notification
71 |
72 |
73 | We have added an example card on our github that you can use if you need some inspiration
74 |
75 | Samanage integration
76 |
77 | Now I will only touch upon this but if the user has “failed” to complete the quiz / training within the given time, a support ticket to disable the user account is created. This is done using azure automations but could ofcourse be created in other ways.
78 |
79 | On our github you can see a powershell example of this and the integration with our AD.
80 |
81 | ## Part three, The reccuring flows
82 | There are two daily reccuring flows. One that checks if it is time to send reminders and one that updates support tickets to verify if the user has yet to complete the training. Here I will only go through the reminder flow
83 |
84 | ### Overview
85 |
86 |
87 |
88 | ### Description
89 |
90 | It is a very straight forward flow. If the users next reminder date is less or equal to todays date, then they should recieve a new reminder
91 |
92 | ## Part four, Training completed
93 |
94 | ### Overview
95 |
96 |
97 |
98 | ### Description
99 |
100 | Again a straight forward flow. Upon completing the requested training form the users are removed from the active users list. If there is a pending support ticket then this is updated with information that the user has completed the training.
101 |
102 |
103 |
--------------------------------------------------------------------------------
/gophishautomationstop.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import boto3
4 | import requests
5 | import os
6 | from datetime import *
7 | import urllib3
8 | import json
9 | import configparser
10 |
11 | #Import from config
12 | config = configparser.ConfigParser()
13 | config.read('gophish.ini')
14 | userpath = config['localsys']['userpath']
15 | gophishapikey = config['gophish']['gophishapikey']
16 | availabilityzone = config['aws']['availabilityzone']
17 | securitygroup = config['aws']['securitygroup']
18 | instanceid = config['aws']['instanceid']
19 | campaigndays = config['gophish']['campaigndays']
20 | gophishurl = config['gophish']['gophishurl']
21 | elbprefix = config['aws']['elbprefix']
22 | groupprefix = config['gophish']['groupprefix']
23 | delaydays = config['gophish']['delaydays']
24 | headers = {'Authorization': 'Bearer '+ gophishapikey,}
25 |
26 | #Datime objects
27 | class Time:
28 | def __init__(self):
29 | self.today = datetime.today().strftime('%Y-%m-%d')
30 | self.year = date.today().strftime('%Y')
31 | self.datenow = datetime.now()
32 | #XYself.date_today = self.d.date()
33 | self.today = datetime.today()
34 | self.delay = timedelta(days=int(delaydays))
35 |
36 | # This function returns an object of Time
37 | def ret():
38 | return Time()
39 |
40 | time = ret()
41 |
42 | urllib3.disable_warnings()
43 |
44 | #Get Gophish Campagin ID
45 | def get_campaginid():
46 | id_list = []
47 | response = requests.get(gophishurl + '/api/campaigns/', headers=headers, verify=False)
48 | responsebody = response.content
49 | responsedecode = json.loads(responsebody.decode('utf-8'))
50 | for i in responsedecode:
51 | id_list.append(i['id'])
52 | return id_list
53 |
54 | #Get GoPhish Campagin status
55 | def get_campaginstatus(id_list):
56 | datelist = []
57 | dateform = []
58 | campaginid = []
59 | formatted_id_dict = {}
60 | for campaign in id_list:
61 | response = requests.get(gophishurl + '/api/campaigns/'+ str(campaign) + '/summary', headers=headers, verify=False)
62 | responsebody = response.content
63 | responsedecode = json.loads(responsebody.decode('utf-8'))
64 | if responsedecode['status'] == 'In progress':
65 | datelist.append(responsedecode['send_by_date'])
66 | campaginid.append(responsedecode['id'])
67 | else:
68 | continue
69 | for dateformat in datelist:
70 | dateformat = dateformat[:10]
71 | dateformatted = datetime.strptime(dateformat, "%Y-%m-%d")
72 | dateform.append(dateformatted)
73 | for key in campaginid:
74 | for value in dateform:
75 | formatted_id_dict[key] = value
76 | break
77 | return formatted_id_dict, dateform
78 |
79 | #Check and compare dates
80 | def date_check(formatted_id_dict, dateform):
81 | end_id = []
82 | end_campagin_id = []
83 | for key, value in formatted_id_dict.items():
84 | datenow = time.datenow
85 | enddate = value + time.delay
86 | if enddate <= datenow:
87 | end_campagin_id.append(key)
88 | else:
89 | return False
90 | if not end_campagin_id:
91 | return False
92 | else:
93 | return end_campagin_id
94 |
95 | #End Gophish campagin ID
96 | def end_campagin(end_campagin_id):
97 | for ending in end_campagin_id:
98 | response = requests.get(gophishurl + '/api/campaigns/'+ str(ending)+'/complete', headers=headers, verify=False)
99 | responsebody = response.content
100 | return True
101 |
102 | #Get AWS ELB
103 | def get_elb():
104 | elbnames = []
105 | dnsnames = []
106 | client = boto3.client('elb')
107 | response = client.describe_load_balancers()
108 | elbnames.append(response['LoadBalancerDescriptions'])
109 | for names in elbnames:
110 | for name in names:
111 | for key,value in name.items():
112 | if key == 'LoadBalancerName':
113 | dnsnames.append(value)
114 | return dnsnames
115 |
116 | #Remove AWS ELB
117 | def remove_elb(dnsnames):
118 | if dnsnames == []:
119 | pass
120 | else:
121 | client = boto3.client('elb')
122 | for elbs in dnsnames:
123 | response = client.delete_load_balancer(
124 | LoadBalancerName=
125 | elbs,
126 | )
127 | if response['ResponseMetadata']['HTTPStatusCode'] == 200:
128 | return True
129 |
130 | #Remove Gophish usergroup
131 | def remove_usergrp():
132 | groupidlst = []
133 | response = requests.get(config['gophish']['gophishurl'] + '/api/groups/', headers=headers, verify=False)
134 | responsebody = response.content
135 | responsedecode = json.loads(responsebody.decode('utf-8'))
136 | for usergrp in responsedecode:
137 | for key,value in usergrp.items():
138 | if 'name' in key:
139 | if groupprefix in value:
140 | groupid = usergrp.get("id")
141 | groupidlst.append(groupid)
142 | else:
143 | pass
144 | for groups in groupidlst:
145 | response = requests.delete(gophishurl + '/api/groups/' + str(groups), headers=headers, verify=False)
146 | responsebody = response.content
147 |
148 | def main():
149 | get_campagin = get_campaginid()
150 | formatted_id_dict, dateform = get_campaginstatus(get_campagin)
151 | getelb = get_elb()
152 | get_date = date_check(formatted_id_dict, dateform)
153 | if get_date == False:
154 | quit()
155 | else:
156 | end_camp = end_campagin(get_date)
157 | rm_elb = remove_elb(getelb)
158 | if rm_elb == True:
159 | rm_grp = remove_usergrp()
160 |
161 | main()
162 |
--------------------------------------------------------------------------------
/gophishautomationstart.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import boto3
4 | import botocore
5 | import requests
6 | import os
7 | from datetime import *
8 | import urllib3
9 | import json
10 | import configparser
11 | import random
12 |
13 | config = configparser.ConfigParser()
14 | config.read('gophish.ini')
15 |
16 | userpath = config['localsys']['userpath']
17 | availabilityzone = config['aws']['availabilityzone']
18 | securitygroup = config['aws']['securitygroup']
19 | instanceid = config['aws']['instanceid']
20 | elbprefix = config['aws']['elbprefix']
21 | gophishapikey = config['gophish']['gophishapikey']
22 | campaigndays = config['gophish']['campaigndays']
23 | elb_remove_date_tag = config['gophish']['elb_remove_date_tag']
24 | gophishurl = config['gophish']['gophishurl']
25 | groupprefix = config['gophish']['groupprefix']
26 |
27 |
28 | client = boto3.client('elb')
29 | urllib3.disable_warnings()
30 |
31 | #Datetime objects
32 | class Time:
33 | def __init__(self):
34 | self.new_time = datetime.now().astimezone().replace(microsecond=0) + timedelta(minutes=1)
35 | self.send_now = self.new_time.isoformat()
36 | self.elb_remove_date_time = date.today() + timedelta(days=int(elb_remove_date_tag))
37 | self.elb_date_str = self.elb_remove_date_time.strftime("%Y-%m-%d")
38 | self.today = datetime.today()
39 | self.amount_days = timedelta(int(campaigndays))
40 | self.add_days = self.new_time + self.amount_days
41 | self.send_by = self.add_days.isoformat()
42 |
43 | # This function returns an object of Time
44 | def ret():
45 | return Time()
46 |
47 | time = ret()
48 |
49 | def count_files():
50 | initial_count = 0
51 | for path in os.listdir(userpath):
52 | if os.path.isfile(os.path.join(userpath, path)):
53 | initial_count += 1
54 | return initial_count
55 |
56 |
57 | #Creates ELBs and returns ELB names
58 | def create_elbs(elbpref, initial_count):
59 | elbnames = []
60 | for i in range(1, int(initial_count) + 1):
61 | elbname = elbpref + "-" + str(i)
62 | response = client.create_load_balancer(
63 | LoadBalancerName=elbname,
64 | Listeners=[
65 | {
66 | 'Protocol': 'HTTP',
67 | 'LoadBalancerPort': 80,
68 | 'InstanceProtocol': 'HTTP',
69 | 'InstancePort': 80
70 | },
71 | ],
72 | AvailabilityZones=[
73 | availabilityzone,
74 | ],
75 | SecurityGroups=[
76 | securitygroup,
77 | ],
78 | Scheme='internet-facing',
79 | Tags=[
80 | {
81 | 'Key': 'owner',
82 | 'Value': 'secops',
83 | 'Key': 'date to remove',
84 | 'Value': str(time.elb_date_str)
85 | },
86 | ]
87 | )
88 | elbnames.append(response['DNSName'])
89 | response = client.register_instances_with_load_balancer(
90 | LoadBalancerName=elbname,
91 | Instances=[
92 | {
93 | 'InstanceId': instanceid
94 | },
95 | ]
96 | )
97 | elbnames = ["http://" + sub for sub in elbnames]
98 | return elbnames
99 |
100 | #Grabs local csv and creates gophish user groups, returns group name
101 | def convertcreate_group(files, groupprefix):
102 | gophishgrpname = []
103 | try:
104 | files = os.listdir(userpath)
105 | for csv in files:
106 | headers = {'Authorization': 'Bearer '+ gophishapikey,}
107 | files = {
108 | 'file': (userpath + csv, open(userpath + csv, 'rb')),
109 | }
110 | response = requests.post(gophishurl + '/api/import/group',
111 | headers=headers, files=files, verify=False)
112 | responseusers = response.text
113 | targets = json.loads(responseusers)
114 | headers = {
115 | 'Authorization': 'Bearer '+ gophishapikey,
116 | 'Content-Type': 'application/json',
117 | }
118 | data = {"name": groupprefix + csv, "targets": targets}
119 | jsondata = json.dumps(data)
120 | response = requests.post(gophishurl + '/api/groups/', headers=headers, data=jsondata, verify=False)
121 | responsebody = response.content
122 | gophishresp = json.loads(responsebody.decode('utf-8'))
123 | gophishgrpname.append(gophishresp['name'])
124 | except KeyError:
125 | print("Error: Groups already exist")
126 | return gophishgrpname
127 |
128 | #creates dictionary with gophish group IDs and ELB urls
129 | def create_dict(group, diction):
130 | elbgodict = dict(zip(group, diction))
131 | return elbgodict
132 |
133 |
134 | # Get random template
135 | def get_temp():
136 | headers = {'Authorization': 'Bearer '+ gophishapikey,}
137 | response = requests.get(gophishurl + '/api/templates/', headers=headers, verify=False)
138 | responsebody = response.content
139 | responsedecode = json.loads(responsebody.decode('utf-8'))
140 | templist = []
141 | for i in responsedecode:
142 | for key, value in i.items():
143 | if key == 'name':
144 | templist.append(value)
145 | random_tmp = random.choice(templist)
146 | return random_tmp
147 |
148 | #Get random page
149 | def get_page():
150 | headers = {'Authorization': 'Bearer '+ gophishapikey,}
151 | response = requests.get(gophishurl + '/api/pages/', headers=headers, verify=False)
152 | responsebody = response.content
153 | responsedecode = json.loads(responsebody.decode('utf-8'))
154 | pagelist = []
155 | for i in responsedecode:
156 | for key, value in i.items():
157 | if key == 'name':
158 | pagelist.append(value)
159 | random_pg = random.choice(pagelist)
160 | return random_pg
161 |
162 |
163 | #Creates campaign
164 | def create_campaign(sendby, sendnow, elbgodict,random_tmp, random_pg):
165 | if not elbgodict:
166 | print("Error: Gophish and/or ELB names not collected")
167 | else:
168 | headers = {'Authorization': 'Bearer '+ gophishapikey,}
169 | for groups,elbs in elbgodict.items():
170 | data = {
171 | "name": groups,
172 | "template": {"name": random_tmp},
173 | "url": elbs,
174 | "page": {"name": random_pg},
175 | "smtp": {"name": "DWteams@outlook.com"},
176 | "launch_date": str(sendnow),
177 | "send_by_date": str(sendby),
178 | "groups": [{"name": groups}]
179 | }
180 | jsondata = json.dumps(data)
181 | response = requests.post(gophishurl + '/api/campaigns/', headers=headers, data=jsondata, verify=False)
182 | responsebody = response.content
183 | responsedecode = json.loads(responsebody.decode('utf-8'))
184 |
185 | #Main function
186 | def main():
187 | numberoffiles = count_files()
188 | if not os.listdir(userpath) :
189 | print("No user groups found in specified directory")
190 | return False
191 | else:
192 | files = os.listdir(userpath)
193 | random_tmp = get_temp()
194 | random_pg = get_page()
195 | gophishgroupname = convertcreate_group(files, groupprefix)
196 | elboutput = create_elbs(elbprefix, numberoffiles)
197 | elbgodict = create_dict(gophishgroupname, elboutput)
198 | startcampagin = create_campaign(time.send_by, time.send_now, elbgodict, random_tmp, random_pg)
199 | main()
200 |
--------------------------------------------------------------------------------