github.com/korniux/gophish@v0.9.0/util/util.go (about) 1 package util 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/elliptic" 6 "crypto/rand" 7 "crypto/x509" 8 "crypto/x509/pkix" 9 "encoding/csv" 10 "encoding/pem" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "math/big" 15 "net/http" 16 "net/mail" 17 "os" 18 "regexp" 19 "time" 20 21 log "github.com/gophish/gophish/logger" 22 "github.com/gophish/gophish/models" 23 "github.com/jordan-wright/email" 24 "golang.org/x/crypto/bcrypt" 25 ) 26 27 var ( 28 firstNameRegex = regexp.MustCompile(`(?i)first[\s_-]*name`) 29 lastNameRegex = regexp.MustCompile(`(?i)last[\s_-]*name`) 30 emailRegex = regexp.MustCompile(`(?i)email`) 31 positionRegex = regexp.MustCompile(`(?i)position`) 32 ) 33 34 // ParseMail takes in an HTTP Request and returns an Email object 35 // TODO: This function will likely be changed to take in a []byte 36 func ParseMail(r *http.Request) (email.Email, error) { 37 e := email.Email{} 38 m, err := mail.ReadMessage(r.Body) 39 if err != nil { 40 fmt.Println(err) 41 } 42 body, err := ioutil.ReadAll(m.Body) 43 e.HTML = body 44 return e, err 45 } 46 47 // ParseCSV contains the logic to parse the user provided csv file containing Target entries 48 func ParseCSV(r *http.Request) ([]models.Target, error) { 49 mr, err := r.MultipartReader() 50 ts := []models.Target{} 51 if err != nil { 52 return ts, err 53 } 54 for { 55 part, err := mr.NextPart() 56 if err == io.EOF { 57 break 58 } 59 // Skip the "submit" part 60 if part.FileName() == "" { 61 continue 62 } 63 defer part.Close() 64 reader := csv.NewReader(part) 65 reader.TrimLeadingSpace = true 66 record, err := reader.Read() 67 if err == io.EOF { 68 break 69 } 70 fi := -1 71 li := -1 72 ei := -1 73 pi := -1 74 fn := "" 75 ln := "" 76 ea := "" 77 ps := "" 78 for i, v := range record { 79 switch { 80 case firstNameRegex.MatchString(v): 81 fi = i 82 case lastNameRegex.MatchString(v): 83 li = i 84 case emailRegex.MatchString(v): 85 ei = i 86 case positionRegex.MatchString(v): 87 pi = i 88 } 89 } 90 if fi == -1 && li == -1 && ei == -1 && pi == -1 { 91 continue 92 } 93 for { 94 record, err := reader.Read() 95 if err == io.EOF { 96 break 97 } 98 if fi != -1 && len(record) > fi { 99 fn = record[fi] 100 } 101 if li != -1 && len(record) > li { 102 ln = record[li] 103 } 104 if ei != -1 && len(record) > ei { 105 csvEmail, err := mail.ParseAddress(record[ei]) 106 if err != nil { 107 continue 108 } 109 ea = csvEmail.Address 110 } 111 if pi != -1 && len(record) > pi { 112 ps = record[pi] 113 } 114 t := models.Target{ 115 BaseRecipient: models.BaseRecipient{ 116 FirstName: fn, 117 LastName: ln, 118 Email: ea, 119 Position: ps, 120 }, 121 } 122 ts = append(ts, t) 123 } 124 } 125 return ts, nil 126 } 127 128 // CheckAndCreateSSL is a helper to setup self-signed certificates for the administrative interface. 129 func CheckAndCreateSSL(cp string, kp string) error { 130 // Check whether there is an existing SSL certificate and/or key, and if so, abort execution of this function 131 if _, err := os.Stat(cp); !os.IsNotExist(err) { 132 return nil 133 } 134 if _, err := os.Stat(kp); !os.IsNotExist(err) { 135 return nil 136 } 137 138 log.Infof("Creating new self-signed certificates for administration interface") 139 140 priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) 141 142 notBefore := time.Now() 143 // Generate a certificate that lasts for 10 years 144 notAfter := notBefore.Add(10 * 365 * 24 * time.Hour) 145 146 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 147 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 148 149 if err != nil { 150 return fmt.Errorf("TLS Certificate Generation: Failed to generate a random serial number: %s", err) 151 } 152 153 template := x509.Certificate{ 154 SerialNumber: serialNumber, 155 Subject: pkix.Name{ 156 Organization: []string{"Gophish"}, 157 }, 158 NotBefore: notBefore, 159 NotAfter: notAfter, 160 161 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 162 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 163 BasicConstraintsValid: true, 164 } 165 166 derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, priv.Public(), priv) 167 if err != nil { 168 return fmt.Errorf("TLS Certificate Generation: Failed to create certificate: %s", err) 169 } 170 171 certOut, err := os.Create(cp) 172 if err != nil { 173 return fmt.Errorf("TLS Certificate Generation: Failed to open %s for writing: %s", cp, err) 174 } 175 pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) 176 certOut.Close() 177 178 keyOut, err := os.OpenFile(kp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 179 if err != nil { 180 return fmt.Errorf("TLS Certificate Generation: Failed to open %s for writing", kp) 181 } 182 183 b, err := x509.MarshalECPrivateKey(priv) 184 if err != nil { 185 return fmt.Errorf("TLS Certificate Generation: Unable to marshal ECDSA private key: %v", err) 186 } 187 188 pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) 189 keyOut.Close() 190 191 log.Info("TLS Certificate Generation complete") 192 return nil 193 } 194 195 // GenerateSecureKey creates a secure key to use as an API key 196 func GenerateSecureKey() string { 197 // Inspired from gorilla/securecookie 198 k := make([]byte, 32) 199 io.ReadFull(rand.Reader, k) 200 return fmt.Sprintf("%x", k) 201 } 202 203 // NewHash hashes the provided password and returns the bcrypt hash (using the 204 // default 10 rounds) as a string. 205 func NewHash(pass string) (string, error) { 206 hash, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost) 207 if err != nil { 208 return "", err 209 } 210 return string(hash), nil 211 }