github.com/merlinepedra/gopphish-attack@v0.9.0/controllers/api/import.go (about)

     1  package api
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/PuerkitoBio/goquery"
    13  	log "github.com/gophish/gophish/logger"
    14  	"github.com/gophish/gophish/models"
    15  	"github.com/gophish/gophish/util"
    16  	"github.com/jordan-wright/email"
    17  )
    18  
    19  type cloneRequest struct {
    20  	URL              string `json:"url"`
    21  	IncludeResources bool   `json:"include_resources"`
    22  }
    23  
    24  func (cr *cloneRequest) validate() error {
    25  	if cr.URL == "" {
    26  		return errors.New("No URL Specified")
    27  	}
    28  	return nil
    29  }
    30  
    31  type cloneResponse struct {
    32  	HTML string `json:"html"`
    33  }
    34  
    35  type emailResponse struct {
    36  	Text    string `json:"text"`
    37  	HTML    string `json:"html"`
    38  	Subject string `json:"subject"`
    39  }
    40  
    41  // ImportGroup imports a CSV of group members
    42  func (as *Server) ImportGroup(w http.ResponseWriter, r *http.Request) {
    43  	ts, err := util.ParseCSV(r)
    44  	if err != nil {
    45  		JSONResponse(w, models.Response{Success: false, Message: "Error parsing CSV"}, http.StatusInternalServerError)
    46  		return
    47  	}
    48  	JSONResponse(w, ts, http.StatusOK)
    49  	return
    50  }
    51  
    52  // ImportEmail allows for the importing of email.
    53  // Returns a Message object
    54  func (as *Server) ImportEmail(w http.ResponseWriter, r *http.Request) {
    55  	if r.Method != "POST" {
    56  		JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest)
    57  		return
    58  	}
    59  	ir := struct {
    60  		Content      string `json:"content"`
    61  		ConvertLinks bool   `json:"convert_links"`
    62  	}{}
    63  	err := json.NewDecoder(r.Body).Decode(&ir)
    64  	if err != nil {
    65  		JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest)
    66  		return
    67  	}
    68  	e, err := email.NewEmailFromReader(strings.NewReader(ir.Content))
    69  	if err != nil {
    70  		log.Error(err)
    71  	}
    72  	// If the user wants to convert links to point to
    73  	// the landing page, let's make it happen by changing up
    74  	// e.HTML
    75  	if ir.ConvertLinks {
    76  		d, err := goquery.NewDocumentFromReader(bytes.NewReader(e.HTML))
    77  		if err != nil {
    78  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
    79  			return
    80  		}
    81  		d.Find("a").Each(func(i int, a *goquery.Selection) {
    82  			a.SetAttr("href", "{{.URL}}")
    83  		})
    84  		h, err := d.Html()
    85  		if err != nil {
    86  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
    87  			return
    88  		}
    89  		e.HTML = []byte(h)
    90  	}
    91  	er := emailResponse{
    92  		Subject: e.Subject,
    93  		Text:    string(e.Text),
    94  		HTML:    string(e.HTML),
    95  	}
    96  	JSONResponse(w, er, http.StatusOK)
    97  	return
    98  }
    99  
   100  // ImportSite allows for the importing of HTML from a website
   101  // Without "include_resources" set, it will merely place a "base" tag
   102  // so that all resources can be loaded relative to the given URL.
   103  func (as *Server) ImportSite(w http.ResponseWriter, r *http.Request) {
   104  	cr := cloneRequest{}
   105  	if r.Method != "POST" {
   106  		JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest)
   107  		return
   108  	}
   109  	err := json.NewDecoder(r.Body).Decode(&cr)
   110  	if err != nil {
   111  		JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest)
   112  		return
   113  	}
   114  	if err = cr.validate(); err != nil {
   115  		JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   116  		return
   117  	}
   118  	tr := &http.Transport{
   119  		TLSClientConfig: &tls.Config{
   120  			InsecureSkipVerify: true,
   121  		},
   122  	}
   123  	client := &http.Client{Transport: tr}
   124  	resp, err := client.Get(cr.URL)
   125  	if err != nil {
   126  		JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   127  		return
   128  	}
   129  	// Insert the base href tag to better handle relative resources
   130  	d, err := goquery.NewDocumentFromResponse(resp)
   131  	if err != nil {
   132  		JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   133  		return
   134  	}
   135  	// Assuming we don't want to include resources, we'll need a base href
   136  	if d.Find("head base").Length() == 0 {
   137  		d.Find("head").PrependHtml(fmt.Sprintf("<base href=\"%s\">", cr.URL))
   138  	}
   139  	forms := d.Find("form")
   140  	forms.Each(func(i int, f *goquery.Selection) {
   141  		// We'll want to store where we got the form from
   142  		// (the current URL)
   143  		url := f.AttrOr("action", cr.URL)
   144  		if !strings.HasPrefix(url, "http") {
   145  			url = fmt.Sprintf("%s%s", cr.URL, url)
   146  		}
   147  		f.PrependHtml(fmt.Sprintf("<input type=\"hidden\" name=\"__original_url\" value=\"%s\"/>", url))
   148  	})
   149  	h, err := d.Html()
   150  	if err != nil {
   151  		JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   152  		return
   153  	}
   154  	cs := cloneResponse{HTML: h}
   155  	JSONResponse(w, cs, http.StatusOK)
   156  	return
   157  }