├── 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 | ![diagram](https://user-images.githubusercontent.com/70007848/136763087-70a5387b-534f-4679-a1dd-4e6ace94b40a.png) 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 | The architecture diagram 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 | The flow overview 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 | When you spot the phish 47 | 48 | ## Part two, send phishing reminder notifications 49 | 50 | ### Flow overview 51 | 52 | The reminder flow 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 | The reminder flow 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 | When you get phished and need to take a training 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 | The reccuring flow 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 | The training completed flow 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 | --------------------------------------------------------------------------------