github.com/korniux/gophish@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 }