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  }