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