github.com/amar224/phishing-tool@v0.9.0/models/result.go (about) 1 package models 2 3 import ( 4 "crypto/rand" 5 "encoding/json" 6 "math/big" 7 "net" 8 "time" 9 10 log "github.com/gophish/gophish/logger" 11 "github.com/jinzhu/gorm" 12 "github.com/oschwald/maxminddb-golang" 13 ) 14 15 type mmCity struct { 16 GeoPoint mmGeoPoint `maxminddb:"location"` 17 } 18 19 type mmGeoPoint struct { 20 Latitude float64 `maxminddb:"latitude"` 21 Longitude float64 `maxminddb:"longitude"` 22 } 23 24 // Result contains the fields for a result object, 25 // which is a representation of a target in a campaign. 26 type Result struct { 27 Id int64 `json:"-"` 28 CampaignId int64 `json:"-"` 29 UserId int64 `json:"-"` 30 RId string `json:"id"` 31 Status string `json:"status" sql:"not null"` 32 IP string `json:"ip"` 33 Latitude float64 `json:"latitude"` 34 Longitude float64 `json:"longitude"` 35 SendDate time.Time `json:"send_date"` 36 Reported bool `json:"reported" sql:"not null"` 37 ModifiedDate time.Time `json:"modified_date"` 38 BaseRecipient 39 } 40 41 func (r *Result) createEvent(status string, details interface{}) (*Event, error) { 42 c, err := GetCampaign(r.CampaignId, r.UserId) 43 if err != nil { 44 return nil, err 45 } 46 e := &Event{Email: r.Email, Message: status} 47 if details != nil { 48 dj, err := json.Marshal(details) 49 if err != nil { 50 return nil, err 51 } 52 e.Details = string(dj) 53 } 54 c.AddEvent(e) 55 return e, nil 56 } 57 58 // HandleEmailSent updates a Result to indicate that the email has been 59 // successfully sent to the remote SMTP server 60 func (r *Result) HandleEmailSent() error { 61 event, err := r.createEvent(EventSent, nil) 62 if err != nil { 63 return err 64 } 65 r.SendDate = event.Time 66 r.Status = EventSent 67 r.ModifiedDate = event.Time 68 return db.Save(r).Error 69 } 70 71 // HandleEmailError updates a Result to indicate that there was an error when 72 // attempting to send the email to the remote SMTP server. 73 func (r *Result) HandleEmailError(err error) error { 74 event, err := r.createEvent(EventSendingError, EventError{Error: err.Error()}) 75 if err != nil { 76 return err 77 } 78 r.Status = Error 79 r.ModifiedDate = event.Time 80 return db.Save(r).Error 81 } 82 83 // HandleEmailBackoff updates a Result to indicate that the email received a 84 // temporary error and needs to be retried 85 func (r *Result) HandleEmailBackoff(err error, sendDate time.Time) error { 86 event, err := r.createEvent(EventSendingError, EventError{Error: err.Error()}) 87 if err != nil { 88 return err 89 } 90 r.Status = StatusRetry 91 r.SendDate = sendDate 92 r.ModifiedDate = event.Time 93 return db.Save(r).Error 94 } 95 96 // HandleEmailOpened updates a Result in the case where the recipient opened the 97 // email. 98 func (r *Result) HandleEmailOpened(details EventDetails) error { 99 event, err := r.createEvent(EventOpened, details) 100 if err != nil { 101 return err 102 } 103 // Don't update the status if the user already clicked the link 104 // or submitted data to the campaign 105 if r.Status == EventClicked || r.Status == EventDataSubmit { 106 return nil 107 } 108 r.Status = EventOpened 109 r.ModifiedDate = event.Time 110 return db.Save(r).Error 111 } 112 113 // HandleClickedLink updates a Result in the case where the recipient clicked 114 // the link in an email. 115 func (r *Result) HandleClickedLink(details EventDetails) error { 116 event, err := r.createEvent(EventClicked, details) 117 if err != nil { 118 return err 119 } 120 // Don't update the status if the user has already submitted data via the 121 // landing page form. 122 if r.Status == EventDataSubmit { 123 return nil 124 } 125 r.Status = EventClicked 126 r.ModifiedDate = event.Time 127 return db.Save(r).Error 128 } 129 130 // HandleFormSubmit updates a Result in the case where the recipient submitted 131 // credentials to the form on a Landing Page. 132 func (r *Result) HandleFormSubmit(details EventDetails) error { 133 event, err := r.createEvent(EventDataSubmit, details) 134 if err != nil { 135 return err 136 } 137 r.Status = EventDataSubmit 138 r.ModifiedDate = event.Time 139 return db.Save(r).Error 140 } 141 142 // HandleEmailReport updates a Result in the case where they report a simulated 143 // phishing email using the HTTP handler. 144 func (r *Result) HandleEmailReport(details EventDetails) error { 145 event, err := r.createEvent(EventReported, details) 146 if err != nil { 147 return err 148 } 149 r.Reported = true 150 r.ModifiedDate = event.Time 151 return db.Save(r).Error 152 } 153 154 // UpdateGeo updates the latitude and longitude of the result in 155 // the database given an IP address 156 func (r *Result) UpdateGeo(addr string) error { 157 // Open a connection to the maxmind db 158 mmdb, err := maxminddb.Open("static/db/geolite2-city.mmdb") 159 if err != nil { 160 log.Fatal(err) 161 } 162 defer mmdb.Close() 163 ip := net.ParseIP(addr) 164 var city mmCity 165 // Get the record 166 err = mmdb.Lookup(ip, &city) 167 if err != nil { 168 return err 169 } 170 // Update the database with the record information 171 r.IP = addr 172 r.Latitude = city.GeoPoint.Latitude 173 r.Longitude = city.GeoPoint.Longitude 174 return db.Save(r).Error 175 } 176 177 func generateResultId() (string, error) { 178 const alphaNum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 179 k := make([]byte, 7) 180 for i := range k { 181 idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(alphaNum)))) 182 if err != nil { 183 return "", err 184 } 185 k[i] = alphaNum[idx.Int64()] 186 } 187 return string(k), nil 188 } 189 190 // GenerateId generates a unique key to represent the result 191 // in the database 192 func (r *Result) GenerateId(tx *gorm.DB) error { 193 // Keep trying until we generate a unique key (shouldn't take more than one or two iterations) 194 for { 195 rid, err := generateResultId() 196 if err != nil { 197 return err 198 } 199 r.RId = rid 200 err = tx.Table("results").Where("r_id=?", r.RId).First(&Result{}).Error 201 if err == gorm.ErrRecordNotFound { 202 break 203 } 204 } 205 return nil 206 } 207 208 // GetResult returns the Result object from the database 209 // given the ResultId 210 func GetResult(rid string) (Result, error) { 211 r := Result{} 212 err := db.Where("r_id=?", rid).First(&r).Error 213 return r, err 214 }